r354: Added Projection Polar conversions
[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.51 2001/01/04 21:28:41 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
57 #if defined(MSVC) || HAVE_SSTREAM
58 #include <sstream>
59 #else
60 #include <sstream_subst>
61 #endif
62
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;
75   m_xCursor = -1;
76   m_yCursor = -1;
77 }
78
79 void 
80 ImageFileCanvas::OnDraw(wxDC& dc)
81 {
82   if (m_pView)
83     m_pView->OnDraw(& dc);
84 }
85
86 void 
87 ImageFileCanvas::DrawRubberBandCursor (wxDC& dc, int x, int y)
88 {
89   const ImageFile& rIF = m_pView->GetDocument()->getImageFile();
90   int nx = rIF.nx();
91   int ny = rIF.ny();
92   
93   int yPt = ny - y - 1;
94   dc.SetLogicalFunction (wxINVERT);
95   dc.SetPen (*wxGREEN_PEN);
96   dc.DrawLine (0, yPt, nx, yPt);
97   dc.DrawLine (x, 0, x, ny);
98   dc.SetLogicalFunction (wxCOPY);
99 }
100
101 bool
102 ImageFileCanvas::GetCurrentCursor (int& x, int& y)
103 {
104   x = m_xCursor;
105   y = m_yCursor;
106   
107   if (m_xCursor >= 0 && m_yCursor >= 0)
108     return true;
109   else
110     return false;
111 }
112
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   
124   const ImageFile& rIF = m_pView->GetDocument()->getImageFile();
125   ImageFileArrayConst v = rIF.getArray();
126   int nx = rIF.nx();
127   int ny = rIF.ny();
128   const int yPt = ny - 1 - pt.y;
129   if (event.RightIsDown()) {
130     if (pt.x >= 0 && pt.x < nx && pt.y >= 0 && pt.y < ny) {
131       std::ostringstream os;
132       os << "Image value (" << pt.x << "," << yPt << ") = " << v[pt.x][yPt];
133       if (rIF.isComplex()) {
134         double dImag = rIF.getImaginaryArray()[pt.x][yPt];
135         if (dImag < 0)
136           os << " - " << -dImag;
137         else
138           os << " + " << dImag;
139         os << "i\n";
140       } else
141         os << "\n";
142       *theApp->getLog() << os.str().c_str();
143     } else
144       *theApp->getLog() << "Mouse out of image range (" << pt.x << "," << yPt << ")\n";
145   }
146   else if (event.LeftIsDown() || event.LeftUp() || event.RightUp()) {
147     if (pt.x >= 0 && pt.x < nx && pt.y >= 0 && pt.y < ny) {
148       if (m_xCursor >= 0 && m_yCursor >= 0) {
149         DrawRubberBandCursor (dc, m_xCursor, m_yCursor);
150       }
151       DrawRubberBandCursor (dc, pt.x, yPt);
152       m_xCursor = pt.x;
153       m_yCursor = yPt;
154     } else
155       *theApp->getLog() << "Mouse out of image range (" << pt.x << "," << yPt << ")\n";
156   }
157   if (event.LeftUp()) {
158     std::ostringstream os;
159     os << "Selected column " << pt.x << " , row " << yPt << "\n";
160     *theApp->getLog() << os.str().c_str();
161   }
162 }
163
164 // ImageFileView
165
166 IMPLEMENT_DYNAMIC_CLASS(ImageFileView, wxView)
167
168 BEGIN_EVENT_TABLE(ImageFileView, wxView)
169 EVT_MENU(IFMENU_FILE_EXPORT, ImageFileView::OnExport)
170 EVT_MENU(IFMENU_FILE_PROPERTIES, ImageFileView::OnProperties)
171 EVT_MENU(IFMENU_VIEW_SCALE_MINMAX, ImageFileView::OnScaleMinMax)
172 EVT_MENU(IFMENU_VIEW_SCALE_AUTO, ImageFileView::OnScaleAuto)
173 EVT_MENU(IFMENU_VIEW_SCALE_FULL, ImageFileView::OnScaleFull)
174 EVT_MENU(IFMENU_COMPARE_IMAGES, ImageFileView::OnCompare)
175 EVT_MENU(IFMENU_COMPARE_ROW, ImageFileView::OnCompareRow)
176 EVT_MENU(IFMENU_COMPARE_COL, ImageFileView::OnCompareCol)
177 EVT_MENU(IFMENU_FILTER_INVERTVALUES, ImageFileView::OnInvertValues)
178 EVT_MENU(IFMENU_FILTER_SQUARE, ImageFileView::OnSquare)
179 EVT_MENU(IFMENU_FILTER_SQRT, ImageFileView::OnSquareRoot)
180 EVT_MENU(IFMENU_FILTER_LOG, ImageFileView::OnLog)
181 EVT_MENU(IFMENU_FILTER_EXP, ImageFileView::OnExp)
182 EVT_MENU(IFMENU_FILTER_FOURIER, ImageFileView::OnFourier)
183 EVT_MENU(IFMENU_FILTER_INVERSE_FOURIER, ImageFileView::OnInverseFourier)
184 EVT_MENU(IFMENU_FILTER_SHUFFLEFOURIERTONATURALORDER, ImageFileView::OnShuffleFourierToNaturalOrder)
185 EVT_MENU(IFMENU_FILTER_SHUFFLENATURALTOFOURIERORDER, ImageFileView::OnShuffleNaturalToFourierOrder)
186 EVT_MENU(IFMENU_IMAGE_ADD, ImageFileView::OnAdd)
187 EVT_MENU(IFMENU_IMAGE_SUBTRACT, ImageFileView::OnSubtract)
188 EVT_MENU(IFMENU_IMAGE_MULTIPLY, ImageFileView::OnMultiply)
189 EVT_MENU(IFMENU_IMAGE_DIVIDE, ImageFileView::OnDivide)
190 EVT_MENU(IFMENU_IMAGE_SCALESIZE, ImageFileView::OnScaleSize)
191 #ifdef HAVE_FFTW
192 EVT_MENU(IFMENU_FILTER_FFT, ImageFileView::OnFFT)
193 EVT_MENU(IFMENU_FILTER_IFFT, ImageFileView::OnIFFT)
194 #endif
195 EVT_MENU(IFMENU_FILTER_MAGNITUDE, ImageFileView::OnMagnitude)
196 EVT_MENU(IFMENU_FILTER_PHASE, ImageFileView::OnPhase)
197 EVT_MENU(IFMENU_PLOT_ROW, ImageFileView::OnPlotRow)
198 EVT_MENU(IFMENU_PLOT_COL, ImageFileView::OnPlotCol)
199 EVT_MENU(IFMENU_PLOT_HISTOGRAM, ImageFileView::OnPlotHistogram)
200 END_EVENT_TABLE()
201
202 ImageFileView::ImageFileView(void) 
203 : wxView(), m_canvas(NULL), m_frame(NULL), m_bMinSpecified(false), m_bMaxSpecified(false)
204 {
205   m_iDefaultExportFormatID = ImageFile::FORMAT_PNG;
206 }
207
208 ImageFileView::~ImageFileView(void)
209 {
210 }
211
212 void
213 ImageFileView::OnProperties (wxCommandEvent& event)
214 {
215   const ImageFile& rIF = GetDocument()->getImageFile();
216   if (rIF.nx() == 0 || rIF.ny() == 0)
217     *theApp->getLog() << "Properties: empty imagefile\n";
218   else {
219     const std::string& rFilename = rIF.getFilename();
220     std::ostringstream os;
221     double min, max, mean, mode, median, stddev;
222     rIF.statistics (rIF.getArray(), min, max, mean, mode, median, stddev);
223     os << "Filename: " << rFilename << "\n";
224     os << "Size: (" << rIF.nx() << "," << rIF.ny() << ")\n";
225     os << "Data type: ";
226     if (rIF.isComplex())
227       os << "Complex\n";
228     else
229       os << "Real\n";
230     os << "\nMinimum: "<<min<<"\nMaximum: "<<max<<"\nMean: "<<mean<<"\nMedian: "<<median<<"\nMode: "<<mode<<"\nStandard Deviation: "<<stddev << "\n";
231     if (rIF.isComplex()) {
232       rIF.statistics (rIF.getImaginaryArray(), min, max, mean, mode, median, stddev);
233       os << "\nImaginary: min: "<<min<<"\nmax: "<<max<<"\nmean: "<<mean<<"\nmedian: "<<median<<"\nmode: "<<mode<<"\nstddev: "<<stddev << "\n";
234     }
235     if (rIF.nLabels() > 0) {
236       os << "\n";
237       rIF.printLabelsBrief (os);
238     }
239     *theApp->getLog() << os.str().c_str();
240     wxMessageDialog dialogMsg (m_frame, os.str().c_str(), "Imagefile Properties", wxOK | wxICON_INFORMATION);
241     dialogMsg.ShowModal();
242   }
243 }
244
245 void 
246 ImageFileView::OnScaleAuto (wxCommandEvent& event)
247 {
248   const ImageFile& rIF = GetDocument()->getImageFile();
249   double min, max, mean, mode, median, stddev;
250   rIF.statistics(min, max, mean, mode, median, stddev);
251   DialogAutoScaleParameters dialogAutoScale (m_frame, mean, mode, median, stddev, m_dAutoScaleFactor);
252   int iRetVal = dialogAutoScale.ShowModal();
253   if (iRetVal == wxID_OK) {
254     m_bMinSpecified = true;
255     m_bMaxSpecified = true;
256     double dMin, dMax;
257     if (dialogAutoScale.getMinMax (&dMin, &dMax)) {
258       m_dMinPixel = dMin;
259       m_dMaxPixel = dMax;
260       m_dAutoScaleFactor = dialogAutoScale.getAutoScaleFactor();
261       OnUpdate (this, NULL);
262     }
263   }
264 }
265
266 void 
267 ImageFileView::OnScaleMinMax (wxCommandEvent& event)
268 {
269   const ImageFile& rIF = GetDocument()->getImageFile();
270   double min, max;
271   if (! m_bMinSpecified && ! m_bMaxSpecified)
272     rIF.getMinMax (min, max);
273   
274   if (m_bMinSpecified)
275     min = m_dMinPixel;
276   if (m_bMaxSpecified)
277     max = m_dMaxPixel;
278   
279   DialogGetMinMax dialogMinMax (m_frame, "Set Image Minimum & Maximum", min, max);
280   int retVal = dialogMinMax.ShowModal();
281   if (retVal == wxID_OK) {
282     m_bMinSpecified = true;
283     m_bMaxSpecified = true;
284     m_dMinPixel = dialogMinMax.getMinimum();
285     m_dMaxPixel = dialogMinMax.getMaximum();
286     OnUpdate (this, NULL);
287   }
288 }
289
290 void 
291 ImageFileView::OnScaleFull (wxCommandEvent& event)
292 {
293   if (m_bMinSpecified || m_bMaxSpecified) {
294     m_bMinSpecified = false;
295     m_bMaxSpecified = false;
296     OnUpdate (this, NULL);
297   }
298 }
299
300 void
301 ImageFileView::OnCompare (wxCommandEvent& event)
302 {
303   std::vector<ImageFileDocument*> vecIF;
304   theApp->getCompatibleImages (GetDocument(), vecIF);
305   
306   if (vecIF.size() == 0) {
307     wxMessageBox("There are no compatible image files open for comparision", "No comparison images");
308   } else {
309     DialogGetComparisonImage dialogGetCompare(m_frame, "Get Comparison Image", vecIF, true);
310     
311     if (dialogGetCompare.ShowModal() == wxID_OK) {
312       const ImageFile& rIF = GetDocument()->getImageFile();
313       ImageFileDocument* pCompareDoc = dialogGetCompare.getImageFileDocument();
314       const ImageFile& rCompareIF = pCompareDoc->getImageFile();
315       std::ostringstream os;
316       double min, max, mean, mode, median, stddev;
317       rIF.statistics (min, max, mean, mode, median, stddev);
318       os << GetFrame()->GetTitle().c_str() << ": minimum=" << min << ", maximum=" << max << ", mean=" << mean << ", mode=" << mode << ", median=" << median << ", stddev=" << stddev << "\n";
319       rCompareIF.statistics (min, max, mean, mode, median, stddev);
320       os << pCompareDoc->GetFirstView()->GetFrame()->GetTitle().c_str() << ": minimum=" << min << ", maximum=" << max << ", mean=" << mean << ", mode=" << mode << ", median=" << median << ", stddev=" << stddev << "\n";
321       os << "\n";
322       double d, r, e;
323       rIF.comparativeStatistics (rCompareIF, d, r, e);
324       os << "Comparative Statistics: d=" << d << ", r=" << r << ", e=" << e << "\n";
325       *theApp->getLog() << os.str().c_str();
326       if (dialogGetCompare.getMakeDifferenceImage()) {
327         ImageFileDocument* pDifferenceDoc = dynamic_cast<ImageFileDocument*>(theApp->getDocManager()->CreateDocument("untitled.if", wxDOC_SILENT));
328         if (! pDifferenceDoc) {
329           sys_error (ERR_SEVERE, "Unable to create image file");
330           return;
331         }
332         ImageFile& differenceImage = pDifferenceDoc->getImageFile();
333         
334         differenceImage.setArraySize (rIF.nx(), rIF.ny());
335         if (! rIF.subtractImages (rCompareIF, differenceImage)) {
336           pDifferenceDoc->DeleteAllViews();
337           return;
338         }
339         
340         wxString s = GetFrame()->GetTitle() + ": ";
341         differenceImage.labelsCopy (rIF, s.c_str());
342         s = pCompareDoc->GetFirstView()->GetFrame()->GetTitle() + ": ";
343         differenceImage.labelsCopy (rCompareIF, s.c_str());
344         std::ostringstream osLabel;
345         osLabel << "Compare image " << GetDocument()->GetFirstView()->GetFrame()->GetTitle().c_str() 
346           << " and " << pCompareDoc->GetFirstView()->GetFrame()->GetTitle().c_str() << ": "
347           << os.str().c_str();
348         differenceImage.labelAdd (os.str().c_str());
349         if (theApp->getSetModifyNewDocs())
350           pDifferenceDoc->Modify(true);
351         pDifferenceDoc->UpdateAllViews(this);
352         pDifferenceDoc->GetFirstView()->OnUpdate (this, NULL);
353       }
354       wxMessageBox(os.str().c_str(), "Image Comparison");
355     }
356   }
357 }
358
359 void
360 ImageFileView::OnInvertValues (wxCommandEvent& event)
361 {
362   ImageFile& rIF = GetDocument()->getImageFile();
363   rIF.invertPixelValues (rIF);
364   rIF.labelAdd ("Invert Pixel Values");
365   if (theApp->getSetModifyNewDocs())
366     GetDocument()->Modify(TRUE);
367   GetDocument()->UpdateAllViews(this);
368 }
369
370 void
371 ImageFileView::OnSquare (wxCommandEvent& event)
372 {
373   ImageFile& rIF = GetDocument()->getImageFile();
374   rIF.square (rIF);
375   rIF.labelAdd ("Square Pixel Values");
376   if (theApp->getSetModifyNewDocs())
377     GetDocument()->Modify(TRUE);
378   GetDocument()->UpdateAllViews(this);
379 }
380
381 void
382 ImageFileView::OnSquareRoot (wxCommandEvent& event)
383 {
384   ImageFile& rIF = GetDocument()->getImageFile();
385   rIF.sqrt (rIF);
386   rIF.labelAdd ("Square-root Pixel Values");
387   if (theApp->getSetModifyNewDocs())
388     GetDocument()->Modify(TRUE);
389   GetDocument()->UpdateAllViews(this);
390 }
391
392 void
393 ImageFileView::OnLog (wxCommandEvent& event)
394 {
395   ImageFile& rIF = GetDocument()->getImageFile();
396   rIF.log (rIF);
397   rIF.labelAdd ("Logrithm base-e Pixel Values");
398   if (theApp->getSetModifyNewDocs())
399     GetDocument()->Modify(TRUE);
400   GetDocument()->UpdateAllViews(this);
401 }
402
403 void
404 ImageFileView::OnExp (wxCommandEvent& event)
405 {
406   ImageFile& rIF = GetDocument()->getImageFile();
407   rIF.exp (rIF);
408   rIF.labelAdd ("Exponent base-e Pixel Values");
409   if (theApp->getSetModifyNewDocs())
410     GetDocument()->Modify(TRUE);
411   GetDocument()->UpdateAllViews(this);
412 }
413
414 void
415 ImageFileView::OnAdd (wxCommandEvent& event)
416 {
417   std::vector<ImageFileDocument*> vecIF;
418   theApp->getCompatibleImages (GetDocument(), vecIF);
419   
420   if (vecIF.size() == 0) {
421     wxMessageBox ("There are no compatible image files open for comparision", "No comparison images");
422   } else {
423     DialogGetComparisonImage dialogGetCompare (m_frame, "Get Image to Add", vecIF, false);
424     
425     if (dialogGetCompare.ShowModal() == wxID_OK) {
426       ImageFile& rIF = GetDocument()->getImageFile();
427       ImageFileDocument* pRHSDoc = dialogGetCompare.getImageFileDocument();
428       const ImageFile& rRHSIF = pRHSDoc->getImageFile();
429       ImageFileDocument* pNewDoc = dynamic_cast<ImageFileDocument*>(theApp->getDocManager()->CreateDocument("untitled.if", wxDOC_SILENT));
430         if (! pNewDoc) {
431           sys_error (ERR_SEVERE, "Unable to create image file");
432           return;
433         }
434       ImageFile& newImage = pNewDoc->getImageFile();  
435       newImage.setArraySize (rIF.nx(), rIF.ny());
436       rIF.addImages (rRHSIF, newImage);
437       std::ostringstream os;
438       os << "Add image " << GetDocument()->GetFirstView()->GetFrame()->GetTitle().c_str() << " and " 
439         << pRHSDoc->GetFirstView()->GetFrame()->GetTitle().c_str();
440       wxString s = GetDocument()->GetFirstView()->GetFrame()->GetTitle() + ": ";
441       newImage.labelsCopy (rIF, s.c_str());
442       s = pRHSDoc->GetFirstView()->GetFrame()->GetTitle() + ": ";
443       newImage.labelsCopy (rRHSIF, s.c_str());
444       newImage.labelAdd (os.str().c_str());
445       *theApp->getLog() << os.str().c_str() << "\n";
446       if (theApp->getSetModifyNewDocs())
447         pNewDoc->Modify(TRUE);
448       pNewDoc->UpdateAllViews(this);
449       pNewDoc->GetFirstView()->OnUpdate (this, NULL);
450     }
451   }
452 }
453
454 void
455 ImageFileView::OnSubtract (wxCommandEvent& event)
456 {
457   std::vector<ImageFileDocument*> vecIF;
458   theApp->getCompatibleImages (GetDocument(), vecIF);
459   
460   if (vecIF.size() == 0) {
461     wxMessageBox ("There are no compatible image files open for comparision", "No comparison images");
462   } else {
463     DialogGetComparisonImage dialogGetCompare (m_frame, "Get Image to Subtract", vecIF, false);
464     
465     if (dialogGetCompare.ShowModal() == wxID_OK) {
466       ImageFile& rIF = GetDocument()->getImageFile();
467       ImageFileDocument* pRHSDoc = dialogGetCompare.getImageFileDocument();
468       const ImageFile& rRHSIF = pRHSDoc->getImageFile();
469       ImageFileDocument* pNewDoc = dynamic_cast<ImageFileDocument*>(theApp->getDocManager()->CreateDocument("untitled.if", wxDOC_SILENT));
470         if (! pNewDoc) {
471           sys_error (ERR_SEVERE, "Unable to create image file");
472           return;
473         }
474       ImageFile& newImage = pNewDoc->getImageFile();  
475       newImage.setArraySize (rIF.nx(), rIF.ny());
476       rIF.subtractImages (rRHSIF, newImage);
477       std::ostringstream os;
478       os << "Subtract image " << GetDocument()->GetFirstView()->GetFrame()->GetTitle().c_str() << " and " 
479         << pRHSDoc->GetFirstView()->GetFrame()->GetTitle().c_str();
480       wxString s = GetDocument()->GetFirstView()->GetFrame()->GetTitle() + ": ";
481       newImage.labelsCopy (rIF, s.c_str());
482       s = pRHSDoc->GetFirstView()->GetFrame()->GetTitle() + ": ";
483       newImage.labelsCopy (rRHSIF, s.c_str());
484       newImage.labelAdd (os.str().c_str());
485       *theApp->getLog() << os.str().c_str() << "\n";
486       if (theApp->getSetModifyNewDocs())
487         pNewDoc->Modify(TRUE);
488       pNewDoc->UpdateAllViews(this);
489       pNewDoc->GetFirstView()->OnUpdate (this, NULL);
490     }
491   }
492 }
493
494 void
495 ImageFileView::OnMultiply (wxCommandEvent& event)
496 {
497   std::vector<ImageFileDocument*> vecIF;
498   theApp->getCompatibleImages (GetDocument(), vecIF);
499   
500   if (vecIF.size() == 0) {
501     wxMessageBox ("There are no compatible image files open for comparision", "No comparison images");
502   } else {
503     DialogGetComparisonImage dialogGetCompare (m_frame, "Get Image to Multiply", vecIF, false);
504     
505     if (dialogGetCompare.ShowModal() == wxID_OK) {
506       ImageFile& rIF = GetDocument()->getImageFile();
507       ImageFileDocument* pRHSDoc = dialogGetCompare.getImageFileDocument();
508       const ImageFile& rRHSIF = pRHSDoc->getImageFile();
509       ImageFileDocument* pNewDoc = dynamic_cast<ImageFileDocument*>(theApp->getDocManager()->CreateDocument("untitled.if", wxDOC_SILENT));
510         if (! pNewDoc) {
511           sys_error (ERR_SEVERE, "Unable to create image file");
512           return;
513         }
514       ImageFile& newImage = pNewDoc->getImageFile();  
515       newImage.setArraySize (rIF.nx(), rIF.ny());
516       rIF.multiplyImages (rRHSIF, newImage);
517       std::ostringstream os;
518       os << "Multiply image " << GetDocument()->GetFirstView()->GetFrame()->GetTitle().c_str() << " and " 
519         << pRHSDoc->GetFirstView()->GetFrame()->GetTitle().c_str();
520       wxString s = GetDocument()->GetFirstView()->GetFrame()->GetTitle() + ": ";
521       newImage.labelsCopy (rIF, s.c_str());
522       s = pRHSDoc->GetFirstView()->GetFrame()->GetTitle() + ": ";
523       newImage.labelsCopy (rRHSIF, s.c_str());
524       newImage.labelAdd (os.str().c_str());
525       *theApp->getLog() << os.str().c_str() << "\n";
526       if (theApp->getSetModifyNewDocs())
527         pNewDoc->Modify(TRUE);
528       pNewDoc->UpdateAllViews(this);
529       pNewDoc->GetFirstView()->OnUpdate (this, NULL);
530     }
531   }
532 }
533
534 void
535 ImageFileView::OnDivide (wxCommandEvent& event)
536 {
537   std::vector<ImageFileDocument*> vecIF;
538   theApp->getCompatibleImages (GetDocument(), vecIF);
539   
540   if (vecIF.size() == 0) {
541     wxMessageBox ("There are no compatible image files open for comparision", "No comparison images");
542   } else {
543     DialogGetComparisonImage dialogGetCompare (m_frame, "Get Image to Divide", vecIF, false);
544     
545     if (dialogGetCompare.ShowModal() == wxID_OK) {
546       ImageFile& rIF = GetDocument()->getImageFile();
547       ImageFileDocument* pRHSDoc = dialogGetCompare.getImageFileDocument();
548       const ImageFile& rRHSIF = pRHSDoc->getImageFile();
549       ImageFileDocument* pNewDoc = dynamic_cast<ImageFileDocument*>(theApp->getDocManager()->CreateDocument("untitled.if", wxDOC_SILENT));
550         if (! pNewDoc) {
551           sys_error (ERR_SEVERE, "Unable to create image file");
552           return;
553         }
554       ImageFile& newImage = pNewDoc->getImageFile();  
555       newImage.setArraySize (rIF.nx(), rIF.ny());
556       rIF.divideImages (rRHSIF, newImage);
557       std::ostringstream os;
558       os << "Divide image " << GetDocument()->GetFirstView()->GetFrame()->GetTitle().c_str() << " by " 
559         << pRHSDoc->GetFirstView()->GetFrame()->GetTitle().c_str();
560       wxString s = GetDocument()->GetFirstView()->GetFrame()->GetTitle() + ": ";
561       newImage.labelsCopy (rIF, s.c_str());
562       s = pRHSDoc->GetFirstView()->GetFrame()->GetTitle() + ": ";
563       newImage.labelsCopy (rRHSIF, s.c_str());
564       newImage.labelAdd (os.str().c_str());
565       *theApp->getLog() << os.str().c_str() << "\n";
566       if (theApp->getSetModifyNewDocs())
567         pNewDoc->Modify(TRUE);
568       pNewDoc->UpdateAllViews(this);
569       pNewDoc->GetFirstView()->OnUpdate (this, NULL);
570     }
571   }
572 }
573
574
575 #ifdef HAVE_FFT
576 void
577 ImageFileView::OnFFT (wxCommandEvent& event)
578 {
579   ImageFile& rIF = GetDocument()->getImageFile();
580   wxProgressDialog dlgProgress (wxString("FFT"), wxString("FFT Progress"), 1, m_frame, wxPD_APP_MODAL);
581   rIF.fft (rIF);
582   rIF.labelAdd ("FFT Image");
583   m_bMinSpecified = false;
584   m_bMaxSpecified = false;
585   if (theApp->getSetModifyNewDocs())
586     GetDocument()->Modify(TRUE);
587   GetDocument()->UpdateAllViews(this);
588 }
589
590 void
591 ImageFileView::OnIFFT (wxCommandEvent& event)
592 {
593   ImageFile& rIF = GetDocument()->getImageFile();
594   wxProgressDialog dlgProgress (wxString("IFFT"), wxString("IFFT Progress"), 1, m_frame, wxPD_APP_MODAL);
595   rIF.ifft (rIF);
596   rIF.labelAdd ("IFFT Image");
597   m_bMinSpecified = false;
598   m_bMaxSpecified = false;
599   if (theApp->getSetModifyNewDocs())
600     GetDocument()->Modify(TRUE);
601   GetDocument()->UpdateAllViews(this);
602 }
603 #endif
604
605 void
606 ImageFileView::OnFourier (wxCommandEvent& event)
607 {
608   ImageFile& rIF = GetDocument()->getImageFile();
609   wxProgressDialog dlgProgress (wxString("Fourier"), wxString("Fourier Progress"), 1, m_frame, wxPD_APP_MODAL);
610   rIF.fourier (rIF);
611   rIF.labelAdd ("Fourier Image");
612   m_bMinSpecified = false;
613   m_bMaxSpecified = false;
614   if (theApp->getSetModifyNewDocs())
615     GetDocument()->Modify(TRUE);
616   GetDocument()->UpdateAllViews(this);
617 }
618
619 void
620 ImageFileView::OnInverseFourier (wxCommandEvent& event)
621 {
622   ImageFile& rIF = GetDocument()->getImageFile();
623   wxProgressDialog dlgProgress (wxString("Inverse Fourier"), wxString("Inverse Fourier Progress"), 1, m_frame, wxPD_APP_MODAL);
624   rIF.inverseFourier (rIF);
625   rIF.labelAdd ("Inverse Fourier Image");
626   m_bMinSpecified = false;
627   m_bMaxSpecified = false;
628   if (theApp->getSetModifyNewDocs())
629     GetDocument()->Modify(TRUE);
630   GetDocument()->UpdateAllViews(this);
631 }
632
633 void
634 ImageFileView::OnShuffleNaturalToFourierOrder (wxCommandEvent& event)
635 {
636   ImageFile& rIF = GetDocument()->getImageFile();
637   Fourier::shuffleNaturalToFourierOrder (rIF);
638   rIF.labelAdd ("Shuffle Natural To Fourier Order");
639   m_bMinSpecified = false;
640   m_bMaxSpecified = false;
641   if (theApp->getSetModifyNewDocs())
642     GetDocument()->Modify(TRUE);
643   GetDocument()->UpdateAllViews(this);
644 }
645
646 void
647 ImageFileView::OnShuffleFourierToNaturalOrder (wxCommandEvent& event)
648 {
649   ImageFile& rIF = GetDocument()->getImageFile();
650   Fourier::shuffleFourierToNaturalOrder (rIF);
651   rIF.labelAdd ("Shuffle Fourier To Natural Order");
652   m_bMinSpecified = false;
653   m_bMaxSpecified = false;
654   if (theApp->getSetModifyNewDocs())
655     GetDocument()->Modify(TRUE);
656   GetDocument()->UpdateAllViews(this);
657 }
658
659 void
660 ImageFileView::OnMagnitude (wxCommandEvent& event)
661 {
662   ImageFile& rIF = GetDocument()->getImageFile();
663   if (rIF.isComplex()) {
664     rIF.magnitude (rIF);
665     rIF.labelAdd ("Magnitude of complex-image");
666   m_bMinSpecified = false;
667   m_bMaxSpecified = false;
668   if (theApp->getSetModifyNewDocs())
669     GetDocument()->Modify(TRUE);
670   GetDocument()->UpdateAllViews(this);
671   }
672 }
673
674 void
675 ImageFileView::OnPhase (wxCommandEvent& event)
676 {
677   ImageFile& rIF = GetDocument()->getImageFile();
678   if (rIF.isComplex()) {
679     rIF.phase (rIF);
680     rIF.labelAdd ("Phase of complex-image");
681   m_bMinSpecified = false;
682   m_bMaxSpecified = false;
683   if (theApp->getSetModifyNewDocs())
684     GetDocument()->Modify(TRUE);
685   GetDocument()->UpdateAllViews(this);
686   }
687 }
688
689
690 ImageFileCanvas* 
691 ImageFileView::CreateCanvas (wxView *view, wxFrame *parent)
692 {
693   ImageFileCanvas* pCanvas;
694   int width, height;
695   parent->GetClientSize(&width, &height);
696   
697   pCanvas = new ImageFileCanvas (dynamic_cast<ImageFileView*>(view), parent, wxPoint(0, 0), wxSize(width, height), 0);
698   
699   pCanvas->SetScrollbars(20, 20, 50, 50);
700   pCanvas->SetBackgroundColour(*wxWHITE);
701   pCanvas->Clear();
702   
703   return pCanvas;
704 }
705
706 wxFrame*
707 ImageFileView::CreateChildFrame(wxDocument *doc, wxView *view)
708 {
709   wxDocChildFrame *subframe = new wxDocChildFrame(doc, view, theApp->getMainFrame(), -1, "ImageFile Frame", wxPoint(-1, -1), wxSize(0, 0), wxDEFAULT_FRAME_STYLE);
710   
711   wxMenu *file_menu = new wxMenu;
712   
713   file_menu->Append(MAINMENU_FILE_CREATE_PHANTOM, "Cr&eate Phantom...");
714   file_menu->Append(MAINMENU_FILE_CREATE_FILTER, "Create &Filter...");
715   file_menu->Append(wxID_OPEN, "&Open...");
716   file_menu->Append(wxID_SAVE, "&Save");
717   file_menu->Append(wxID_SAVEAS, "Save &As...");
718   file_menu->Append(wxID_CLOSE, "&Close");
719   
720   file_menu->AppendSeparator();
721   file_menu->Append(IFMENU_FILE_PROPERTIES, "P&roperties");
722   file_menu->Append(IFMENU_FILE_EXPORT, "&Export...");
723   
724   file_menu->AppendSeparator();
725   file_menu->Append(wxID_PRINT, "&Print...");
726   file_menu->Append(wxID_PRINT_SETUP, "Print &Setup...");
727   file_menu->Append(wxID_PREVIEW, "Print Pre&view");
728   
729   wxMenu *view_menu = new wxMenu;
730   view_menu->Append(IFMENU_VIEW_SCALE_MINMAX, "Display Scale &Set...");
731   view_menu->Append(IFMENU_VIEW_SCALE_AUTO, "Display Scale &Auto...");
732   view_menu->Append(IFMENU_VIEW_SCALE_FULL, "Display &Full Scale");
733   
734   wxMenu* filter_menu = new wxMenu;
735   filter_menu->Append (IFMENU_FILTER_INVERTVALUES, "&Invert Values");
736   filter_menu->Append (IFMENU_FILTER_SQUARE, "&Square");
737   filter_menu->Append (IFMENU_FILTER_SQRT, "Square &Root");
738   filter_menu->Append (IFMENU_FILTER_LOG, "&Log");
739   filter_menu->Append (IFMENU_FILTER_EXP, "&Exp");
740   filter_menu->AppendSeparator();
741 #ifdef HAVE_FFT
742   filter_menu->Append (IFMENU_FILTER_FFT, "&FFT");
743   filter_menu->Append (IFMENU_FILTER_IFFT, "&IFFT");
744   filter_menu->Append (IFMENU_FILTER_FOURIER, "F&ourier");
745   filter_menu->Append (IFMENU_FILTER_INVERSE_FOURIER, "Inverse Fo&urier");
746 #else
747   filter_menu->Append (IFMENU_FILTER_FOURIER, "&Fourier");
748   filter_menu->Append (IFMENU_FILTER_INVERSE_FOURIER, "&Inverse Fourier");
749 #endif
750   filter_menu->Append (IFMENU_FILTER_SHUFFLEFOURIERTONATURALORDER, "S&huffle Fourier to Natural Order");
751   filter_menu->Append (IFMENU_FILTER_SHUFFLENATURALTOFOURIERORDER, "Shu&ffle Natural to Fourier Order");
752   filter_menu->Append (IFMENU_FILTER_MAGNITUDE, "&Magnitude");
753   filter_menu->Append (IFMENU_FILTER_PHASE, "&Phase");
754   
755   wxMenu* image_menu = new wxMenu;
756   image_menu->Append (IFMENU_IMAGE_ADD, "&Add...");
757   image_menu->Append (IFMENU_IMAGE_SUBTRACT, "&Subtract...");
758   image_menu->Append (IFMENU_IMAGE_MULTIPLY, "&Multiply...");
759   image_menu->Append (IFMENU_IMAGE_DIVIDE, "&Divide...");
760   image_menu->AppendSeparator();
761   image_menu->Append (IFMENU_IMAGE_SCALESIZE, "S&cale Size...");
762
763   wxMenu *analyze_menu = new wxMenu;
764   analyze_menu->Append (IFMENU_PLOT_ROW, "Plot &Row");
765   analyze_menu->Append (IFMENU_PLOT_COL, "Plot &Column");
766   analyze_menu->Append (IFMENU_PLOT_HISTOGRAM, "Plot &Histogram");
767   analyze_menu->AppendSeparator();
768   analyze_menu->Append (IFMENU_COMPARE_IMAGES, "Compare &Images...");
769   analyze_menu->Append (IFMENU_COMPARE_ROW, "Compare &Row");
770   analyze_menu->Append (IFMENU_COMPARE_COL, "Compare &Column");
771   
772   wxMenu *help_menu = new wxMenu;
773   help_menu->Append(MAINMENU_HELP_ABOUT, "&About");
774   
775   wxMenuBar *menu_bar = new wxMenuBar;
776   
777   menu_bar->Append(file_menu, "&File");
778   menu_bar->Append(view_menu, "&View");
779   menu_bar->Append(image_menu, "&Image");
780   menu_bar->Append(filter_menu, "Fi&lter");
781   menu_bar->Append(analyze_menu, "&Analyze");
782   menu_bar->Append(help_menu, "&Help");
783   
784   subframe->SetMenuBar(menu_bar);
785   
786   subframe->Centre(wxBOTH);
787   
788   return subframe;
789 }
790
791
792 bool 
793 ImageFileView::OnCreate (wxDocument *doc, long WXUNUSED(flags) )
794 {
795   m_frame = CreateChildFrame(doc, this);
796   SetFrame (m_frame);
797   
798   m_bMinSpecified = false;
799   m_bMaxSpecified = false;
800   m_dAutoScaleFactor = 1.;
801   
802   int width, height;
803   m_frame->GetClientSize (&width, &height);
804   m_frame->SetTitle("ImageFileView");
805   m_canvas = CreateCanvas (this, m_frame);
806   
807   int x, y;  // X requires a forced resize
808   m_frame->GetSize(&x, &y);
809   m_frame->SetSize(-1, -1, x, y);
810   m_frame->SetFocus();
811   m_frame->Show(true);
812   Activate(true);
813   
814   return true;
815 }
816
817 void 
818 ImageFileView::OnDraw (wxDC* dc)
819 {
820   if (m_bitmap.Ok())
821     dc->DrawBitmap(m_bitmap, 0, 0, false);
822   
823   int xCursor, yCursor;
824   if (m_canvas->GetCurrentCursor (xCursor, yCursor))
825     m_canvas->DrawRubberBandCursor (*dc, xCursor, yCursor);
826 }
827
828
829 void 
830 ImageFileView::OnUpdate (wxView *WXUNUSED(sender), wxObject *WXUNUSED(hint) )
831 {
832   const ImageFile& rIF = dynamic_cast<ImageFileDocument*>(GetDocument())->getImageFile();
833   ImageFileArrayConst v = rIF.getArray();
834   int nx = rIF.nx();
835   int ny = rIF.ny();
836   if (v != NULL && nx != 0 && ny != 0) {
837     if (! m_bMinSpecified || ! m_bMaxSpecified) {
838       double min, max;
839       rIF.getMinMax (min, max);
840       if (! m_bMinSpecified)
841         m_dMinPixel = min;
842       if (! m_bMaxSpecified)
843         m_dMaxPixel = max;
844     }
845     double scaleWidth = m_dMaxPixel - m_dMinPixel;
846     
847     unsigned char* imageData = new unsigned char [nx * ny * 3];
848     for (int ix = 0; ix < nx; ix++) {
849       for (int iy = 0; iy < ny; iy++) {
850         double scaleValue = ((v[ix][iy] - m_dMinPixel) / scaleWidth) * 255;
851         int intensity = static_cast<int>(scaleValue + 0.5);
852         intensity = clamp (intensity, 0, 255);
853         int baseAddr = ((ny - 1 - iy) * nx + ix) * 3;
854         imageData[baseAddr] = imageData[baseAddr+1] = imageData[baseAddr+2] = intensity;
855       }
856     }
857     wxImage image (nx, ny, imageData, true);
858     m_bitmap = image.ConvertToBitmap();
859     delete imageData;
860     int xSize = nx;
861     int ySize = ny;
862     xSize = clamp (xSize, 0, 800);
863     ySize = clamp (ySize, 0, 800);
864     m_frame->SetClientSize (xSize, ySize);
865     m_canvas->SetScrollbars(20, 20, nx/20, ny/20);
866     m_canvas->SetBackgroundColour(*wxWHITE);
867   } 
868   
869   if (m_canvas)
870     m_canvas->Refresh();
871 }
872
873 bool 
874 ImageFileView::OnClose (bool deleteWindow)
875 {
876   if (!GetDocument()->Close())
877     return false;
878   
879   m_canvas->Clear();
880   m_canvas->m_pView = NULL;
881   m_canvas = NULL;
882   wxString s(theApp->GetAppName());
883   if (m_frame)
884     m_frame->SetTitle(s);
885   SetFrame(NULL);
886   
887   Activate(false);
888   
889   if (deleteWindow) {
890     delete m_frame;
891     return true;
892   }
893   return true;
894 }
895
896 void
897 ImageFileView::OnExport (wxCommandEvent& event)
898 {
899   ImageFile& rIF = dynamic_cast<ImageFileDocument*>(GetDocument())->getImageFile();
900   ImageFileArrayConst v = rIF.getArray();
901   int nx = rIF.nx();
902   int ny = rIF.ny();
903   if (v != NULL && nx != 0 && ny != 0) {
904     if (! m_bMinSpecified || ! m_bMaxSpecified) {
905       double min, max;
906       rIF.getMinMax (min, max);
907       if (! m_bMinSpecified)
908         m_dMinPixel = min;
909       if (! m_bMaxSpecified)
910         m_dMaxPixel = max;
911     }
912
913     DialogExportParameters dialogExport (m_frame, m_iDefaultExportFormatID);
914     if (dialogExport.ShowModal() == wxID_OK) {
915       wxString strFormatName (dialogExport.getFormatName ());
916       m_iDefaultExportFormatID = ImageFile::convertFormatNameToID (strFormatName.c_str());
917
918       wxString strExt;
919       wxString strWildcard;
920       if (m_iDefaultExportFormatID == ImageFile::FORMAT_PGM || m_iDefaultExportFormatID == ImageFile::FORMAT_PGMASCII) {
921         strExt = ".pgm";
922         strWildcard = "PGM Files (*.pgm)|*.pgm";
923       }
924 #ifdef HAVE_PNG
925       else if (m_iDefaultExportFormatID == ImageFile::FORMAT_PNG || m_iDefaultExportFormatID == ImageFile::FORMAT_PNG16) {
926         strExt = ".png";
927         strWildcard = "PNG Files (*.png)|*.png";
928       }
929 #endif
930
931       const wxString& strFilename = wxFileSelector (wxString("Export Filename"), wxString(""), 
932         wxString(""), strExt, strWildcard, wxOVERWRITE_PROMPT | wxHIDE_READONLY | wxSAVE);
933       if (strFilename) {
934         rIF.exportImage (strFormatName.c_str(), strFilename.c_str(), 1, 1, m_dMinPixel, m_dMaxPixel);
935         *theApp->getLog() << "Exported file " << strFilename << "\n";
936       }
937     }
938   }
939 }
940
941 void
942 ImageFileView::OnScaleSize (wxCommandEvent& event)
943 {
944   ImageFile& rIF = GetDocument()->getImageFile();
945   unsigned int iOldNX = rIF.nx();
946   unsigned int iOldNY = rIF.ny();
947
948   DialogGetXYSize dialogGetXYSize (m_frame, "Set New X & Y Dimensions", iOldNX, iOldNY);
949   if (dialogGetXYSize.ShowModal() == wxID_OK) {
950     unsigned int iNewNX = dialogGetXYSize.getXSize();
951     unsigned int iNewNY = dialogGetXYSize.getYSize();
952     std::ostringstream os;
953     os << "Scale Size from (" << iOldNX << "," << iOldNY << ") to (" << iNewNX << "," << iNewNY << ")";
954     ImageFileDocument* pScaledDoc = dynamic_cast<ImageFileDocument*>(theApp->getDocManager()->CreateDocument("untitled.if", wxDOC_SILENT));
955     if (! pScaledDoc) {
956       sys_error (ERR_SEVERE, "Unable to create image file");
957       return;
958     }
959     ImageFile& rScaledIF = pScaledDoc->getImageFile();
960     rScaledIF.setArraySize (iNewNX, iNewNY);
961     rScaledIF.labelAdd (os.str().c_str());
962     rIF.scaleImage (rScaledIF);
963     *theApp->getLog() << os.str().c_str() << "\n";
964     if (theApp->getSetModifyNewDocs())
965       pScaledDoc->Modify(TRUE);
966     pScaledDoc->UpdateAllViews (this);
967     pScaledDoc->GetFirstView()->OnUpdate (this, NULL);
968   }
969 }
970
971 void
972 ImageFileView::OnPlotRow (wxCommandEvent& event)
973 {
974   int xCursor, yCursor;
975   if (! m_canvas->GetCurrentCursor (xCursor, yCursor)) {
976     wxMessageBox ("No row selected. Please use left mouse button on image to select column","Error");
977     return;
978   }
979   
980   const ImageFile& rIF = dynamic_cast<ImageFileDocument*>(GetDocument())->getImageFile();
981   ImageFileArrayConst v = rIF.getArray();
982   int nx = rIF.nx();
983   int ny = rIF.ny();
984   
985   if (v != NULL && yCursor < ny) {
986     double* pX = new double [nx];
987     double* pY = new double [nx];
988     for (int i = 0; i < nx; i++) {
989       pX[i] = i;
990       pY[i] = v[i][yCursor];
991     }
992     PlotFileDocument* pPlotDoc = dynamic_cast<PlotFileDocument*>(theApp->getDocManager()->CreateDocument("untitled.plt", wxDOC_SILENT));
993     if (! pPlotDoc) {
994       sys_error (ERR_SEVERE, "Internal error: unable to create Plot file");
995     } else {
996       PlotFile& rPlotFile = pPlotDoc->getPlotFile();
997       std::ostringstream os;
998       os << "Row " << yCursor;
999       std::string title("title ");
1000       title += os.str();
1001       rPlotFile.addEzsetCommand (title.c_str());
1002       rPlotFile.addEzsetCommand ("xlabel Column");
1003       rPlotFile.addEzsetCommand ("ylabel Pixel Value");
1004       rPlotFile.addEzsetCommand ("lxfrac 0");
1005       rPlotFile.addEzsetCommand ("box");
1006       rPlotFile.addEzsetCommand ("grid");
1007       rPlotFile.setCurveSize (2, nx);
1008       rPlotFile.addColumn (0, pX);
1009       rPlotFile.addColumn (1, pY);
1010     }
1011     delete pX;
1012     delete pY;
1013     if (theApp->getSetModifyNewDocs())
1014       pPlotDoc->Modify(true);
1015     pPlotDoc->UpdateAllViews();
1016   }
1017 }
1018
1019 void
1020 ImageFileView::OnPlotCol (wxCommandEvent& event)
1021 {
1022   int xCursor, yCursor;
1023   if (! m_canvas->GetCurrentCursor (xCursor, yCursor)) {
1024     wxMessageBox ("No column selected. Please use left mouse button on image to select column","Error");
1025     return;
1026   }
1027   
1028   const ImageFile& rIF = dynamic_cast<ImageFileDocument*>(GetDocument())->getImageFile();
1029   ImageFileArrayConst v = rIF.getArray();
1030   int nx = rIF.nx();
1031   int ny = rIF.ny();
1032   
1033   if (v != NULL && xCursor < nx) {
1034     double* pX = new double [ny];
1035     double* pY = new double [ny];
1036     for (int i = 0; i < ny; i++) {
1037       pX[i] = i;
1038       pY[i] = v[xCursor][i];
1039     }
1040     PlotFileDocument* pPlotDoc = dynamic_cast<PlotFileDocument*>(theApp->getDocManager()->CreateDocument("untitled.plt", wxDOC_SILENT));
1041     if (! pPlotDoc) {
1042       sys_error (ERR_SEVERE, "Internal error: unable to create Plot file");
1043     } else {
1044       PlotFile& rPlotFile = pPlotDoc->getPlotFile();
1045       std::ostringstream os;
1046       os << "Column " << xCursor;
1047       std::string title("title ");
1048       title += os.str();
1049       rPlotFile.addEzsetCommand (title.c_str());
1050       rPlotFile.addEzsetCommand ("xlabel Row");
1051       rPlotFile.addEzsetCommand ("ylabel Pixel Value");
1052       rPlotFile.addEzsetCommand ("lxfrac 0");
1053       rPlotFile.addEzsetCommand ("box");
1054       rPlotFile.addEzsetCommand ("grid");
1055       rPlotFile.setCurveSize (2, nx);
1056       rPlotFile.addColumn (0, pX);
1057       rPlotFile.addColumn (1, pY);
1058     }
1059     delete pX;
1060     delete pY;
1061     if (theApp->getSetModifyNewDocs())
1062       pPlotDoc->Modify(true);
1063     pPlotDoc->UpdateAllViews();
1064   }
1065 }
1066
1067 void
1068 ImageFileView::OnCompareCol (wxCommandEvent& event)
1069 {
1070   int xCursor, yCursor;
1071   if (! m_canvas->GetCurrentCursor (xCursor, yCursor)) {
1072     wxMessageBox ("No column selected. Please use left mouse button on image to select column","Error");
1073     return;
1074   }
1075   
1076   std::vector<ImageFileDocument*> vecIFDoc;
1077   theApp->getCompatibleImages (GetDocument(), vecIFDoc);
1078   if (vecIFDoc.size() == 0) {
1079     wxMessageBox ("No compatible images for Column Comparison", "Error");
1080     return;
1081   }
1082   DialogGetComparisonImage dialogGetCompare (m_frame, "Get Comparison Image", vecIFDoc, false);
1083   
1084   if (dialogGetCompare.ShowModal() == wxID_OK) {
1085     ImageFileDocument* pCompareDoc = dialogGetCompare.getImageFileDocument();
1086     const ImageFile& rIF = GetDocument()->getImageFile();
1087     const ImageFile& rCompareIF = pCompareDoc->getImageFile();
1088     
1089     ImageFileArrayConst v1 = rIF.getArray();
1090     ImageFileArrayConst v2 = rCompareIF.getArray();
1091     int nx = rIF.nx();
1092     int ny = rIF.ny();
1093     
1094     if (v1 != NULL && xCursor < nx) {
1095       double* pX = new double [ny];
1096       double* pY1 = new double [ny];
1097       double* pY2 = new double [ny];
1098       for (int i = 0; i < ny; i++) {
1099         pX[i] = i;
1100         pY1[i] = v1[xCursor][i];
1101         pY2[i] = v2[xCursor][i];
1102       }
1103       PlotFileDocument* pPlotDoc = dynamic_cast<PlotFileDocument*>(theApp->getDocManager()->CreateDocument("untitled.plt", wxDOC_SILENT));
1104       if (! pPlotDoc) {
1105         sys_error (ERR_SEVERE, "Internal error: unable to create Plot file");
1106       } else {
1107         PlotFile& rPlotFile = pPlotDoc->getPlotFile();
1108         std::ostringstream os;
1109         os << "Column " << xCursor << " Comparison";
1110         std::string title("title ");
1111         title += os.str();
1112         rPlotFile.addEzsetCommand (title.c_str());
1113         rPlotFile.addEzsetCommand ("xlabel Row");
1114         rPlotFile.addEzsetCommand ("ylabel Pixel Value");
1115         rPlotFile.addEzsetCommand ("lxfrac 0");
1116         rPlotFile.addEzsetCommand ("curve 1");
1117         rPlotFile.addEzsetCommand ("color 2");
1118         rPlotFile.addEzsetCommand ("curve 2");
1119         rPlotFile.addEzsetCommand ("color 4");
1120         rPlotFile.addEzsetCommand ("dash 5");
1121         rPlotFile.addEzsetCommand ("box");
1122         rPlotFile.addEzsetCommand ("grid");
1123         rPlotFile.setCurveSize (3, nx);
1124         rPlotFile.addColumn (0, pX);
1125         rPlotFile.addColumn (1, pY1);
1126         rPlotFile.addColumn (2, pY2);
1127       }
1128       delete pX;
1129       delete pY1;
1130       delete pY2;
1131       if (theApp->getSetModifyNewDocs())
1132         pPlotDoc->Modify(true);
1133       pPlotDoc->UpdateAllViews();
1134     }
1135   }
1136 }
1137
1138 void
1139 ImageFileView::OnCompareRow (wxCommandEvent& event)
1140 {
1141   int xCursor, yCursor;
1142   if (! m_canvas->GetCurrentCursor (xCursor, yCursor)) {
1143     wxMessageBox ("No column selected. Please use left mouse button on image to select column","Error");
1144     return;
1145   }
1146   
1147   std::vector<ImageFileDocument*> vecIFDoc;
1148   theApp->getCompatibleImages (GetDocument(), vecIFDoc);
1149   
1150   if (vecIFDoc.size() == 0) {
1151     wxMessageBox ("No compatible images for Row Comparison", "Error");
1152     return;
1153   }
1154   
1155   DialogGetComparisonImage dialogGetCompare (m_frame, "Get Comparison Image", vecIFDoc, false);
1156   
1157   if (dialogGetCompare.ShowModal() == wxID_OK) {
1158     ImageFileDocument* pCompareDoc = dialogGetCompare.getImageFileDocument();
1159     const ImageFile& rIF = GetDocument()->getImageFile();
1160     const ImageFile& rCompareIF = pCompareDoc->getImageFile();
1161     
1162     ImageFileArrayConst v1 = rIF.getArray();
1163     ImageFileArrayConst v2 = rCompareIF.getArray();
1164     int nx = rIF.nx();
1165     int ny = rIF.ny();
1166     
1167     if (v1 != NULL && yCursor < ny) {
1168       double* pX = new double [nx];
1169       double* pY1 = new double [nx];
1170       double* pY2 = new double [nx];
1171       for (int i = 0; i < nx; i++) {
1172         pX[i] = i;
1173         pY1[i] = v1[i][yCursor];
1174         pY2[i] = v2[i][yCursor];
1175       }
1176       PlotFileDocument* pPlotDoc = dynamic_cast<PlotFileDocument*>(theApp->getDocManager()->CreateDocument("untitled.plt", wxDOC_SILENT));
1177       if (! pPlotDoc) {
1178         sys_error (ERR_SEVERE, "Internal error: unable to create Plot file");
1179       } else {
1180         PlotFile& rPlotFile = pPlotDoc->getPlotFile();
1181         std::ostringstream os;
1182         os << "Row " << yCursor << " Comparison";
1183         std::string title("title ");
1184         title += os.str();
1185         rPlotFile.addEzsetCommand (title.c_str());
1186         rPlotFile.addEzsetCommand ("xlabel Column");
1187         rPlotFile.addEzsetCommand ("ylabel Pixel Value");
1188         rPlotFile.addEzsetCommand ("lxfrac 0");
1189         rPlotFile.addEzsetCommand ("curve 1");
1190         rPlotFile.addEzsetCommand ("color 2");
1191         rPlotFile.addEzsetCommand ("curve 2");
1192         rPlotFile.addEzsetCommand ("color 4");
1193         rPlotFile.addEzsetCommand ("dash 5");
1194         rPlotFile.addEzsetCommand ("box");
1195         rPlotFile.addEzsetCommand ("grid");
1196         rPlotFile.setCurveSize (3, ny);
1197         rPlotFile.addColumn (0, pX);
1198         rPlotFile.addColumn (1, pY1);
1199         rPlotFile.addColumn (2, pY2);
1200       }
1201       delete pX;
1202       delete pY1;
1203       delete pY2;
1204       if (theApp->getSetModifyNewDocs())
1205         pPlotDoc->Modify(true);
1206       pPlotDoc->UpdateAllViews();
1207     }
1208   }
1209 }
1210
1211 static int NUMBER_HISTOGRAM_BINS = 256;
1212
1213 void
1214 ImageFileView::OnPlotHistogram (wxCommandEvent& event)
1215
1216   const ImageFile& rIF = dynamic_cast<ImageFileDocument*>(GetDocument())->getImageFile();
1217   ImageFileArrayConst v = rIF.getArray();
1218   int nx = rIF.nx();
1219   int ny = rIF.ny();
1220   
1221   if (v != NULL && nx > 0 && ny > 0) {
1222     PlotFileDocument* pPlotDoc = dynamic_cast<PlotFileDocument*>(theApp->getDocManager()->CreateDocument("untitled.plt", wxDOC_SILENT));
1223     if (! pPlotDoc) {
1224       sys_error (ERR_SEVERE, "Internal error: unable to create Plot file");
1225       return;
1226     }
1227
1228     double* pX = new double [NUMBER_HISTOGRAM_BINS];
1229     double* pY = new double [NUMBER_HISTOGRAM_BINS];
1230     double dMin, dMax;
1231     rIF.getMinMax (dMin, dMax);
1232     double dBinWidth = (dMax - dMin) / NUMBER_HISTOGRAM_BINS;
1233
1234     for (int i = 0; i < NUMBER_HISTOGRAM_BINS; i++) {
1235       pX[i] = dMin + (i + 0.5) * dBinWidth;
1236       pY[i] = 0;
1237     }
1238     for (int ix = 0; ix < nx; ix++)
1239       for (int iy = 0; iy < ny; iy++) {
1240         int iBin = nearest<int> ((v[ix][iy] - dMin) / dBinWidth);
1241         if (iBin >= 0 && iBin < NUMBER_HISTOGRAM_BINS)
1242           pY[iBin] += 1;
1243       }
1244
1245     PlotFile& rPlotFile = pPlotDoc->getPlotFile();
1246     std::ostringstream os;
1247     os << "Histogram";
1248     std::string title("title ");
1249     title += os.str();
1250     rPlotFile.addEzsetCommand (title.c_str());
1251     rPlotFile.addEzsetCommand ("xlabel Pixel Value");
1252     rPlotFile.addEzsetCommand ("ylabel Count");
1253     rPlotFile.addEzsetCommand ("box");
1254     rPlotFile.addEzsetCommand ("grid");
1255     rPlotFile.setCurveSize (2, nx);
1256     rPlotFile.addColumn (0, pX);
1257     rPlotFile.addColumn (1, pY);
1258     delete pX;
1259     delete pY;
1260     if (theApp->getSetModifyNewDocs())
1261       pPlotDoc->Modify(true);
1262     pPlotDoc->UpdateAllViews();
1263   }
1264 }
1265
1266
1267 // PhantomCanvas
1268
1269 PhantomCanvas::PhantomCanvas (PhantomView* v, wxFrame *frame, const wxPoint& pos, const wxSize& size, const long style)
1270 : wxScrolledWindow(frame, -1, pos, size, style)
1271 {
1272   m_pView = v;
1273 }
1274
1275 void 
1276 PhantomCanvas::OnDraw (wxDC& dc)
1277 {
1278   if (m_pView)
1279     m_pView->OnDraw(& dc);
1280 }
1281
1282
1283 // PhantomView
1284
1285 IMPLEMENT_DYNAMIC_CLASS(PhantomView, wxView)
1286
1287 BEGIN_EVENT_TABLE(PhantomView, wxView)
1288 EVT_MENU(PHMMENU_FILE_PROPERTIES, PhantomView::OnProperties)
1289 EVT_MENU(PHMMENU_PROCESS_RASTERIZE, PhantomView::OnRasterize)
1290 EVT_MENU(PHMMENU_PROCESS_PROJECTIONS, PhantomView::OnProjections)
1291 END_EVENT_TABLE()
1292
1293 PhantomView::PhantomView(void) 
1294 : wxView(), m_canvas(NULL), m_frame(NULL)
1295 {
1296   m_iDefaultNDet = 367;
1297   m_iDefaultNView = 320;
1298   m_iDefaultNSample = 2;
1299   m_dDefaultRotation = 1;
1300   m_dDefaultFocalLength = 2;
1301   m_dDefaultFieldOfView = 1;
1302   m_iDefaultGeometry = Scanner::GEOMETRY_PARALLEL;
1303   m_iDefaultTrace = Trace::TRACE_NONE;
1304 }
1305
1306 PhantomView::~PhantomView(void)
1307 {
1308 }
1309
1310 void
1311 PhantomView::OnProperties (wxCommandEvent& event)
1312 {
1313   const int idPhantom = GetDocument()->getPhantomID();
1314   const wxString& namePhantom = GetDocument()->getPhantomName();
1315   std::ostringstream os;
1316   os << "Phantom " << namePhantom.c_str() << " (" << idPhantom << ")" << "\n";
1317   const Phantom& rPhantom = GetDocument()->getPhantom();
1318   rPhantom.printDefinitions (os);
1319 #if DEBUG
1320   rPhantom.print (os);
1321 #endif
1322   *theApp->getLog() << os.str().c_str() << "\n";
1323   wxMessageBox (os.str().c_str(), "Phantom Properties");
1324 }
1325
1326
1327 void
1328 PhantomView::OnProjections (wxCommandEvent& event)
1329 {
1330   DialogGetProjectionParameters dialogProjection (m_frame, m_iDefaultNDet, m_iDefaultNView, m_iDefaultNSample, m_dDefaultRotation, m_dDefaultFocalLength, m_dDefaultFieldOfView, m_iDefaultGeometry, m_iDefaultTrace);
1331   int retVal = dialogProjection.ShowModal();
1332   if (retVal == wxID_OK) {
1333     m_iDefaultNDet = dialogProjection.getNDet();
1334     m_iDefaultNView = dialogProjection.getNView();
1335     m_iDefaultNSample = dialogProjection.getNSamples();
1336     m_iDefaultTrace = dialogProjection.getTrace();
1337     m_dDefaultRotation = dialogProjection.getRotAngle();
1338     m_dDefaultFocalLength = dialogProjection.getFocalLengthRatio();
1339     m_dDefaultFieldOfView = dialogProjection.getFieldOfViewRatio();
1340     wxString sGeometry = dialogProjection.getGeometry();
1341     m_iDefaultGeometry = Scanner::convertGeometryNameToID (sGeometry.c_str());
1342     
1343     if (m_iDefaultNDet > 0 && m_iDefaultNView > 0 && sGeometry != "") {
1344       const Phantom& rPhantom = GetDocument()->getPhantom();
1345       ProjectionFileDocument* pProjectionDoc = dynamic_cast<ProjectionFileDocument*>(theApp->getDocManager()->CreateDocument("untitled.pj", wxDOC_SILENT));
1346       if (! pProjectionDoc) {
1347         sys_error (ERR_SEVERE, "Unable to create projection document");
1348         return;
1349       }
1350       Projections& rProj = pProjectionDoc->getProjections();
1351       Scanner theScanner (rPhantom, sGeometry.c_str(), m_iDefaultNDet, m_iDefaultNView, m_iDefaultNSample, m_dDefaultRotation, m_dDefaultFocalLength, m_dDefaultFieldOfView);
1352       if (theScanner.fail()) {
1353         *theApp->getLog() << "Failed making scanner: " << theScanner.failMessage().c_str() << "\n";
1354         return;
1355       }
1356       rProj.initFromScanner (theScanner);
1357       m_dDefaultRotation /= PI;  // convert back to PI units
1358       
1359       Timer timer;
1360       if (m_iDefaultTrace > Trace::TRACE_CONSOLE) {
1361         ProjectionsDialog dialogProjections (theScanner, rProj, rPhantom, m_iDefaultTrace, dynamic_cast<wxWindow*>(m_frame));
1362         for (int iView = 0; iView < rProj.nView(); iView++) {
1363           ::wxYield();
1364           ::wxYield();
1365           if (dialogProjections.isCancelled() || ! dialogProjections.projectView (iView)) {
1366             pProjectionDoc->DeleteAllViews();
1367             return;
1368           }
1369           ::wxYield();
1370           ::wxYield();
1371           while (dialogProjections.isPaused()) {
1372             ::wxYield();
1373             ::wxUsleep(50);
1374           }
1375         }
1376       } else {
1377         wxProgressDialog dlgProgress (wxString("Projection"), wxString("Projection Progress"), rProj.nView() + 1, m_frame, wxPD_CAN_ABORT);
1378         for (int i = 0; i < rProj.nView(); i++) {
1379           theScanner.collectProjections (rProj, rPhantom, i, 1, true, m_iDefaultTrace);
1380           if (! dlgProgress.Update (i+1)) {
1381             pProjectionDoc->DeleteAllViews();
1382             return;
1383           }
1384         }
1385       }
1386       
1387       std::ostringstream os;
1388       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();
1389       rProj.setCalcTime (timer.timerEnd());
1390       rProj.setRemark (os.str());
1391       *theApp->getLog() << os.str().c_str() << "\n";
1392       
1393       m_frame->Lower();
1394       ::wxYield();
1395       ProjectionFileView* projView = dynamic_cast<ProjectionFileView*>(pProjectionDoc->GetFirstView());
1396       if (projView) {
1397         projView->getFrame()->SetFocus();
1398         projView->OnUpdate (projView, NULL);
1399       }
1400       if (wxView* pView = pProjectionDoc->GetFirstView()) {
1401         if (wxFrame* pFrame = pView->GetFrame()) {
1402           pFrame->SetFocus();
1403           pFrame->Raise();
1404         }
1405         theApp->getDocManager()->ActivateView (pView, true, false);
1406       }
1407       ::wxYield();
1408       if (theApp->getSetModifyNewDocs())
1409         pProjectionDoc->Modify(true);
1410       pProjectionDoc->UpdateAllViews(this);
1411     }
1412   }
1413 }
1414
1415
1416 void
1417 PhantomView::OnRasterize (wxCommandEvent& event)
1418 {
1419   DialogGetRasterParameters dialogRaster (m_frame, 256, 256, 1);
1420   int retVal = dialogRaster.ShowModal();
1421   if (retVal == wxID_OK) {
1422     int xSize = dialogRaster.getXSize();
1423     int ySize = dialogRaster.getYSize();
1424     int nSamples = dialogRaster.getNSamples();
1425     if (nSamples < 1)
1426       nSamples = 1;
1427     if (xSize > 0 && ySize > 0) {
1428       const Phantom& rPhantom = GetDocument()->getPhantom();
1429       ImageFileDocument* pRasterDoc = dynamic_cast<ImageFileDocument*>(theApp->getDocManager()->CreateDocument("untitled.if", wxDOC_SILENT));
1430       if (! pRasterDoc) {
1431         sys_error (ERR_SEVERE, "Unable to create image file");
1432         return;
1433       }
1434       ImageFile& imageFile = pRasterDoc->getImageFile();
1435       
1436       imageFile.setArraySize (xSize, ySize);
1437       wxProgressDialog dlgProgress (wxString("Rasterize"), wxString("Rasterization Progress"), imageFile.nx() + 1, m_frame, wxPD_CAN_ABORT);
1438       Timer timer;
1439       for (unsigned int i = 0; i < imageFile.nx(); i++) {
1440         rPhantom.convertToImagefile (imageFile, nSamples, Trace::TRACE_NONE, i, 1, true);
1441         if (! dlgProgress.Update(i+1)) {
1442           pRasterDoc->DeleteAllViews();
1443           return;
1444         }
1445       }
1446       if (theApp->getSetModifyNewDocs())
1447         pRasterDoc->Modify(true);
1448       pRasterDoc->UpdateAllViews(this);
1449       std::ostringstream os;
1450       os << "Rasterize Phantom " << rPhantom.name() << ": XSize=" << xSize << ", YSize=" << ySize << ", nSamples=" << nSamples;
1451       *theApp->getLog() << os.str().c_str() << "\n";
1452       imageFile.labelAdd (os.str().c_str(), timer.timerEnd());
1453       ImageFileView* rasterView = dynamic_cast<ImageFileView*>(pRasterDoc->GetFirstView());
1454       if (rasterView) {
1455         rasterView->getFrame()->SetFocus();
1456         rasterView->OnUpdate (rasterView, NULL);
1457       }
1458       
1459     }
1460   }
1461 }
1462
1463
1464 PhantomCanvas* 
1465 PhantomView::CreateCanvas (wxView *view, wxFrame *parent)
1466 {
1467   PhantomCanvas* pCanvas;
1468   int width, height;
1469   parent->GetClientSize(&width, &height);
1470   
1471   pCanvas = new PhantomCanvas (dynamic_cast<PhantomView*>(view), parent, wxPoint(0, 0), wxSize(width, height), 0);
1472   
1473   pCanvas->SetBackgroundColour(*wxWHITE);
1474   pCanvas->Clear();
1475   
1476   return pCanvas;
1477 }
1478
1479 wxFrame*
1480 PhantomView::CreateChildFrame(wxDocument *doc, wxView *view)
1481 {
1482   wxDocChildFrame *subframe = new wxDocChildFrame(doc, view, theApp->getMainFrame(), -1, "Phantom Frame", wxPoint(10, 10), wxSize(256, 256), wxDEFAULT_FRAME_STYLE);
1483   
1484   wxMenu *file_menu = new wxMenu;
1485   
1486   file_menu->Append(MAINMENU_FILE_CREATE_PHANTOM, "Cr&eate Phantom...");
1487   file_menu->Append(MAINMENU_FILE_CREATE_FILTER, "Create &Filter...");
1488   file_menu->Append(wxID_OPEN, "&Open...");
1489   file_menu->Append(wxID_SAVEAS, "Save &As...");
1490   file_menu->Append(wxID_CLOSE, "&Close");
1491   
1492   file_menu->AppendSeparator();
1493   file_menu->Append(PHMMENU_FILE_PROPERTIES, "P&roperties");
1494   
1495   file_menu->AppendSeparator();
1496   file_menu->Append(wxID_PRINT, "&Print...");
1497   file_menu->Append(wxID_PRINT_SETUP, "Print &Setup...");
1498   file_menu->Append(wxID_PREVIEW, "Print Pre&view");
1499   
1500   wxMenu *process_menu = new wxMenu;
1501   process_menu->Append(PHMMENU_PROCESS_RASTERIZE, "&Rasterize...");
1502   process_menu->Append(PHMMENU_PROCESS_PROJECTIONS, "&Projections...");
1503   
1504   wxMenu *help_menu = new wxMenu;
1505   help_menu->Append(MAINMENU_HELP_CONTENTS, "&Contents");
1506   help_menu->Append(MAINMENU_HELP_ABOUT, "&About");
1507   
1508   wxMenuBar *menu_bar = new wxMenuBar;
1509   
1510   menu_bar->Append(file_menu, "&File");
1511   menu_bar->Append(process_menu, "&Process");
1512   menu_bar->Append(help_menu, "&Help");
1513   
1514   subframe->SetMenuBar(menu_bar);
1515   
1516   subframe->Centre(wxBOTH);
1517   
1518   return subframe;
1519 }
1520
1521
1522 bool 
1523 PhantomView::OnCreate(wxDocument *doc, long WXUNUSED(flags) )
1524 {
1525   m_frame = CreateChildFrame(doc, this);
1526   SetFrame(m_frame);
1527   
1528   int width, height;
1529   m_frame->GetClientSize(&width, &height);
1530   m_frame->SetTitle("PhantomView");
1531   m_canvas = CreateCanvas(this, m_frame);
1532   
1533 #ifdef __X__
1534   int x, y;  // X requires a forced resize
1535   m_frame->GetSize(&x, &y);
1536   m_frame->SetSize(-1, -1, x, y);
1537 #endif
1538   
1539   m_frame->Show(true);
1540   Activate(true);
1541   
1542   return true;
1543 }
1544
1545
1546 void 
1547 PhantomView::OnUpdate(wxView *WXUNUSED(sender), wxObject *WXUNUSED(hint) )
1548 {
1549   if (m_canvas)
1550     m_canvas->Refresh();
1551 }
1552
1553 bool 
1554 PhantomView::OnClose (bool deleteWindow)
1555 {
1556   if (!GetDocument()->Close())
1557     return false;
1558   
1559   m_canvas->Clear();
1560   m_canvas->m_pView = NULL;
1561   m_canvas = NULL;
1562   wxString s(wxTheApp->GetAppName());
1563   if (m_frame)
1564     m_frame->SetTitle(s);
1565   SetFrame(NULL);
1566   
1567   Activate(false);
1568   
1569   if (deleteWindow) {
1570     delete m_frame;
1571     return true;
1572   }
1573   return true;
1574 }
1575
1576 void
1577 PhantomView::OnDraw (wxDC* dc)
1578 {
1579   int xsize, ysize;
1580   m_canvas->GetClientSize (&xsize, &ysize);
1581   SGPDriver driver (dc, xsize, ysize);
1582   SGP sgp (driver);
1583   const Phantom& rPhantom = GetDocument()->getPhantom();
1584   sgp.setColor (C_RED);
1585   rPhantom.show (sgp);
1586 }
1587
1588 // ProjectionCanvas
1589
1590 ProjectionFileCanvas::ProjectionFileCanvas (ProjectionFileView* v, wxFrame *frame, const wxPoint& pos, const wxSize& size, const long style)
1591 : wxScrolledWindow(frame, -1, pos, size, style)
1592 {
1593   m_pView = v;
1594 }
1595
1596 void 
1597 ProjectionFileCanvas::OnDraw(wxDC& dc)
1598 {
1599   if (m_pView)
1600     m_pView->OnDraw(& dc);
1601 }
1602
1603 // ProjectionFileView
1604
1605 IMPLEMENT_DYNAMIC_CLASS(ProjectionFileView, wxView)
1606
1607 BEGIN_EVENT_TABLE(ProjectionFileView, wxView)
1608 EVT_MENU(PJMENU_FILE_PROPERTIES, ProjectionFileView::OnProperties)
1609 EVT_MENU(PJMENU_RECONSTRUCT_FBP, ProjectionFileView::OnReconstructFBP)
1610 EVT_MENU(PJMENU_CONVERT_POLAR, ProjectionFileView::OnConvertPolar)
1611 EVT_MENU(PJMENU_CONVERT_FFT_POLAR, ProjectionFileView::OnConvertFFTPolar)
1612 END_EVENT_TABLE()
1613
1614 ProjectionFileView::ProjectionFileView(void) 
1615 : wxView(), m_canvas(NULL), m_frame(NULL)
1616 {
1617   m_iDefaultNX = 256;
1618   m_iDefaultNY = 256;
1619   m_iDefaultFilter = SignalFilter::FILTER_ABS_BANDLIMIT;
1620   m_dDefaultFilterParam = 1.;
1621 #if HAVE_FFTW
1622   m_iDefaultFilterMethod = ProcessSignal::FILTER_METHOD_RFFTW;
1623   m_iDefaultFilterGeneration = ProcessSignal::FILTER_GENERATION_INVERSE_FOURIER;
1624 #else
1625   m_iDefaultFilterMethod = ProcessSignal::FILTER_METHOD_CONVOLUTION;
1626   m_iDefaultFilterGeneration = ProcessSignal::FILTER_GENERATION_DIRECT;
1627 #endif
1628   m_iDefaultZeropad = 1;
1629   m_iDefaultBackprojector = Backprojector::BPROJ_IDIFF3;
1630   m_iDefaultInterpolation = Backprojector::INTERP_LINEAR;
1631   m_iDefaultInterpParam = 1;
1632   m_iDefaultTrace = Trace::TRACE_NONE;
1633
1634   m_iDefaultPolarNX = 256;
1635   m_iDefaultPolarNY = 256;
1636   m_iDefaultPolarInterpolation = Projections::POLAR_INTERP_BILINEAR;
1637   m_iDefaultPolarZeropad = 1;
1638 }
1639
1640 ProjectionFileView::~ProjectionFileView(void)
1641 {
1642 }
1643
1644 void
1645 ProjectionFileView::OnProperties (wxCommandEvent& event)
1646 {
1647   const Projections& rProj = GetDocument()->getProjections();
1648   std::ostringstream os;
1649   rProj.printScanInfo(os);
1650   *theApp->getLog() << os.str().c_str();
1651   wxMessageDialog dialogMsg (m_frame, os.str().c_str(), "Projection File Properties", wxOK | wxICON_INFORMATION);
1652   dialogMsg.ShowModal();
1653 }
1654
1655
1656 void
1657 ProjectionFileView::OnConvertPolar (wxCommandEvent& event)
1658 {
1659   Projections& rProj = GetDocument()->getProjections();
1660   DialogGetConvertPolarParameters dialogPolar (m_frame, "Convert Polar", m_iDefaultPolarNX, m_iDefaultPolarNY,
1661     m_iDefaultPolarInterpolation, -1);
1662   if (dialogPolar.ShowModal() == wxID_OK) {
1663     wxString strInterpolation (dialogPolar.getInterpolationName());
1664     m_iDefaultPolarNX = dialogPolar.getXSize();
1665     m_iDefaultPolarNY = dialogPolar.getYSize();
1666     ImageFileDocument* pPolarDoc = dynamic_cast<ImageFileDocument*>(theApp->getDocManager()->CreateDocument("untitled.if", wxDOC_SILENT));
1667     ImageFile& rIF = pPolarDoc->getImageFile();
1668     if (! pPolarDoc) {
1669       sys_error (ERR_SEVERE, "Unable to create image file");
1670       return;
1671     }
1672     rIF.setArraySize (m_iDefaultPolarNX, m_iDefaultPolarNY);
1673     m_iDefaultPolarInterpolation = Projections::convertInterpNameToID (strInterpolation.c_str());
1674     rProj.convertPolar (rIF, m_iDefaultPolarInterpolation);
1675     rIF.labelAdd (rProj.getLabel().getLabelString().c_str(), rProj.calcTime());
1676     std::ostringstream os;
1677     os << "Convert projection file " << GetFrame()->GetTitle().c_str() << " to polar image: xSize=" 
1678       << m_iDefaultPolarNX << ", ySize=" << m_iDefaultPolarNY << ", interpolation=" 
1679       << strInterpolation.c_str();
1680     *theApp->getLog() << os.str().c_str() << "\n";
1681     rIF.labelAdd (os.str().c_str());
1682     if (theApp->getSetModifyNewDocs())
1683       pPolarDoc->Modify(true);
1684     pPolarDoc->UpdateAllViews();
1685     pPolarDoc->GetFirstView()->OnUpdate (this, NULL);
1686   }
1687 }
1688
1689 void
1690 ProjectionFileView::OnConvertFFTPolar (wxCommandEvent& event)
1691 {
1692   Projections& rProj = GetDocument()->getProjections();
1693   DialogGetConvertPolarParameters dialogPolar (m_frame, "Convert to FFT Polar", m_iDefaultPolarNX, m_iDefaultPolarNY,
1694     m_iDefaultPolarInterpolation, m_iDefaultPolarZeropad);
1695   if (dialogPolar.ShowModal() == wxID_OK) {
1696     wxString strInterpolation (dialogPolar.getInterpolationName());
1697     m_iDefaultPolarNX = dialogPolar.getXSize();
1698     m_iDefaultPolarNY = dialogPolar.getYSize();
1699     m_iDefaultPolarZeropad = dialogPolar.getZeropad();
1700     ImageFileDocument* pPolarDoc = dynamic_cast<ImageFileDocument*>(theApp->getDocManager()->CreateDocument("untitled.if", wxDOC_SILENT));
1701     ImageFile& rIF = pPolarDoc->getImageFile();
1702     if (! pPolarDoc) {
1703       sys_error (ERR_SEVERE, "Unable to create image file");
1704       return;
1705     }
1706     rIF.setArraySize (m_iDefaultPolarNX, m_iDefaultPolarNY);
1707     m_iDefaultPolarInterpolation = Projections::convertInterpNameToID (strInterpolation.c_str());
1708     rProj.convertFFTPolar (rIF, m_iDefaultPolarInterpolation, m_iDefaultPolarZeropad);
1709     rIF.labelAdd (rProj.getLabel().getLabelString().c_str(), rProj.calcTime());
1710     std::ostringstream os;
1711     os << "Convert projection file " << GetFrame()->GetTitle().c_str() << " to FFT polar image: xSize=" 
1712       << m_iDefaultPolarNX << ", ySize=" << m_iDefaultPolarNY << ", interpolation=" 
1713       << strInterpolation.c_str() << ", zeropad=" << m_iDefaultPolarZeropad;
1714     *theApp->getLog() << os.str().c_str() << "\n";
1715     rIF.labelAdd (os.str().c_str());
1716     if (theApp->getSetModifyNewDocs())
1717       pPolarDoc->Modify(true);
1718     pPolarDoc->UpdateAllViews();
1719     pPolarDoc->GetFirstView()->OnUpdate (this, NULL);
1720   }}
1721
1722 void
1723 ProjectionFileView::OnReconstructFourier (wxCommandEvent& event)
1724 {
1725   wxMessageBox ("Fourier Reconstruction is not yet supported", "Unimplemented function");
1726 }
1727
1728 void
1729 ProjectionFileView::OnReconstructFBP (wxCommandEvent& event)
1730 {
1731   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);
1732   
1733   int retVal = dialogReconstruction.ShowModal();
1734   if (retVal == wxID_OK) {
1735     m_iDefaultNX = dialogReconstruction.getXSize();
1736     m_iDefaultNY = dialogReconstruction.getYSize();
1737     wxString optFilterName = dialogReconstruction.getFilterName();
1738     m_iDefaultFilter = SignalFilter::convertFilterNameToID (optFilterName.c_str());
1739     m_dDefaultFilterParam = dialogReconstruction.getFilterParam();
1740     wxString optFilterMethodName = dialogReconstruction.getFilterMethodName();
1741     m_iDefaultFilterMethod = ProcessSignal::convertFilterMethodNameToID(optFilterMethodName.c_str());
1742     m_iDefaultZeropad = dialogReconstruction.getZeropad();
1743     wxString optFilterGenerationName = dialogReconstruction.getFilterGenerationName();
1744     m_iDefaultFilterGeneration = ProcessSignal::convertFilterGenerationNameToID (optFilterGenerationName.c_str());
1745     wxString optInterpName = dialogReconstruction.getInterpName();
1746     m_iDefaultInterpolation = Backprojector::convertInterpNameToID (optInterpName.c_str());
1747     m_iDefaultInterpParam = dialogReconstruction.getInterpParam();
1748     wxString optBackprojectName = dialogReconstruction.getBackprojectName();
1749     m_iDefaultBackprojector = Backprojector::convertBackprojectNameToID (optBackprojectName.c_str());
1750     m_iDefaultTrace = dialogReconstruction.getTrace();
1751     if (m_iDefaultNX > 0 && m_iDefaultNY > 0) {
1752       ImageFileDocument* pReconDoc = dynamic_cast<ImageFileDocument*>(theApp->getDocManager()->CreateDocument("untitled.if", wxDOC_SILENT));
1753       if (! pReconDoc) {
1754         sys_error (ERR_SEVERE, "Unable to create image file");
1755         return;
1756       }
1757       ImageFile& imageFile = pReconDoc->getImageFile();
1758       const Projections& rProj = GetDocument()->getProjections();
1759       imageFile.setArraySize (m_iDefaultNX, m_iDefaultNY);
1760       
1761       if (m_iDefaultFilterMethod != ProcessSignal::FILTER_METHOD_CONVOLUTION && m_iDefaultFilterGeneration == ProcessSignal::FILTER_GENERATION_DIRECT && rProj.geometry() != Scanner::GEOMETRY_PARALLEL) {
1762         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);
1763         return;
1764       }
1765
1766       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);
1767
1768       Timer timerRecon;
1769       if (m_iDefaultTrace > Trace::TRACE_CONSOLE) {
1770         ReconstructDialog* pDlgReconstruct = new ReconstructDialog (*pReconstruct, rProj, imageFile, m_iDefaultTrace, m_frame);
1771         for (int iView = 0; iView < rProj.nView(); iView++) {
1772           ::wxYield();
1773           ::wxYield();
1774           if (pDlgReconstruct->isCancelled() || ! pDlgReconstruct->reconstructView (iView)) {
1775             delete pDlgReconstruct;
1776             delete pReconstruct;
1777             pReconDoc->DeleteAllViews();
1778             return;
1779           }
1780           ::wxYield();
1781           ::wxYield();
1782           while (pDlgReconstruct->isPaused()) {
1783             ::wxYield();
1784             ::wxUsleep(50);
1785           }
1786         }
1787         delete pDlgReconstruct;
1788       } else {
1789         wxProgressDialog dlgProgress (wxString("Reconstruction"), wxString("Reconstruction Progress"), rProj.nView() + 1, m_frame, wxPD_CAN_ABORT);
1790         for (int i = 0; i < rProj.nView(); i++) {
1791           pReconstruct->reconstructView (i, 1);
1792           if (! dlgProgress.Update(i + 1)) {
1793             delete pReconstruct;
1794             pReconDoc->DeleteAllViews();
1795             return;
1796           }
1797         }
1798       }
1799       delete pReconstruct;
1800       if (theApp->getSetModifyNewDocs())
1801         pReconDoc->Modify(true);
1802       pReconDoc->UpdateAllViews(this);
1803       ImageFileView* rasterView = dynamic_cast<ImageFileView*>(pReconDoc->GetFirstView());
1804       if (rasterView) {
1805         rasterView->getFrame()->SetFocus();
1806         rasterView->OnUpdate (rasterView, NULL);
1807       }
1808       std::ostringstream os;
1809       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();
1810       *theApp->getLog() << os.str().c_str() << "\n";
1811       imageFile.labelAdd (rProj.getLabel());
1812       imageFile.labelAdd (os.str().c_str(), timerRecon.timerEnd());
1813     }
1814   }
1815 }
1816
1817
1818 ProjectionFileCanvas* 
1819 ProjectionFileView::CreateCanvas (wxView *view, wxFrame *parent)
1820 {
1821   ProjectionFileCanvas* pCanvas;
1822   int width, height;
1823   parent->GetClientSize(&width, &height);
1824   
1825   pCanvas = new ProjectionFileCanvas (dynamic_cast<ProjectionFileView*>(view), parent, wxPoint(0, 0), wxSize(width, height), 0);
1826   
1827   pCanvas->SetScrollbars(20, 20, 50, 50);
1828   pCanvas->SetBackgroundColour(*wxWHITE);
1829   pCanvas->Clear();
1830   
1831   return pCanvas;
1832 }
1833
1834 wxFrame*
1835 ProjectionFileView::CreateChildFrame(wxDocument *doc, wxView *view)
1836 {
1837   wxDocChildFrame *subframe = new wxDocChildFrame(doc, view, theApp->getMainFrame(), -1, "Projection Frame", wxPoint(10, 10), wxSize(0, 0), wxDEFAULT_FRAME_STYLE);
1838   
1839   wxMenu *file_menu = new wxMenu;
1840   
1841   file_menu->Append(MAINMENU_FILE_CREATE_PHANTOM, "Cr&eate Phantom...");
1842   file_menu->Append(MAINMENU_FILE_CREATE_FILTER, "Create &Filter...");
1843   file_menu->Append(wxID_OPEN, "&Open...");
1844   file_menu->Append(wxID_SAVE, "&Save");
1845   file_menu->Append(wxID_SAVEAS, "Save &As...");
1846   file_menu->Append(wxID_CLOSE, "&Close");
1847   
1848   file_menu->AppendSeparator();
1849   file_menu->Append(PJMENU_FILE_PROPERTIES, "P&roperties");
1850   
1851   file_menu->AppendSeparator();
1852   file_menu->Append(wxID_PRINT, "&Print...");
1853   file_menu->Append(wxID_PRINT_SETUP, "Print &Setup...");
1854   file_menu->Append(wxID_PREVIEW, "Print Pre&view");
1855   
1856   wxMenu *convert_menu = new wxMenu;
1857   convert_menu->Append (PJMENU_CONVERT_POLAR, "&Polar Image...");
1858   convert_menu->Append (PJMENU_CONVERT_FFT_POLAR, "&FFT->Polar Image...");
1859   
1860   wxMenu *reconstruct_menu = new wxMenu;
1861   reconstruct_menu->Append (PJMENU_RECONSTRUCT_FBP, "&Filtered Backprojection...");
1862   reconstruct_menu->Append (PJMENU_RECONSTRUCT_FOURIER, "&Fourier...");
1863
1864   wxMenu *help_menu = new wxMenu;
1865   help_menu->Append(MAINMENU_HELP_CONTENTS, "&Contents");
1866   help_menu->AppendSeparator();
1867   help_menu->Append(MAINMENU_HELP_ABOUT, "&About");
1868   
1869   wxMenuBar *menu_bar = new wxMenuBar;
1870   
1871   menu_bar->Append (file_menu, "&File");
1872   menu_bar->Append (convert_menu, "&Convert");
1873   menu_bar->Append (reconstruct_menu, "&Reconstruct");
1874   menu_bar->Append (help_menu, "&Help");
1875   
1876   subframe->SetMenuBar(menu_bar);
1877   
1878   subframe->Centre(wxBOTH);
1879   
1880   return subframe;
1881 }
1882
1883
1884 bool 
1885 ProjectionFileView::OnCreate(wxDocument *doc, long WXUNUSED(flags) )
1886 {
1887   m_frame = CreateChildFrame(doc, this);
1888   SetFrame(m_frame);
1889   
1890   int width, height;
1891   m_frame->GetClientSize(&width, &height);
1892   m_frame->SetTitle("ProjectionFileView");
1893   m_canvas = CreateCanvas(this, m_frame);
1894   
1895 #ifdef __X__
1896   int x, y;  // X requires a forced resize
1897   m_frame->GetSize(&x, &y);
1898   m_frame->SetSize(-1, -1, x, y);
1899 #endif
1900   
1901   m_frame->Show(true);
1902   Activate(true);
1903   
1904   return true;
1905 }
1906
1907 void 
1908 ProjectionFileView::OnDraw (wxDC* dc)
1909 {
1910   if (m_bitmap.Ok())
1911     dc->DrawBitmap (m_bitmap, 0, 0, false);
1912 }
1913
1914
1915 void 
1916 ProjectionFileView::OnUpdate(wxView *WXUNUSED(sender), wxObject *WXUNUSED(hint) )
1917 {
1918   const Projections& rProj = GetDocument()->getProjections();
1919   const int nDet = rProj.nDet();
1920   const int nView = rProj.nView();
1921   if (nDet != 0 && nView != 0) {
1922     const DetectorArray& detarray = rProj.getDetectorArray(0);
1923     const DetectorValue* detval = detarray.detValues();
1924     double min = detval[0];
1925     double max = detval[0];
1926     for (int iy = 0; iy < nView; iy++) {
1927       const DetectorArray& detarray = rProj.getDetectorArray(iy);
1928       const DetectorValue* detval = detarray.detValues();
1929       for (int ix = 0; ix < nDet; ix++) {
1930         if (min > detval[ix])
1931           min = detval[ix];
1932         else if (max < detval[ix])
1933           max = detval[ix];
1934       }
1935     }
1936     
1937     unsigned char* imageData = new unsigned char [nDet * nView * 3];
1938     double scale = (max - min) / 255;
1939     for (int iy2 = 0; iy2 < nView; iy2++) {
1940       const DetectorArray& detarray = rProj.getDetectorArray (iy2);
1941       const DetectorValue* detval = detarray.detValues();
1942       for (int ix = 0; ix < nDet; ix++) {
1943         int intensity = static_cast<int>(((detval[ix] - min) / scale) + 0.5);
1944         intensity = clamp(intensity, 0, 255);
1945         int baseAddr = (iy2 * nDet + ix) * 3;
1946         imageData[baseAddr] = imageData[baseAddr+1] = imageData[baseAddr+2] = intensity;
1947       }
1948     }
1949     wxImage image (nDet, nView, imageData, true);
1950     m_bitmap = image.ConvertToBitmap();
1951     delete imageData;
1952     int xSize = nDet;
1953     int ySize = nView;
1954     xSize = clamp (xSize, 0, 800);
1955     ySize = clamp (ySize, 0, 800);
1956     m_frame->SetClientSize (xSize, ySize);
1957     m_canvas->SetScrollbars (20, 20, nDet/20, nView/20);
1958   }
1959   
1960   if (m_canvas)
1961     m_canvas->Refresh();
1962 }
1963
1964 bool 
1965 ProjectionFileView::OnClose (bool deleteWindow)
1966 {
1967   if (!GetDocument()->Close())
1968     return false;
1969   
1970   m_canvas->Clear();
1971   m_canvas->m_pView = NULL;
1972   m_canvas = NULL;
1973   wxString s(wxTheApp->GetAppName());
1974   if (m_frame)
1975     m_frame->SetTitle(s);
1976   SetFrame(NULL);
1977   
1978   Activate(false);
1979   
1980   if (deleteWindow) {
1981     delete m_frame;
1982     return true;
1983   }
1984   return true;
1985 }
1986
1987
1988
1989 // PlotFileCanvas
1990 PlotFileCanvas::PlotFileCanvas (PlotFileView* v, wxFrame *frame, const wxPoint& pos, const wxSize& size, const long style)
1991 : wxScrolledWindow(frame, -1, pos, size, style)
1992 {
1993   m_pView = v;
1994 }
1995
1996 void 
1997 PlotFileCanvas::OnDraw(wxDC& dc)
1998 {
1999   if (m_pView)
2000     m_pView->OnDraw(& dc);
2001 }
2002
2003
2004 // PlotFileView
2005
2006 IMPLEMENT_DYNAMIC_CLASS(PlotFileView, wxView)
2007
2008 BEGIN_EVENT_TABLE(PlotFileView, wxView)
2009 EVT_MENU(PJMENU_FILE_PROPERTIES, PlotFileView::OnProperties)
2010 EVT_MENU(PLOTMENU_VIEW_SCALE_MINMAX, PlotFileView::OnScaleMinMax)
2011 EVT_MENU(PLOTMENU_VIEW_SCALE_AUTO, PlotFileView::OnScaleAuto)
2012 EVT_MENU(PLOTMENU_VIEW_SCALE_FULL, PlotFileView::OnScaleFull)
2013 END_EVENT_TABLE()
2014
2015 PlotFileView::PlotFileView(void) 
2016 : wxView(), m_canvas(NULL), m_frame(NULL), m_pEZPlot(NULL)
2017 {
2018   m_bMinSpecified = false;
2019   m_bMaxSpecified = false;
2020 }
2021
2022 PlotFileView::~PlotFileView(void)
2023 {
2024   if (m_pEZPlot)
2025     delete m_pEZPlot;
2026 }
2027
2028 void
2029 PlotFileView::OnProperties (wxCommandEvent& event)
2030 {
2031   const PlotFile& rPlot = GetDocument()->getPlotFile();
2032   std::ostringstream os;
2033   os << "Columns: " << rPlot.getNumColumns() << ", Records: " << rPlot.getNumRecords() << "\n";
2034   rPlot.printHeaders (os);
2035   *theApp->getLog() << os.str().c_str();
2036   wxMessageDialog dialogMsg (m_frame, os.str().c_str(), "Plot File Properties", wxOK | wxICON_INFORMATION);
2037   dialogMsg.ShowModal();
2038 }
2039
2040
2041 void 
2042 PlotFileView::OnScaleAuto (wxCommandEvent& event)
2043 {
2044   const PlotFile& rPlotFile = GetDocument()->getPlotFile();
2045   double min, max, mean, mode, median, stddev;
2046   rPlotFile.statistics (1, min, max, mean, mode, median, stddev);
2047   DialogAutoScaleParameters dialogAutoScale (m_frame, mean, mode, median, stddev, m_dAutoScaleFactor);
2048   int iRetVal = dialogAutoScale.ShowModal();
2049   if (iRetVal == wxID_OK) {
2050     m_bMinSpecified = true;
2051     m_bMaxSpecified = true;
2052     double dMin, dMax;
2053     if (dialogAutoScale.getMinMax (&dMin, &dMax)) {
2054       m_dMinPixel = dMin;
2055       m_dMaxPixel = dMax;
2056       m_dAutoScaleFactor = dialogAutoScale.getAutoScaleFactor();
2057       OnUpdate (this, NULL);
2058     }
2059   }
2060 }
2061
2062 void 
2063 PlotFileView::OnScaleMinMax (wxCommandEvent& event)
2064 {
2065   const PlotFile& rPlotFile = GetDocument()->getPlotFile();
2066   double min;
2067   double max;
2068
2069   if (! m_bMinSpecified || ! m_bMaxSpecified) {
2070     if (! rPlotFile.getMinMax (1, min, max)) {
2071       *theApp->getLog() << "Error: unable to find Min/Max\n";
2072       return;
2073     }
2074   }
2075   
2076   if (m_bMinSpecified)
2077     min = m_dMinPixel;
2078   if (m_bMaxSpecified)
2079     max = m_dMaxPixel;
2080   
2081   DialogGetMinMax dialogMinMax (m_frame, "Set Y-axis Minimum & Maximum", min, max);
2082   int retVal = dialogMinMax.ShowModal();
2083   if (retVal == wxID_OK) {
2084     m_bMinSpecified = true;
2085     m_bMaxSpecified = true;
2086     m_dMinPixel = dialogMinMax.getMinimum();
2087     m_dMaxPixel = dialogMinMax.getMaximum();
2088     OnUpdate (this, NULL);
2089   }
2090 }
2091
2092 void 
2093 PlotFileView::OnScaleFull (wxCommandEvent& event)
2094 {
2095   if (m_bMinSpecified || m_bMaxSpecified) {
2096     m_bMinSpecified = false;
2097     m_bMaxSpecified = false;
2098     OnUpdate (this, NULL);
2099   }
2100 }
2101
2102
2103 PlotFileCanvas* 
2104 PlotFileView::CreateCanvas (wxView *view, wxFrame *parent)
2105 {
2106   PlotFileCanvas* pCanvas;
2107   int width, height;
2108   parent->GetClientSize(&width, &height);
2109   
2110   pCanvas = new PlotFileCanvas (dynamic_cast<PlotFileView*>(view), parent, wxPoint(0, 0), wxSize(width, height), 0);
2111   
2112   pCanvas->SetBackgroundColour(*wxWHITE);
2113   pCanvas->Clear();
2114   
2115   return pCanvas;
2116 }
2117
2118 wxFrame*
2119 PlotFileView::CreateChildFrame(wxDocument *doc, wxView *view)
2120 {
2121   wxDocChildFrame *subframe = new wxDocChildFrame(doc, view, theApp->getMainFrame(), -1, "Plot Frame", wxPoint(10, 10), wxSize(500, 300), wxDEFAULT_FRAME_STYLE);
2122   
2123   wxMenu *file_menu = new wxMenu;
2124   
2125   file_menu->Append(MAINMENU_FILE_CREATE_PHANTOM, "Cr&eate Phantom...");
2126   file_menu->Append(MAINMENU_FILE_CREATE_FILTER, "Create &Filter...");
2127   file_menu->Append(wxID_OPEN, "&Open...");
2128   file_menu->Append(wxID_SAVE, "&Save");
2129   file_menu->Append(wxID_SAVEAS, "Save &As...");
2130   file_menu->Append(wxID_CLOSE, "&Close");
2131   
2132   file_menu->AppendSeparator();
2133   file_menu->Append(PJMENU_FILE_PROPERTIES, "P&roperties");
2134   
2135   file_menu->AppendSeparator();
2136   file_menu->Append(wxID_PRINT, "&Print...");
2137   file_menu->Append(wxID_PRINT_SETUP, "Print &Setup...");
2138   file_menu->Append(wxID_PREVIEW, "Print Pre&view");
2139   
2140   wxMenu *view_menu = new wxMenu;
2141   view_menu->Append(PLOTMENU_VIEW_SCALE_MINMAX, "Display Scale &Set...");
2142   view_menu->Append(PLOTMENU_VIEW_SCALE_AUTO, "Display Scale &Auto...");
2143   view_menu->Append(PLOTMENU_VIEW_SCALE_FULL, "Display &Full Scale");
2144   
2145   wxMenu *help_menu = new wxMenu;
2146   help_menu->Append(MAINMENU_HELP_CONTENTS, "&Contents");
2147   help_menu->AppendSeparator();
2148   help_menu->Append(MAINMENU_HELP_ABOUT, "&About");
2149   
2150   wxMenuBar *menu_bar = new wxMenuBar;
2151   
2152   menu_bar->Append(file_menu, "&File");
2153   menu_bar->Append(view_menu, "&View");
2154   menu_bar->Append(help_menu, "&Help");
2155   
2156   subframe->SetMenuBar(menu_bar);
2157   
2158   subframe->Centre(wxBOTH);
2159   
2160   return subframe;
2161 }
2162
2163
2164 bool 
2165 PlotFileView::OnCreate (wxDocument *doc, long WXUNUSED(flags) )
2166 {
2167   m_frame = CreateChildFrame(doc, this);
2168   SetFrame(m_frame);
2169   
2170   m_bMinSpecified = false;
2171   m_bMaxSpecified = false;
2172   m_dAutoScaleFactor = 1.;
2173   
2174   int width, height;
2175   m_frame->GetClientSize(&width, &height);
2176   m_frame->SetTitle ("Plot File");
2177   m_canvas = CreateCanvas (this, m_frame);
2178   
2179 #ifdef __X__
2180   int x, y;  // X requires a forced resize
2181   m_frame->GetSize(&x, &y);
2182   m_frame->SetSize(-1, -1, x, y);
2183 #endif
2184   
2185   m_frame->Show(true);
2186   Activate(true);
2187    
2188   return true;
2189 }
2190
2191 void 
2192 PlotFileView::OnDraw (wxDC* dc)
2193 {
2194   const PlotFile& rPlotFile = GetDocument()->getPlotFile();
2195   const int iNColumns = rPlotFile.getNumColumns();
2196   const int iNRecords = rPlotFile.getNumRecords();
2197   
2198   if (iNColumns > 0 && iNRecords > 0) {
2199     int xsize, ysize;
2200     m_canvas->GetClientSize (&xsize, &ysize);
2201     SGPDriver driver (dc, xsize, ysize);
2202     SGP sgp (driver);
2203     if (m_pEZPlot)
2204       m_pEZPlot->plot (&sgp);
2205   }
2206 }
2207
2208
2209 void 
2210 PlotFileView::OnUpdate (wxView *WXUNUSED(sender), wxObject *WXUNUSED(hint) )
2211 {
2212     const PlotFile& rPlotFile = GetDocument()->getPlotFile();
2213     const int iNColumns = rPlotFile.getNumColumns();
2214     const int iNRecords = rPlotFile.getNumRecords();
2215     
2216     if (iNColumns > 0 && iNRecords > 0) {
2217       if (m_pEZPlot)
2218         delete m_pEZPlot;
2219       m_pEZPlot = new EZPlot;
2220       
2221       for (unsigned int iEzset = 0; iEzset < rPlotFile.getNumEzsetCommands(); iEzset++)
2222         m_pEZPlot->ezset (rPlotFile.getEzsetCommand (iEzset));
2223       
2224       if (m_bMinSpecified) {
2225         std::ostringstream os;
2226         os << "ymin " << m_dMinPixel;
2227         m_pEZPlot->ezset (os.str());
2228       }
2229       
2230       if (m_bMaxSpecified) {
2231         std::ostringstream os;
2232         os << "ymax " << m_dMaxPixel;
2233         m_pEZPlot->ezset (os.str());
2234       }
2235       
2236       m_pEZPlot->ezset("box");
2237       m_pEZPlot->ezset("grid");
2238       
2239       double* pdXaxis = new double [iNRecords];
2240       rPlotFile.getColumn (0, pdXaxis);
2241       
2242       double* pdY = new double [iNRecords];
2243       for (int iCol = 1; iCol < iNColumns; iCol++) {
2244         rPlotFile.getColumn (iCol, pdY);
2245         m_pEZPlot->addCurve (pdXaxis, pdY, iNRecords);
2246       }
2247       
2248       delete pdXaxis;
2249       delete pdY;
2250     }
2251
2252     if (m_canvas)
2253       m_canvas->Refresh();
2254 }
2255
2256 bool 
2257 PlotFileView::OnClose (bool deleteWindow)
2258 {
2259   if (!GetDocument()->Close())
2260     return false;
2261   
2262   m_canvas->Clear();
2263   m_canvas->m_pView = NULL;
2264   m_canvas = NULL;
2265   wxString s(wxTheApp->GetAppName());
2266   if (m_frame)
2267     m_frame->SetTitle(s);
2268   SetFrame(NULL);
2269   
2270   Activate(false);
2271   
2272   if (deleteWindow) {
2273     delete m_frame;
2274     return true;
2275   }
2276   return true;
2277 }
2278