r311: image comparison functions
[ctsim.git] / src / views.cpp
1 /*****************************************************************************
2 ** FILE IDENTIFICATION
3 **
4 **   Name:          view.cpp
5 **   Purpose:       View & Canvas routines for CTSim program
6 **   Programmer:    Kevin Rosenberg
7 **   Date Started:  July 2000
8 **
9 **  This is part of the CTSim program
10 **  Copyright (C) 1983-2000 Kevin Rosenberg
11 **
12 **  $Id: views.cpp,v 1.36 2000/12/22 04:18:00 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 // For compilers that support precompilation, includes "wx/wx.h".
29 #include "wx/wxprec.h"
30
31 #ifdef __BORLANDC__
32 #pragma hdrstop
33 #endif
34
35 #ifndef WX_PRECOMP
36 #include "wx/wx.h"
37 #endif
38
39 #if !wxUSE_DOC_VIEW_ARCHITECTURE
40 #error You must set wxUSE_DOC_VIEW_ARCHITECTURE to 1 in setup.h!
41 #endif
42
43 #include "wx/image.h"
44 #include "wx/progdlg.h"
45
46 #include "ct.h"
47 #include "ctsim.h"
48 #include "docs.h"
49 #include "views.h"
50 #include "dialogs.h"
51 #include "dlgprojections.h"
52 #include "dlgreconstruct.h"
53 #include "backprojectors.h"
54 #include "reconstruct.h"
55 #include "timer.h"
56 \r
57 #if defined(MSVC) || HAVE_SSTREAM\r
58 #include <sstream>\r
59 #else\r
60 #include <sstream_subst>\r
61 #endif\r
62 \r
63
64 // ImageFileCanvas
65
66 BEGIN_EVENT_TABLE(ImageFileCanvas, wxScrolledWindow)
67 EVT_MOUSE_EVENTS(ImageFileCanvas::OnMouseEvent)
68 END_EVENT_TABLE()
69
70
71 ImageFileCanvas::ImageFileCanvas (ImageFileView* v, wxFrame *frame, const wxPoint& pos, const wxSize& size, const long style)
72 : wxScrolledWindow(frame, -1, pos, size, style)
73 {
74   m_pView = v;\r
75   m_xCursor = -1;\r
76   m_yCursor = -1;
77 }
78
79 void 
80 ImageFileCanvas::OnDraw(wxDC& dc)
81 {
82   if (m_pView)
83     m_pView->OnDraw(& dc);
84 }\r
85 \r
86 void \r
87 ImageFileCanvas::DrawRubberBandCursor (wxDC& dc, int x, int y)\r
88 {\r
89   const ImageFile& rIF = m_pView->GetDocument()->getImageFile();\r
90   ImageFileArrayConst v = rIF.getArray();\r
91   int nx = rIF.nx();\r
92   int ny = rIF.ny();\r
93   \r
94   dc.SetLogicalFunction (wxINVERT);\r
95   dc.SetPen (*wxGREEN_PEN);\r
96   dc.DrawLine (0, y, nx, y);\r
97   dc.DrawLine (x, 0, x, ny);\r
98   dc.SetLogicalFunction (wxCOPY);\r
99 }\r
100 \r
101 bool\r
102 ImageFileCanvas::GetCurrentCursor (int& x, int& y)\r
103 {\r
104   x = m_xCursor;\r
105   y = m_yCursor;\r
106   \r
107   if (m_xCursor >= 0 && m_yCursor >= 0)\r
108     return true;\r
109   else\r
110     return false;\r
111 }
112 \r
113 void 
114 ImageFileCanvas::OnMouseEvent(wxMouseEvent& event)
115 {
116   if (! m_pView)
117     return;
118   
119   wxClientDC dc(this);
120   PrepareDC(dc);
121   
122   wxPoint pt(event.GetLogicalPosition(dc));
123   \r
124   if (event.RightIsDown()) {\r
125     const ImageFile& rIF = m_pView->GetDocument()->getImageFile();\r
126     ImageFileArrayConst v = rIF.getArray();\r
127     int nx = rIF.nx();\r
128     int ny = rIF.ny();\r
129     \r
130     if (pt.x >= 0 && pt.x < nx && pt.y >= 0 && pt.y < ny) {\r
131       std::ostringstream os;\r
132       os << "Image value (" << pt.x << "," << pt.y << ") = " << v[pt.x][ny - 1 - pt.y] << "\n";\r
133       *theApp->getLog() << os.str().c_str();\r
134     } else\r
135       *theApp->getLog() << "Mouse out of image range (" << pt.x << "," << pt.y << ")\n";\r
136   }\r
137   else if (event.LeftIsDown() || event.LeftUp()) {\r
138     const ImageFile& rIF = m_pView->GetDocument()->getImageFile();\r
139     ImageFileArrayConst v = rIF.getArray();\r
140     int nx = rIF.nx();\r
141     int ny = rIF.ny();\r
142     \r
143     if (pt.x >= 0 && pt.x < nx && pt.y >= 0 && pt.y < ny) {\r
144       if (m_xCursor >= 0 && m_yCursor >= 0) {\r
145         DrawRubberBandCursor (dc, m_xCursor, m_yCursor);\r
146       }\r
147       DrawRubberBandCursor (dc, pt.x, pt.y);\r
148       m_xCursor = pt.x;\r
149       m_yCursor = pt.y;\r
150     } else\r
151       *theApp->getLog() << "Mouse out of image range (" << pt.x << "," << pt.y << ")\n";\r
152   }\r
153   if (event.LeftUp()) {\r
154     std::ostringstream os;\r
155     os << "Selected column" << pt.x << " and row" << pt.y << "\n";\r
156     *theApp->getLog() << os.str().c_str();\r
157   }\r
158 }
159
160 // ImageFileView
161
162 IMPLEMENT_DYNAMIC_CLASS(ImageFileView, wxView)
163
164 BEGIN_EVENT_TABLE(ImageFileView, wxView)
165 EVT_MENU(IFMENU_FILE_PROPERTIES, ImageFileView::OnProperties)
166 EVT_MENU(IFMENU_VIEW_SCALE_MINMAX, ImageFileView::OnScaleMinMax)
167 EVT_MENU(IFMENU_VIEW_SCALE_AUTO, ImageFileView::OnScaleAuto)\r
168 EVT_MENU(IFMENU_COMPARE_IMAGES, ImageFileView::OnCompare)\r
169 EVT_MENU(IFMENU_COMPARE_ROW, ImageFileView::OnCompareRow)\r
170 EVT_MENU(IFMENU_COMPARE_COL, ImageFileView::OnCompareCol)\r
171 EVT_MENU(IFMENU_PLOT_ROW, ImageFileView::OnPlotRow)\r
172 EVT_MENU(IFMENU_PLOT_COL, ImageFileView::OnPlotCol)\r
173 END_EVENT_TABLE()
174
175 ImageFileView::ImageFileView(void) 
176 : wxView(), m_canvas(NULL), m_frame(NULL), m_bMinSpecified(false), m_bMaxSpecified(false)
177 {
178 }
179
180 ImageFileView::~ImageFileView(void)
181 {
182 }
183
184 void
185 ImageFileView::OnProperties (wxCommandEvent& event)
186 {
187   double min, max, mean, mode, median, stddev;
188   const ImageFile& rIF = GetDocument()->getImageFile();
189   if (rIF.nx() == 0 || rIF.ny() == 0)
190     *theApp->getLog() << "Properties: empty imagefile\n";
191   else {
192     const std::string& rFilename = rIF.getFilename();
193     rIF.statistics (min, max, mean, mode, median, stddev);
194     std::ostringstream os;
195     os << "file: " << rFilename << "\nmin: "<<min<<"\nmax: "<<max<<"\nmean: "<<mean<<"\nmedian: "<<median<<"\nmode: "<<mode<<"\nstddev: "<<stddev << "\n";
196     *theApp->getLog() << os.str().c_str();
197     wxMessageDialog dialogMsg (m_frame, os.str().c_str(), "Imagefile Properties", wxOK | wxICON_INFORMATION);
198     dialogMsg.ShowModal();
199   }
200 }
201
202 void 
203 ImageFileView::OnScaleAuto (wxCommandEvent& event)
204 {
205   const ImageFile& rIF = GetDocument()->getImageFile();
206   double min, max, mean, mode, median, stddev;\r
207   rIF.statistics(min, max, mean, mode, median, stddev);\r
208   DialogAutoScaleParameters dialogAutoScale (m_frame, mean, mode, median, stddev, m_dAutoScaleFactor);
209   int iRetVal = dialogAutoScale.ShowModal();
210   if (iRetVal == wxID_OK) {
211     m_bMinSpecified = true;
212     m_bMaxSpecified = true;
213     double dMin, dMax;\r
214     if (dialogAutoScale.getMinMax (&dMin, &dMax)) {
215       m_dMinPixel = dMin;
216       m_dMaxPixel = dMax;
217       m_dAutoScaleFactor = dialogAutoScale.getAutoScaleFactor();
218       OnUpdate (this, NULL);\r
219     }
220   }
221 }
222
223 void 
224 ImageFileView::OnScaleMinMax (wxCommandEvent& event)
225 {
226   const ImageFile& rIF = GetDocument()->getImageFile();
227   double min, max;
228   if (! m_bMinSpecified && ! m_bMaxSpecified)
229     rIF.getMinMax (min, max);
230   
231   if (m_bMinSpecified)
232     min = m_dMinPixel;
233   if (m_bMaxSpecified)
234     max = m_dMaxPixel;
235   
236   DialogGetMinMax dialogMinMax (m_frame, "Set Image Minimum & Maximum", min, max);
237   int retVal = dialogMinMax.ShowModal();
238   if (retVal == wxID_OK) {
239     m_bMinSpecified = true;
240     m_bMaxSpecified = true;
241     m_dMinPixel = dialogMinMax.getMinimum();
242     m_dMaxPixel = dialogMinMax.getMaximum();
243     OnUpdate (this, NULL);
244   }
245 }
246 \r
247 void\r
248 ImageFileView::OnCompare (wxCommandEvent& event)\r
249 {\r
250   std::vector<ImageFileDocument*> vecIF;\r
251   theApp->getCompatibleImages (GetDocument(), vecIF);\r
252 \r
253   if (vecIF.size() == 0) {\r
254     wxMessageBox("There are no compatible image files open for comparision", "No comparison images");\r
255   } else {\r
256     DialogGetComparisonImage dialogGetCompare(m_frame, "Get Comparison Image", vecIF, true);\r
257 \r
258     if (dialogGetCompare.ShowModal() == wxID_OK) {\r
259       ImageFileDocument* pCompareDoc = dialogGetCompare.getImageFileDocument();\r
260       const ImageFile& rIF = GetDocument()->getImageFile();\r
261       const ImageFile& rCompareIF = pCompareDoc->getImageFile();\r
262       std::ostringstream os;\r
263       double min, max, mean, mode, median, stddev;\r
264       rIF.statistics (min, max, mean, mode, median, stddev);\r
265       os << rIF.getFilename() << ": minimum=" << min << ", maximum=" << max << ", mean=" << mean << ", mode=" << mode << ", median=" << median << ", stddev=" << stddev << "\n";\r
266       rCompareIF.statistics (min, max, mean, mode, median, stddev);\r
267       os << rCompareIF.getFilename() << ": minimum=" << min << ", maximum=" << max << ", mean=" << mean << ", mode=" << mode << ", median=" << median << ", stddev=" << stddev << "\n";\r
268       os << "\n";\r
269       double d, r, e;\r
270       rIF.comparativeStatistics (rCompareIF, d, r, e);\r
271       os << "Comparative Statistics: d=" << d << ", r=" << r << ", e=" << e << "\n";\r
272       *theApp->getLog() << os.str().c_str();\r
273       if (dialogGetCompare.getMakeDifferenceImage()) {\r
274         ImageFileDocument* pDifferenceDoc = dynamic_cast<ImageFileDocument*>(theApp->getDocManager()->CreateDocument("untitled.if", wxDOC_SILENT));\r
275         if (! pDifferenceDoc) {\r
276           sys_error (ERR_SEVERE, "Unable to create image file");\r
277           return;\r
278         }\r
279         ImageFile& differenceImage = pDifferenceDoc->getImageFile();\r
280       \r
281         differenceImage.setArraySize (rIF.nx(), rIF.ny());\r
282         if (! rIF.subtractImages (rCompareIF, differenceImage)) {\r
283           pDifferenceDoc->DeleteAllViews();\r
284           return;\r
285         }\r
286 \r
287         pDifferenceDoc->Modify(true);\r
288         pDifferenceDoc->UpdateAllViews(this);\r
289         pDifferenceDoc->GetFirstView()->OnUpdate (this, NULL);\r
290       }\r
291       wxMessageBox(os.str().c_str(), "Image Comparison");\r
292     }\r
293   }\r
294 }
295
296 ImageFileCanvas* 
297 ImageFileView::CreateCanvas (wxView *view, wxFrame *parent)
298 {
299   ImageFileCanvas* pCanvas;
300   int width, height;
301   parent->GetClientSize(&width, &height);
302   
303   pCanvas = new ImageFileCanvas (dynamic_cast<ImageFileView*>(view), parent, wxPoint(0, 0), wxSize(width, height), 0);
304   
305   pCanvas->SetScrollbars(20, 20, 50, 50);
306   pCanvas->SetBackgroundColour(*wxWHITE);
307   pCanvas->Clear();
308   
309   return pCanvas;
310 }
311
312 wxFrame*
313 ImageFileView::CreateChildFrame(wxDocument *doc, wxView *view)
314 {
315   wxDocChildFrame *subframe = new wxDocChildFrame(doc, view, theApp->getMainFrame(), -1, "ImageFile Frame", wxPoint(-1, -1), wxSize(0, 0), wxDEFAULT_FRAME_STYLE);
316   
317   wxMenu *file_menu = new wxMenu;
318   
319   file_menu->Append(MAINMENU_FILE_CREATE_PHANTOM, "Cr&eate Phantom...");
320   file_menu->Append(wxID_OPEN, "&Open...");
321   file_menu->Append(wxID_SAVE, "&Save");
322   file_menu->Append(wxID_SAVEAS, "Save &As...");
323   file_menu->Append(wxID_CLOSE, "&Close");
324   
325   file_menu->AppendSeparator();
326   file_menu->Append(IFMENU_FILE_PROPERTIES, "P&roperties");
327   
328   file_menu->AppendSeparator();
329   file_menu->Append(wxID_PRINT, "&Print...");
330   file_menu->Append(wxID_PRINT_SETUP, "Print &Setup...");
331   file_menu->Append(wxID_PREVIEW, "Print Pre&view");
332   
333   wxMenu *view_menu = new wxMenu;
334   view_menu->Append(IFMENU_VIEW_SCALE_MINMAX, "Display Scale &Set...");
335   view_menu->Append(IFMENU_VIEW_SCALE_AUTO, "Display Scale &Auto...");
336   \r
337   wxMenu *plot_menu = new wxMenu;\r
338   plot_menu->Append (IFMENU_PLOT_ROW, "Plot &Row");\r
339   plot_menu->Append (IFMENU_PLOT_COL, "Plot &Column");\r
340   
341   wxMenu *compare_menu = new wxMenu;\r
342   compare_menu->Append (IFMENU_COMPARE_IMAGES, "Compare &Images...");\r
343   compare_menu->Append (IFMENU_COMPARE_ROW, "Compare &Row");\r
344   compare_menu->Append (IFMENU_COMPARE_COL, "Compare &Column");\r
345 \r
346   wxMenu *help_menu = new wxMenu;
347   help_menu->Append(MAINMENU_HELP_ABOUT, "&About");
348   
349   wxMenuBar *menu_bar = new wxMenuBar;
350   
351   menu_bar->Append(file_menu, "&File");
352   menu_bar->Append(view_menu, "&View");\r
353   menu_bar->Append(plot_menu, "&Plot");\r
354   menu_bar->Append(compare_menu, "&Compare");
355   menu_bar->Append(help_menu, "&Help");
356   
357   subframe->SetMenuBar(menu_bar);
358   
359   subframe->Centre(wxBOTH);
360   
361   return subframe;
362 }
363
364
365 bool 
366 ImageFileView::OnCreate(wxDocument *doc, long WXUNUSED(flags) )
367 {
368   m_frame = CreateChildFrame(doc, this);
369   SetFrame (m_frame);
370   
371   m_bMinSpecified = false;
372   m_bMaxSpecified = false;
373   m_dAutoScaleFactor = 1.;
374   
375   int width, height;
376   m_frame->GetClientSize (&width, &height);
377   m_frame->SetTitle("ImageFileView");
378   m_canvas = CreateCanvas (this, m_frame);
379   
380   int x, y;  // X requires a forced resize
381   m_frame->GetSize(&x, &y);
382   m_frame->SetSize(-1, -1, x, y);
383   m_frame->SetFocus();\r
384   m_frame->Show(true);
385   Activate(true);
386   
387   return true;
388 }
389
390 void 
391 ImageFileView::OnDraw (wxDC* dc)
392 {
393   if (m_bitmap.Ok())
394     dc->DrawBitmap(m_bitmap, 0, 0, false);
395   \r
396   int xCursor, yCursor;\r
397   if (m_canvas->GetCurrentCursor (xCursor, yCursor))\r
398     m_canvas->DrawRubberBandCursor (*dc, xCursor, yCursor);\r
399 }
400
401
402 void 
403 ImageFileView::OnUpdate (wxView *WXUNUSED(sender), wxObject *WXUNUSED(hint) )
404 {
405   const ImageFile& rIF = dynamic_cast<ImageFileDocument*>(GetDocument())->getImageFile();
406   ImageFileArrayConst v = rIF.getArray();
407   int nx = rIF.nx();
408   int ny = rIF.ny();
409   if (v != NULL && nx != 0 && ny != 0) {
410     if (! m_bMinSpecified || ! m_bMaxSpecified) {
411       double min, max;
412       rIF.getMinMax (min, max);
413       if (! m_bMinSpecified)
414         m_dMinPixel = min;
415       if (! m_bMaxSpecified)
416         m_dMaxPixel = max;
417     }
418     double scaleWidth = m_dMaxPixel - m_dMinPixel;
419     
420     unsigned char* imageData = new unsigned char [nx * ny * 3];
421     for (int ix = 0; ix < nx; ix++) {
422       for (int iy = 0; iy < ny; iy++) {
423         double scaleValue = ((v[ix][iy] - m_dMinPixel) / scaleWidth) * 255;
424         int intensity = static_cast<int>(scaleValue + 0.5);
425         intensity = clamp (intensity, 0, 255);
426         int baseAddr = ((ny - 1 - iy) * nx + ix) * 3;
427         imageData[baseAddr] = imageData[baseAddr+1] = imageData[baseAddr+2] = intensity;
428       }
429     }
430     wxImage image (nx, ny, imageData, true);
431     m_bitmap = image.ConvertToBitmap();
432     delete imageData;
433     int xSize = nx;
434     int ySize = ny;
435     xSize = clamp (xSize, 0, 800);
436     ySize = clamp (ySize, 0, 800);
437     m_frame->SetClientSize (xSize, ySize);
438     m_canvas->SetScrollbars(20, 20, nx/20, ny/20);
439     m_canvas->SetBackgroundColour(*wxWHITE);\r
440   } 
441   
442   if (m_canvas)
443     m_canvas->Refresh();
444 }
445
446 bool 
447 ImageFileView::OnClose (bool deleteWindow)
448 {
449   if (!GetDocument()->Close())
450     return false;
451   
452   m_canvas->Clear();
453   m_canvas->m_pView = NULL;
454   m_canvas = NULL;
455   wxString s(theApp->GetAppName());
456   if (m_frame)
457     m_frame->SetTitle(s);
458   SetFrame(NULL);
459   
460   Activate(false);
461   
462   if (deleteWindow) {
463     delete m_frame;
464     return true;
465   }
466   return true;
467 }
468 \r
469 \r
470 void\r
471 ImageFileView::OnPlotRow (wxCommandEvent& event)\r
472 {\r
473   int xCursor, yCursor;\r
474   if (! m_canvas->GetCurrentCursor (xCursor, yCursor)) {\r
475     wxMessageBox ("No row selected. Please use left mouse button on image to select column","Error");\r
476     return;\r
477   }\r
478   \r
479   const ImageFile& rIF = dynamic_cast<ImageFileDocument*>(GetDocument())->getImageFile();\r
480   ImageFileArrayConst v = rIF.getArray();\r
481   int nx = rIF.nx();\r
482   int ny = rIF.ny();\r
483   \r
484   if (v != NULL && yCursor < ny) {\r
485     double* pX = new double [nx];\r
486     double* pY = new double [nx];\r
487     for (int i = 0; i < nx; i++) {\r
488       pX[i] = i;\r
489       pY[i] = v[i][yCursor];\r
490     }\r
491     PlotFileDocument* pPlotDoc = dynamic_cast<PlotFileDocument*>(theApp->getDocManager()->CreateDocument("untitled.plt", wxDOC_SILENT));\r
492     if (! pPlotDoc) {\r
493       sys_error (ERR_SEVERE, "Internal error: unable to create Plot file");\r
494     } else {\r
495       PlotFile& rPlotFile = pPlotDoc->getPlotFile();\r
496       std::ostringstream title;\r
497       title << "Row " << yCursor;\r
498       rPlotFile.setTitle(title.str());\r
499       rPlotFile.setXLabel("Column");\r
500       rPlotFile.setYLabel("Pixel Value");\r
501       rPlotFile.setCurveSize (2, nx);\r
502       rPlotFile.addColumn (0, pX);\r
503       rPlotFile.addColumn (1, pY);\r
504     }\r
505     delete pX;\r
506     delete pY;\r
507     pPlotDoc->Modify(true);\r
508   }\r
509   \r
510 }\r
511
512 void\r
513 ImageFileView::OnPlotCol (wxCommandEvent& event)\r
514 {\r
515   int xCursor, yCursor;\r
516   if (! m_canvas->GetCurrentCursor (xCursor, yCursor)) {\r
517     wxMessageBox ("No column selected. Please use left mouse button on image to select column","Error");\r
518     return;\r
519   }\r
520   \r
521   const ImageFile& rIF = dynamic_cast<ImageFileDocument*>(GetDocument())->getImageFile();\r
522   ImageFileArrayConst v = rIF.getArray();\r
523   int nx = rIF.nx();\r
524   int ny = rIF.ny();\r
525   \r
526   if (v != NULL && xCursor < nx) {\r
527     double* pX = new double [ny];\r
528     double* pY = new double [ny];\r
529     for (int i = 0; i < ny; i++) {\r
530       pX[i] = i;\r
531       pY[i] = v[xCursor][i];\r
532     }\r
533     PlotFileDocument* pPlotDoc = dynamic_cast<PlotFileDocument*>(theApp->getDocManager()->CreateDocument("untitled.plt", wxDOC_SILENT));\r
534     if (! pPlotDoc) {\r
535       sys_error (ERR_SEVERE, "Internal error: unable to create Plot file");\r
536     } else {\r
537       PlotFile& rPlotFile = pPlotDoc->getPlotFile();\r
538       std::ostringstream title;\r
539       title << "Column " << xCursor;\r
540       rPlotFile.setTitle(title.str());\r
541       rPlotFile.setXLabel("Row");\r
542       rPlotFile.setYLabel("Pixel Value");\r
543       rPlotFile.setCurveSize (2, nx);\r
544       rPlotFile.addColumn (0, pX);\r
545       rPlotFile.addColumn (1, pY);\r
546     }\r
547     delete pX;\r
548     delete pY;\r
549     pPlotDoc->Modify(true);\r
550   }\r
551 }\r
552 \r
553 void\r
554 ImageFileView::OnCompareCol (wxCommandEvent& event)\r
555 {\r
556   int xCursor, yCursor;\r
557   if (! m_canvas->GetCurrentCursor (xCursor, yCursor)) {\r
558     wxMessageBox ("No column selected. Please use left mouse button on image to select column","Error");\r
559     return;\r
560   }\r
561   \r
562   std::vector<ImageFileDocument*> vecIFDoc;\r
563   theApp->getCompatibleImages (GetDocument(), vecIFDoc);\r
564   \r
565   DialogGetComparisonImage dialogGetCompare (m_frame, "Get Comparison Image", vecIFDoc, false);\r
566   \r
567   if (dialogGetCompare.ShowModal() == wxID_OK) {\r
568     ImageFileDocument* pCompareDoc = dialogGetCompare.getImageFileDocument();\r
569     const ImageFile& rIF = GetDocument()->getImageFile();\r
570     const ImageFile& rCompareIF = pCompareDoc->getImageFile();\r
571     \r
572     ImageFileArrayConst v1 = rIF.getArray();\r
573     ImageFileArrayConst v2 = rCompareIF.getArray();\r
574     int nx = rIF.nx();\r
575     int ny = rIF.ny();\r
576     \r
577     if (v1 != NULL && xCursor < nx) {\r
578       double* pX = new double [ny];\r
579       double* pY1 = new double [ny];\r
580       double* pY2 = new double [ny];\r
581       for (int i = 0; i < ny; i++) {\r
582         pX[i] = i;\r
583         pY1[i] = v1[xCursor][i];\r
584         pY2[i] = v2[xCursor][i];\r
585       }\r
586       PlotFileDocument* pPlotDoc = dynamic_cast<PlotFileDocument*>(theApp->getDocManager()->CreateDocument("untitled.plt", wxDOC_SILENT));\r
587       if (! pPlotDoc) {\r
588         sys_error (ERR_SEVERE, "Internal error: unable to create Plot file");\r
589       } else {\r
590         PlotFile& rPlotFile = pPlotDoc->getPlotFile();\r
591         std::ostringstream title;\r
592         title << "Comparison of Column " << xCursor;\r
593         rPlotFile.setTitle(title.str());\r
594         rPlotFile.setXLabel("Row");\r
595         rPlotFile.setYLabel("Pixel Value");\r
596         rPlotFile.setCurveSize (3, nx);\r
597         rPlotFile.addColumn (0, pX);\r
598         rPlotFile.addColumn (1, pY1);\r
599         rPlotFile.addColumn (2, pY2);\r
600       }\r
601       delete pX;\r
602       delete pY1;\r
603       delete pY2;\r
604       pPlotDoc->Modify(true);\r
605     }\r
606   }\r
607 }\r
608
609 void\r
610 ImageFileView::OnCompareRow (wxCommandEvent& event)\r
611 {\r
612   int xCursor, yCursor;\r
613   if (! m_canvas->GetCurrentCursor (xCursor, yCursor)) {\r
614     wxMessageBox ("No column selected. Please use left mouse button on image to select column","Error");\r
615     return;\r
616   }\r
617   \r
618   std::vector<ImageFileDocument*> vecIFDoc;\r
619   theApp->getCompatibleImages (GetDocument(), vecIFDoc);\r
620   \r
621   DialogGetComparisonImage dialogGetCompare (m_frame, "Get Comparison Image", vecIFDoc, false);\r
622   \r
623   if (dialogGetCompare.ShowModal() == wxID_OK) {\r
624     ImageFileDocument* pCompareDoc = dialogGetCompare.getImageFileDocument();\r
625     const ImageFile& rIF = GetDocument()->getImageFile();\r
626     const ImageFile& rCompareIF = pCompareDoc->getImageFile();\r
627     \r
628     ImageFileArrayConst v1 = rIF.getArray();\r
629     ImageFileArrayConst v2 = rCompareIF.getArray();\r
630     int nx = rIF.nx();\r
631     int ny = rIF.ny();\r
632     \r
633     if (v1 != NULL && yCursor < ny) {\r
634       double* pX = new double [nx];\r
635       double* pY1 = new double [nx];\r
636       double* pY2 = new double [nx];\r
637       for (int i = 0; i < nx; i++) {\r
638         pX[i] = i;\r
639         pY1[i] = v1[i][yCursor];\r
640         pY2[i] = v2[i][yCursor];\r
641       }\r
642       PlotFileDocument* pPlotDoc = dynamic_cast<PlotFileDocument*>(theApp->getDocManager()->CreateDocument("untitled.plt", wxDOC_SILENT));\r
643       if (! pPlotDoc) {\r
644         sys_error (ERR_SEVERE, "Internal error: unable to create Plot file");\r
645       } else {\r
646         PlotFile& rPlotFile = pPlotDoc->getPlotFile();\r
647         std::ostringstream title;\r
648         title << "Comparison of Row " << yCursor;\r
649         rPlotFile.setTitle(title.str());\r
650         rPlotFile.setXLabel("Column");\r
651         rPlotFile.setYLabel("Pixel Value");\r
652         rPlotFile.setCurveSize (3, ny);\r
653         rPlotFile.addColumn (0, pX);\r
654         rPlotFile.addColumn (1, pY1);\r
655         rPlotFile.addColumn (2, pY2);\r
656       }\r
657       delete pX;\r
658       delete pY1;\r
659       delete pY2;\r
660       pPlotDoc->Modify(true);\r
661     }\r
662   }\r
663 }\r
664 \r
665 \r
666 // PhantomCanvas
667
668 PhantomCanvas::PhantomCanvas (PhantomView* v, wxFrame *frame, const wxPoint& pos, const wxSize& size, const long style)
669 : wxScrolledWindow(frame, -1, pos, size, style)
670 {
671   m_pView = v;
672 }
673
674 void 
675 PhantomCanvas::OnDraw(wxDC& dc)
676 {
677   if (m_pView)
678     m_pView->OnDraw(& dc);
679 }
680
681
682 // PhantomView
683
684 IMPLEMENT_DYNAMIC_CLASS(PhantomView, wxView)
685
686 BEGIN_EVENT_TABLE(PhantomView, wxView)
687 EVT_MENU(PHMMENU_FILE_PROPERTIES, PhantomView::OnProperties)
688 EVT_MENU(PHMMENU_PROCESS_RASTERIZE, PhantomView::OnRasterize)
689 EVT_MENU(PHMMENU_PROCESS_PROJECTIONS, PhantomView::OnProjections)
690 END_EVENT_TABLE()
691
692 PhantomView::PhantomView(void) 
693 : wxView(), m_canvas(NULL), m_frame(NULL)
694 {
695   m_iDefaultNDet = 367;
696   m_iDefaultNView = 320;
697   m_iDefaultNSample = 2;
698   m_dDefaultRotation = 2;
699   m_dDefaultFocalLength = 2;
700   m_dDefaultFieldOfView = 1;
701   m_iDefaultGeometry = Scanner::GEOMETRY_PARALLEL;
702   m_iDefaultTrace = Trace::TRACE_NONE;
703 }
704
705 PhantomView::~PhantomView(void)
706 {
707 }
708
709 void
710 PhantomView::OnProperties (wxCommandEvent& event)
711 {
712   const int idPhantom = GetDocument()->getPhantomID();
713   const wxString& namePhantom = GetDocument()->getPhantomName();
714   std::ostringstream os;
715   os << "Phantom " << namePhantom.c_str() << " (" << idPhantom << ")\n";
716   *theApp->getLog() << os.str().c_str();
717 #if DEBUG
718   const Phantom& rPhantom = GetDocument()->getPhantom();
719   rPhantom.print();
720 #endif
721 }
722
723
724 void
725 PhantomView::OnProjections (wxCommandEvent& event)
726 {
727   DialogGetProjectionParameters dialogProjection (m_frame, m_iDefaultNDet, m_iDefaultNView, m_iDefaultNSample, m_dDefaultRotation, m_dDefaultFocalLength, m_dDefaultFieldOfView, m_iDefaultGeometry, m_iDefaultTrace);
728   int retVal = dialogProjection.ShowModal();
729   if (retVal == wxID_OK) {
730     m_iDefaultNDet = dialogProjection.getNDet();
731     m_iDefaultNView = dialogProjection.getNView();
732     m_iDefaultNSample = dialogProjection.getNSamples();
733     m_iDefaultTrace = dialogProjection.getTrace();
734     m_dDefaultRotation = dialogProjection.getRotAngle();
735     m_dDefaultFocalLength = dialogProjection.getFocalLengthRatio();
736     m_dDefaultFieldOfView = dialogProjection.getFieldOfViewRatio();
737     wxString sGeometry = dialogProjection.getGeometry();
738     m_iDefaultGeometry = Scanner::convertGeometryNameToID (sGeometry.c_str());
739     
740     if (m_iDefaultNDet > 0 && m_iDefaultNView > 0 && sGeometry != "") {
741       const Phantom& rPhantom = GetDocument()->getPhantom();
742       ProjectionFileDocument* pProjectionDoc = dynamic_cast<ProjectionFileDocument*>(theApp->getDocManager()->CreateDocument("untitled.pj", wxDOC_SILENT));\r
743       if (! pProjectionDoc) {\r
744         sys_error (ERR_SEVERE, "Unable to create projection document");\r
745         return;\r
746       }
747       Projections& rProj = pProjectionDoc->getProjections();
748       Scanner theScanner (rPhantom, sGeometry.c_str(), m_iDefaultNDet, m_iDefaultNView, m_iDefaultNSample, m_dDefaultRotation, m_dDefaultFocalLength, m_dDefaultFieldOfView);
749       if (theScanner.fail()) {
750         *theApp->getLog() << "Failed making scanner: " << theScanner.failMessage().c_str() << "\n";
751         return;
752       }
753       rProj.initFromScanner (theScanner);
754       m_dDefaultRotation /= PI;  // convert back to PI units
755       
756       if (m_iDefaultTrace > Trace::TRACE_CONSOLE) {
757         ProjectionsDialog dialogProjections (theScanner, rProj, rPhantom, m_iDefaultTrace, dynamic_cast<wxWindow*>(m_frame));
758         for (int iView = 0; iView < rProj.nView(); iView++) {
759           ::wxYield();
760           ::wxYield();
761           if (dialogProjections.isCancelled() || ! dialogProjections.projectView (iView)) {
762             pProjectionDoc->DeleteAllViews();
763             return;
764           }
765           ::wxYield();
766           ::wxYield();
767           while (dialogProjections.isPaused()) {
768             ::wxYield();
769             ::wxUsleep(50);
770           }
771         }
772       } else {
773         wxProgressDialog dlgProgress (wxString("Projection"), wxString("Projection Progress"), rProj.nView() + 1, m_frame, wxPD_CAN_ABORT);
774         for (int i = 0; i < rProj.nView(); i++) {
775           theScanner.collectProjections (rProj, rPhantom, i, 1, true, m_iDefaultTrace);
776           if (! dlgProgress.Update (i+1)) {
777             pProjectionDoc->DeleteAllViews();
778             return;
779           }
780         }
781       }
782       
783       std::ostringstream os;
784       os << "Projections for " << rPhantom.name() << ": nDet=" << m_iDefaultNDet << ", nView=" << m_iDefaultNView << ", nSamples=" << m_iDefaultNSample << ", RotAngle=" << m_dDefaultRotation << ", FocalLengthRatio=" << m_dDefaultFocalLength << ", FieldOfViewRatio=" << m_dDefaultFieldOfView << ", Geometry=" << sGeometry.c_str();
785       rProj.setRemark (os.str());
786       *theApp->getLog() << os.str().c_str() << "\n";
787       
788       m_frame->Lower();
789       ::wxYield();
790       ProjectionFileView* projView = dynamic_cast<ProjectionFileView*>(pProjectionDoc->GetFirstView());\r
791       if (projView) {\r
792         projView->getFrame()->SetFocus();\r
793         projView->OnUpdate (projView, NULL);\r
794       }\r
795       if (wxView* pView = pProjectionDoc->GetFirstView()) {
796         if (wxFrame* pFrame = pView->GetFrame()) {
797           pFrame->SetFocus();
798           pFrame->Raise();
799         }
800         theApp->getDocManager()->ActivateView (pView, true, false);
801       }
802       ::wxYield();
803       pProjectionDoc->Modify(true);
804       pProjectionDoc->UpdateAllViews(this);
805     }
806   }
807 }
808
809
810 void
811 PhantomView::OnRasterize (wxCommandEvent& event)
812 {
813   DialogGetRasterParameters dialogRaster (m_frame, 256, 256, 1);
814   int retVal = dialogRaster.ShowModal();
815   if (retVal == wxID_OK) {
816     int xSize = dialogRaster.getXSize();
817     int ySize = dialogRaster.getYSize();
818     int nSamples = dialogRaster.getNSamples();
819     if (nSamples < 1)
820       nSamples = 1;
821     if (xSize > 0 && ySize > 0) {
822       const Phantom& rPhantom = GetDocument()->getPhantom();
823       ImageFileDocument* pRasterDoc = dynamic_cast<ImageFileDocument*>(theApp->getDocManager()->CreateDocument("untitled.if", wxDOC_SILENT));\r
824       if (! pRasterDoc) {\r
825         sys_error (ERR_SEVERE, "Unable to create image file");\r
826         return;\r
827       }
828       ImageFile& imageFile = pRasterDoc->getImageFile();
829       
830       imageFile.setArraySize (xSize, ySize);
831       wxProgressDialog dlgProgress (wxString("Rasterize"), wxString("Rasterization Progress"), imageFile.nx() + 1, m_frame, wxPD_CAN_ABORT);
832       for (unsigned int i = 0; i < imageFile.nx(); i++) {
833         rPhantom.convertToImagefile (imageFile, nSamples, Trace::TRACE_NONE, i, 1, true);
834         if (! dlgProgress.Update(i+1)) {
835           pRasterDoc->DeleteAllViews();
836           return;
837         }
838       }
839       pRasterDoc->Modify(true);
840       pRasterDoc->UpdateAllViews(this);
841       ImageFileView* rasterView = dynamic_cast<ImageFileView*>(pRasterDoc->GetFirstView());\r
842       if (rasterView) {\r
843         rasterView->getFrame()->SetFocus();\r
844         rasterView->OnUpdate (rasterView, NULL);\r
845       }\r
846       
847       std::ostringstream os;
848       os << "Rasterize Phantom " << rPhantom.name() << ": XSize=" << xSize << ", YSize=" << ySize << ", nSamples=" << nSamples << "\n";
849       *theApp->getLog() << os.str().c_str();
850     }\r
851   }
852 }
853
854
855 PhantomCanvas* 
856 PhantomView::CreateCanvas (wxView *view, wxFrame *parent)
857 {
858   PhantomCanvas* pCanvas;
859   int width, height;
860   parent->GetClientSize(&width, &height);
861   
862   pCanvas = new PhantomCanvas (dynamic_cast<PhantomView*>(view), parent, wxPoint(0, 0), wxSize(width, height), 0);
863   
864   pCanvas->SetBackgroundColour(*wxWHITE);
865   pCanvas->Clear();
866   
867   return pCanvas;
868 }
869
870 wxFrame*
871 PhantomView::CreateChildFrame(wxDocument *doc, wxView *view)
872 {
873   wxDocChildFrame *subframe = new wxDocChildFrame(doc, view, theApp->getMainFrame(), -1, "Phantom Frame", wxPoint(10, 10), wxSize(256, 256), wxDEFAULT_FRAME_STYLE);
874   
875   wxMenu *file_menu = new wxMenu;
876   
877   file_menu->Append(MAINMENU_FILE_CREATE_PHANTOM, "Cr&eate Phantom...");
878   file_menu->Append(wxID_OPEN, "&Open...");
879   file_menu->Append(wxID_CLOSE, "&Close");
880   
881   file_menu->AppendSeparator();
882   file_menu->Append(PHMMENU_FILE_PROPERTIES, "P&roperties");
883   
884   file_menu->AppendSeparator();
885   file_menu->Append(wxID_PRINT, "&Print...");
886   file_menu->Append(wxID_PRINT_SETUP, "Print &Setup...");
887   file_menu->Append(wxID_PREVIEW, "Print Pre&view");
888   
889   wxMenu *process_menu = new wxMenu;
890   process_menu->Append(PHMMENU_PROCESS_RASTERIZE, "&Rasterize...");
891   process_menu->Append(PHMMENU_PROCESS_PROJECTIONS, "&Projections...");
892   
893   wxMenu *help_menu = new wxMenu;
894   help_menu->Append(MAINMENU_HELP_CONTENTS, "&Contents");
895   help_menu->Append(MAINMENU_HELP_ABOUT, "&About");
896   
897   wxMenuBar *menu_bar = new wxMenuBar;
898   
899   menu_bar->Append(file_menu, "&File");
900   menu_bar->Append(process_menu, "&Process");
901   menu_bar->Append(help_menu, "&Help");
902   
903   subframe->SetMenuBar(menu_bar);
904   
905   subframe->Centre(wxBOTH);
906   
907   return subframe;
908 }
909
910
911 bool 
912 PhantomView::OnCreate(wxDocument *doc, long WXUNUSED(flags) )
913 {
914   m_frame = CreateChildFrame(doc, this);
915   SetFrame(m_frame);
916   
917   int width, height;
918   m_frame->GetClientSize(&width, &height);
919   m_frame->SetTitle("PhantomView");
920   m_canvas = CreateCanvas(this, m_frame);
921   
922 #ifdef __X__
923   int x, y;  // X requires a forced resize
924   m_frame->GetSize(&x, &y);
925   m_frame->SetSize(-1, -1, x, y);
926 #endif
927   
928   m_frame->Show(true);
929   Activate(true);
930   
931   return true;
932 }
933
934
935 void 
936 PhantomView::OnUpdate(wxView *WXUNUSED(sender), wxObject *WXUNUSED(hint) )
937 {
938   if (m_canvas)
939     m_canvas->Refresh();
940 }
941
942 bool 
943 PhantomView::OnClose (bool deleteWindow)
944 {
945   if (!GetDocument()->Close())
946     return false;
947   
948   m_canvas->Clear();
949   m_canvas->m_pView = NULL;
950   m_canvas = NULL;
951   wxString s(wxTheApp->GetAppName());
952   if (m_frame)
953     m_frame->SetTitle(s);
954   SetFrame(NULL);
955   
956   Activate(false);
957   
958   if (deleteWindow) {
959     delete m_frame;
960     return true;
961   }
962   return true;
963 }
964
965 void
966 PhantomView::OnDraw (wxDC* dc)
967 {
968   int xsize, ysize;
969   m_canvas->GetClientSize (&xsize, &ysize);
970   SGPDriver driver (dc, xsize, ysize);
971   SGP sgp (driver);
972   const Phantom& rPhantom = GetDocument()->getPhantom();
973   sgp.setColor (C_RED);
974   rPhantom.show (sgp);
975 }
976
977 // ProjectionCanvas
978
979 ProjectionFileCanvas::ProjectionFileCanvas (ProjectionFileView* v, wxFrame *frame, const wxPoint& pos, const wxSize& size, const long style)
980 : wxScrolledWindow(frame, -1, pos, size, style)
981 {
982   m_pView = v;
983 }
984
985 void 
986 ProjectionFileCanvas::OnDraw(wxDC& dc)
987 {
988   if (m_pView)
989     m_pView->OnDraw(& dc);
990 }
991
992 // ProjectionFileView
993
994 IMPLEMENT_DYNAMIC_CLASS(ProjectionFileView, wxView)
995
996 BEGIN_EVENT_TABLE(ProjectionFileView, wxView)
997 EVT_MENU(PJMENU_FILE_PROPERTIES, ProjectionFileView::OnProperties)
998 EVT_MENU(PJMENU_PROCESS_RECONSTRUCT, ProjectionFileView::OnReconstruct)
999 END_EVENT_TABLE()
1000
1001 ProjectionFileView::ProjectionFileView(void) 
1002 : wxView(), m_canvas(NULL), m_frame(NULL)
1003 {
1004   m_iDefaultNX = 256;
1005   m_iDefaultNY = 256;
1006   m_iDefaultFilter = SignalFilter::FILTER_ABS_BANDLIMIT;
1007   m_dDefaultFilterParam = 1.;
1008 #if HAVE_FFTW
1009   m_iDefaultFilterMethod = ProcessSignal::FILTER_METHOD_RFFTW;
1010   m_iDefaultFilterGeneration = ProcessSignal::FILTER_GENERATION_INVERSE_FOURIER;
1011 #else
1012   m_iDefaultFilterMethod = ProcessSignal::FILTER_METHOD_CONVOLUTION;
1013   m_iDefaultFilterGeneration = ProcessSignal::FILTER_GENERATION_DIRECT;
1014 #endif
1015   m_iDefaultZeropad = 1;
1016   m_iDefaultBackprojector = Backprojector::BPROJ_IDIFF3;
1017   m_iDefaultInterpolation = Backprojector::INTERP_LINEAR;
1018   m_iDefaultInterpParam = 1;
1019   m_iDefaultTrace = Trace::TRACE_NONE;
1020 }
1021
1022 ProjectionFileView::~ProjectionFileView(void)
1023 {
1024 }
1025
1026 void
1027 ProjectionFileView::OnProperties (wxCommandEvent& event)
1028 {
1029   const Projections& rProj = GetDocument()->getProjections();
1030   std::ostringstream os;
1031   rProj.printScanInfo(os);
1032   *theApp->getLog() << os.str().c_str();
1033   wxMessageDialog dialogMsg (m_frame, os.str().c_str(), "Projection File Properties", wxOK | wxICON_INFORMATION);
1034   dialogMsg.ShowModal();
1035 }
1036
1037
1038 void
1039 ProjectionFileView::OnReconstruct (wxCommandEvent& event)
1040 {
1041   DialogGetReconstructionParameters dialogReconstruction (m_frame, m_iDefaultNX, m_iDefaultNY, m_iDefaultFilter, m_dDefaultFilterParam, m_iDefaultFilterMethod, m_iDefaultFilterGeneration, m_iDefaultZeropad, m_iDefaultInterpolation, m_iDefaultInterpParam, m_iDefaultBackprojector, m_iDefaultTrace);
1042   
1043   int retVal = dialogReconstruction.ShowModal();
1044   if (retVal == wxID_OK) {
1045     m_iDefaultNX = dialogReconstruction.getXSize();
1046     m_iDefaultNY = dialogReconstruction.getYSize();
1047     wxString optFilterName = dialogReconstruction.getFilterName();
1048     m_iDefaultFilter = SignalFilter::convertFilterNameToID (optFilterName.c_str());
1049     m_dDefaultFilterParam = dialogReconstruction.getFilterParam();
1050     wxString optFilterMethodName = dialogReconstruction.getFilterMethodName();
1051     m_iDefaultFilterMethod = ProcessSignal::convertFilterMethodNameToID(optFilterMethodName.c_str());
1052     m_iDefaultZeropad = dialogReconstruction.getZeropad();
1053     wxString optFilterGenerationName = dialogReconstruction.getFilterGenerationName();
1054     m_iDefaultFilterGeneration = ProcessSignal::convertFilterGenerationNameToID (optFilterGenerationName.c_str());
1055     wxString optInterpName = dialogReconstruction.getInterpName();
1056     m_iDefaultInterpolation = Backprojector::convertInterpNameToID (optInterpName.c_str());
1057     m_iDefaultInterpParam = dialogReconstruction.getInterpParam();
1058     wxString optBackprojectName = dialogReconstruction.getBackprojectName();
1059     m_iDefaultBackprojector = Backprojector::convertBackprojectNameToID (optBackprojectName.c_str());
1060     m_iDefaultTrace = dialogReconstruction.getTrace();
1061     if (m_iDefaultNX > 0 && m_iDefaultNY > 0) {
1062       ImageFileDocument* pReconDoc = dynamic_cast<ImageFileDocument*>(theApp->getDocManager()->CreateDocument("untitled.if", wxDOC_SILENT));\r
1063       if (pReconDoc) {\r
1064         sys_error (ERR_SEVERE, "Unable to create image file");\r
1065         return;\r
1066       }
1067       ImageFile& imageFile = pReconDoc->getImageFile();
1068       const Projections& rProj = GetDocument()->getProjections();
1069       imageFile.setArraySize (m_iDefaultNX, m_iDefaultNY);
1070       
1071       if (m_iDefaultFilterMethod != ProcessSignal::FILTER_METHOD_CONVOLUTION && m_iDefaultFilterGeneration == ProcessSignal::FILTER_GENERATION_DIRECT && rProj.geometry() != Scanner::GEOMETRY_PARALLEL) {
1072         wxMessageBox ("Sorry!\nCurrently, frequency-based filtering with direct filter generation is not support for geometries other than parallel.\nAborting command.", "Not Supported", wxOK | wxICON_WARNING, m_frame);
1073         return;
1074       }
1075 #if 0
1076       SGPDriver* pSGPDriver = NULL;
1077       SGP* pSGP = NULL;
1078       wxMemoryDC* pDCPlot = NULL;
1079       wxBitmap bitmap;
1080       if (m_iDefaultTrace >= Trace::TRACE_PLOT) {
1081         bitmap.Create (500, 500);
1082         pDCPlot = new wxMemoryDC;
1083         pDCPlot->SelectObject (bitmap);
1084         pSGPDriver = new SGPDriver (dynamic_cast<wxDC*>pDCPlot, 500, 500);
1085         pSGP = new SGP (*pSGPDriver);
1086       }
1087       Reconstructor* pReconstruct = new Reconstructor (rProj, imageFile, optFilterName.c_str(), m_dDefaultFilterParam, optFilterMethodName.c_str(), m_iDefaultZeropad, optFilterGenerationName.c_str(), optInterpName.c_str(), m_iDefaultInterpParam, optBackprojectName.c_str(), m_iDefaultTrace, pSGP);
1088       delete pSGP;
1089 #else
1090       Reconstructor* pReconstruct = new Reconstructor (rProj, imageFile, optFilterName.c_str(), m_dDefaultFilterParam, optFilterMethodName.c_str(), m_iDefaultZeropad, optFilterGenerationName.c_str(), optInterpName.c_str(), m_iDefaultInterpParam, optBackprojectName.c_str(), m_iDefaultTrace);
1091 #endif
1092       \r
1093       Timer timerRecon;
1094       if (m_iDefaultTrace > Trace::TRACE_CONSOLE) {
1095         ReconstructDialog* pDlgReconstruct = new ReconstructDialog (*pReconstruct, rProj, imageFile, m_iDefaultTrace, m_frame);
1096         for (int iView = 0; iView < rProj.nView(); iView++) {
1097           ::wxYield();
1098           ::wxYield();
1099           if (pDlgReconstruct->isCancelled() || ! pDlgReconstruct->reconstructView (iView)) {
1100             delete pDlgReconstruct;
1101             delete pReconstruct;
1102             pReconDoc->DeleteAllViews();
1103             return;
1104           }
1105           ::wxYield();
1106           ::wxYield();
1107           while (pDlgReconstruct->isPaused()) {
1108             ::wxYield();
1109             ::wxUsleep(50);
1110           }
1111         }
1112         delete pDlgReconstruct;
1113       } else {
1114         wxProgressDialog dlgProgress (wxString("Reconstruction"), wxString("Reconstruction Progress"), rProj.nView() + 1, m_frame, wxPD_CAN_ABORT);
1115         for (int i = 0; i < rProj.nView(); i++) {
1116           pReconstruct->reconstructView (i, 1);
1117           if (! dlgProgress.Update(i + 1)) {
1118             delete pReconstruct;
1119             pReconDoc->DeleteAllViews();
1120             return;
1121           }
1122         }
1123       }
1124       delete pReconstruct;
1125       pReconDoc->Modify(true);
1126       pReconDoc->UpdateAllViews(this);
1127       ImageFileView* rasterView = dynamic_cast<ImageFileView*>(pReconDoc->GetFirstView());\r
1128       if (rasterView) {\r
1129         rasterView->getFrame()->SetFocus();\r
1130         rasterView->OnUpdate (rasterView, NULL);\r
1131       }\r
1132       std::ostringstream os;
1133       os << "Reconstruct " << rProj.getFilename() << ": xSize=" << m_iDefaultNX << ", ySize=" << m_iDefaultNY << ", Filter=" << optFilterName.c_str() << ", FilterParam=" << m_dDefaultFilterParam << ", FilterMethod=" << optFilterMethodName.c_str() << ", FilterGeneration=" << optFilterGenerationName.c_str() << ", Zeropad=" << m_iDefaultZeropad << ", Interpolation=" << optInterpName.c_str() << ", InterpolationParam=" << m_iDefaultInterpParam << ", Backprojection=" << optBackprojectName.c_str();
1134       *theApp->getLog() << os.str().c_str() << "\n";
1135       imageFile.labelAdd (rProj.getLabel());
1136       imageFile.labelAdd (Array2dFileLabel::L_HISTORY, os.str().c_str(), timerRecon.timerEnd());
1137     }
1138   }
1139 }
1140
1141
1142 ProjectionFileCanvas* 
1143 ProjectionFileView::CreateCanvas (wxView *view, wxFrame *parent)
1144 {
1145   ProjectionFileCanvas* pCanvas;
1146   int width, height;
1147   parent->GetClientSize(&width, &height);
1148   
1149   pCanvas = new ProjectionFileCanvas (dynamic_cast<ProjectionFileView*>(view), parent, wxPoint(0, 0), wxSize(width, height), 0);
1150   
1151   pCanvas->SetScrollbars(20, 20, 50, 50);
1152   pCanvas->SetBackgroundColour(*wxWHITE);
1153   pCanvas->Clear();
1154   
1155   return pCanvas;
1156 }
1157
1158 wxFrame*
1159 ProjectionFileView::CreateChildFrame(wxDocument *doc, wxView *view)
1160 {
1161   wxDocChildFrame *subframe = new wxDocChildFrame(doc, view, theApp->getMainFrame(), -1, "Projection Frame", wxPoint(10, 10), wxSize(0, 0), wxDEFAULT_FRAME_STYLE);
1162   
1163   wxMenu *file_menu = new wxMenu;
1164   
1165   file_menu->Append(MAINMENU_FILE_CREATE_PHANTOM, "Cr&eate Phantom...");
1166   file_menu->Append(wxID_OPEN, "&Open...");
1167   file_menu->Append(wxID_SAVE, "&Save");
1168   file_menu->Append(wxID_SAVEAS, "Save &As...");
1169   file_menu->Append(wxID_CLOSE, "&Close");
1170   
1171   file_menu->AppendSeparator();
1172   file_menu->Append(PJMENU_FILE_PROPERTIES, "P&roperties");
1173   
1174   file_menu->AppendSeparator();
1175   file_menu->Append(wxID_PRINT, "&Print...");
1176   file_menu->Append(wxID_PRINT_SETUP, "Print &Setup...");
1177   file_menu->Append(wxID_PREVIEW, "Print Pre&view");
1178   
1179   wxMenu *process_menu = new wxMenu;
1180   process_menu->Append(PJMENU_PROCESS_RECONSTRUCT, "R&econstruct...");
1181   
1182   wxMenu *help_menu = new wxMenu;
1183   help_menu->Append(MAINMENU_HELP_CONTENTS, "&Contents");
1184   help_menu->AppendSeparator();
1185   help_menu->Append(MAINMENU_HELP_ABOUT, "&About");
1186   
1187   wxMenuBar *menu_bar = new wxMenuBar;
1188   
1189   menu_bar->Append(file_menu, "&File");
1190   menu_bar->Append(process_menu, "&Process");
1191   menu_bar->Append(help_menu, "&Help");
1192   
1193   subframe->SetMenuBar(menu_bar);
1194   
1195   subframe->Centre(wxBOTH);
1196   
1197   return subframe;
1198 }
1199
1200
1201 bool 
1202 ProjectionFileView::OnCreate(wxDocument *doc, long WXUNUSED(flags) )
1203 {
1204   m_frame = CreateChildFrame(doc, this);
1205   SetFrame(m_frame);
1206   
1207   int width, height;
1208   m_frame->GetClientSize(&width, &height);
1209   m_frame->SetTitle("ProjectionFileView");
1210   m_canvas = CreateCanvas(this, m_frame);
1211   
1212 #ifdef __X__
1213   int x, y;  // X requires a forced resize
1214   m_frame->GetSize(&x, &y);
1215   m_frame->SetSize(-1, -1, x, y);
1216 #endif
1217   
1218   m_frame->Show(true);
1219   Activate(true);
1220   
1221   return true;
1222 }
1223
1224 void 
1225 ProjectionFileView::OnDraw (wxDC* dc)
1226 {
1227   if (m_bitmap.Ok())
1228     dc->DrawBitmap (m_bitmap, 0, 0, false);
1229 }
1230
1231
1232 void 
1233 ProjectionFileView::OnUpdate(wxView *WXUNUSED(sender), wxObject *WXUNUSED(hint) )
1234 {
1235   const Projections& rProj = GetDocument()->getProjections();
1236   const int nDet = rProj.nDet();
1237   const int nView = rProj.nView();
1238   if (nDet != 0 && nView != 0) {
1239     const DetectorArray& detarray = rProj.getDetectorArray(0);
1240     const DetectorValue* detval = detarray.detValues();
1241     double min = detval[0];
1242     double max = detval[0];
1243     for (int iy = 0; iy < nView; iy++) {
1244       const DetectorArray& detarray = rProj.getDetectorArray(iy);
1245       const DetectorValue* detval = detarray.detValues();
1246       for (int ix = 0; ix < nDet; ix++) {
1247         if (min > detval[ix])
1248           min = detval[ix];
1249         else if (max < detval[ix])
1250           max = detval[ix];
1251       }
1252     }
1253     
1254     unsigned char* imageData = new unsigned char [nDet * nView * 3];
1255     double scale = (max - min) / 255;
1256     for (int iy2 = 0; iy2 < nView; iy2++) {
1257       const DetectorArray& detarray = rProj.getDetectorArray (iy2);
1258       const DetectorValue* detval = detarray.detValues();
1259       for (int ix = 0; ix < nDet; ix++) {
1260         int intensity = static_cast<int>(((detval[ix] - min) / scale) + 0.5);
1261         intensity = clamp(intensity, 0, 255);
1262         int baseAddr = (iy2 * nDet + ix) * 3;
1263         imageData[baseAddr] = imageData[baseAddr+1] = imageData[baseAddr+2] = intensity;
1264       }
1265     }
1266     wxImage image (nDet, nView, imageData, true);
1267     m_bitmap = image.ConvertToBitmap();
1268     delete imageData;
1269     int xSize = nDet;
1270     int ySize = nView;
1271     xSize = clamp (xSize, 0, 800);
1272     ySize = clamp (ySize, 0, 800);
1273     m_frame->SetClientSize (xSize, ySize);
1274     m_canvas->SetScrollbars (20, 20, nDet/20, nView/20);
1275   }
1276   
1277   if (m_canvas)
1278     m_canvas->Refresh();
1279 }
1280
1281 bool 
1282 ProjectionFileView::OnClose (bool deleteWindow)
1283 {
1284   if (!GetDocument()->Close())
1285     return false;
1286   
1287   m_canvas->Clear();
1288   m_canvas->m_pView = NULL;
1289   m_canvas = NULL;
1290   wxString s(wxTheApp->GetAppName());
1291   if (m_frame)
1292     m_frame->SetTitle(s);
1293   SetFrame(NULL);
1294   
1295   Activate(false);
1296   
1297   if (deleteWindow) {
1298     delete m_frame;
1299     return true;
1300   }
1301   return true;
1302 }
1303
1304
1305
1306 // PlotFileCanvas
1307 PlotFileCanvas::PlotFileCanvas (PlotFileView* v, wxFrame *frame, const wxPoint& pos, const wxSize& size, const long style)
1308 : wxScrolledWindow(frame, -1, pos, size, style)
1309 {
1310   m_pView = v;
1311 }
1312
1313 void 
1314 PlotFileCanvas::OnDraw(wxDC& dc)
1315 {
1316   if (m_pView)
1317     m_pView->OnDraw(& dc);
1318 }\r
1319
1320
1321 // PlotFileView
1322
1323 IMPLEMENT_DYNAMIC_CLASS(PlotFileView, wxView)
1324
1325 BEGIN_EVENT_TABLE(PlotFileView, wxView)
1326 EVT_MENU(PJMENU_FILE_PROPERTIES, PlotFileView::OnProperties)
1327 EVT_MENU(PLOTMENU_VIEW_SCALE_MINMAX, PlotFileView::OnScaleMinMax)\r
1328 EVT_MENU(PLOTMENU_VIEW_SCALE_AUTO, PlotFileView::OnScaleAuto)\r
1329 END_EVENT_TABLE()
1330
1331 PlotFileView::PlotFileView(void) 
1332 : wxView(), m_canvas(NULL), m_frame(NULL)
1333 {
1334 }
1335
1336 PlotFileView::~PlotFileView(void)
1337 {
1338 }
1339
1340 void
1341 PlotFileView::OnProperties (wxCommandEvent& event)
1342 {
1343   const PlotFile& rProj = GetDocument()->getPlotFile();
1344   std::ostringstream os;\r
1345   os << "Columns: " << rProj.getNumColumns() << ", Records: " << rProj.getNumRecords() << "\n";
1346   *theApp->getLog() << os.str().c_str();
1347   wxMessageDialog dialogMsg (m_frame, os.str().c_str(), "Plot File Properties", wxOK | wxICON_INFORMATION);
1348   dialogMsg.ShowModal();
1349 }
1350
1351
1352 void \r
1353 PlotFileView::OnScaleAuto (wxCommandEvent& event)\r
1354 {\r
1355   const PlotFile& rPlotFile = GetDocument()->getPlotFile();\r
1356   double min, max, mean, mode, median, stddev;\r
1357   rPlotFile.statistics (1, min, max, mean, mode, median, stddev);\r
1358   DialogAutoScaleParameters dialogAutoScale (m_frame, mean, mode, median, stddev, m_dAutoScaleFactor);\r
1359   int iRetVal = dialogAutoScale.ShowModal();\r
1360   if (iRetVal == wxID_OK) {\r
1361     m_bMinSpecified = true;\r
1362     m_bMaxSpecified = true;\r
1363     double dMin, dMax;\r
1364     if (dialogAutoScale.getMinMax (&dMin, &dMax)) {\r
1365       m_dMinPixel = dMin;\r
1366       m_dMaxPixel = dMax;\r
1367       m_dAutoScaleFactor = dialogAutoScale.getAutoScaleFactor();\r
1368       OnUpdate (this, NULL);\r
1369     }\r
1370   }\r
1371 }\r
1372 \r
1373 void \r
1374 PlotFileView::OnScaleMinMax (wxCommandEvent& event)\r
1375 {\r
1376   const PlotFile& rPlotFile = GetDocument()->getPlotFile();\r
1377   double min, max;\r
1378   \r
1379   if (! m_bMinSpecified && ! m_bMaxSpecified) {\r
1380     if (! rPlotFile.getMinMax (1, min, max)) {\r
1381       *theApp->getLog() << "Error: unable to find Min/Max\n";\r
1382       return;\r
1383     }\r
1384   }\r
1385   \r
1386   if (m_bMinSpecified)\r
1387     min = m_dMinPixel;\r
1388   if (m_bMaxSpecified)\r
1389     max = m_dMaxPixel;\r
1390   \r
1391   DialogGetMinMax dialogMinMax (m_frame, "Set Y-axis Minimum & Maximum", min, max);\r
1392   int retVal = dialogMinMax.ShowModal();\r
1393   if (retVal == wxID_OK) {\r
1394     m_bMinSpecified = true;\r
1395     m_bMaxSpecified = true;\r
1396     m_dMinPixel = dialogMinMax.getMinimum();\r
1397     m_dMaxPixel = dialogMinMax.getMaximum();\r
1398     OnUpdate (this, NULL);\r
1399   }\r
1400 }\r
1401 \r
1402 \r
1403 PlotFileCanvas* 
1404 PlotFileView::CreateCanvas (wxView *view, wxFrame *parent)
1405 {
1406   PlotFileCanvas* pCanvas;
1407   int width, height;
1408   parent->GetClientSize(&width, &height);
1409   
1410   pCanvas = new PlotFileCanvas (dynamic_cast<PlotFileView*>(view), parent, wxPoint(0, 0), wxSize(width, height), 0);
1411   
1412   pCanvas->SetBackgroundColour(*wxWHITE);
1413   pCanvas->Clear();
1414   
1415   return pCanvas;
1416 }
1417
1418 wxFrame*
1419 PlotFileView::CreateChildFrame(wxDocument *doc, wxView *view)
1420 {
1421   wxDocChildFrame *subframe = new wxDocChildFrame(doc, view, theApp->getMainFrame(), -1, "Plot Frame", wxPoint(10, 10), wxSize(500, 300), wxDEFAULT_FRAME_STYLE);
1422   
1423   wxMenu *file_menu = new wxMenu;
1424   
1425   file_menu->Append(MAINMENU_FILE_CREATE_PHANTOM, "Cr&eate Phantom...");
1426   file_menu->Append(wxID_OPEN, "&Open...");
1427   file_menu->Append(wxID_SAVE, "&Save");
1428   file_menu->Append(wxID_SAVEAS, "Save &As...");
1429   file_menu->Append(wxID_CLOSE, "&Close");
1430   
1431   file_menu->AppendSeparator();
1432   file_menu->Append(PJMENU_FILE_PROPERTIES, "P&roperties");
1433   
1434   file_menu->AppendSeparator();
1435   file_menu->Append(wxID_PRINT, "&Print...");
1436   file_menu->Append(wxID_PRINT_SETUP, "Print &Setup...");
1437   file_menu->Append(wxID_PREVIEW, "Print Pre&view");
1438   
1439   wxMenu *view_menu = new wxMenu;\r
1440   view_menu->Append(PLOTMENU_VIEW_SCALE_MINMAX, "Display Scale &Set...");\r
1441   view_menu->Append(PLOTMENU_VIEW_SCALE_AUTO, "Display Scale &Auto...");\r
1442   \r
1443   wxMenu *help_menu = new wxMenu;
1444   help_menu->Append(MAINMENU_HELP_CONTENTS, "&Contents");
1445   help_menu->AppendSeparator();
1446   help_menu->Append(MAINMENU_HELP_ABOUT, "&About");
1447   
1448   wxMenuBar *menu_bar = new wxMenuBar;
1449   
1450   menu_bar->Append(file_menu, "&File");\r
1451   menu_bar->Append(view_menu, "&View");
1452   menu_bar->Append(help_menu, "&Help");
1453   
1454   subframe->SetMenuBar(menu_bar);
1455   
1456   subframe->Centre(wxBOTH);
1457   
1458   return subframe;
1459 }
1460
1461
1462 bool 
1463 PlotFileView::OnCreate(wxDocument *doc, long WXUNUSED(flags) )
1464 {
1465   m_frame = CreateChildFrame(doc, this);
1466   SetFrame(m_frame);
1467   
1468   m_bMinSpecified = false;\r
1469   m_bMaxSpecified = false;\r
1470   m_dAutoScaleFactor = 1.;\r
1471   \r
1472   int width, height;
1473   m_frame->GetClientSize(&width, &height);
1474   m_frame->SetTitle ("Plot File");
1475   m_canvas = CreateCanvas (this, m_frame);
1476   
1477 #ifdef __X__
1478   int x, y;  // X requires a forced resize
1479   m_frame->GetSize(&x, &y);
1480   m_frame->SetSize(-1, -1, x, y);
1481 #endif
1482   
1483   m_frame->Show(true);
1484   Activate(true);
1485   
1486   return true;
1487 }
1488
1489 void 
1490 PlotFileView::OnDraw (wxDC* dc)
1491 {
1492   const PlotFile& rPlotFile = GetDocument()->getPlotFile();\r
1493   const int iNColumns = rPlotFile.getNumColumns();\r
1494   const int iNRecords = rPlotFile.getNumRecords();\r
1495   \r
1496   if (iNColumns > 0 && iNRecords > 0) {\r
1497     int xsize, ysize;\r
1498     m_canvas->GetClientSize (&xsize, &ysize);\r
1499     SGPDriver driver (dc, xsize, ysize);\r
1500     SGP sgp (driver);\r
1501     const PlotFile& rPhantom = GetDocument()->getPlotFile();\r
1502     EZPlot plot (sgp);\r
1503     \r
1504     if (! rPlotFile.getTitle().empty()) {\r
1505       std::string s("title ");\r
1506       s += rPlotFile.getTitle();\r
1507       plot.ezset (s);\r
1508     }\r
1509     if (! rPlotFile.getXLabel().empty()) {\r
1510       std::string s("xlabel ");\r
1511       s += rPlotFile.getXLabel();\r
1512       plot.ezset (s);\r
1513     }\r
1514     if (! rPlotFile.getYLabel().empty()) {\r
1515       std::string s("ylabel ");\r
1516       s += rPlotFile.getYLabel();\r
1517       plot.ezset (s);\r
1518     }\r
1519     \r
1520     if (m_bMinSpecified) {\r
1521       std::ostringstream os;\r
1522       os << "ymin " << m_dMinPixel;\r
1523       plot.ezset (os.str());\r
1524     }\r
1525     \r
1526     if (m_bMaxSpecified) {\r
1527       std::ostringstream os;\r
1528       os << "ymax " << m_dMaxPixel;\r
1529       plot.ezset (os.str());\r
1530     }\r
1531     \r
1532     plot.ezset("box");\r
1533     plot.ezset("grid");\r
1534     \r
1535     double* pdXaxis = new double [iNRecords];\r
1536     rPlotFile.getColumn (0, pdXaxis);\r
1537     \r
1538     double* pdY = new double [iNRecords];\r
1539     for (int iCol = 1; iCol < iNColumns; iCol++) {\r
1540       rPlotFile.getColumn (iCol, pdY);\r
1541       plot.addCurve (pdXaxis, pdY, iNRecords);\r
1542     }\r
1543     \r
1544     delete pdXaxis;\r
1545     delete pdY;\r
1546     \r
1547     plot.plot();\r
1548   }\r
1549 }
1550
1551
1552 void 
1553 PlotFileView::OnUpdate(wxView *WXUNUSED(sender), wxObject *WXUNUSED(hint) )
1554 {
1555   if (m_canvas)
1556     m_canvas->Refresh();
1557 }
1558
1559 bool 
1560 PlotFileView::OnClose (bool deleteWindow)
1561 {
1562   if (!GetDocument()->Close())
1563     return false;
1564   
1565   m_canvas->Clear();
1566   m_canvas->m_pView = NULL;
1567   m_canvas = NULL;
1568   wxString s(wxTheApp->GetAppName());
1569   if (m_frame)
1570     m_frame->SetTitle(s);
1571   SetFrame(NULL);
1572   
1573   Activate(false);
1574   
1575   if (deleteWindow) {
1576     delete m_frame;
1577     return true;
1578   }
1579   return true;
1580 }
1581