r575: no message
[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.5 2001/02/23 03:28:26 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                                               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_vecpThread.reserve (m_iNumThreads);
74   
75   for (unsigned int iProc = 0; iProc < m_iNumThreads; iProc++) {
76     m_vecpChildImageFile[iProc] = new ImageFile (iImageNX, iImageNY);
77     
78     int iStartView = iProc * iBaseViews;
79     int iNumViews = iBaseViews;
80     if (iProc < iExtraViews)
81       ++iNumViews;
82       m_vecpThread[iProc] = new ReconstructionThread (this, pProjView, m_vecpChildImageFile[iProc], iProc, iStartView, iNumViews,
83       pszFilterName, dFilterParam, pszFilterMethod, iZeropad, pszFilterGenerationName, pszInterpName, iInterpParam, pszBackprojectName);
84     if (m_vecpThread[iProc]->Create () != wxTHREAD_NO_ERROR) {
85       m_bFail = true;
86       break;
87     }
88   }
89   
90 }
91
92
93 bool
94 ThreadedReconstructor::start()
95 {
96   if (m_bFail)
97     return false;
98   
99   m_pProjView->GetDocument()->addReconstructor (this);
100   if (! theApp->getUseBackgroundTasks())
101     m_pDialogProgress = new wxProgressDialog (_T("Filtered Backprojection"), _T("Reconstruction Progress"), m_iTotalViews, m_pProjView->getFrame(), wxPD_CAN_ABORT | wxPD_AUTO_HIDE);
102   else {
103     std::string strLabel ("Reconstructing ");
104     strLabel += m_pProjView->GetFrame()->GetTitle();
105     m_pGauge = theApp->getBackgroundManager()->addTask (this, m_iTotalViews, strLabel.c_str());
106   }
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   if (m_bCancelled)
117     return false;
118   
119   return true;
120 }
121
122 void
123 ThreadedReconstructor::cancel()
124 {
125   if (isDone() || m_bCancelled)
126     return;
127   m_bCancelled = true;
128   m_bCancelling = false;
129   cleanUp();
130 }
131
132 void
133 ThreadedReconstructor::cleanUp()
134 {
135   wxCriticalSection cleanSection;
136   cleanSection.Enter();
137   
138   for (int i = 0; i < m_iNumThreads; i++) 
139     if (m_vecpThread[i] && m_vecpThread[i]->IsRunning()) {
140       m_vecpThread[i]->Pause();
141       m_vecpThread[i]->Delete();
142     }
143     
144     for (i = 0; i < m_iNumThreads; i++) {
145       delete m_vecpChildImageFile[i];
146       m_vecpChildImageFile[i] = NULL;
147     }    
148     
149     m_iNumThreads = 0;
150     m_iRunning = 0;
151     delete m_pDialogProgress;
152     delete m_pTimer;
153     m_pDialogProgress = NULL;
154     m_pGauge = NULL;
155     setDone();
156     m_pProjView->GetDocument()->removeReconstructor (this);
157     theApp->getBackgroundManager()->taskDone (this);
158     
159     cleanSection.Leave();
160 }
161
162 void
163 ThreadedReconstructor::onDone()
164 {
165   wxCriticalSection doneSection;
166   doneSection.Enter();
167   
168   m_pProjView->GetDocument()->removeReconstructor (this);
169   ImageFileDocument* pReconDoc = theApp->newImageDoc();
170   if (! pReconDoc) {
171     sys_error (ERR_SEVERE, "Unable to create image file");
172     doneSection.Leave();
173     return;
174   }
175     
176   ImageFile* pImageFile = getImageFile();
177   pReconDoc->setImageFile (pImageFile);
178   if (theApp->getAskDeleteNewDocs())
179     pReconDoc->Modify (true);
180   pReconDoc->UpdateAllViews (m_pProjView);
181   if (ImageFileView* rasterView = pReconDoc->getView()) {
182     rasterView->OnUpdate (rasterView, NULL);
183     rasterView->getFrame()->SetFocus();
184     rasterView->getFrame()->Show(true);
185   }
186   *theApp->getLog() << m_strLabel << "\n";
187   pImageFile->labelAdd (m_pProjView->GetDocument()->getProjections().getLabel());
188   pImageFile->labelAdd (m_strLabel.c_str(), m_pTimer->timerEnd());
189   
190   doneSection.Leave();
191   cleanUp();
192 }
193
194
195 void
196 ThreadedReconstructor::OnThreadEvent (wxCommandEvent& event)
197 {
198   if (isDone())
199     return;
200   wxCriticalSection eventSection;
201   eventSection.Enter();
202   if (isDone()) {
203     eventSection.Leave();
204     return;
205   }
206   
207   int iEventId = event.GetInt();
208   if (iEventId == RTHREAD_UNIT_COMPLETE) {
209     ++m_iViewsDone;
210     
211 #ifdef DEBUG
212     if (theApp->getVerboseLogging())
213       *theApp->getLog() << "Views done: " << static_cast<int>(m_iViewsDone) <<"\n";
214 #endif
215     
216     if (m_pDialogProgress)
217       m_bCancelling = ! m_pDialogProgress->Update (m_iViewsDone - 1);
218     else if (m_pGauge) {
219       m_pGauge->SetValue (m_iViewsDone - 1); 
220       eventSection.Leave();
221       m_bCancelling = theApp->getBackgroundManager()->isCancelling(this);
222     }
223     if (! isDone() && m_bCancelling) {
224       eventSection.Leave();
225       cancel();
226       return;
227     }
228   } 
229   else if (event.GetInt() >= 0) {
230     m_iRunning--;
231     m_vecpThread[event.GetInt()] = NULL;
232     *theApp->getLog() << "Thread finished. Remaining threads: " << m_iRunning << "\n";
233     if (m_iRunning <= 0) {
234       eventSection.Leave();
235       onDone();
236       return;
237     }
238   }
239   else
240     *theApp->getLog() << "Got event #" << iEventId << "\n";
241   
242   eventSection.Leave();
243 }
244
245 ImageFile*
246 ThreadedReconstructor::getImageFile()
247 {
248   ImageFile* pImageFile = new ImageFile (m_iImageNX, m_iImageNY);
249   pImageFile->arrayDataClear();
250   ImageFileArray pArray = pImageFile->getArray();
251   
252   int i;
253   for (i = 0; i < m_iNumThreads; i++) {
254     ImageFileArrayConst pChildArray = m_vecpChildImageFile[i]->getArray();
255     for (int ix = 0; ix < m_iImageNX; ix++)
256       for (int iy = 0; iy < m_iImageNY; iy++)
257         pArray[ix][iy] += pChildArray[ix][iy];
258   }
259   
260   return (pImageFile);
261 }
262
263 bool
264 ThreadedReconstructor::testDone()
265 {
266   return (m_iRunning <= 0 ? true : false);
267 }
268
269
270 ThreadedReconstructor::~ThreadedReconstructor()
271 {
272 }
273
274
275 ReconstructionThread::ReconstructionThread 
276 (ThreadedReconstructor* pSupervisor, ProjectionFileView* pProjView, ImageFile* pImageFile, int iThread, int iStartView, int iNumViews, 
277                                             const char* pszFilterName, double dFilterParam, const char* pszFilterMethod, 
278                                             int iZeropad, const char* pszFilterGenerationName, const char* pszInterpName, int iInterpParam,
279                                             const char* pszBackprojectName)
280                                             : m_pSupervisor(pSupervisor), 
281                                             m_iStartView(iStartView), m_iNumViews(iNumViews), m_iThread(iThread), 
282                                             wxThread(wxTHREAD_DETACHED)
283 {
284   m_pReconstructor = new Reconstructor (pProjView->GetDocument()->getProjections(), 
285     *pImageFile, pszFilterName, dFilterParam, pszFilterMethod, iZeropad, pszFilterGenerationName, 
286     pszInterpName, iInterpParam, pszBackprojectName, Trace::TRACE_NONE);
287 }
288
289 wxThread::ExitCode
290 ReconstructionThread::Entry ()
291 {
292   wxCommandEvent eventProgress (wxEVT_COMMAND_MENU_SELECTED, RECONSTRUCTION_THREAD_EVENT);
293
294   for (int iView = 0; iView < m_iNumViews; iView++) {
295     if (TestDestroy()) {
296       wxString msg;
297       msg.Printf("TestDestroy TRUE at view #%d\n", iView);  
298       wxCommandEvent event( wxEVT_COMMAND_MENU_SELECTED, MAINMENU_LOG_EVENT );
299       event.SetString( msg );
300       wxPostEvent( theApp->getMainFrame(), event ); // send in a thread-safe way
301       return reinterpret_cast<wxThread::ExitCode>(RTHREAD_THREAD_CANCELLED);
302     }
303     m_pReconstructor->reconstructView (iView + m_iStartView, 1);
304     eventProgress.SetInt (RTHREAD_UNIT_COMPLETE);
305     wxPostEvent (m_pSupervisor, eventProgress);
306   }
307   m_pReconstructor->postProcessing();
308   
309   eventProgress.SetInt (m_iThread); // Send back thread# that has finished
310   wxPostEvent (m_pSupervisor, eventProgress);
311   
312   return reinterpret_cast<wxThread::ExitCode>(0);
313 }
314
315 void
316 ReconstructionThread::OnExit ()
317 {
318    delete m_pReconstructor;
319 }