+/*****************************************************************************
+** FILE IDENTIFICATION
+**
+** Name: threadrecon.cpp
+** Purpose: Threaded reconstruction class
+** Programmer: Kevin Rosenberg
+** Date Started: February 2001
+**
+** This is part of the CTSim program
+** Copyright (C) 1983-2001 Kevin Rosenberg
+**
+** $Id: threadrecon.cpp,v 1.1 2001/02/22 11:05:38 kevin Exp $
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License (version 2) as
+** published by the Free Software Foundation.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+******************************************************************************/
+
+#include "wx/wxprec.h"
+
+#ifndef WX_PRECOMP
+#include "wx/wx.h"
+#endif
+
+#include "ct.h"
+#include "ctsim.h"
+#include "docs.h"
+#include "views.h"
+#include "threadrecon.h"
+
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
+
+
+IMPLEMENT_DYNAMIC_CLASS(ThreadedReconstructor, wxEvtHandler)
+BEGIN_EVENT_TABLE(ThreadedReconstructor, wxEvtHandler)
+EVT_MENU(RECONSTRUCTION_THREAD_EVENT, ThreadedReconstructor::OnThreadEvent)
+END_EVENT_TABLE()
+
+
+enum {
+ RTHREAD_UNIT_COMPLETE = -1,
+ RTHREAD_THREAD_DONE = -2,
+ RTHREAD_THREAD_CANCELLED = -3,
+};
+
+ThreadedReconstructor::ThreadedReconstructor (ProjectionFileView* pProjView,
+ int iImageNX, int iImageNY, const char* pszFilterName, double dFilterParam, const char* pszFilterMethod,
+ int iZeropad, const char* pszFilterGenerationName, const char* pszInterpName, int iInterpParam,
+ const char* pszBackprojectName, const char* const pszLabel)
+ : m_pProjView(pProjView), m_pDialogProgress(NULL), m_bFail(false), m_iNumThreads(0), m_iImageNX(iImageNX),
+ m_iImageNY(iImageNY), m_strLabel(pszLabel), m_pTimer(NULL), m_bCancelled(false), m_bCancelling(false),
+ m_bDone(false), wxEvtHandler()
+{
+ m_iNumThreads = theApp->getNumberCPU();
+// ++m_iNumThreads;
+ m_iTotalViews = m_pProjView->GetDocument()->getProjections().nView();
+ int iBaseViews = m_iTotalViews / m_iNumThreads;
+ int iExtraViews = m_iTotalViews % m_iNumThreads;
+
+ m_vecpChildImageFile.reserve (m_iNumThreads);
+ m_vecpReconstructor.reserve (m_iNumThreads);
+ m_vecpThread.reserve (m_iNumThreads);
+
+ for (unsigned int iProc = 0; iProc < m_iNumThreads; iProc++) {
+ m_vecpChildImageFile[iProc] = new ImageFile (iImageNX, iImageNY);
+ m_vecpReconstructor[iProc] = new Reconstructor (m_pProjView->GetDocument()->getProjections(), *m_vecpChildImageFile[iProc],
+ pszFilterName, dFilterParam, pszFilterMethod, iZeropad, pszFilterGenerationName,
+ pszInterpName, iInterpParam, pszBackprojectName, Trace::TRACE_NONE);
+
+ int iStartView = iProc * iBaseViews;
+ int iNumViews = iBaseViews;
+ if (iProc < iExtraViews)
+ ++iNumViews;
+ m_vecpThread[iProc] = new ReconstructionThread (this, m_vecpReconstructor[iProc], iProc, iStartView, iNumViews);
+ if (m_vecpThread[iProc]->Create () != wxTHREAD_NO_ERROR) {
+ m_bFail = true;
+ break;
+ }
+ }
+
+ m_pProjView->GetDocument()->addReconstructor (this);
+ // m_pDialogProgress = new wxProgressDialog (_T("Filtered Backprojection"), _T("Reconstruction Progress"), m_iTotalViews, pProjView->getFrame(), wxPD_CAN_ABORT | wxPD_AUTO_HIDE);
+}
+
+
+bool
+ThreadedReconstructor::start()
+{
+ if (m_bFail)
+ return false;
+
+ // starting all threads
+ m_iRunning = m_iNumThreads;
+ m_iViewsDone = 0;
+ m_pTimer = new Timer;
+// theApp->addBackgroundTask (this, m_iTotalViews);
+
+ int i;
+ for (i = 0; i < m_iNumThreads; i++)
+ m_vecpThread[i]->Run();
+
+
+ if (m_bCancelled)
+ return false;
+
+ return true;
+}
+
+void
+ThreadedReconstructor::cancel()
+{
+ if (m_bCancelled)
+ return;
+
+ wxCriticalSectionLocker locker (m_criticalSection);
+
+ for (int i = 0; i < m_iNumThreads; i++)
+ if (m_vecpThread[i])
+ m_vecpThread[i]->Delete();
+
+ for (i = 0; i < m_iNumThreads; i++)
+ delete m_vecpReconstructor[i];
+
+ m_iNumThreads = 0;
+ m_iRunning = 0;
+ delete m_pDialogProgress;
+ delete m_pTimer;
+ m_pDialogProgress = NULL;
+ m_pProjView->GetDocument()->removeReconstructor (this);
+ m_bCancelled = true;
+ m_bDone = true;
+ // theApp->removeBackgroundTask (this);
+}
+
+void
+ThreadedReconstructor::onDone()
+{
+ for (int i = 0; i < m_iNumThreads; i++)
+ delete m_vecpReconstructor[i];
+
+ m_pProjView->GetDocument()->removeReconstructor (this);
+ ImageFileDocument* pReconDoc = theApp->newImageDoc();
+ if (! pReconDoc) {
+ sys_error (ERR_SEVERE, "Unable to create image file");
+ return;
+ }
+
+ ImageFile* pImageFile = getImageFile();
+ pReconDoc->setImageFile (pImageFile);
+ if (theApp->getAskDeleteNewDocs())
+ pReconDoc->Modify (true);
+ pReconDoc->UpdateAllViews (m_pProjView);
+ if (ImageFileView* rasterView = pReconDoc->getView()) {
+ rasterView->OnUpdate (rasterView, NULL);
+ rasterView->getFrame()->SetFocus();
+ rasterView->getFrame()->Show(true);
+ }
+ *theApp->getLog() << m_strLabel << "\n";
+ pImageFile->labelAdd (m_pProjView->GetDocument()->getProjections().getLabel());
+ pImageFile->labelAdd (m_strLabel.c_str(), m_pTimer->timerEnd());
+ delete m_pTimer;
+
+ wxIdleEvent event;
+ theApp->OnIdle(event);
+ m_bDone = true;
+ // theApp->removeBackgroundTask (this);
+ // delete this;
+}
+
+
+void
+ThreadedReconstructor::OnThreadEvent (wxCommandEvent& event)
+{
+ if (m_bCancelling) {
+ cancel();
+ return;
+ }
+
+ wxCriticalSectionLocker locker (m_criticalSection);
+
+ int iEventId = event.GetInt();
+ if (iEventId == RTHREAD_UNIT_COMPLETE) {
+ ++m_iViewsDone;
+ *theApp->getLog() << "Views done: " << static_cast<int>(m_iViewsDone) <<"\n";
+
+ if (m_pDialogProgress)
+ m_bCancelling = ! m_pDialogProgress->Update (m_iViewsDone - 1);
+
+ // m_bCancelling = theApp->updateBackgroundTask (this, m_iViewsDone);
+ if (m_iViewsDone == m_iTotalViews) {
+ delete m_pDialogProgress;
+ m_pDialogProgress = NULL;
+ onDone();
+ }
+ if (m_bCancelling) {
+ cancel();
+ return;
+ }
+ }
+ else if (event.GetInt() >= 0) {
+ m_iRunning--;
+ m_vecpThread[event.GetInt()] = NULL;
+ *theApp->getLog() << "Thread finished. Remaining threads: " << m_iRunning << "\n";
+ }
+ else
+ *theApp->getLog() << "Got event #" << iEventId << "\n";
+
+}
+
+ImageFile*
+ThreadedReconstructor::getImageFile() const
+{
+ ImageFile* pImageFile = new ImageFile (m_iImageNX, m_iImageNY);
+ pImageFile->arrayDataClear();
+ ImageFileArray pArray = pImageFile->getArray();
+
+ int i;
+ for (i = 0; i < m_iNumThreads; i++) {
+ ImageFileArrayConst pChildArray = m_vecpChildImageFile[i]->getArray();
+ for (int ix = 0; ix < m_iImageNX; ix++)
+ for (int iy = 0; iy < m_iImageNY; iy++)
+ pArray[ix][iy] += pChildArray[ix][iy];
+ }
+
+ for (i = 0; i < m_iNumThreads; i++) {
+ delete m_vecpChildImageFile[i];
+// m_vecpChildImageFile[i] = NULL;
+ }
+
+ return (pImageFile);
+}
+
+bool
+ThreadedReconstructor::testDone()
+{
+ return (m_iRunning <= 0 ? true : false);
+}
+
+
+ThreadedReconstructor::~ThreadedReconstructor()
+{
+}
+
+
+ReconstructionThread::ReconstructionThread (ThreadedReconstructor* pSupervisor,
+ Reconstructor* pReconstructor, int iThread, int iStartView, int iNumViews)
+: m_pSupervisor(pSupervisor), m_pReconstructor(pReconstructor),
+ m_iStartView(iStartView), m_iNumViews(iNumViews), m_iThread(iThread),
+ wxThread(wxTHREAD_DETACHED)
+{
+}
+
+wxThread::ExitCode
+ReconstructionThread::Entry ()
+{
+ wxCommandEvent eventProgress (wxEVT_COMMAND_MENU_SELECTED, RECONSTRUCTION_THREAD_EVENT);
+ for (int iView = 0; iView < m_iNumViews; iView++) {
+ if (TestDestroy()) {
+ wxString msg;
+ msg.Printf("TestDestroy TRUE at view #%d\n", iView);
+ wxCommandEvent event( wxEVT_COMMAND_MENU_SELECTED, MAINMENU_LOG_EVENT );
+ event.SetString( msg );
+ wxPostEvent( theApp->getMainFrame(), event ); // send in a thread-safe way
+ return reinterpret_cast<wxThread::ExitCode>(RTHREAD_THREAD_CANCELLED);
+ }
+ m_pReconstructor->reconstructView (iView + m_iStartView, 1);
+ eventProgress.SetInt (RTHREAD_UNIT_COMPLETE);
+ wxPostEvent (m_pSupervisor, eventProgress);
+ }
+
+ eventProgress.SetInt (m_iThread); // Send back thread# that has finished
+ wxPostEvent (m_pSupervisor, eventProgress);
+
+ return reinterpret_cast<wxThread::ExitCode>(0);
+}
+
+void
+ReconstructionThread::OnExit ()
+{
+}