r312: *** empty log message ***
[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.37 2000/12/23 18:12:35 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 os;\r
497       os << "Row " << yCursor;\r
498       std::string title("title ");\r
499       title += os.str();\r
500       rPlotFile.addEzsetCommand (title.c_str());\r
501       rPlotFile.addEzsetCommand ("xlabel Column");\r
502       rPlotFile.addEzsetCommand ("ylabel Pixel Value");\r
503       rPlotFile.addEzsetCommand ("box");\r
504       rPlotFile.addEzsetCommand ("grid");\r
505       rPlotFile.setCurveSize (2, nx);\r
506       rPlotFile.addColumn (0, pX);\r
507       rPlotFile.addColumn (1, pY);\r
508     }\r
509     delete pX;\r
510     delete pY;\r
511     pPlotDoc->Modify(true);\r
512   }\r
513   \r
514 }\r
515
516 void\r
517 ImageFileView::OnPlotCol (wxCommandEvent& event)\r
518 {\r
519   int xCursor, yCursor;\r
520   if (! m_canvas->GetCurrentCursor (xCursor, yCursor)) {\r
521     wxMessageBox ("No column selected. Please use left mouse button on image to select column","Error");\r
522     return;\r
523   }\r
524   \r
525   const ImageFile& rIF = dynamic_cast<ImageFileDocument*>(GetDocument())->getImageFile();\r
526   ImageFileArrayConst v = rIF.getArray();\r
527   int nx = rIF.nx();\r
528   int ny = rIF.ny();\r
529   \r
530   if (v != NULL && xCursor < nx) {\r
531     double* pX = new double [ny];\r
532     double* pY = new double [ny];\r
533     for (int i = 0; i < ny; i++) {\r
534       pX[i] = i;\r
535       pY[i] = v[xCursor][i];\r
536     }\r
537     PlotFileDocument* pPlotDoc = dynamic_cast<PlotFileDocument*>(theApp->getDocManager()->CreateDocument("untitled.plt", wxDOC_SILENT));\r
538     if (! pPlotDoc) {\r
539       sys_error (ERR_SEVERE, "Internal error: unable to create Plot file");\r
540     } else {\r
541       PlotFile& rPlotFile = pPlotDoc->getPlotFile();\r
542       std::ostringstream os;\r
543       os << "Column " << xCursor;\r
544       std::string title("title ");\r
545       title += os.str();\r
546       rPlotFile.addEzsetCommand (title.c_str());\r
547       rPlotFile.addEzsetCommand ("xlabel Row");\r
548       rPlotFile.addEzsetCommand ("ylabel Pixel Value");\r
549       rPlotFile.addEzsetCommand ("box");\r
550       rPlotFile.addEzsetCommand ("grid");\r
551       rPlotFile.setCurveSize (2, nx);\r
552       rPlotFile.addColumn (0, pX);\r
553       rPlotFile.addColumn (1, pY);\r
554     }\r
555     delete pX;\r
556     delete pY;\r
557     pPlotDoc->Modify(true);\r
558   }\r
559 }\r
560 \r
561 void\r
562 ImageFileView::OnCompareCol (wxCommandEvent& event)\r
563 {\r
564   int xCursor, yCursor;\r
565   if (! m_canvas->GetCurrentCursor (xCursor, yCursor)) {\r
566     wxMessageBox ("No column selected. Please use left mouse button on image to select column","Error");\r
567     return;\r
568   }\r
569   \r
570   std::vector<ImageFileDocument*> vecIFDoc;\r
571   theApp->getCompatibleImages (GetDocument(), vecIFDoc);\r
572   if (vecIFDoc.size() == 0) {\r
573     wxMessageBox ("No compatible images for Column Comparison", "Error");\r
574     return;\r
575   }\r
576   DialogGetComparisonImage dialogGetCompare (m_frame, "Get Comparison Image", vecIFDoc, false);\r
577   \r
578   if (dialogGetCompare.ShowModal() == wxID_OK) {\r
579     ImageFileDocument* pCompareDoc = dialogGetCompare.getImageFileDocument();\r
580     const ImageFile& rIF = GetDocument()->getImageFile();\r
581     const ImageFile& rCompareIF = pCompareDoc->getImageFile();\r
582     \r
583     ImageFileArrayConst v1 = rIF.getArray();\r
584     ImageFileArrayConst v2 = rCompareIF.getArray();\r
585     int nx = rIF.nx();\r
586     int ny = rIF.ny();\r
587     \r
588     if (v1 != NULL && xCursor < nx) {\r
589       double* pX = new double [ny];\r
590       double* pY1 = new double [ny];\r
591       double* pY2 = new double [ny];\r
592       for (int i = 0; i < ny; i++) {\r
593         pX[i] = i;\r
594         pY1[i] = v1[xCursor][i];\r
595         pY2[i] = v2[xCursor][i];\r
596       }\r
597       PlotFileDocument* pPlotDoc = dynamic_cast<PlotFileDocument*>(theApp->getDocManager()->CreateDocument("untitled.plt", wxDOC_SILENT));\r
598       if (! pPlotDoc) {\r
599         sys_error (ERR_SEVERE, "Internal error: unable to create Plot file");\r
600       } else {\r
601         PlotFile& rPlotFile = pPlotDoc->getPlotFile();\r
602         std::ostringstream os;\r
603         os << "Column " << xCursor << " Comparison";\r
604         std::string title("title ");\r
605         title += os.str();\r
606         rPlotFile.addEzsetCommand (title.c_str());\r
607         rPlotFile.addEzsetCommand ("xlabel Row");\r
608         rPlotFile.addEzsetCommand ("ylabel Pixel Value");\r
609         rPlotFile.addEzsetCommand ("box");\r
610         rPlotFile.addEzsetCommand ("grid");\r
611         rPlotFile.setCurveSize (3, nx);\r
612         rPlotFile.addColumn (0, pX);\r
613         rPlotFile.addColumn (1, pY1);\r
614         rPlotFile.addColumn (2, pY2);\r
615       }\r
616       delete pX;\r
617       delete pY1;\r
618       delete pY2;\r
619       pPlotDoc->Modify(true);\r
620     }\r
621   }\r
622 }\r
623
624 void\r
625 ImageFileView::OnCompareRow (wxCommandEvent& event)\r
626 {\r
627   int xCursor, yCursor;\r
628   if (! m_canvas->GetCurrentCursor (xCursor, yCursor)) {\r
629     wxMessageBox ("No column selected. Please use left mouse button on image to select column","Error");\r
630     return;\r
631   }\r
632   \r
633   std::vector<ImageFileDocument*> vecIFDoc;\r
634   theApp->getCompatibleImages (GetDocument(), vecIFDoc);\r
635   \r
636   if (vecIFDoc.size() == 0) {\r
637     wxMessageBox ("No compatible images for Row Comparison", "Error");\r
638     return;\r
639   }\r
640 \r
641   DialogGetComparisonImage dialogGetCompare (m_frame, "Get Comparison Image", vecIFDoc, false);\r
642   \r
643   if (dialogGetCompare.ShowModal() == wxID_OK) {\r
644     ImageFileDocument* pCompareDoc = dialogGetCompare.getImageFileDocument();\r
645     const ImageFile& rIF = GetDocument()->getImageFile();\r
646     const ImageFile& rCompareIF = pCompareDoc->getImageFile();\r
647     \r
648     ImageFileArrayConst v1 = rIF.getArray();\r
649     ImageFileArrayConst v2 = rCompareIF.getArray();\r
650     int nx = rIF.nx();\r
651     int ny = rIF.ny();\r
652     \r
653     if (v1 != NULL && yCursor < ny) {\r
654       double* pX = new double [nx];\r
655       double* pY1 = new double [nx];\r
656       double* pY2 = new double [nx];\r
657       for (int i = 0; i < nx; i++) {\r
658         pX[i] = i;\r
659         pY1[i] = v1[i][yCursor];\r
660         pY2[i] = v2[i][yCursor];\r
661       }\r
662       PlotFileDocument* pPlotDoc = dynamic_cast<PlotFileDocument*>(theApp->getDocManager()->CreateDocument("untitled.plt", wxDOC_SILENT));\r
663       if (! pPlotDoc) {\r
664         sys_error (ERR_SEVERE, "Internal error: unable to create Plot file");\r
665       } else {\r
666         PlotFile& rPlotFile = pPlotDoc->getPlotFile();\r
667         std::ostringstream os;\r
668         os << "Row " << yCursor << " Comparison";\r
669         std::string title("title ");\r
670         title += os.str();\r
671         rPlotFile.addEzsetCommand (title.c_str());\r
672         rPlotFile.addEzsetCommand ("xlabel Column");\r
673         rPlotFile.addEzsetCommand ("ylabel Pixel Value");\r
674         rPlotFile.addEzsetCommand ("box");\r
675         rPlotFile.addEzsetCommand ("grid");\r
676         rPlotFile.setCurveSize (3, ny);\r
677         rPlotFile.addColumn (0, pX);\r
678         rPlotFile.addColumn (1, pY1);\r
679         rPlotFile.addColumn (2, pY2);\r
680       }\r
681       delete pX;\r
682       delete pY1;\r
683       delete pY2;\r
684       pPlotDoc->Modify(true);\r
685     }\r
686   }\r
687 }\r
688 \r
689 \r
690 // PhantomCanvas
691
692 PhantomCanvas::PhantomCanvas (PhantomView* v, wxFrame *frame, const wxPoint& pos, const wxSize& size, const long style)
693 : wxScrolledWindow(frame, -1, pos, size, style)
694 {
695   m_pView = v;
696 }
697
698 void 
699 PhantomCanvas::OnDraw(wxDC& dc)
700 {
701   if (m_pView)
702     m_pView->OnDraw(& dc);
703 }
704
705
706 // PhantomView
707
708 IMPLEMENT_DYNAMIC_CLASS(PhantomView, wxView)
709
710 BEGIN_EVENT_TABLE(PhantomView, wxView)
711 EVT_MENU(PHMMENU_FILE_PROPERTIES, PhantomView::OnProperties)
712 EVT_MENU(PHMMENU_PROCESS_RASTERIZE, PhantomView::OnRasterize)
713 EVT_MENU(PHMMENU_PROCESS_PROJECTIONS, PhantomView::OnProjections)
714 END_EVENT_TABLE()
715
716 PhantomView::PhantomView(void) 
717 : wxView(), m_canvas(NULL), m_frame(NULL)
718 {
719   m_iDefaultNDet = 367;
720   m_iDefaultNView = 320;
721   m_iDefaultNSample = 2;
722   m_dDefaultRotation = 2;
723   m_dDefaultFocalLength = 2;
724   m_dDefaultFieldOfView = 1;
725   m_iDefaultGeometry = Scanner::GEOMETRY_PARALLEL;
726   m_iDefaultTrace = Trace::TRACE_NONE;
727 }
728
729 PhantomView::~PhantomView(void)
730 {
731 }
732
733 void
734 PhantomView::OnProperties (wxCommandEvent& event)
735 {
736   const int idPhantom = GetDocument()->getPhantomID();
737   const wxString& namePhantom = GetDocument()->getPhantomName();
738   std::ostringstream os;
739   os << "Phantom " << namePhantom.c_str() << " (" << idPhantom << ")\n";
740   *theApp->getLog() << os.str().c_str();
741 #if DEBUG
742   const Phantom& rPhantom = GetDocument()->getPhantom();
743   rPhantom.print();
744 #endif
745 }
746
747
748 void
749 PhantomView::OnProjections (wxCommandEvent& event)
750 {
751   DialogGetProjectionParameters dialogProjection (m_frame, m_iDefaultNDet, m_iDefaultNView, m_iDefaultNSample, m_dDefaultRotation, m_dDefaultFocalLength, m_dDefaultFieldOfView, m_iDefaultGeometry, m_iDefaultTrace);
752   int retVal = dialogProjection.ShowModal();
753   if (retVal == wxID_OK) {
754     m_iDefaultNDet = dialogProjection.getNDet();
755     m_iDefaultNView = dialogProjection.getNView();
756     m_iDefaultNSample = dialogProjection.getNSamples();
757     m_iDefaultTrace = dialogProjection.getTrace();
758     m_dDefaultRotation = dialogProjection.getRotAngle();
759     m_dDefaultFocalLength = dialogProjection.getFocalLengthRatio();
760     m_dDefaultFieldOfView = dialogProjection.getFieldOfViewRatio();
761     wxString sGeometry = dialogProjection.getGeometry();
762     m_iDefaultGeometry = Scanner::convertGeometryNameToID (sGeometry.c_str());
763     
764     if (m_iDefaultNDet > 0 && m_iDefaultNView > 0 && sGeometry != "") {
765       const Phantom& rPhantom = GetDocument()->getPhantom();
766       ProjectionFileDocument* pProjectionDoc = dynamic_cast<ProjectionFileDocument*>(theApp->getDocManager()->CreateDocument("untitled.pj", wxDOC_SILENT));\r
767       if (! pProjectionDoc) {\r
768         sys_error (ERR_SEVERE, "Unable to create projection document");\r
769         return;\r
770       }
771       Projections& rProj = pProjectionDoc->getProjections();
772       Scanner theScanner (rPhantom, sGeometry.c_str(), m_iDefaultNDet, m_iDefaultNView, m_iDefaultNSample, m_dDefaultRotation, m_dDefaultFocalLength, m_dDefaultFieldOfView);
773       if (theScanner.fail()) {
774         *theApp->getLog() << "Failed making scanner: " << theScanner.failMessage().c_str() << "\n";
775         return;
776       }
777       rProj.initFromScanner (theScanner);
778       m_dDefaultRotation /= PI;  // convert back to PI units
779       
780       if (m_iDefaultTrace > Trace::TRACE_CONSOLE) {
781         ProjectionsDialog dialogProjections (theScanner, rProj, rPhantom, m_iDefaultTrace, dynamic_cast<wxWindow*>(m_frame));
782         for (int iView = 0; iView < rProj.nView(); iView++) {
783           ::wxYield();
784           ::wxYield();
785           if (dialogProjections.isCancelled() || ! dialogProjections.projectView (iView)) {
786             pProjectionDoc->DeleteAllViews();
787             return;
788           }
789           ::wxYield();
790           ::wxYield();
791           while (dialogProjections.isPaused()) {
792             ::wxYield();
793             ::wxUsleep(50);
794           }
795         }
796       } else {
797         wxProgressDialog dlgProgress (wxString("Projection"), wxString("Projection Progress"), rProj.nView() + 1, m_frame, wxPD_CAN_ABORT);
798         for (int i = 0; i < rProj.nView(); i++) {
799           theScanner.collectProjections (rProj, rPhantom, i, 1, true, m_iDefaultTrace);
800           if (! dlgProgress.Update (i+1)) {
801             pProjectionDoc->DeleteAllViews();
802             return;
803           }
804         }
805       }
806       
807       std::ostringstream os;
808       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();
809       rProj.setRemark (os.str());
810       *theApp->getLog() << os.str().c_str() << "\n";
811       
812       m_frame->Lower();
813       ::wxYield();
814       ProjectionFileView* projView = dynamic_cast<ProjectionFileView*>(pProjectionDoc->GetFirstView());\r
815       if (projView) {\r
816         projView->getFrame()->SetFocus();\r
817         projView->OnUpdate (projView, NULL);\r
818       }\r
819       if (wxView* pView = pProjectionDoc->GetFirstView()) {
820         if (wxFrame* pFrame = pView->GetFrame()) {
821           pFrame->SetFocus();
822           pFrame->Raise();
823         }
824         theApp->getDocManager()->ActivateView (pView, true, false);
825       }
826       ::wxYield();
827       pProjectionDoc->Modify(true);
828       pProjectionDoc->UpdateAllViews(this);
829     }
830   }
831 }
832
833
834 void
835 PhantomView::OnRasterize (wxCommandEvent& event)
836 {
837   DialogGetRasterParameters dialogRaster (m_frame, 256, 256, 1);
838   int retVal = dialogRaster.ShowModal();
839   if (retVal == wxID_OK) {
840     int xSize = dialogRaster.getXSize();
841     int ySize = dialogRaster.getYSize();
842     int nSamples = dialogRaster.getNSamples();
843     if (nSamples < 1)
844       nSamples = 1;
845     if (xSize > 0 && ySize > 0) {
846       const Phantom& rPhantom = GetDocument()->getPhantom();
847       ImageFileDocument* pRasterDoc = dynamic_cast<ImageFileDocument*>(theApp->getDocManager()->CreateDocument("untitled.if", wxDOC_SILENT));\r
848       if (! pRasterDoc) {\r
849         sys_error (ERR_SEVERE, "Unable to create image file");\r
850         return;\r
851       }
852       ImageFile& imageFile = pRasterDoc->getImageFile();
853       
854       imageFile.setArraySize (xSize, ySize);
855       wxProgressDialog dlgProgress (wxString("Rasterize"), wxString("Rasterization Progress"), imageFile.nx() + 1, m_frame, wxPD_CAN_ABORT);
856       for (unsigned int i = 0; i < imageFile.nx(); i++) {
857         rPhantom.convertToImagefile (imageFile, nSamples, Trace::TRACE_NONE, i, 1, true);
858         if (! dlgProgress.Update(i+1)) {
859           pRasterDoc->DeleteAllViews();
860           return;
861         }
862       }
863       pRasterDoc->Modify(true);
864       pRasterDoc->UpdateAllViews(this);
865       ImageFileView* rasterView = dynamic_cast<ImageFileView*>(pRasterDoc->GetFirstView());\r
866       if (rasterView) {\r
867         rasterView->getFrame()->SetFocus();\r
868         rasterView->OnUpdate (rasterView, NULL);\r
869       }\r
870       
871       std::ostringstream os;
872       os << "Rasterize Phantom " << rPhantom.name() << ": XSize=" << xSize << ", YSize=" << ySize << ", nSamples=" << nSamples << "\n";
873       *theApp->getLog() << os.str().c_str();
874     }\r
875   }
876 }
877
878
879 PhantomCanvas* 
880 PhantomView::CreateCanvas (wxView *view, wxFrame *parent)
881 {
882   PhantomCanvas* pCanvas;
883   int width, height;
884   parent->GetClientSize(&width, &height);
885   
886   pCanvas = new PhantomCanvas (dynamic_cast<PhantomView*>(view), parent, wxPoint(0, 0), wxSize(width, height), 0);
887   
888   pCanvas->SetBackgroundColour(*wxWHITE);
889   pCanvas->Clear();
890   
891   return pCanvas;
892 }
893
894 wxFrame*
895 PhantomView::CreateChildFrame(wxDocument *doc, wxView *view)
896 {
897   wxDocChildFrame *subframe = new wxDocChildFrame(doc, view, theApp->getMainFrame(), -1, "Phantom Frame", wxPoint(10, 10), wxSize(256, 256), wxDEFAULT_FRAME_STYLE);
898   
899   wxMenu *file_menu = new wxMenu;
900   
901   file_menu->Append(MAINMENU_FILE_CREATE_PHANTOM, "Cr&eate Phantom...");
902   file_menu->Append(wxID_OPEN, "&Open...");
903   file_menu->Append(wxID_CLOSE, "&Close");
904   
905   file_menu->AppendSeparator();
906   file_menu->Append(PHMMENU_FILE_PROPERTIES, "P&roperties");
907   
908   file_menu->AppendSeparator();
909   file_menu->Append(wxID_PRINT, "&Print...");
910   file_menu->Append(wxID_PRINT_SETUP, "Print &Setup...");
911   file_menu->Append(wxID_PREVIEW, "Print Pre&view");
912   
913   wxMenu *process_menu = new wxMenu;
914   process_menu->Append(PHMMENU_PROCESS_RASTERIZE, "&Rasterize...");
915   process_menu->Append(PHMMENU_PROCESS_PROJECTIONS, "&Projections...");
916   
917   wxMenu *help_menu = new wxMenu;
918   help_menu->Append(MAINMENU_HELP_CONTENTS, "&Contents");
919   help_menu->Append(MAINMENU_HELP_ABOUT, "&About");
920   
921   wxMenuBar *menu_bar = new wxMenuBar;
922   
923   menu_bar->Append(file_menu, "&File");
924   menu_bar->Append(process_menu, "&Process");
925   menu_bar->Append(help_menu, "&Help");
926   
927   subframe->SetMenuBar(menu_bar);
928   
929   subframe->Centre(wxBOTH);
930   
931   return subframe;
932 }
933
934
935 bool 
936 PhantomView::OnCreate(wxDocument *doc, long WXUNUSED(flags) )
937 {
938   m_frame = CreateChildFrame(doc, this);
939   SetFrame(m_frame);
940   
941   int width, height;
942   m_frame->GetClientSize(&width, &height);
943   m_frame->SetTitle("PhantomView");
944   m_canvas = CreateCanvas(this, m_frame);
945   
946 #ifdef __X__
947   int x, y;  // X requires a forced resize
948   m_frame->GetSize(&x, &y);
949   m_frame->SetSize(-1, -1, x, y);
950 #endif
951   
952   m_frame->Show(true);
953   Activate(true);
954   
955   return true;
956 }
957
958
959 void 
960 PhantomView::OnUpdate(wxView *WXUNUSED(sender), wxObject *WXUNUSED(hint) )
961 {
962   if (m_canvas)
963     m_canvas->Refresh();
964 }
965
966 bool 
967 PhantomView::OnClose (bool deleteWindow)
968 {
969   if (!GetDocument()->Close())
970     return false;
971   
972   m_canvas->Clear();
973   m_canvas->m_pView = NULL;
974   m_canvas = NULL;
975   wxString s(wxTheApp->GetAppName());
976   if (m_frame)
977     m_frame->SetTitle(s);
978   SetFrame(NULL);
979   
980   Activate(false);
981   
982   if (deleteWindow) {
983     delete m_frame;
984     return true;
985   }
986   return true;
987 }
988
989 void
990 PhantomView::OnDraw (wxDC* dc)
991 {
992   int xsize, ysize;
993   m_canvas->GetClientSize (&xsize, &ysize);
994   SGPDriver driver (dc, xsize, ysize);
995   SGP sgp (driver);
996   const Phantom& rPhantom = GetDocument()->getPhantom();
997   sgp.setColor (C_RED);
998   rPhantom.show (sgp);
999 }
1000
1001 // ProjectionCanvas
1002
1003 ProjectionFileCanvas::ProjectionFileCanvas (ProjectionFileView* v, wxFrame *frame, const wxPoint& pos, const wxSize& size, const long style)
1004 : wxScrolledWindow(frame, -1, pos, size, style)
1005 {
1006   m_pView = v;
1007 }
1008
1009 void 
1010 ProjectionFileCanvas::OnDraw(wxDC& dc)
1011 {
1012   if (m_pView)
1013     m_pView->OnDraw(& dc);
1014 }
1015
1016 // ProjectionFileView
1017
1018 IMPLEMENT_DYNAMIC_CLASS(ProjectionFileView, wxView)
1019
1020 BEGIN_EVENT_TABLE(ProjectionFileView, wxView)
1021 EVT_MENU(PJMENU_FILE_PROPERTIES, ProjectionFileView::OnProperties)
1022 EVT_MENU(PJMENU_PROCESS_RECONSTRUCT, ProjectionFileView::OnReconstruct)
1023 END_EVENT_TABLE()
1024
1025 ProjectionFileView::ProjectionFileView(void) 
1026 : wxView(), m_canvas(NULL), m_frame(NULL)
1027 {
1028   m_iDefaultNX = 256;
1029   m_iDefaultNY = 256;
1030   m_iDefaultFilter = SignalFilter::FILTER_ABS_BANDLIMIT;
1031   m_dDefaultFilterParam = 1.;
1032 #if HAVE_FFTW
1033   m_iDefaultFilterMethod = ProcessSignal::FILTER_METHOD_RFFTW;
1034   m_iDefaultFilterGeneration = ProcessSignal::FILTER_GENERATION_INVERSE_FOURIER;
1035 #else
1036   m_iDefaultFilterMethod = ProcessSignal::FILTER_METHOD_CONVOLUTION;
1037   m_iDefaultFilterGeneration = ProcessSignal::FILTER_GENERATION_DIRECT;
1038 #endif
1039   m_iDefaultZeropad = 1;
1040   m_iDefaultBackprojector = Backprojector::BPROJ_IDIFF3;
1041   m_iDefaultInterpolation = Backprojector::INTERP_LINEAR;
1042   m_iDefaultInterpParam = 1;
1043   m_iDefaultTrace = Trace::TRACE_NONE;
1044 }
1045
1046 ProjectionFileView::~ProjectionFileView(void)
1047 {
1048 }
1049
1050 void
1051 ProjectionFileView::OnProperties (wxCommandEvent& event)
1052 {
1053   const Projections& rProj = GetDocument()->getProjections();
1054   std::ostringstream os;
1055   rProj.printScanInfo(os);
1056   *theApp->getLog() << os.str().c_str();
1057   wxMessageDialog dialogMsg (m_frame, os.str().c_str(), "Projection File Properties", wxOK | wxICON_INFORMATION);
1058   dialogMsg.ShowModal();
1059 }
1060
1061
1062 void
1063 ProjectionFileView::OnReconstruct (wxCommandEvent& event)
1064 {
1065   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);
1066   
1067   int retVal = dialogReconstruction.ShowModal();
1068   if (retVal == wxID_OK) {
1069     m_iDefaultNX = dialogReconstruction.getXSize();
1070     m_iDefaultNY = dialogReconstruction.getYSize();
1071     wxString optFilterName = dialogReconstruction.getFilterName();
1072     m_iDefaultFilter = SignalFilter::convertFilterNameToID (optFilterName.c_str());
1073     m_dDefaultFilterParam = dialogReconstruction.getFilterParam();
1074     wxString optFilterMethodName = dialogReconstruction.getFilterMethodName();
1075     m_iDefaultFilterMethod = ProcessSignal::convertFilterMethodNameToID(optFilterMethodName.c_str());
1076     m_iDefaultZeropad = dialogReconstruction.getZeropad();
1077     wxString optFilterGenerationName = dialogReconstruction.getFilterGenerationName();
1078     m_iDefaultFilterGeneration = ProcessSignal::convertFilterGenerationNameToID (optFilterGenerationName.c_str());
1079     wxString optInterpName = dialogReconstruction.getInterpName();
1080     m_iDefaultInterpolation = Backprojector::convertInterpNameToID (optInterpName.c_str());
1081     m_iDefaultInterpParam = dialogReconstruction.getInterpParam();
1082     wxString optBackprojectName = dialogReconstruction.getBackprojectName();
1083     m_iDefaultBackprojector = Backprojector::convertBackprojectNameToID (optBackprojectName.c_str());
1084     m_iDefaultTrace = dialogReconstruction.getTrace();
1085     if (m_iDefaultNX > 0 && m_iDefaultNY > 0) {
1086       ImageFileDocument* pReconDoc = dynamic_cast<ImageFileDocument*>(theApp->getDocManager()->CreateDocument("untitled.if", wxDOC_SILENT));\r
1087       if (! pReconDoc) {\r
1088         sys_error (ERR_SEVERE, "Unable to create image file");\r
1089         return;\r
1090       }
1091       ImageFile& imageFile = pReconDoc->getImageFile();
1092       const Projections& rProj = GetDocument()->getProjections();
1093       imageFile.setArraySize (m_iDefaultNX, m_iDefaultNY);
1094       
1095       if (m_iDefaultFilterMethod != ProcessSignal::FILTER_METHOD_CONVOLUTION && m_iDefaultFilterGeneration == ProcessSignal::FILTER_GENERATION_DIRECT && rProj.geometry() != Scanner::GEOMETRY_PARALLEL) {
1096         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);
1097         return;
1098       }
1099 #if 0
1100       SGPDriver* pSGPDriver = NULL;
1101       SGP* pSGP = NULL;
1102       wxMemoryDC* pDCPlot = NULL;
1103       wxBitmap bitmap;
1104       if (m_iDefaultTrace >= Trace::TRACE_PLOT) {
1105         bitmap.Create (500, 500);
1106         pDCPlot = new wxMemoryDC;
1107         pDCPlot->SelectObject (bitmap);
1108         pSGPDriver = new SGPDriver (dynamic_cast<wxDC*>pDCPlot, 500, 500);
1109         pSGP = new SGP (*pSGPDriver);
1110       }
1111       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);
1112       delete pSGP;
1113 #else
1114       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);
1115 #endif
1116       \r
1117       Timer timerRecon;
1118       if (m_iDefaultTrace > Trace::TRACE_CONSOLE) {
1119         ReconstructDialog* pDlgReconstruct = new ReconstructDialog (*pReconstruct, rProj, imageFile, m_iDefaultTrace, m_frame);
1120         for (int iView = 0; iView < rProj.nView(); iView++) {
1121           ::wxYield();
1122           ::wxYield();
1123           if (pDlgReconstruct->isCancelled() || ! pDlgReconstruct->reconstructView (iView)) {
1124             delete pDlgReconstruct;
1125             delete pReconstruct;
1126             pReconDoc->DeleteAllViews();
1127             return;
1128           }
1129           ::wxYield();
1130           ::wxYield();
1131           while (pDlgReconstruct->isPaused()) {
1132             ::wxYield();
1133             ::wxUsleep(50);
1134           }
1135         }
1136         delete pDlgReconstruct;
1137       } else {
1138         wxProgressDialog dlgProgress (wxString("Reconstruction"), wxString("Reconstruction Progress"), rProj.nView() + 1, m_frame, wxPD_CAN_ABORT);
1139         for (int i = 0; i < rProj.nView(); i++) {
1140           pReconstruct->reconstructView (i, 1);
1141           if (! dlgProgress.Update(i + 1)) {
1142             delete pReconstruct;
1143             pReconDoc->DeleteAllViews();
1144             return;
1145           }
1146         }
1147       }
1148       delete pReconstruct;
1149       pReconDoc->Modify(true);
1150       pReconDoc->UpdateAllViews(this);
1151       ImageFileView* rasterView = dynamic_cast<ImageFileView*>(pReconDoc->GetFirstView());\r
1152       if (rasterView) {\r
1153         rasterView->getFrame()->SetFocus();\r
1154         rasterView->OnUpdate (rasterView, NULL);\r
1155       }\r
1156       std::ostringstream os;
1157       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();
1158       *theApp->getLog() << os.str().c_str() << "\n";
1159       imageFile.labelAdd (rProj.getLabel());
1160       imageFile.labelAdd (Array2dFileLabel::L_HISTORY, os.str().c_str(), timerRecon.timerEnd());
1161     }
1162   }
1163 }
1164
1165
1166 ProjectionFileCanvas* 
1167 ProjectionFileView::CreateCanvas (wxView *view, wxFrame *parent)
1168 {
1169   ProjectionFileCanvas* pCanvas;
1170   int width, height;
1171   parent->GetClientSize(&width, &height);
1172   
1173   pCanvas = new ProjectionFileCanvas (dynamic_cast<ProjectionFileView*>(view), parent, wxPoint(0, 0), wxSize(width, height), 0);
1174   
1175   pCanvas->SetScrollbars(20, 20, 50, 50);
1176   pCanvas->SetBackgroundColour(*wxWHITE);
1177   pCanvas->Clear();
1178   
1179   return pCanvas;
1180 }
1181
1182 wxFrame*
1183 ProjectionFileView::CreateChildFrame(wxDocument *doc, wxView *view)
1184 {
1185   wxDocChildFrame *subframe = new wxDocChildFrame(doc, view, theApp->getMainFrame(), -1, "Projection Frame", wxPoint(10, 10), wxSize(0, 0), wxDEFAULT_FRAME_STYLE);
1186   
1187   wxMenu *file_menu = new wxMenu;
1188   
1189   file_menu->Append(MAINMENU_FILE_CREATE_PHANTOM, "Cr&eate Phantom...");
1190   file_menu->Append(wxID_OPEN, "&Open...");
1191   file_menu->Append(wxID_SAVE, "&Save");
1192   file_menu->Append(wxID_SAVEAS, "Save &As...");
1193   file_menu->Append(wxID_CLOSE, "&Close");
1194   
1195   file_menu->AppendSeparator();
1196   file_menu->Append(PJMENU_FILE_PROPERTIES, "P&roperties");
1197   
1198   file_menu->AppendSeparator();
1199   file_menu->Append(wxID_PRINT, "&Print...");
1200   file_menu->Append(wxID_PRINT_SETUP, "Print &Setup...");
1201   file_menu->Append(wxID_PREVIEW, "Print Pre&view");
1202   
1203   wxMenu *process_menu = new wxMenu;
1204   process_menu->Append(PJMENU_PROCESS_RECONSTRUCT, "R&econstruct...");
1205   
1206   wxMenu *help_menu = new wxMenu;
1207   help_menu->Append(MAINMENU_HELP_CONTENTS, "&Contents");
1208   help_menu->AppendSeparator();
1209   help_menu->Append(MAINMENU_HELP_ABOUT, "&About");
1210   
1211   wxMenuBar *menu_bar = new wxMenuBar;
1212   
1213   menu_bar->Append(file_menu, "&File");
1214   menu_bar->Append(process_menu, "&Process");
1215   menu_bar->Append(help_menu, "&Help");
1216   
1217   subframe->SetMenuBar(menu_bar);
1218   
1219   subframe->Centre(wxBOTH);
1220   
1221   return subframe;
1222 }
1223
1224
1225 bool 
1226 ProjectionFileView::OnCreate(wxDocument *doc, long WXUNUSED(flags) )
1227 {
1228   m_frame = CreateChildFrame(doc, this);
1229   SetFrame(m_frame);
1230   
1231   int width, height;
1232   m_frame->GetClientSize(&width, &height);
1233   m_frame->SetTitle("ProjectionFileView");
1234   m_canvas = CreateCanvas(this, m_frame);
1235   
1236 #ifdef __X__
1237   int x, y;  // X requires a forced resize
1238   m_frame->GetSize(&x, &y);
1239   m_frame->SetSize(-1, -1, x, y);
1240 #endif
1241   
1242   m_frame->Show(true);
1243   Activate(true);
1244   
1245   return true;
1246 }
1247
1248 void 
1249 ProjectionFileView::OnDraw (wxDC* dc)
1250 {
1251   if (m_bitmap.Ok())
1252     dc->DrawBitmap (m_bitmap, 0, 0, false);
1253 }
1254
1255
1256 void 
1257 ProjectionFileView::OnUpdate(wxView *WXUNUSED(sender), wxObject *WXUNUSED(hint) )
1258 {
1259   const Projections& rProj = GetDocument()->getProjections();
1260   const int nDet = rProj.nDet();
1261   const int nView = rProj.nView();
1262   if (nDet != 0 && nView != 0) {
1263     const DetectorArray& detarray = rProj.getDetectorArray(0);
1264     const DetectorValue* detval = detarray.detValues();
1265     double min = detval[0];
1266     double max = detval[0];
1267     for (int iy = 0; iy < nView; iy++) {
1268       const DetectorArray& detarray = rProj.getDetectorArray(iy);
1269       const DetectorValue* detval = detarray.detValues();
1270       for (int ix = 0; ix < nDet; ix++) {
1271         if (min > detval[ix])
1272           min = detval[ix];
1273         else if (max < detval[ix])
1274           max = detval[ix];
1275       }
1276     }
1277     
1278     unsigned char* imageData = new unsigned char [nDet * nView * 3];
1279     double scale = (max - min) / 255;
1280     for (int iy2 = 0; iy2 < nView; iy2++) {
1281       const DetectorArray& detarray = rProj.getDetectorArray (iy2);
1282       const DetectorValue* detval = detarray.detValues();
1283       for (int ix = 0; ix < nDet; ix++) {
1284         int intensity = static_cast<int>(((detval[ix] - min) / scale) + 0.5);
1285         intensity = clamp(intensity, 0, 255);
1286         int baseAddr = (iy2 * nDet + ix) * 3;
1287         imageData[baseAddr] = imageData[baseAddr+1] = imageData[baseAddr+2] = intensity;
1288       }
1289     }
1290     wxImage image (nDet, nView, imageData, true);
1291     m_bitmap = image.ConvertToBitmap();
1292     delete imageData;
1293     int xSize = nDet;
1294     int ySize = nView;
1295     xSize = clamp (xSize, 0, 800);
1296     ySize = clamp (ySize, 0, 800);
1297     m_frame->SetClientSize (xSize, ySize);
1298     m_canvas->SetScrollbars (20, 20, nDet/20, nView/20);
1299   }
1300   
1301   if (m_canvas)
1302     m_canvas->Refresh();
1303 }
1304
1305 bool 
1306 ProjectionFileView::OnClose (bool deleteWindow)
1307 {
1308   if (!GetDocument()->Close())
1309     return false;
1310   
1311   m_canvas->Clear();
1312   m_canvas->m_pView = NULL;
1313   m_canvas = NULL;
1314   wxString s(wxTheApp->GetAppName());
1315   if (m_frame)
1316     m_frame->SetTitle(s);
1317   SetFrame(NULL);
1318   
1319   Activate(false);
1320   
1321   if (deleteWindow) {
1322     delete m_frame;
1323     return true;
1324   }
1325   return true;
1326 }
1327
1328
1329
1330 // PlotFileCanvas
1331 PlotFileCanvas::PlotFileCanvas (PlotFileView* v, wxFrame *frame, const wxPoint& pos, const wxSize& size, const long style)
1332 : wxScrolledWindow(frame, -1, pos, size, style)
1333 {
1334   m_pView = v;
1335 }
1336
1337 void 
1338 PlotFileCanvas::OnDraw(wxDC& dc)
1339 {
1340   if (m_pView)
1341     m_pView->OnDraw(& dc);
1342 }\r
1343
1344
1345 // PlotFileView
1346
1347 IMPLEMENT_DYNAMIC_CLASS(PlotFileView, wxView)
1348
1349 BEGIN_EVENT_TABLE(PlotFileView, wxView)
1350 EVT_MENU(PJMENU_FILE_PROPERTIES, PlotFileView::OnProperties)
1351 EVT_MENU(PLOTMENU_VIEW_SCALE_MINMAX, PlotFileView::OnScaleMinMax)\r
1352 EVT_MENU(PLOTMENU_VIEW_SCALE_AUTO, PlotFileView::OnScaleAuto)\r
1353 END_EVENT_TABLE()
1354
1355 PlotFileView::PlotFileView(void) 
1356 : wxView(), m_canvas(NULL), m_frame(NULL)
1357 {
1358 }
1359
1360 PlotFileView::~PlotFileView(void)
1361 {
1362 }
1363
1364 void
1365 PlotFileView::OnProperties (wxCommandEvent& event)
1366 {
1367   const PlotFile& rProj = GetDocument()->getPlotFile();
1368   std::ostringstream os;\r
1369   os << "Columns: " << rProj.getNumColumns() << ", Records: " << rProj.getNumRecords() << "\n";
1370   *theApp->getLog() << os.str().c_str();
1371   wxMessageDialog dialogMsg (m_frame, os.str().c_str(), "Plot File Properties", wxOK | wxICON_INFORMATION);
1372   dialogMsg.ShowModal();
1373 }
1374
1375
1376 void \r
1377 PlotFileView::OnScaleAuto (wxCommandEvent& event)\r
1378 {\r
1379   const PlotFile& rPlotFile = GetDocument()->getPlotFile();\r
1380   double min, max, mean, mode, median, stddev;\r
1381   rPlotFile.statistics (1, min, max, mean, mode, median, stddev);\r
1382   DialogAutoScaleParameters dialogAutoScale (m_frame, mean, mode, median, stddev, m_dAutoScaleFactor);\r
1383   int iRetVal = dialogAutoScale.ShowModal();\r
1384   if (iRetVal == wxID_OK) {\r
1385     m_bMinSpecified = true;\r
1386     m_bMaxSpecified = true;\r
1387     double dMin, dMax;\r
1388     if (dialogAutoScale.getMinMax (&dMin, &dMax)) {\r
1389       m_dMinPixel = dMin;\r
1390       m_dMaxPixel = dMax;\r
1391       m_dAutoScaleFactor = dialogAutoScale.getAutoScaleFactor();\r
1392       OnUpdate (this, NULL);\r
1393     }\r
1394   }\r
1395 }\r
1396 \r
1397 void \r
1398 PlotFileView::OnScaleMinMax (wxCommandEvent& event)\r
1399 {\r
1400   const PlotFile& rPlotFile = GetDocument()->getPlotFile();\r
1401   double min, max;\r
1402   \r
1403   if (! m_bMinSpecified && ! m_bMaxSpecified) {\r
1404     if (! rPlotFile.getMinMax (1, min, max)) {\r
1405       *theApp->getLog() << "Error: unable to find Min/Max\n";\r
1406       return;\r
1407     }\r
1408   }\r
1409   \r
1410   if (m_bMinSpecified)\r
1411     min = m_dMinPixel;\r
1412   if (m_bMaxSpecified)\r
1413     max = m_dMaxPixel;\r
1414   \r
1415   DialogGetMinMax dialogMinMax (m_frame, "Set Y-axis Minimum & Maximum", min, max);\r
1416   int retVal = dialogMinMax.ShowModal();\r
1417   if (retVal == wxID_OK) {\r
1418     m_bMinSpecified = true;\r
1419     m_bMaxSpecified = true;\r
1420     m_dMinPixel = dialogMinMax.getMinimum();\r
1421     m_dMaxPixel = dialogMinMax.getMaximum();\r
1422     OnUpdate (this, NULL);\r
1423   }\r
1424 }\r
1425 \r
1426 \r
1427 PlotFileCanvas* 
1428 PlotFileView::CreateCanvas (wxView *view, wxFrame *parent)
1429 {
1430   PlotFileCanvas* pCanvas;
1431   int width, height;
1432   parent->GetClientSize(&width, &height);
1433   
1434   pCanvas = new PlotFileCanvas (dynamic_cast<PlotFileView*>(view), parent, wxPoint(0, 0), wxSize(width, height), 0);
1435   
1436   pCanvas->SetBackgroundColour(*wxWHITE);
1437   pCanvas->Clear();
1438   
1439   return pCanvas;
1440 }
1441
1442 wxFrame*
1443 PlotFileView::CreateChildFrame(wxDocument *doc, wxView *view)
1444 {
1445   wxDocChildFrame *subframe = new wxDocChildFrame(doc, view, theApp->getMainFrame(), -1, "Plot Frame", wxPoint(10, 10), wxSize(500, 300), wxDEFAULT_FRAME_STYLE);
1446   
1447   wxMenu *file_menu = new wxMenu;
1448   
1449   file_menu->Append(MAINMENU_FILE_CREATE_PHANTOM, "Cr&eate Phantom...");
1450   file_menu->Append(wxID_OPEN, "&Open...");
1451   file_menu->Append(wxID_SAVE, "&Save");
1452   file_menu->Append(wxID_SAVEAS, "Save &As...");
1453   file_menu->Append(wxID_CLOSE, "&Close");
1454   
1455   file_menu->AppendSeparator();
1456   file_menu->Append(PJMENU_FILE_PROPERTIES, "P&roperties");
1457   
1458   file_menu->AppendSeparator();
1459   file_menu->Append(wxID_PRINT, "&Print...");
1460   file_menu->Append(wxID_PRINT_SETUP, "Print &Setup...");
1461   file_menu->Append(wxID_PREVIEW, "Print Pre&view");
1462   
1463   wxMenu *view_menu = new wxMenu;\r
1464   view_menu->Append(PLOTMENU_VIEW_SCALE_MINMAX, "Display Scale &Set...");\r
1465   view_menu->Append(PLOTMENU_VIEW_SCALE_AUTO, "Display Scale &Auto...");\r
1466   \r
1467   wxMenu *help_menu = new wxMenu;
1468   help_menu->Append(MAINMENU_HELP_CONTENTS, "&Contents");
1469   help_menu->AppendSeparator();
1470   help_menu->Append(MAINMENU_HELP_ABOUT, "&About");
1471   
1472   wxMenuBar *menu_bar = new wxMenuBar;
1473   
1474   menu_bar->Append(file_menu, "&File");\r
1475   menu_bar->Append(view_menu, "&View");
1476   menu_bar->Append(help_menu, "&Help");
1477   
1478   subframe->SetMenuBar(menu_bar);
1479   
1480   subframe->Centre(wxBOTH);
1481   
1482   return subframe;
1483 }
1484
1485
1486 bool 
1487 PlotFileView::OnCreate(wxDocument *doc, long WXUNUSED(flags) )
1488 {
1489   m_frame = CreateChildFrame(doc, this);
1490   SetFrame(m_frame);
1491   
1492   m_bMinSpecified = false;\r
1493   m_bMaxSpecified = false;\r
1494   m_dAutoScaleFactor = 1.;\r
1495   \r
1496   int width, height;
1497   m_frame->GetClientSize(&width, &height);
1498   m_frame->SetTitle ("Plot File");
1499   m_canvas = CreateCanvas (this, m_frame);
1500   
1501 #ifdef __X__
1502   int x, y;  // X requires a forced resize
1503   m_frame->GetSize(&x, &y);
1504   m_frame->SetSize(-1, -1, x, y);
1505 #endif
1506   
1507   m_frame->Show(true);
1508   Activate(true);
1509   
1510   return true;
1511 }
1512
1513 void 
1514 PlotFileView::OnDraw (wxDC* dc)
1515 {
1516   const PlotFile& rPlotFile = GetDocument()->getPlotFile();\r
1517   const int iNColumns = rPlotFile.getNumColumns();\r
1518   const int iNRecords = rPlotFile.getNumRecords();\r
1519   \r
1520   if (iNColumns > 0 && iNRecords > 0) {\r
1521     int xsize, ysize;\r
1522     m_canvas->GetClientSize (&xsize, &ysize);\r
1523     SGPDriver driver (dc, xsize, ysize);\r
1524     SGP sgp (driver);\r
1525     const PlotFile& rPhantom = GetDocument()->getPlotFile();\r
1526     EZPlot plot (sgp);\r
1527 \r
1528     for (int iEzset = 0; iEzset < rPlotFile.getNumEzsetCommands(); iEzset++)\r
1529        plot.ezset (rPlotFile.getEzsetCommand (iEzset));\r
1530 \r
1531     if (m_bMinSpecified) {\r
1532       std::ostringstream os;\r
1533       os << "ymin " << m_dMinPixel;\r
1534       plot.ezset (os.str());\r
1535     }\r
1536     \r
1537     if (m_bMaxSpecified) {\r
1538       std::ostringstream os;\r
1539       os << "ymax " << m_dMaxPixel;\r
1540       plot.ezset (os.str());\r
1541     }\r
1542     \r
1543     plot.ezset("box");\r
1544     plot.ezset("grid");\r
1545     \r
1546     double* pdXaxis = new double [iNRecords];\r
1547     rPlotFile.getColumn (0, pdXaxis);\r
1548     \r
1549     double* pdY = new double [iNRecords];\r
1550     for (int iCol = 1; iCol < iNColumns; iCol++) {\r
1551       rPlotFile.getColumn (iCol, pdY);\r
1552       plot.addCurve (pdXaxis, pdY, iNRecords);\r
1553     }\r
1554     \r
1555     delete pdXaxis;\r
1556     delete pdY;\r
1557     \r
1558     plot.plot();\r
1559   }\r
1560 }
1561
1562
1563 void 
1564 PlotFileView::OnUpdate(wxView *WXUNUSED(sender), wxObject *WXUNUSED(hint) )
1565 {
1566   if (m_canvas)
1567     m_canvas->Refresh();
1568 }
1569
1570 bool 
1571 PlotFileView::OnClose (bool deleteWindow)
1572 {
1573   if (!GetDocument()->Close())
1574     return false;
1575   
1576   m_canvas->Clear();
1577   m_canvas->m_pView = NULL;
1578   m_canvas = NULL;
1579   wxString s(wxTheApp->GetAppName());
1580   if (m_frame)
1581     m_frame->SetTitle(s);
1582   SetFrame(NULL);
1583   
1584   Activate(false);
1585   
1586   if (deleteWindow) {
1587     delete m_frame;
1588     return true;
1589   }
1590   return true;
1591 }
1592