r310: plotfile updates
[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.35 2000/12/21 03:40:58 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_PLOT_ROW, ImageFileView::OnPlotRow)\r
170 EVT_MENU(IFMENU_PLOT_COL, ImageFileView::OnPlotCol)\r
171 END_EVENT_TABLE()
172
173 ImageFileView::ImageFileView(void) 
174 : wxView(), m_canvas(NULL), m_frame(NULL), m_bMinSpecified(false), m_bMaxSpecified(false)
175 {
176 }
177
178 ImageFileView::~ImageFileView(void)
179 {
180 }
181
182 void
183 ImageFileView::OnProperties (wxCommandEvent& event)
184 {
185         double min, max, mean, mode, median, stddev;
186         const ImageFile& rIF = GetDocument()->getImageFile();
187         if (rIF.nx() == 0 || rIF.ny() == 0)
188                 *theApp->getLog() << "Properties: empty imagefile\n";
189         else {
190                 const std::string& rFilename = rIF.getFilename();
191                 rIF.statistics (min, max, mean, mode, median, stddev);
192                 std::ostringstream os;
193                 os << "file: " << rFilename << "\nmin: "<<min<<"\nmax: "<<max<<"\nmean: "<<mean<<"\nmedian: "<<median<<"\nmode: "<<mode<<"\nstddev: "<<stddev << "\n";
194                 *theApp->getLog() << os.str().c_str();
195                 wxMessageDialog dialogMsg (m_frame, os.str().c_str(), "Imagefile Properties", wxOK | wxICON_INFORMATION);
196                 dialogMsg.ShowModal();
197         }
198 }
199
200 void 
201 ImageFileView::OnScaleAuto (wxCommandEvent& event)
202 {
203     const ImageFile& rIF = GetDocument()->getImageFile();
204     double min, max, mean, mode, median, stddev;\r
205         rIF.statistics(min, max, mean, mode, median, stddev);\r
206     DialogAutoScaleParameters dialogAutoScale (m_frame, mean, mode, median, stddev, m_dAutoScaleFactor);
207     int iRetVal = dialogAutoScale.ShowModal();
208     if (iRetVal == wxID_OK) {
209                 m_bMinSpecified = true;
210                 m_bMaxSpecified = true;
211                 double dMin, dMax;\r
212                 if (dialogAutoScale.getMinMax (&dMin, &dMax)) {
213                         m_dMinPixel = dMin;
214                         m_dMaxPixel = dMax;
215                         m_dAutoScaleFactor = dialogAutoScale.getAutoScaleFactor();
216                         OnUpdate (this, NULL);\r
217                 }
218     }
219 }
220
221 void 
222 ImageFileView::OnScaleMinMax (wxCommandEvent& event)
223 {
224     const ImageFile& rIF = GetDocument()->getImageFile();
225     double min, max;
226     if (! m_bMinSpecified && ! m_bMaxSpecified)
227                 rIF.getMinMax (min, max);
228         
229     if (m_bMinSpecified)
230                 min = m_dMinPixel;
231     if (m_bMaxSpecified)
232                 max = m_dMaxPixel;
233         
234     DialogGetMinMax dialogMinMax (m_frame, "Set Image Minimum & Maximum", min, max);
235     int retVal = dialogMinMax.ShowModal();
236     if (retVal == wxID_OK) {
237                 m_bMinSpecified = true;
238                 m_bMaxSpecified = true;
239                 m_dMinPixel = dialogMinMax.getMinimum();
240                 m_dMaxPixel = dialogMinMax.getMaximum();
241                 OnUpdate (this, NULL);
242     }
243 }
244 \r
245 void\r
246 ImageFileView::OnCompare (wxCommandEvent& event)\r
247 {\r
248   wxList& rListDocs = theApp->getDocManager()->GetDocuments();\r
249   std::vector<ImageFileDocument*> vecIF;\r
250   std::vector<std::string> vecFilename;\r
251   int nCompares = 0;\r
252   for (wxNode* pNode = rListDocs.GetFirst(); pNode != NULL; pNode = pNode->GetNext()) {\r
253     wxDocument* pDoc = reinterpret_cast<wxDocument*>(pNode->GetData());\r
254         ImageFileDocument* pIFDoc = dynamic_cast<ImageFileDocument*>(pDoc);\r
255         if (pIFDoc) {\r
256                 if (pIFDoc->GetFirstView() != this) {\r
257                         std::string strFilename = pDoc->GetFilename();\r
258                         vecIF.push_back (pIFDoc);\r
259                         vecFilename.push_back (strFilename);\r
260                         nCompares++;\r
261                 }\r
262         }\r
263   }\r
264   for (int i = 0; i < nCompares; i++) {\r
265           const std::string& s = vecFilename[i];\r
266           *theApp->getLog() << s.c_str() << "\n";\r
267   }\r
268 }
269
270 ImageFileCanvas* 
271 ImageFileView::CreateCanvas (wxView *view, wxFrame *parent)
272 {
273     ImageFileCanvas* pCanvas;
274     int width, height;
275     parent->GetClientSize(&width, &height);
276     
277     pCanvas = new ImageFileCanvas (dynamic_cast<ImageFileView*>(view), parent, wxPoint(0, 0), wxSize(width, height), 0);
278     
279     pCanvas->SetScrollbars(20, 20, 50, 50);
280     pCanvas->SetBackgroundColour(*wxWHITE);
281     pCanvas->Clear();
282     
283     return pCanvas;
284 }
285
286 wxFrame*
287 ImageFileView::CreateChildFrame(wxDocument *doc, wxView *view)
288 {
289     wxDocChildFrame *subframe = new wxDocChildFrame(doc, view, theApp->getMainFrame(), -1, "ImageFile Frame", wxPoint(-1, -1), wxSize(0, 0), wxDEFAULT_FRAME_STYLE);
290     
291     wxMenu *file_menu = new wxMenu;
292     
293     file_menu->Append(MAINMENU_FILE_CREATE_PHANTOM, "Cr&eate Phantom...");
294     file_menu->Append(wxID_OPEN, "&Open...");
295     file_menu->Append(wxID_SAVE, "&Save");
296     file_menu->Append(wxID_SAVEAS, "Save &As...");
297     file_menu->Append(wxID_CLOSE, "&Close");
298     
299     file_menu->AppendSeparator();
300     file_menu->Append(IFMENU_FILE_PROPERTIES, "P&roperties");
301         
302     file_menu->AppendSeparator();
303     file_menu->Append(wxID_PRINT, "&Print...");
304     file_menu->Append(wxID_PRINT_SETUP, "Print &Setup...");
305     file_menu->Append(wxID_PREVIEW, "Print Pre&view");
306     
307     wxMenu *view_menu = new wxMenu;
308     view_menu->Append(IFMENU_VIEW_SCALE_MINMAX, "Display Scale &Set...");
309     view_menu->Append(IFMENU_VIEW_SCALE_AUTO, "Display Scale &Auto...");
310     \r
311         wxMenu *plot_menu = new wxMenu;\r
312         plot_menu->Append (IFMENU_PLOT_ROW, "Plot &Row");\r
313         plot_menu->Append (IFMENU_PLOT_COL, "Plot &Column");\r
314         
315         wxMenu *compare_menu = new wxMenu;\r
316         compare_menu->Append (IFMENU_COMPARE_IMAGES, "&Compare Images");\r
317 \r
318     wxMenu *help_menu = new wxMenu;
319     help_menu->Append(MAINMENU_HELP_ABOUT, "&About");
320     
321     wxMenuBar *menu_bar = new wxMenuBar;
322     
323     menu_bar->Append(file_menu, "&File");
324     menu_bar->Append(view_menu, "&View");\r
325         menu_bar->Append(plot_menu, "&Plot");\r
326         menu_bar->Append(compare_menu, "&Compare");
327     menu_bar->Append(help_menu, "&Help");
328     
329     subframe->SetMenuBar(menu_bar);
330     
331     subframe->Centre(wxBOTH);
332     
333     return subframe;
334 }
335
336
337 bool 
338 ImageFileView::OnCreate(wxDocument *doc, long WXUNUSED(flags) )
339 {
340     m_frame = CreateChildFrame(doc, this);
341     SetFrame (m_frame);
342     
343     m_bMinSpecified = false;
344     m_bMaxSpecified = false;
345     m_dAutoScaleFactor = 1.;
346         
347     int width, height;
348     m_frame->GetClientSize (&width, &height);
349     m_frame->SetTitle("ImageFileView");
350     m_canvas = CreateCanvas (this, m_frame);
351         
352     int x, y;  // X requires a forced resize
353     m_frame->GetSize(&x, &y);
354     m_frame->SetSize(-1, -1, x, y);
355         m_frame->SetFocus();\r
356         m_frame->Show(true);
357     Activate(true);
358     
359     return true;
360 }
361
362 void 
363 ImageFileView::OnDraw (wxDC* dc)
364 {
365         if (m_bitmap.Ok())
366                 dc->DrawBitmap(m_bitmap, 0, 0, false);
367         \r
368         int xCursor, yCursor;\r
369         if (m_canvas->GetCurrentCursor (xCursor, yCursor))\r
370                 m_canvas->DrawRubberBandCursor (*dc, xCursor, yCursor);\r
371 }
372
373
374 void 
375 ImageFileView::OnUpdate (wxView *WXUNUSED(sender), wxObject *WXUNUSED(hint) )
376 {
377     const ImageFile& rIF = dynamic_cast<ImageFileDocument*>(GetDocument())->getImageFile();
378     ImageFileArrayConst v = rIF.getArray();
379     int nx = rIF.nx();
380     int ny = rIF.ny();
381     if (v != NULL && nx != 0 && ny != 0) {
382                 if (! m_bMinSpecified || ! m_bMaxSpecified) {
383                         double min, max;
384                         rIF.getMinMax (min, max);
385                         if (! m_bMinSpecified)
386                                 m_dMinPixel = min;
387                         if (! m_bMaxSpecified)
388                                 m_dMaxPixel = max;
389                 }
390                 double scaleWidth = m_dMaxPixel - m_dMinPixel;
391                 
392                 unsigned char* imageData = new unsigned char [nx * ny * 3];
393                 for (int ix = 0; ix < nx; ix++) {
394                         for (int iy = 0; iy < ny; iy++) {
395                                 double scaleValue = ((v[ix][iy] - m_dMinPixel) / scaleWidth) * 255;
396                                 int intensity = static_cast<int>(scaleValue + 0.5);
397                                 intensity = clamp (intensity, 0, 255);
398                                 int baseAddr = ((ny - 1 - iy) * nx + ix) * 3;
399                                 imageData[baseAddr] = imageData[baseAddr+1] = imageData[baseAddr+2] = intensity;
400                         }
401                 }
402                 wxImage image (nx, ny, imageData, true);
403                 m_bitmap = image.ConvertToBitmap();
404                 delete imageData;
405                 int xSize = nx;
406                 int ySize = ny;
407                 xSize = clamp (xSize, 0, 800);
408                 ySize = clamp (ySize, 0, 800);
409                 m_frame->SetClientSize (xSize, ySize);
410                 m_canvas->SetScrollbars(20, 20, nx/20, ny/20);
411                 m_canvas->SetBackgroundColour(*wxWHITE);\r
412     } 
413         
414     if (m_canvas)
415                 m_canvas->Refresh();
416 }
417
418 bool 
419 ImageFileView::OnClose (bool deleteWindow)
420 {
421     if (!GetDocument()->Close())
422         return false;
423         
424     m_canvas->Clear();
425     m_canvas->m_pView = NULL;
426     m_canvas = NULL;
427     wxString s(theApp->GetAppName());
428     if (m_frame)
429                 m_frame->SetTitle(s);
430     SetFrame(NULL);
431         
432     Activate(false);
433     
434     if (deleteWindow) {
435         delete m_frame;
436         return true;
437     }
438     return true;
439 }
440 \r
441 #include "wx/plot.h"\r
442 \r
443 void\r
444 ImageFileView::OnPlotRow (wxCommandEvent& event)\r
445 {\r
446         int xCursor, yCursor;\r
447         if (! m_canvas->GetCurrentCursor (xCursor, yCursor)) {\r
448                 *theApp->getLog() << "No row selected. Please use left mouse button on image to select row\n";\r
449                 return;\r
450         }\r
451         \r
452     const ImageFile& rIF = dynamic_cast<ImageFileDocument*>(GetDocument())->getImageFile();\r
453     ImageFileArrayConst v = rIF.getArray();\r
454     int nx = rIF.nx();\r
455     int ny = rIF.ny();\r
456         \r
457     if (v != NULL && yCursor < ny) {\r
458                 double* pX = new double [nx];\r
459                 double* pY = new double [nx];\r
460                 for (int i = 0; i < nx; i++) {\r
461                         pX[i] = i;\r
462                         pY[i] = v[i][yCursor];\r
463                 }\r
464                 PlotFileDocument* pPlotDoc = dynamic_cast<PlotFileDocument*>(theApp->getDocManager()->CreateDocument("untitled.plt", wxDOC_SILENT));\r
465                 if (! pPlotDoc) {\r
466                         sys_error (ERR_SEVERE, "Internal error: unable to create Plot file");\r
467                 } else {\r
468                         PlotFile& rPlotFile = pPlotDoc->getPlotFile();\r
469                         std::ostringstream title;\r
470                         title << "Row " << yCursor;\r
471                         rPlotFile.setTitle(title.str());\r
472                         rPlotFile.setXLabel("Column");\r
473                         rPlotFile.setYLabel("Pixel Value");\r
474                         rPlotFile.setCurveSize (2, nx);\r
475                         rPlotFile.addColumn (0, pX);\r
476                         rPlotFile.addColumn (1, pY);\r
477                 }\r
478                 delete pX;\r
479                 delete pY;\r
480                 pPlotDoc->Modify(true);\r
481         }\r
482         \r
483 }\r
484
485 void\r
486 ImageFileView::OnPlotCol (wxCommandEvent& event)\r
487 {\r
488         int xCursor, yCursor;\r
489         if (! m_canvas->GetCurrentCursor (xCursor, yCursor)) {\r
490                 // os << "No column selected. Please use left mouse button on image to select column\n";\r
491                 return;\r
492         }\r
493         \r
494     const ImageFile& rIF = dynamic_cast<ImageFileDocument*>(GetDocument())->getImageFile();\r
495     ImageFileArrayConst v = rIF.getArray();\r
496     int nx = rIF.nx();\r
497     int ny = rIF.ny();\r
498         \r
499     if (v != NULL && xCursor < nx) {\r
500                 double* pX = new double [ny];\r
501                 double* pY = new double [ny];\r
502                 double minVal = v[xCursor][0];\r
503                 double maxVal = minVal;\r
504                 for (int i = 0; i < ny; i++) {\r
505                         double y = v[xCursor][i];\r
506                         if (minVal < y)\r
507                                 minVal = y;\r
508                         else if (maxVal > y)\r
509                                 maxVal = y;\r
510                         pX[i] = i;\r
511                         pY[i] = y;\r
512                 }\r
513                 PlotFileDocument* pPlotDoc = dynamic_cast<PlotFileDocument*>(theApp->getDocManager()->CreateDocument("untitled.plt", wxDOC_SILENT));\r
514                 if (! pPlotDoc) {\r
515                         sys_error (ERR_SEVERE, "Internal error: unable to create Plot file");\r
516                 } else {\r
517                         PlotFile& rPlotFile = pPlotDoc->getPlotFile();\r
518                         std::ostringstream title;\r
519                         title << "Column " << xCursor;\r
520                         rPlotFile.setTitle(title.str());\r
521                         rPlotFile.setXLabel("Row");\r
522                         rPlotFile.setYLabel("Pixel Value");\r
523                         rPlotFile.setCurveSize (2, nx);\r
524                         rPlotFile.addColumn (0, pX);\r
525                         rPlotFile.addColumn (1, pY);\r
526                 }\r
527                 delete pX;\r
528                 delete pY;\r
529                 pPlotDoc->Modify(true);\r
530         }\r
531 }\r
532 \r
533
534 // PhantomCanvas
535
536 PhantomCanvas::PhantomCanvas (PhantomView* v, wxFrame *frame, const wxPoint& pos, const wxSize& size, const long style)
537 : wxScrolledWindow(frame, -1, pos, size, style)
538 {
539     m_pView = v;
540 }
541
542 void 
543 PhantomCanvas::OnDraw(wxDC& dc)
544 {
545     if (m_pView)
546         m_pView->OnDraw(& dc);
547 }
548
549
550 // PhantomView
551
552 IMPLEMENT_DYNAMIC_CLASS(PhantomView, wxView)
553
554 BEGIN_EVENT_TABLE(PhantomView, wxView)
555 EVT_MENU(PHMMENU_FILE_PROPERTIES, PhantomView::OnProperties)
556 EVT_MENU(PHMMENU_PROCESS_RASTERIZE, PhantomView::OnRasterize)
557 EVT_MENU(PHMMENU_PROCESS_PROJECTIONS, PhantomView::OnProjections)
558 END_EVENT_TABLE()
559
560 PhantomView::PhantomView(void) 
561 : wxView(), m_canvas(NULL), m_frame(NULL)
562 {
563     m_iDefaultNDet = 367;
564     m_iDefaultNView = 320;
565     m_iDefaultNSample = 2;
566     m_dDefaultRotation = 2;
567     m_dDefaultFocalLength = 2;
568     m_dDefaultFieldOfView = 1;
569     m_iDefaultGeometry = Scanner::GEOMETRY_PARALLEL;
570     m_iDefaultTrace = Trace::TRACE_NONE;
571 }
572
573 PhantomView::~PhantomView(void)
574 {
575 }
576
577 void
578 PhantomView::OnProperties (wxCommandEvent& event)
579 {
580         const int idPhantom = GetDocument()->getPhantomID();
581         const wxString& namePhantom = GetDocument()->getPhantomName();
582         std::ostringstream os;
583         os << "Phantom " << namePhantom.c_str() << " (" << idPhantom << ")\n";
584         *theApp->getLog() << os.str().c_str();
585 #if DEBUG
586         const Phantom& rPhantom = GetDocument()->getPhantom();
587         rPhantom.print();
588 #endif
589 }
590
591
592 void
593 PhantomView::OnProjections (wxCommandEvent& event)
594 {
595         DialogGetProjectionParameters dialogProjection (m_frame, m_iDefaultNDet, m_iDefaultNView, m_iDefaultNSample, m_dDefaultRotation, m_dDefaultFocalLength, m_dDefaultFieldOfView, m_iDefaultGeometry, m_iDefaultTrace);
596         int retVal = dialogProjection.ShowModal();
597         if (retVal == wxID_OK) {
598                 m_iDefaultNDet = dialogProjection.getNDet();
599                 m_iDefaultNView = dialogProjection.getNView();
600                 m_iDefaultNSample = dialogProjection.getNSamples();
601                 m_iDefaultTrace = dialogProjection.getTrace();
602                 m_dDefaultRotation = dialogProjection.getRotAngle();
603                 m_dDefaultFocalLength = dialogProjection.getFocalLengthRatio();
604                 m_dDefaultFieldOfView = dialogProjection.getFieldOfViewRatio();
605                 wxString sGeometry = dialogProjection.getGeometry();
606                 m_iDefaultGeometry = Scanner::convertGeometryNameToID (sGeometry.c_str());
607                 
608                 if (m_iDefaultNDet > 0 && m_iDefaultNView > 0 && sGeometry != "") {
609                         const Phantom& rPhantom = GetDocument()->getPhantom();
610                         ProjectionFileDocument* pProjectionDoc = dynamic_cast<ProjectionFileDocument*>(theApp->getDocManager()->CreateDocument("untitled.pj", wxDOC_SILENT));\r
611                         if (! pProjectionDoc) {\r
612                                 sys_error (ERR_SEVERE, "Unable to create projection document");\r
613                                 return;\r
614                         }
615                         Projections& rProj = pProjectionDoc->getProjections();
616                         Scanner theScanner (rPhantom, sGeometry.c_str(), m_iDefaultNDet, m_iDefaultNView, m_iDefaultNSample, m_dDefaultRotation, m_dDefaultFocalLength, m_dDefaultFieldOfView);
617                         if (theScanner.fail()) {
618                                 *theApp->getLog() << "Failed making scanner: " << theScanner.failMessage().c_str() << "\n";
619                                 return;
620                         }
621                         rProj.initFromScanner (theScanner);
622                         m_dDefaultRotation /= PI;  // convert back to PI units
623                         
624                         if (m_iDefaultTrace > Trace::TRACE_CONSOLE) {
625                                 ProjectionsDialog dialogProjections (theScanner, rProj, rPhantom, m_iDefaultTrace, dynamic_cast<wxWindow*>(m_frame));
626                                 for (int iView = 0; iView < rProj.nView(); iView++) {
627                                         ::wxYield();
628                                         ::wxYield();
629                                         if (dialogProjections.isCancelled() || ! dialogProjections.projectView (iView)) {
630                                                 pProjectionDoc->DeleteAllViews();
631                                                 return;
632                                         }
633                                         ::wxYield();
634                                         ::wxYield();
635                                         while (dialogProjections.isPaused()) {
636                                                 ::wxYield();
637                                                 ::wxUsleep(50);
638                                         }
639                                 }
640                         } else {
641                                 wxProgressDialog dlgProgress (wxString("Projection"), wxString("Projection Progress"), rProj.nView() + 1, m_frame, wxPD_CAN_ABORT);
642                                 for (int i = 0; i < rProj.nView(); i++) {
643                                         theScanner.collectProjections (rProj, rPhantom, i, 1, true, m_iDefaultTrace);
644                                         if (! dlgProgress.Update (i+1)) {
645                                                 pProjectionDoc->DeleteAllViews();
646                                                 return;
647                                         }
648                                 }
649                         }
650                         
651                         std::ostringstream os;
652                         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();
653                         rProj.setRemark (os.str());
654                         *theApp->getLog() << os.str().c_str() << "\n";
655                         
656                         m_frame->Lower();
657                         ::wxYield();
658                         ProjectionFileView* projView = dynamic_cast<ProjectionFileView*>(pProjectionDoc->GetFirstView());\r
659                         if (projView) {\r
660                                 projView->getFrame()->SetFocus();\r
661                                 projView->OnUpdate (projView, NULL);\r
662                         }\r
663                         if (wxView* pView = pProjectionDoc->GetFirstView()) {
664                                 if (wxFrame* pFrame = pView->GetFrame()) {
665                                         pFrame->SetFocus();
666                                         pFrame->Raise();
667                                 }
668                                 theApp->getDocManager()->ActivateView (pView, true, false);
669                         }
670                         ::wxYield();
671                         pProjectionDoc->Modify(true);
672                         pProjectionDoc->UpdateAllViews(this);
673                 }
674         }
675 }
676
677
678 void
679 PhantomView::OnRasterize (wxCommandEvent& event)
680 {
681         DialogGetRasterParameters dialogRaster (m_frame, 256, 256, 1);
682         int retVal = dialogRaster.ShowModal();
683         if (retVal == wxID_OK) {
684                 int xSize = dialogRaster.getXSize();
685                 int ySize = dialogRaster.getYSize();
686                 int nSamples = dialogRaster.getNSamples();
687                 if (nSamples < 1)
688                         nSamples = 1;
689                 if (xSize > 0 && ySize > 0) {
690                         const Phantom& rPhantom = GetDocument()->getPhantom();
691                         ImageFileDocument* pRasterDoc = dynamic_cast<ImageFileDocument*>(theApp->getDocManager()->CreateDocument("untitled.if", wxDOC_SILENT));\r
692                         if (! pRasterDoc) {\r
693                                 sys_error (ERR_SEVERE, "Unable to create image file");\r
694                                 return;\r
695                         }
696                         ImageFile& imageFile = pRasterDoc->getImageFile();
697                         
698                         imageFile.setArraySize (xSize, ySize);
699                         wxProgressDialog dlgProgress (wxString("Rasterize"), wxString("Rasterization Progress"), imageFile.nx() + 1, m_frame, wxPD_CAN_ABORT);
700                         for (unsigned int i = 0; i < imageFile.nx(); i++) {
701                                 rPhantom.convertToImagefile (imageFile, nSamples, Trace::TRACE_NONE, i, 1, true);
702                                 if (! dlgProgress.Update(i+1)) {
703                                         pRasterDoc->DeleteAllViews();
704                                         return;
705                                 }
706                         }
707                         pRasterDoc->Modify(true);
708                         pRasterDoc->UpdateAllViews(this);
709                         ImageFileView* rasterView = dynamic_cast<ImageFileView*>(pRasterDoc->GetFirstView());\r
710                         if (rasterView) {\r
711                                 rasterView->getFrame()->SetFocus();\r
712                                 rasterView->OnUpdate (rasterView, NULL);\r
713                         }\r
714                         
715                         std::ostringstream os;
716                         os << "Rasterize Phantom " << rPhantom.name() << ": XSize=" << xSize << ", YSize=" << ySize << ", nSamples=" << nSamples << "\n";
717                         *theApp->getLog() << os.str().c_str();
718                 }\r
719         }
720 }
721
722
723 PhantomCanvas* 
724 PhantomView::CreateCanvas (wxView *view, wxFrame *parent)
725 {
726     PhantomCanvas* pCanvas;
727     int width, height;
728     parent->GetClientSize(&width, &height);
729     
730     pCanvas = new PhantomCanvas (dynamic_cast<PhantomView*>(view), parent, wxPoint(0, 0), wxSize(width, height), 0);
731     
732     pCanvas->SetBackgroundColour(*wxWHITE);
733     pCanvas->Clear();
734     
735     return pCanvas;
736 }
737
738 wxFrame*
739 PhantomView::CreateChildFrame(wxDocument *doc, wxView *view)
740 {
741     wxDocChildFrame *subframe = new wxDocChildFrame(doc, view, theApp->getMainFrame(), -1, "Phantom Frame", wxPoint(10, 10), wxSize(256, 256), wxDEFAULT_FRAME_STYLE);
742     
743     wxMenu *file_menu = new wxMenu;
744     
745     file_menu->Append(MAINMENU_FILE_CREATE_PHANTOM, "Cr&eate Phantom...");
746     file_menu->Append(wxID_OPEN, "&Open...");
747     file_menu->Append(wxID_CLOSE, "&Close");
748     
749     file_menu->AppendSeparator();
750     file_menu->Append(PHMMENU_FILE_PROPERTIES, "P&roperties");
751         
752     file_menu->AppendSeparator();
753     file_menu->Append(wxID_PRINT, "&Print...");
754     file_menu->Append(wxID_PRINT_SETUP, "Print &Setup...");
755     file_menu->Append(wxID_PREVIEW, "Print Pre&view");
756     
757     wxMenu *process_menu = new wxMenu;
758     process_menu->Append(PHMMENU_PROCESS_RASTERIZE, "&Rasterize...");
759     process_menu->Append(PHMMENU_PROCESS_PROJECTIONS, "&Projections...");
760         
761     wxMenu *help_menu = new wxMenu;
762     help_menu->Append(MAINMENU_HELP_CONTENTS, "&Contents");
763     help_menu->Append(MAINMENU_HELP_ABOUT, "&About");
764     
765     wxMenuBar *menu_bar = new wxMenuBar;
766     
767     menu_bar->Append(file_menu, "&File");
768     menu_bar->Append(process_menu, "&Process");
769     menu_bar->Append(help_menu, "&Help");
770     
771     subframe->SetMenuBar(menu_bar);
772     
773     subframe->Centre(wxBOTH);
774     
775     return subframe;
776 }
777
778
779 bool 
780 PhantomView::OnCreate(wxDocument *doc, long WXUNUSED(flags) )
781 {
782     m_frame = CreateChildFrame(doc, this);
783     SetFrame(m_frame);
784         
785     int width, height;
786     m_frame->GetClientSize(&width, &height);
787     m_frame->SetTitle("PhantomView");
788     m_canvas = CreateCanvas(this, m_frame);
789         
790 #ifdef __X__
791     int x, y;  // X requires a forced resize
792     m_frame->GetSize(&x, &y);
793     m_frame->SetSize(-1, -1, x, y);
794 #endif
795         
796     m_frame->Show(true);
797     Activate(true);
798         
799     return true;
800 }
801
802
803 void 
804 PhantomView::OnUpdate(wxView *WXUNUSED(sender), wxObject *WXUNUSED(hint) )
805 {
806     if (m_canvas)
807                 m_canvas->Refresh();
808 }
809
810 bool 
811 PhantomView::OnClose (bool deleteWindow)
812 {
813     if (!GetDocument()->Close())
814         return false;
815         
816     m_canvas->Clear();
817     m_canvas->m_pView = NULL;
818     m_canvas = NULL;
819     wxString s(wxTheApp->GetAppName());
820     if (m_frame)
821                 m_frame->SetTitle(s);
822     SetFrame(NULL);
823         
824     Activate(false);
825     
826     if (deleteWindow) {
827         delete m_frame;
828         return true;
829     }
830     return true;
831 }
832
833 void
834 PhantomView::OnDraw (wxDC* dc)
835 {
836         int xsize, ysize;
837         m_canvas->GetClientSize (&xsize, &ysize);
838         SGPDriver driver (dc, xsize, ysize);
839         SGP sgp (driver);
840         const Phantom& rPhantom = GetDocument()->getPhantom();
841         sgp.setColor (C_RED);
842         rPhantom.show (sgp);
843 }
844
845 // ProjectionCanvas
846
847 ProjectionFileCanvas::ProjectionFileCanvas (ProjectionFileView* v, wxFrame *frame, const wxPoint& pos, const wxSize& size, const long style)
848 : wxScrolledWindow(frame, -1, pos, size, style)
849 {
850     m_pView = v;
851 }
852
853 void 
854 ProjectionFileCanvas::OnDraw(wxDC& dc)
855 {
856     if (m_pView)
857         m_pView->OnDraw(& dc);
858 }
859
860 // ProjectionFileView
861
862 IMPLEMENT_DYNAMIC_CLASS(ProjectionFileView, wxView)
863
864 BEGIN_EVENT_TABLE(ProjectionFileView, wxView)
865 EVT_MENU(PJMENU_FILE_PROPERTIES, ProjectionFileView::OnProperties)
866 EVT_MENU(PJMENU_PROCESS_RECONSTRUCT, ProjectionFileView::OnReconstruct)
867 END_EVENT_TABLE()
868
869 ProjectionFileView::ProjectionFileView(void) 
870 : wxView(), m_canvas(NULL), m_frame(NULL)
871 {
872     m_iDefaultNX = 256;
873     m_iDefaultNY = 256;
874         m_iDefaultFilter = SignalFilter::FILTER_ABS_BANDLIMIT;
875         m_dDefaultFilterParam = 1.;
876 #if HAVE_FFTW
877         m_iDefaultFilterMethod = ProcessSignal::FILTER_METHOD_RFFTW;
878         m_iDefaultFilterGeneration = ProcessSignal::FILTER_GENERATION_INVERSE_FOURIER;
879 #else
880         m_iDefaultFilterMethod = ProcessSignal::FILTER_METHOD_CONVOLUTION;
881         m_iDefaultFilterGeneration = ProcessSignal::FILTER_GENERATION_DIRECT;
882 #endif
883         m_iDefaultZeropad = 1;
884         m_iDefaultBackprojector = Backprojector::BPROJ_IDIFF3;
885         m_iDefaultInterpolation = Backprojector::INTERP_LINEAR;
886         m_iDefaultInterpParam = 1;
887         m_iDefaultTrace = Trace::TRACE_NONE;
888 }
889
890 ProjectionFileView::~ProjectionFileView(void)
891 {
892 }
893
894 void
895 ProjectionFileView::OnProperties (wxCommandEvent& event)
896 {
897         const Projections& rProj = GetDocument()->getProjections();
898         std::ostringstream os;
899         rProj.printScanInfo(os);
900         *theApp->getLog() << os.str().c_str();
901         wxMessageDialog dialogMsg (m_frame, os.str().c_str(), "Projection File Properties", wxOK | wxICON_INFORMATION);
902         dialogMsg.ShowModal();
903 }
904
905
906 void
907 ProjectionFileView::OnReconstruct (wxCommandEvent& event)
908 {
909         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);
910         
911         int retVal = dialogReconstruction.ShowModal();
912         if (retVal == wxID_OK) {
913                 m_iDefaultNX = dialogReconstruction.getXSize();
914                 m_iDefaultNY = dialogReconstruction.getYSize();
915                 wxString optFilterName = dialogReconstruction.getFilterName();
916                 m_iDefaultFilter = SignalFilter::convertFilterNameToID (optFilterName.c_str());
917                 m_dDefaultFilterParam = dialogReconstruction.getFilterParam();
918                 wxString optFilterMethodName = dialogReconstruction.getFilterMethodName();
919                 m_iDefaultFilterMethod = ProcessSignal::convertFilterMethodNameToID(optFilterMethodName.c_str());
920                 m_iDefaultZeropad = dialogReconstruction.getZeropad();
921                 wxString optFilterGenerationName = dialogReconstruction.getFilterGenerationName();
922                 m_iDefaultFilterGeneration = ProcessSignal::convertFilterGenerationNameToID (optFilterGenerationName.c_str());
923                 wxString optInterpName = dialogReconstruction.getInterpName();
924                 m_iDefaultInterpolation = Backprojector::convertInterpNameToID (optInterpName.c_str());
925                 m_iDefaultInterpParam = dialogReconstruction.getInterpParam();
926                 wxString optBackprojectName = dialogReconstruction.getBackprojectName();
927                 m_iDefaultBackprojector = Backprojector::convertBackprojectNameToID (optBackprojectName.c_str());
928                 m_iDefaultTrace = dialogReconstruction.getTrace();
929                 if (m_iDefaultNX > 0 && m_iDefaultNY > 0) {
930                         ImageFileDocument* pReconDoc = dynamic_cast<ImageFileDocument*>(theApp->getDocManager()->CreateDocument("untitled.if", wxDOC_SILENT));\r
931                         if (pReconDoc) {\r
932                                 sys_error (ERR_SEVERE, "Unable to create image file");\r
933                                 return;\r
934                         }
935                         ImageFile& imageFile = pReconDoc->getImageFile();
936                         const Projections& rProj = GetDocument()->getProjections();
937                         imageFile.setArraySize (m_iDefaultNX, m_iDefaultNY);
938                         
939                         if (m_iDefaultFilterMethod != ProcessSignal::FILTER_METHOD_CONVOLUTION && m_iDefaultFilterGeneration == ProcessSignal::FILTER_GENERATION_DIRECT && rProj.geometry() != Scanner::GEOMETRY_PARALLEL) {
940                                 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);
941                                 return;
942                         }
943 #if 0
944                         SGPDriver* pSGPDriver = NULL;
945                         SGP* pSGP = NULL;
946                         wxMemoryDC* pDCPlot = NULL;
947                         wxBitmap bitmap;
948                         if (m_iDefaultTrace >= Trace::TRACE_PLOT) {
949                                 bitmap.Create (500, 500);
950                                 pDCPlot = new wxMemoryDC;
951                                 pDCPlot->SelectObject (bitmap);
952                                 pSGPDriver = new SGPDriver (dynamic_cast<wxDC*>pDCPlot, 500, 500);
953                                 pSGP = new SGP (*pSGPDriver);
954                         }
955                         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);
956                         delete pSGP;
957 #else
958                         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);
959 #endif
960                         \r
961                         Timer timerRecon;
962                         if (m_iDefaultTrace > Trace::TRACE_CONSOLE) {
963                                 ReconstructDialog* pDlgReconstruct = new ReconstructDialog (*pReconstruct, rProj, imageFile, m_iDefaultTrace, m_frame);
964                                 for (int iView = 0; iView < rProj.nView(); iView++) {
965                                         ::wxYield();
966                                         ::wxYield();
967                                         if (pDlgReconstruct->isCancelled() || ! pDlgReconstruct->reconstructView (iView)) {
968                                                 delete pDlgReconstruct;
969                                                 delete pReconstruct;
970                                                 pReconDoc->DeleteAllViews();
971                                                 return;
972                                         }
973                                         ::wxYield();
974                                         ::wxYield();
975                                         while (pDlgReconstruct->isPaused()) {
976                                                 ::wxYield();
977                                                 ::wxUsleep(50);
978                                         }
979                                 }
980                                 delete pDlgReconstruct;
981                         } else {
982                                 wxProgressDialog dlgProgress (wxString("Reconstruction"), wxString("Reconstruction Progress"), rProj.nView() + 1, m_frame, wxPD_CAN_ABORT);
983                                 for (int i = 0; i < rProj.nView(); i++) {
984                                         pReconstruct->reconstructView (i, 1);
985                                         if (! dlgProgress.Update(i + 1)) {
986                                                 delete pReconstruct;
987                                                 pReconDoc->DeleteAllViews();
988                                                 return;
989                                         }
990                                 }
991                         }
992                         delete pReconstruct;
993                         pReconDoc->Modify(true);
994                         pReconDoc->UpdateAllViews(this);
995                         ImageFileView* rasterView = dynamic_cast<ImageFileView*>(pReconDoc->GetFirstView());\r
996                         if (rasterView) {\r
997                                 rasterView->getFrame()->SetFocus();\r
998                                 rasterView->OnUpdate (rasterView, NULL);\r
999                         }\r
1000                         std::ostringstream os;
1001                         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();
1002                         *theApp->getLog() << os.str().c_str() << "\n";
1003                         imageFile.labelAdd (rProj.getLabel());
1004                         imageFile.labelAdd (Array2dFileLabel::L_HISTORY, os.str().c_str(), timerRecon.timerEnd());
1005                 }
1006         }
1007 }
1008
1009
1010 ProjectionFileCanvas* 
1011 ProjectionFileView::CreateCanvas (wxView *view, wxFrame *parent)
1012 {
1013     ProjectionFileCanvas* pCanvas;
1014     int width, height;
1015     parent->GetClientSize(&width, &height);
1016     
1017     pCanvas = new ProjectionFileCanvas (dynamic_cast<ProjectionFileView*>(view), parent, wxPoint(0, 0), wxSize(width, height), 0);
1018     
1019     pCanvas->SetScrollbars(20, 20, 50, 50);
1020     pCanvas->SetBackgroundColour(*wxWHITE);
1021     pCanvas->Clear();
1022     
1023     return pCanvas;
1024 }
1025
1026 wxFrame*
1027 ProjectionFileView::CreateChildFrame(wxDocument *doc, wxView *view)
1028 {
1029     wxDocChildFrame *subframe = new wxDocChildFrame(doc, view, theApp->getMainFrame(), -1, "Projection Frame", wxPoint(10, 10), wxSize(0, 0), wxDEFAULT_FRAME_STYLE);
1030     
1031     wxMenu *file_menu = new wxMenu;
1032     
1033     file_menu->Append(MAINMENU_FILE_CREATE_PHANTOM, "Cr&eate Phantom...");
1034     file_menu->Append(wxID_OPEN, "&Open...");
1035     file_menu->Append(wxID_SAVE, "&Save");
1036     file_menu->Append(wxID_SAVEAS, "Save &As...");
1037     file_menu->Append(wxID_CLOSE, "&Close");
1038     
1039     file_menu->AppendSeparator();
1040     file_menu->Append(PJMENU_FILE_PROPERTIES, "P&roperties");
1041         
1042     file_menu->AppendSeparator();
1043     file_menu->Append(wxID_PRINT, "&Print...");
1044     file_menu->Append(wxID_PRINT_SETUP, "Print &Setup...");
1045     file_menu->Append(wxID_PREVIEW, "Print Pre&view");
1046     
1047     wxMenu *process_menu = new wxMenu;
1048     process_menu->Append(PJMENU_PROCESS_RECONSTRUCT, "R&econstruct...");
1049         
1050     wxMenu *help_menu = new wxMenu;
1051     help_menu->Append(MAINMENU_HELP_CONTENTS, "&Contents");
1052     help_menu->AppendSeparator();
1053     help_menu->Append(MAINMENU_HELP_ABOUT, "&About");
1054     
1055     wxMenuBar *menu_bar = new wxMenuBar;
1056     
1057     menu_bar->Append(file_menu, "&File");
1058     menu_bar->Append(process_menu, "&Process");
1059     menu_bar->Append(help_menu, "&Help");
1060     
1061     subframe->SetMenuBar(menu_bar);
1062     
1063     subframe->Centre(wxBOTH);
1064     
1065     return subframe;
1066 }
1067
1068
1069 bool 
1070 ProjectionFileView::OnCreate(wxDocument *doc, long WXUNUSED(flags) )
1071 {
1072     m_frame = CreateChildFrame(doc, this);
1073     SetFrame(m_frame);
1074     
1075     int width, height;
1076     m_frame->GetClientSize(&width, &height);
1077     m_frame->SetTitle("ProjectionFileView");
1078     m_canvas = CreateCanvas(this, m_frame);
1079         
1080 #ifdef __X__
1081     int x, y;  // X requires a forced resize
1082     m_frame->GetSize(&x, &y);
1083     m_frame->SetSize(-1, -1, x, y);
1084 #endif
1085         
1086     m_frame->Show(true);
1087     Activate(true);
1088     
1089     return true;
1090 }
1091
1092 void 
1093 ProjectionFileView::OnDraw (wxDC* dc)
1094 {
1095     if (m_bitmap.Ok())
1096                 dc->DrawBitmap (m_bitmap, 0, 0, false);
1097 }
1098
1099
1100 void 
1101 ProjectionFileView::OnUpdate(wxView *WXUNUSED(sender), wxObject *WXUNUSED(hint) )
1102 {
1103     const Projections& rProj = GetDocument()->getProjections();
1104     const int nDet = rProj.nDet();
1105     const int nView = rProj.nView();
1106     if (nDet != 0 && nView != 0) {
1107                 const DetectorArray& detarray = rProj.getDetectorArray(0);
1108                 const DetectorValue* detval = detarray.detValues();
1109                 double min = detval[0];
1110                 double max = detval[0];
1111                 for (int iy = 0; iy < nView; iy++) {
1112                         const DetectorArray& detarray = rProj.getDetectorArray(iy);
1113                         const DetectorValue* detval = detarray.detValues();
1114                         for (int ix = 0; ix < nDet; ix++) {
1115                                 if (min > detval[ix])
1116                                         min = detval[ix];
1117                                 else if (max < detval[ix])
1118                                         max = detval[ix];
1119                         }
1120                 }
1121                 
1122                 unsigned char* imageData = new unsigned char [nDet * nView * 3];
1123                 double scale = (max - min) / 255;
1124                 for (int iy2 = 0; iy2 < nView; iy2++) {
1125                         const DetectorArray& detarray = rProj.getDetectorArray (iy2);
1126                         const DetectorValue* detval = detarray.detValues();
1127                         for (int ix = 0; ix < nDet; ix++) {
1128                                 int intensity = static_cast<int>(((detval[ix] - min) / scale) + 0.5);
1129                                 intensity = clamp(intensity, 0, 255);
1130                                 int baseAddr = (iy2 * nDet + ix) * 3;
1131                                 imageData[baseAddr] = imageData[baseAddr+1] = imageData[baseAddr+2] = intensity;
1132                         }
1133                 }
1134                 wxImage image (nDet, nView, imageData, true);
1135                 m_bitmap = image.ConvertToBitmap();
1136                 delete imageData;
1137                 int xSize = nDet;
1138                 int ySize = nView;
1139                 xSize = clamp (xSize, 0, 800);
1140                 ySize = clamp (ySize, 0, 800);
1141                 m_frame->SetClientSize (xSize, ySize);
1142                 m_canvas->SetScrollbars (20, 20, nDet/20, nView/20);
1143     }
1144         
1145     if (m_canvas)
1146                 m_canvas->Refresh();
1147 }
1148
1149 bool 
1150 ProjectionFileView::OnClose (bool deleteWindow)
1151 {
1152     if (!GetDocument()->Close())
1153         return false;
1154         
1155     m_canvas->Clear();
1156     m_canvas->m_pView = NULL;
1157     m_canvas = NULL;
1158     wxString s(wxTheApp->GetAppName());
1159     if (m_frame)
1160                 m_frame->SetTitle(s);
1161     SetFrame(NULL);
1162         
1163     Activate(false);
1164     
1165     if (deleteWindow) {
1166         delete m_frame;
1167         return true;
1168     }
1169     return true;
1170 }
1171
1172
1173
1174 // PlotFileCanvas
1175 PlotFileCanvas::PlotFileCanvas (PlotFileView* v, wxFrame *frame, const wxPoint& pos, const wxSize& size, const long style)
1176 : wxScrolledWindow(frame, -1, pos, size, style)
1177 {
1178     m_pView = v;
1179 }
1180
1181 void 
1182 PlotFileCanvas::OnDraw(wxDC& dc)
1183 {
1184     if (m_pView)
1185         m_pView->OnDraw(& dc);
1186 }\r
1187
1188
1189 // PlotFileView
1190
1191 IMPLEMENT_DYNAMIC_CLASS(PlotFileView, wxView)
1192
1193 BEGIN_EVENT_TABLE(PlotFileView, wxView)
1194 EVT_MENU(PJMENU_FILE_PROPERTIES, PlotFileView::OnProperties)
1195 EVT_MENU(PLOTMENU_VIEW_SCALE_MINMAX, PlotFileView::OnScaleMinMax)\r
1196 EVT_MENU(PLOTMENU_VIEW_SCALE_AUTO, PlotFileView::OnScaleAuto)\r
1197 END_EVENT_TABLE()
1198
1199 PlotFileView::PlotFileView(void) 
1200 : wxView(), m_canvas(NULL), m_frame(NULL)
1201 {
1202 }
1203
1204 PlotFileView::~PlotFileView(void)
1205 {
1206 }
1207
1208 void
1209 PlotFileView::OnProperties (wxCommandEvent& event)
1210 {
1211         const PlotFile& rProj = GetDocument()->getPlotFile();
1212         std::ostringstream os;\r
1213         os << "Columns: " << rProj.getNumColumns() << ", Records: " << rProj.getNumRecords() << "\n";
1214         *theApp->getLog() << os.str().c_str();
1215         wxMessageDialog dialogMsg (m_frame, os.str().c_str(), "Plot File Properties", wxOK | wxICON_INFORMATION);
1216         dialogMsg.ShowModal();
1217 }
1218
1219
1220 void \r
1221 PlotFileView::OnScaleAuto (wxCommandEvent& event)\r
1222 {\r
1223         const PlotFile& rPlotFile = GetDocument()->getPlotFile();\r
1224         double min, max, mean, mode, median, stddev;\r
1225         rPlotFile.statistics (1, min, max, mean, mode, median, stddev);\r
1226         DialogAutoScaleParameters dialogAutoScale (m_frame, mean, mode, median, stddev, m_dAutoScaleFactor);\r
1227     int iRetVal = dialogAutoScale.ShowModal();\r
1228     if (iRetVal == wxID_OK) {\r
1229                 m_bMinSpecified = true;\r
1230                 m_bMaxSpecified = true;\r
1231                 double dMin, dMax;\r
1232                 if (dialogAutoScale.getMinMax (&dMin, &dMax)) {\r
1233                         m_dMinPixel = dMin;\r
1234                         m_dMaxPixel = dMax;\r
1235                         m_dAutoScaleFactor = dialogAutoScale.getAutoScaleFactor();\r
1236                         OnUpdate (this, NULL);\r
1237                 }\r
1238     }\r
1239 }\r
1240 \r
1241 void \r
1242 PlotFileView::OnScaleMinMax (wxCommandEvent& event)\r
1243 {\r
1244         const PlotFile& rPlotFile = GetDocument()->getPlotFile();\r
1245     double min, max;\r
1246 \r
1247     if (! m_bMinSpecified && ! m_bMaxSpecified) {\r
1248                 if (! rPlotFile.getMinMax (1, min, max)) {\r
1249                         *theApp->getLog() << "Error: unable to find Min/Max\n";\r
1250                         return;\r
1251         }\r
1252         }\r
1253         \r
1254     if (m_bMinSpecified)\r
1255                 min = m_dMinPixel;\r
1256     if (m_bMaxSpecified)\r
1257                 max = m_dMaxPixel;\r
1258         \r
1259     DialogGetMinMax dialogMinMax (m_frame, "Set Y-axis Minimum & Maximum", min, max);\r
1260     int retVal = dialogMinMax.ShowModal();\r
1261     if (retVal == wxID_OK) {\r
1262                 m_bMinSpecified = true;\r
1263                 m_bMaxSpecified = true;\r
1264                 m_dMinPixel = dialogMinMax.getMinimum();\r
1265                 m_dMaxPixel = dialogMinMax.getMaximum();\r
1266                 OnUpdate (this, NULL);\r
1267     }\r
1268 }\r
1269 \r
1270 \r
1271 PlotFileCanvas* 
1272 PlotFileView::CreateCanvas (wxView *view, wxFrame *parent)
1273 {
1274     PlotFileCanvas* pCanvas;
1275     int width, height;
1276     parent->GetClientSize(&width, &height);
1277     
1278     pCanvas = new PlotFileCanvas (dynamic_cast<PlotFileView*>(view), parent, wxPoint(0, 0), wxSize(width, height), 0);
1279     
1280     pCanvas->SetBackgroundColour(*wxWHITE);
1281     pCanvas->Clear();
1282     
1283     return pCanvas;
1284 }
1285
1286 wxFrame*
1287 PlotFileView::CreateChildFrame(wxDocument *doc, wxView *view)
1288 {
1289     wxDocChildFrame *subframe = new wxDocChildFrame(doc, view, theApp->getMainFrame(), -1, "Plot Frame", wxPoint(10, 10), wxSize(500, 300), wxDEFAULT_FRAME_STYLE);
1290     
1291     wxMenu *file_menu = new wxMenu;
1292     
1293     file_menu->Append(MAINMENU_FILE_CREATE_PHANTOM, "Cr&eate Phantom...");
1294     file_menu->Append(wxID_OPEN, "&Open...");
1295     file_menu->Append(wxID_SAVE, "&Save");
1296     file_menu->Append(wxID_SAVEAS, "Save &As...");
1297     file_menu->Append(wxID_CLOSE, "&Close");
1298     
1299     file_menu->AppendSeparator();
1300     file_menu->Append(PJMENU_FILE_PROPERTIES, "P&roperties");
1301         
1302     file_menu->AppendSeparator();
1303     file_menu->Append(wxID_PRINT, "&Print...");
1304     file_menu->Append(wxID_PRINT_SETUP, "Print &Setup...");
1305     file_menu->Append(wxID_PREVIEW, "Print Pre&view");
1306     
1307     wxMenu *view_menu = new wxMenu;\r
1308     view_menu->Append(PLOTMENU_VIEW_SCALE_MINMAX, "Display Scale &Set...");\r
1309     view_menu->Append(PLOTMENU_VIEW_SCALE_AUTO, "Display Scale &Auto...");\r
1310     \r
1311     wxMenu *help_menu = new wxMenu;
1312     help_menu->Append(MAINMENU_HELP_CONTENTS, "&Contents");
1313     help_menu->AppendSeparator();
1314     help_menu->Append(MAINMENU_HELP_ABOUT, "&About");
1315     
1316     wxMenuBar *menu_bar = new wxMenuBar;
1317     
1318     menu_bar->Append(file_menu, "&File");\r
1319         menu_bar->Append(view_menu, "&View");
1320     menu_bar->Append(help_menu, "&Help");
1321     
1322     subframe->SetMenuBar(menu_bar);
1323     
1324     subframe->Centre(wxBOTH);
1325     
1326     return subframe;
1327 }
1328
1329
1330 bool 
1331 PlotFileView::OnCreate(wxDocument *doc, long WXUNUSED(flags) )
1332 {
1333     m_frame = CreateChildFrame(doc, this);
1334     SetFrame(m_frame);
1335     
1336         m_bMinSpecified = false;\r
1337         m_bMaxSpecified = false;\r
1338     m_dAutoScaleFactor = 1.;\r
1339 \r
1340     int width, height;
1341     m_frame->GetClientSize(&width, &height);
1342     m_frame->SetTitle ("Plot File");
1343     m_canvas = CreateCanvas (this, m_frame);
1344         
1345 #ifdef __X__
1346     int x, y;  // X requires a forced resize
1347     m_frame->GetSize(&x, &y);
1348     m_frame->SetSize(-1, -1, x, y);
1349 #endif
1350         
1351     m_frame->Show(true);
1352     Activate(true);
1353     
1354     return true;
1355 }
1356
1357 void 
1358 PlotFileView::OnDraw (wxDC* dc)
1359 {
1360     const PlotFile& rPlotFile = GetDocument()->getPlotFile();\r
1361     const int iNColumns = rPlotFile.getNumColumns();\r
1362         const int iNRecords = rPlotFile.getNumRecords();\r
1363         \r
1364         if (iNColumns > 0 && iNRecords > 0) {\r
1365                 int xsize, ysize;\r
1366                 m_canvas->GetClientSize (&xsize, &ysize);\r
1367                 SGPDriver driver (dc, xsize, ysize);\r
1368                 SGP sgp (driver);\r
1369                 const PlotFile& rPhantom = GetDocument()->getPlotFile();\r
1370                 EZPlot plot (sgp);\r
1371                 \r
1372                 if (! rPlotFile.getTitle().empty()) {\r
1373                         std::string s("title ");\r
1374                         s += rPlotFile.getTitle();\r
1375                         plot.ezset (s);\r
1376                 }\r
1377                 if (! rPlotFile.getXLabel().empty()) {\r
1378                         std::string s("xlabel ");\r
1379                         s += rPlotFile.getXLabel();\r
1380                         plot.ezset (s);\r
1381                 }\r
1382                 if (! rPlotFile.getYLabel().empty()) {\r
1383                         std::string s("ylabel ");\r
1384                         s += rPlotFile.getYLabel();\r
1385                         plot.ezset (s);\r
1386                 }\r
1387                 \r
1388                 if (m_bMinSpecified) {\r
1389                         std::ostringstream os;\r
1390                         os << "ymin " << m_dMinPixel;\r
1391                         plot.ezset (os.str());\r
1392                 }\r
1393 \r
1394                 if (m_bMaxSpecified) {\r
1395                         std::ostringstream os;\r
1396                         os << "ymax " << m_dMaxPixel;\r
1397                         plot.ezset (os.str());\r
1398                 }\r
1399 \r
1400                 plot.ezset("box");\r
1401                 plot.ezset("grid");\r
1402                 \r
1403                 double* pdXaxis = new double [iNRecords];\r
1404                 rPlotFile.getColumn (0, pdXaxis);\r
1405                 \r
1406                 double* pdY = new double [iNRecords];\r
1407                 for (int iCol = 1; iCol < iNColumns; iCol++) {\r
1408                         rPlotFile.getColumn (iCol, pdY);\r
1409                         plot.addCurve (pdXaxis, pdY, iNRecords);\r
1410                 }\r
1411                 \r
1412                 delete pdXaxis;\r
1413                 delete pdY;\r
1414                 \r
1415                 plot.plot();\r
1416         }\r
1417 }
1418
1419
1420 void 
1421 PlotFileView::OnUpdate(wxView *WXUNUSED(sender), wxObject *WXUNUSED(hint) )
1422 {
1423     if (m_canvas)
1424                 m_canvas->Refresh();
1425 }
1426
1427 bool 
1428 PlotFileView::OnClose (bool deleteWindow)
1429 {
1430     if (!GetDocument()->Close())
1431         return false;
1432         
1433     m_canvas->Clear();
1434     m_canvas->m_pView = NULL;
1435     m_canvas = NULL;
1436     wxString s(wxTheApp->GetAppName());
1437     if (m_frame)
1438                 m_frame->SetTitle(s);
1439     SetFrame(NULL);
1440         
1441     Activate(false);
1442     
1443     if (deleteWindow) {
1444                 delete m_frame;
1445                 return true;
1446     }
1447     return true;
1448 }
1449