e43299a356cb925614cbe003a014a566f0420570
[ctsim.git] / src / threadrecon.cpp
1 /*****************************************************************************
2 ** FILE IDENTIFICATION
3 **
4 **   Name:          threadrecon.cpp
5 **   Purpose:       Threaded reconstruction class
6 **   Programmer:    Kevin Rosenberg
7 **   Date Started:  February 2001
8 **
9 **  This is part of the CTSim program
10 **  Copyright (C) 1983-2001 Kevin Rosenberg
11 **
12 **  $Id: threadrecon.cpp,v 1.2 2001/02/22 15:00:20 kevin Exp $
13 **
14 **  This program is free software; you can redistribute it and/or modify
15 **  it under the terms of the GNU General Public License (version 2) as
16 **  published by the Free Software Foundation.
17 **
18 **  This program is distributed in the hope that it will be useful,
19 **  but WITHOUT ANY WARRANTY; without even the implied warranty of
20 **  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21 **  GNU General Public License for more details.
22 **
23 **  You should have received a copy of the GNU General Public License
24 **  along with this program; if not, write to the Free Software
25 **  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26 ******************************************************************************/
27
28 #include "wx/wxprec.h"
29
30 #ifndef WX_PRECOMP
31 #include "wx/wx.h"
32 #endif
33
34 #include "ct.h"
35 #include "ctsim.h"
36 #include "docs.h"
37 #include "views.h"
38 #include "threadrecon.h"
39 #include "backgroundmgr.h"
40
41 #if defined(HAVE_CONFIG_H)
42 #include "config.h"
43 #endif
44
45
46 IMPLEMENT_DYNAMIC_CLASS(ThreadedReconstructor, BackgroundTask)
47 BEGIN_EVENT_TABLE(ThreadedReconstructor, BackgroundTask)
48 EVT_MENU(RECONSTRUCTION_THREAD_EVENT, ThreadedReconstructor::OnThreadEvent)
49 END_EVENT_TABLE()
50
51
52 enum {
53   RTHREAD_UNIT_COMPLETE = -1,
54   RTHREAD_THREAD_DONE = -2,
55   RTHREAD_THREAD_CANCELLED = -3,
56 };
57
58 ThreadedReconstructor::ThreadedReconstructor (ProjectionFileView* pProjView, 
59    int iImageNX, int iImageNY, const char* pszFilterName, double dFilterParam, const char* pszFilterMethod, 
60    int iZeropad, const char* pszFilterGenerationName, const char* pszInterpName, int iInterpParam,
61    const char* pszBackprojectName, const char* const pszLabel)
62     : m_pProjView(pProjView), m_pDialogProgress(NULL), m_pGauge(NULL), m_bFail(false), m_iNumThreads(0), m_iImageNX(iImageNX), 
63     m_iImageNY(iImageNY), m_strLabel(pszLabel), m_pTimer(NULL), m_bCancelled(false), m_bCancelling(false), 
64     m_bDone(false), BackgroundTask()
65 {
66   m_iNumThreads = theApp->getNumberCPU();
67 //  ++m_iNumThreads;
68   m_iTotalViews = m_pProjView->GetDocument()->getProjections().nView();
69   int iBaseViews = m_iTotalViews / m_iNumThreads;
70   int iExtraViews = m_iTotalViews % m_iNumThreads;
71
72   m_vecpChildImageFile.reserve (m_iNumThreads);
73   m_vecpReconstructor.reserve (m_iNumThreads);
74   m_vecpThread.reserve (m_iNumThreads);
75
76   for (unsigned int iProc = 0; iProc < m_iNumThreads; iProc++) {
77     m_vecpChildImageFile[iProc] = new ImageFile (iImageNX, iImageNY);
78     m_vecpReconstructor[iProc] = new Reconstructor (m_pProjView->GetDocument()->getProjections(), *m_vecpChildImageFile[iProc],
79     pszFilterName, dFilterParam, pszFilterMethod, iZeropad, pszFilterGenerationName, 
80     pszInterpName, iInterpParam, pszBackprojectName, Trace::TRACE_NONE);
81
82     int iStartView = iProc * iBaseViews;
83     int iNumViews = iBaseViews;
84     if (iProc < iExtraViews)
85       ++iNumViews;
86     m_vecpThread[iProc] = new ReconstructionThread (this, m_vecpReconstructor[iProc], iProc, iStartView, iNumViews);
87     if (m_vecpThread[iProc]->Create () != wxTHREAD_NO_ERROR) {
88       m_bFail = true;
89       break;
90     }
91   }
92
93 }
94
95
96 bool
97 ThreadedReconstructor::start()
98 {
99   if (m_bFail)
100     return false;
101
102   m_pProjView->GetDocument()->addReconstructor (this);
103   if (! theApp->getUseBackgroundTasks())
104     m_pDialogProgress = new wxProgressDialog (_T("Filtered Backprojection"), _T("Reconstruction Progress"), m_iTotalViews, m_pProjView->getFrame(), wxPD_CAN_ABORT | wxPD_AUTO_HIDE);
105   else
106     m_pGauge = theApp->getBackgroundManager()->addTask (this, m_iTotalViews);
107
108   m_iRunning = m_iNumThreads;
109   m_iViewsDone = 0;
110   m_pTimer = new Timer;
111
112   // starting all threads
113   for (int i = 0; i < m_iNumThreads; i++)
114     m_vecpThread[i]->Run();
115
116
117   if (m_bCancelled)
118     return false;
119
120   return true;
121 }
122
123 void
124 ThreadedReconstructor::cancel()
125 {
126   if (m_bCancelled)
127     return;
128
129   wxCriticalSectionLocker locker (m_criticalSection);
130
131   for (int i = 0; i < m_iNumThreads; i++) 
132     if (m_vecpThread[i])
133       m_vecpThread[i]->Delete();
134
135   for (i = 0; i < m_iNumThreads; i++)
136     delete m_vecpReconstructor[i];
137
138   m_iNumThreads = 0;
139   m_iRunning = 0;
140   delete m_pDialogProgress;
141   delete m_pTimer;
142   m_pDialogProgress = NULL;
143   m_pProjView->GetDocument()->removeReconstructor (this);
144   m_bCancelled = true;
145   m_bDone = true;
146   theApp->getBackgroundManager()->taskDone (this);
147 }
148
149 void
150 ThreadedReconstructor::onDone()
151 {
152     for (int i = 0; i < m_iNumThreads; i++)
153       delete m_vecpReconstructor[i];
154
155     m_pProjView->GetDocument()->removeReconstructor (this);
156       ImageFileDocument* pReconDoc = theApp->newImageDoc();
157       if (! pReconDoc) {
158         sys_error (ERR_SEVERE, "Unable to create image file");
159         return;
160       }
161       
162       ImageFile* pImageFile = getImageFile();
163       pReconDoc->setImageFile (pImageFile);
164       if (theApp->getAskDeleteNewDocs())
165         pReconDoc->Modify (true);
166       pReconDoc->UpdateAllViews (m_pProjView);
167       if (ImageFileView* rasterView = pReconDoc->getView()) {
168         rasterView->OnUpdate (rasterView, NULL);
169         rasterView->getFrame()->SetFocus();
170         rasterView->getFrame()->Show(true);
171       }
172       *theApp->getLog() << m_strLabel << "\n";
173       pImageFile->labelAdd (m_pProjView->GetDocument()->getProjections().getLabel());
174       pImageFile->labelAdd (m_strLabel.c_str(), m_pTimer->timerEnd());
175       delete m_pTimer;
176
177       wxIdleEvent event;
178       theApp->OnIdle(event);
179       m_bDone = true;
180    theApp->getBackgroundManager()->taskDone (this);
181       // delete this;
182 }
183
184
185 void
186 ThreadedReconstructor::OnThreadEvent (wxCommandEvent& event)
187 {
188   if (m_bCancelling) {
189     cancel();
190     return;
191   }
192
193   wxCriticalSectionLocker locker (m_criticalSection);
194
195   int iEventId = event.GetInt();
196   if (iEventId == RTHREAD_UNIT_COMPLETE) {
197     ++m_iViewsDone;
198     *theApp->getLog() << "Views done: " << static_cast<int>(m_iViewsDone) <<"\n";
199
200     if (m_pDialogProgress)
201       m_bCancelling = ! m_pDialogProgress->Update (m_iViewsDone - 1);
202     else if (m_pGauge) {
203       m_pGauge->SetValue (m_iViewsDone - 1);
204       m_bCancelling = theApp->getBackgroundManager()->isCancelling(this);
205     }
206     if (m_iViewsDone == m_iTotalViews) {
207       delete m_pDialogProgress;
208       m_pDialogProgress = NULL;
209       onDone();
210     }
211     if (m_bCancelling) {
212       cancel();
213       return;
214     }
215   } 
216   else if (event.GetInt() >= 0) {
217     m_iRunning--;
218     m_vecpThread[event.GetInt()] = NULL;
219     *theApp->getLog() << "Thread finished. Remaining threads: " << m_iRunning << "\n";
220   }
221   else
222     *theApp->getLog() << "Got event #" << iEventId << "\n";
223
224 }
225
226 ImageFile*
227 ThreadedReconstructor::getImageFile() const
228 {
229   ImageFile* pImageFile = new ImageFile (m_iImageNX, m_iImageNY);
230   pImageFile->arrayDataClear();
231   ImageFileArray pArray = pImageFile->getArray();
232  
233   int i;
234   for (i = 0; i < m_iNumThreads; i++) {
235     ImageFileArrayConst pChildArray = m_vecpChildImageFile[i]->getArray();
236     for (int ix = 0; ix < m_iImageNX; ix++)
237       for (int iy = 0; iy < m_iImageNY; iy++)
238         pArray[ix][iy] += pChildArray[ix][iy];
239   }
240
241   for (i = 0; i < m_iNumThreads; i++) {
242     delete m_vecpChildImageFile[i];
243 //    m_vecpChildImageFile[i] = NULL;
244   }
245
246   return (pImageFile);
247 }
248
249 bool
250 ThreadedReconstructor::testDone()
251 {
252     return (m_iRunning <= 0 ? true : false);
253 }
254
255
256 ThreadedReconstructor::~ThreadedReconstructor()
257 {
258 }
259
260
261 ReconstructionThread::ReconstructionThread (ThreadedReconstructor* pSupervisor, 
262                          Reconstructor* pReconstructor, int iThread, int iStartView, int iNumViews)
263 : m_pSupervisor(pSupervisor), m_pReconstructor(pReconstructor), 
264   m_iStartView(iStartView), m_iNumViews(iNumViews), m_iThread(iThread), 
265   wxThread(wxTHREAD_DETACHED)
266 {
267 }
268
269 wxThread::ExitCode
270 ReconstructionThread::Entry ()
271 {
272    wxCommandEvent eventProgress (wxEVT_COMMAND_MENU_SELECTED, RECONSTRUCTION_THREAD_EVENT);
273    for (int iView = 0; iView < m_iNumViews; iView++) {
274      if (TestDestroy()) {
275       wxString msg;
276       msg.Printf("TestDestroy TRUE at view #%d\n", iView);  
277       wxCommandEvent event( wxEVT_COMMAND_MENU_SELECTED, MAINMENU_LOG_EVENT );
278       event.SetString( msg );
279       wxPostEvent( theApp->getMainFrame(), event ); // send in a thread-safe way
280       return reinterpret_cast<wxThread::ExitCode>(RTHREAD_THREAD_CANCELLED);
281      }
282      m_pReconstructor->reconstructView (iView + m_iStartView, 1);
283      eventProgress.SetInt (RTHREAD_UNIT_COMPLETE);
284      wxPostEvent (m_pSupervisor, eventProgress);
285    }
286
287    eventProgress.SetInt (m_iThread); // Send back thread# that has finished
288    wxPostEvent (m_pSupervisor, eventProgress);
289
290    return reinterpret_cast<wxThread::ExitCode>(0);
291 }
292
293 void
294 ReconstructionThread::OnExit ()
295 {
296 }