r471: Fixed shutdown crash, added Revert
[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-2001 Kevin Rosenberg
11 **
12 **  $Id: views.cpp,v 1.91 2001/01/30 05:05: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 EVT_CHAR(ImageFileCanvas::OnChar)
69 END_EVENT_TABLE()
70
71
72 ImageFileCanvas::ImageFileCanvas (ImageFileView* v, wxFrame *frame, const wxPoint& pos, const wxSize& size, const long style)
73 : wxScrolledWindow(frame, -1, pos, size, style), m_pView(v), m_xCursor(-1), m_yCursor(-1)
74 {
75 }
76
77 ImageFileCanvas::~ImageFileCanvas()
78 {
79   m_pView = NULL;
80 }
81
82 void 
83 ImageFileCanvas::OnDraw(wxDC& dc)
84 {
85   if (m_pView)
86     m_pView->OnDraw(& dc);
87 }
88
89 void 
90 ImageFileCanvas::DrawRubberBandCursor (wxDC& dc, int x, int y)
91 {
92   const ImageFile& rIF = m_pView->GetDocument()->getImageFile();
93   int nx = rIF.nx();
94   int ny = rIF.ny();
95   
96   int yPt = ny - y - 1;
97   dc.SetLogicalFunction (wxINVERT);
98   dc.SetPen (*wxGREEN_PEN);
99   dc.DrawLine (0, yPt, nx, yPt);
100   dc.DrawLine (x, 0, x, ny);
101   dc.SetLogicalFunction (wxCOPY);
102 }
103
104 bool
105 ImageFileCanvas::GetCurrentCursor (int& x, int& y)
106 {
107   x = m_xCursor;
108   y = m_yCursor;
109   
110   if (m_xCursor >= 0 && m_yCursor >= 0)
111     return true;
112   else
113     return false;
114 }
115
116 void 
117 ImageFileCanvas::OnMouseEvent(wxMouseEvent& event)
118 {
119   if (! m_pView)
120     return;
121   
122   wxClientDC dc(this);
123   PrepareDC(dc);
124   
125   wxPoint pt(event.GetLogicalPosition(dc));
126   
127   const ImageFileDocument* pIFDoc = m_pView->GetDocument();
128   if (! pIFDoc)
129     return;
130   const ImageFile& rIF = pIFDoc->getImageFile();
131   ImageFileArrayConst v = rIF.getArray();
132   int nx = rIF.nx();
133   int ny = rIF.ny();
134   const int yPt = ny - 1 - pt.y;
135   if (event.RightIsDown()) {
136     if (pt.x >= 0 && pt.x < nx && pt.y >= 0 && pt.y < ny) {
137       std::ostringstream os;
138       os << "Image value (" << pt.x << "," << yPt << ") = " << v[pt.x][yPt];
139       if (rIF.isComplex()) {
140         double dImag = rIF.getImaginaryArray()[pt.x][yPt];
141         if (dImag < 0)
142           os << " - " << -dImag;
143         else
144           os << " + " << dImag;
145         os << "i\n";
146       } else
147         os << "\n";
148       *theApp->getLog() << os.str().c_str();
149     } else
150       *theApp->getLog() << "Mouse out of image range (" << pt.x << "," << yPt << ")\n";
151   }
152   else if (event.LeftIsDown() || event.LeftUp() || event.RightUp()) {
153     if (pt.x >= 0 && pt.x < nx && pt.y >= 0 && pt.y < ny) {
154       if (m_xCursor >= 0 && m_yCursor >= 0) {
155         DrawRubberBandCursor (dc, m_xCursor, m_yCursor);
156       }
157       DrawRubberBandCursor (dc, pt.x, yPt);
158       m_xCursor = pt.x;
159       m_yCursor = yPt;
160       wxMenu* pMenu = m_pView->getMenuAnalyze();
161       if (pMenu && ! pMenu->IsEnabled(IFMENU_PLOT_ROW)) {
162         pMenu->Enable (IFMENU_PLOT_ROW, true);
163         pMenu->Enable (IFMENU_PLOT_COL, true);
164         pMenu->Enable (IFMENU_COMPARE_ROW, true);
165         pMenu->Enable (IFMENU_COMPARE_COL, true);
166         pMenu->Enable (IFMENU_PLOT_FFT_ROW, true);
167         pMenu->Enable (IFMENU_PLOT_FFT_COL, true);
168       }
169     } else
170       *theApp->getLog() << "Mouse out of image range (" << pt.x << "," << yPt << ")\n";
171   }
172   if (event.LeftUp()) {
173     std::ostringstream os;
174     os << "Selected column " << pt.x << " , row " << yPt << "\n";
175     *theApp->getLog() << os.str().c_str();
176   }
177 }
178
179 void
180 ImageFileCanvas::OnChar (wxKeyEvent& event)
181 {
182   if (event.GetKeyCode() == WXK_ESCAPE) {
183     m_xCursor = -1;
184     m_yCursor = -1;
185     if (m_pView)
186       m_pView->OnUpdate (NULL);
187   } else
188     wxScrolledWindow::OnChar (event);
189 }
190
191 wxSize
192 ImageFileCanvas::GetBestSize() const
193 {
194   if (! m_pView)
195     return wxSize(0,0);
196   
197   const ImageFile& rIF = m_pView->GetDocument()->getImageFile();
198   return wxSize (rIF.nx(), rIF.ny());
199 }
200
201
202 // ImageFileView
203
204 IMPLEMENT_DYNAMIC_CLASS(ImageFileView, wxView)
205
206 BEGIN_EVENT_TABLE(ImageFileView, wxView)
207 EVT_MENU(IFMENU_FILE_EXPORT, ImageFileView::OnExport)
208 EVT_MENU(IFMENU_FILE_PROPERTIES, ImageFileView::OnProperties)
209 EVT_MENU(IFMENU_VIEW_SCALE_MINMAX, ImageFileView::OnScaleMinMax)
210 EVT_MENU(IFMENU_VIEW_SCALE_AUTO, ImageFileView::OnScaleAuto)
211 EVT_MENU(IFMENU_VIEW_SCALE_FULL, ImageFileView::OnScaleFull)
212 EVT_MENU(IFMENU_COMPARE_IMAGES, ImageFileView::OnCompare)
213 EVT_MENU(IFMENU_COMPARE_ROW, ImageFileView::OnCompareRow)
214 EVT_MENU(IFMENU_COMPARE_COL, ImageFileView::OnCompareCol)
215 EVT_MENU(IFMENU_FILTER_INVERTVALUES, ImageFileView::OnInvertValues)
216 EVT_MENU(IFMENU_FILTER_SQUARE, ImageFileView::OnSquare)
217 EVT_MENU(IFMENU_FILTER_SQRT, ImageFileView::OnSquareRoot)
218 EVT_MENU(IFMENU_FILTER_LOG, ImageFileView::OnLog)
219 EVT_MENU(IFMENU_FILTER_EXP, ImageFileView::OnExp)
220 EVT_MENU(IFMENU_FILTER_FOURIER, ImageFileView::OnFourier)
221 EVT_MENU(IFMENU_FILTER_INVERSE_FOURIER, ImageFileView::OnInverseFourier)
222 EVT_MENU(IFMENU_FILTER_SHUFFLEFOURIERTONATURALORDER, ImageFileView::OnShuffleFourierToNaturalOrder)
223 EVT_MENU(IFMENU_FILTER_SHUFFLENATURALTOFOURIERORDER, ImageFileView::OnShuffleNaturalToFourierOrder)
224 EVT_MENU(IFMENU_IMAGE_ADD, ImageFileView::OnAdd)
225 EVT_MENU(IFMENU_IMAGE_SUBTRACT, ImageFileView::OnSubtract)
226 EVT_MENU(IFMENU_IMAGE_MULTIPLY, ImageFileView::OnMultiply)
227 EVT_MENU(IFMENU_IMAGE_DIVIDE, ImageFileView::OnDivide)
228 EVT_MENU(IFMENU_IMAGE_SCALESIZE, ImageFileView::OnScaleSize)
229 #ifdef HAVE_FFT
230 EVT_MENU(IFMENU_FILTER_FFT, ImageFileView::OnFFT)
231 EVT_MENU(IFMENU_FILTER_IFFT, ImageFileView::OnIFFT)
232 EVT_MENU(IFMENU_FILTER_FFT_ROWS, ImageFileView::OnFFTRows)
233 EVT_MENU(IFMENU_FILTER_IFFT_ROWS, ImageFileView::OnIFFTRows)
234 EVT_MENU(IFMENU_FILTER_FFT_COLS, ImageFileView::OnFFTCols)
235 EVT_MENU(IFMENU_FILTER_IFFT_COLS, ImageFileView::OnIFFTCols)
236 #endif
237 EVT_MENU(IFMENU_FILTER_MAGNITUDE, ImageFileView::OnMagnitude)
238 EVT_MENU(IFMENU_FILTER_PHASE, ImageFileView::OnPhase)
239 EVT_MENU(IFMENU_PLOT_ROW, ImageFileView::OnPlotRow)
240 EVT_MENU(IFMENU_PLOT_COL, ImageFileView::OnPlotCol)
241 #ifdef HAVE_FFT
242 EVT_MENU(IFMENU_PLOT_FFT_ROW, ImageFileView::OnPlotFFTRow)
243 EVT_MENU(IFMENU_PLOT_FFT_COL, ImageFileView::OnPlotFFTCol)
244 #endif
245 EVT_MENU(IFMENU_PLOT_HISTOGRAM, ImageFileView::OnPlotHistogram)
246 END_EVENT_TABLE()
247
248 ImageFileView::ImageFileView() 
249 : wxView(), m_pFrame(NULL), m_pCanvas(NULL), m_pFileMenu(0), m_bMinSpecified(false), m_bMaxSpecified(false)
250 {
251   m_iDefaultExportFormatID = ImageFile::FORMAT_PNG;
252 }
253
254 ImageFileView::~ImageFileView()
255 {
256   GetDocumentManager()->FileHistoryRemoveMenu (m_pFileMenu);
257   GetDocumentManager()->ActivateView(this, FALSE, TRUE);
258 }
259
260
261 void
262 ImageFileView::OnProperties (wxCommandEvent& event)
263 {
264   const ImageFile& rIF = GetDocument()->getImageFile();
265   if (rIF.nx() == 0 || rIF.ny() == 0)
266     *theApp->getLog() << "Properties: empty imagefile\n";
267   else {
268     const std::string& rFilename = rIF.getFilename();
269     std::ostringstream os;
270     double min, max, mean, mode, median, stddev;
271     rIF.statistics (rIF.getArray(), min, max, mean, mode, median, stddev);
272     os << "Filename: " << rFilename << "\n";
273     os << "Size: (" << rIF.nx() << "," << rIF.ny() << ")\n";
274     os << "Data type: ";
275     if (rIF.isComplex())
276       os << "Complex\n";
277     else
278       os << "Real\n";
279     os << "Minimum: "<<min<<"\nMaximum: "<<max<<"\nMean: "<<mean<<"\nMedian: "<<median<<"\nMode: "<<mode<<"\nStandard Deviation: "<<stddev << "\n";
280     if (rIF.isComplex()) {
281       rIF.statistics (rIF.getImaginaryArray(), min, max, mean, mode, median, stddev);
282       os << "Imaginary: min: "<<min<<"\nmax: "<<max<<"\nmean: "<<mean<<"\nmedian: "<<median<<"\nmode: "<<mode<<"\nstddev: "<<stddev << "\n";
283     }
284     if (rIF.nLabels() > 0) {
285       rIF.printLabelsBrief (os);
286     }
287     *theApp->getLog() << ">>>>\n" << os.str().c_str() << "<<<<\n";
288     wxMessageDialog dialogMsg (getFrameForChild(), os.str().c_str(), "Imagefile Properties", wxOK | wxICON_INFORMATION);
289     dialogMsg.ShowModal();
290   }
291 }
292
293 void 
294 ImageFileView::OnScaleAuto (wxCommandEvent& event)
295 {
296   const ImageFile& rIF = GetDocument()->getImageFile();
297   double min, max, mean, mode, median, stddev;
298   rIF.statistics(min, max, mean, mode, median, stddev);
299   DialogAutoScaleParameters dialogAutoScale (getFrameForChild(), mean, mode, median, stddev, m_dAutoScaleFactor);
300   int iRetVal = dialogAutoScale.ShowModal();
301   if (iRetVal == wxID_OK) {
302     m_bMinSpecified = true;
303     m_bMaxSpecified = true;
304     double dMin, dMax;
305     if (dialogAutoScale.getMinMax (&dMin, &dMax)) {
306       m_dMinPixel = dMin;
307       m_dMaxPixel = dMax;
308       m_dAutoScaleFactor = dialogAutoScale.getAutoScaleFactor();
309       OnUpdate (this, NULL);
310     }
311   }
312 }
313
314 void 
315 ImageFileView::OnScaleMinMax (wxCommandEvent& event)
316 {
317   const ImageFile& rIF = GetDocument()->getImageFile();
318   double min, max;
319   if (! m_bMinSpecified && ! m_bMaxSpecified)
320     rIF.getMinMax (min, max);
321   
322   if (m_bMinSpecified)
323     min = m_dMinPixel;
324   if (m_bMaxSpecified)
325     max = m_dMaxPixel;
326   
327   DialogGetMinMax dialogMinMax (getFrameForChild(), "Set Image Minimum & Maximum", min, max);
328   int retVal = dialogMinMax.ShowModal();
329   if (retVal == wxID_OK) {
330     m_bMinSpecified = true;
331     m_bMaxSpecified = true;
332     m_dMinPixel = dialogMinMax.getMinimum();
333     m_dMaxPixel = dialogMinMax.getMaximum();
334     OnUpdate (this, NULL);
335   }
336 }
337
338 void 
339 ImageFileView::OnScaleFull (wxCommandEvent& event)
340 {
341   if (m_bMinSpecified || m_bMaxSpecified) {
342     m_bMinSpecified = false;
343     m_bMaxSpecified = false;
344     OnUpdate (this, NULL);
345   }
346 }
347
348 void
349 ImageFileView::OnCompare (wxCommandEvent& event)
350 {
351   std::vector<ImageFileDocument*> vecIF;
352   theApp->getCompatibleImages (GetDocument(), vecIF);
353   
354   if (vecIF.size() == 0) {
355     wxMessageBox("There are no compatible image files open for comparision", "No comparison images");
356   } else {
357     DialogGetComparisonImage dialogGetCompare(getFrameForChild(), "Get Comparison Image", vecIF, true);
358     
359     if (dialogGetCompare.ShowModal() == wxID_OK) {
360       const ImageFile& rIF = GetDocument()->getImageFile();
361       ImageFileDocument* pCompareDoc = dialogGetCompare.getImageFileDocument();
362       const ImageFile& rCompareIF = pCompareDoc->getImageFile();
363       std::ostringstream os;
364       double min, max, mean, mode, median, stddev;
365       rIF.statistics (min, max, mean, mode, median, stddev);
366       os << GetFrame()->GetTitle().c_str() << ": minimum=" << min << ", maximum=" << max << ", mean=" << mean << ", mode=" << mode << ", median=" << median << ", stddev=" << stddev << "\n";
367       rCompareIF.statistics (min, max, mean, mode, median, stddev);
368       os << pCompareDoc->GetFirstView()->GetFrame()->GetTitle().c_str() << ": minimum=" << min << ", maximum=" << max << ", mean=" << mean << ", mode=" << mode << ", median=" << median << ", stddev=" << stddev << "\n";
369       double d, r, e;
370       rIF.comparativeStatistics (rCompareIF, d, r, e);
371       os << "Comparative Statistics: d=" << d << ", r=" << r << ", e=" << e << "\n";
372       *theApp->getLog() << ">>>>\n" << os.str().c_str() << "<<<<\n";
373       if (dialogGetCompare.getMakeDifferenceImage()) {
374         ImageFile* pDifferenceImage = new ImageFile;
375         
376         pDifferenceImage->setArraySize (rIF.nx(), rIF.ny());
377         if (! rIF.subtractImages (rCompareIF, *pDifferenceImage)) {
378           *theApp->getLog() << "Unable to subtract images\n";
379           delete pDifferenceImage;
380           return;
381         }
382         ImageFileDocument* pDifferenceDoc = theApp->newImageDoc();
383         if (! pDifferenceDoc) {
384           sys_error (ERR_SEVERE, "Unable to create image file");
385           return;
386         }
387         pDifferenceDoc->setImageFile (pDifferenceImage);
388
389         wxString s = GetFrame()->GetTitle() + ": ";
390         pDifferenceImage->labelsCopy (rIF, s.c_str());
391         s = pCompareDoc->GetFirstView()->GetFrame()->GetTitle() + ": ";
392         pDifferenceImage->labelsCopy (rCompareIF, s.c_str());
393         std::ostringstream osLabel;
394         osLabel << "Compare image " << GetDocument()->GetFirstView()->GetFrame()->GetTitle().c_str() 
395           << " and " << pCompareDoc->GetFirstView()->GetFrame()->GetTitle().c_str() << ": "
396           << os.str().c_str();
397         pDifferenceImage->labelAdd (os.str().c_str());
398         pDifferenceDoc->Modify (true);
399         pDifferenceDoc->UpdateAllViews (this);
400         pDifferenceDoc->getView()->OnUpdate (this, NULL);
401         pDifferenceDoc->getView()->getFrame()->Show(true);
402       }
403       wxMessageBox(os.str().c_str(), "Image Comparison");
404     }
405   }
406 }
407
408 void
409 ImageFileView::OnInvertValues (wxCommandEvent& event)
410 {
411   ImageFile& rIF = GetDocument()->getImageFile();
412   rIF.invertPixelValues (rIF);
413   rIF.labelAdd ("Invert Pixel Values");
414   GetDocument()->Modify (true);
415   GetDocument()->UpdateAllViews (this);
416 }
417
418 void
419 ImageFileView::OnSquare (wxCommandEvent& event)
420 {
421   ImageFile& rIF = GetDocument()->getImageFile();
422   rIF.square (rIF);
423   rIF.labelAdd ("Square Pixel Values");
424   GetDocument()->Modify (true);
425   GetDocument()->UpdateAllViews (this);
426 }
427
428 void
429 ImageFileView::OnSquareRoot (wxCommandEvent& event)
430 {
431   ImageFile& rIF = GetDocument()->getImageFile();
432   rIF.sqrt (rIF);
433   rIF.labelAdd ("Square-root Pixel Values");
434   GetDocument()->Modify (true);
435   GetDocument()->UpdateAllViews (this);
436 }
437
438 void
439 ImageFileView::OnLog (wxCommandEvent& event)
440 {
441   ImageFile& rIF = GetDocument()->getImageFile();
442   rIF.log (rIF);
443   rIF.labelAdd ("Logrithm base-e Pixel Values");
444   GetDocument()->Modify (true);
445   GetDocument()->UpdateAllViews (this);
446 }
447
448 void
449 ImageFileView::OnExp (wxCommandEvent& event)
450 {
451   ImageFile& rIF = GetDocument()->getImageFile();
452   rIF.exp (rIF);
453   rIF.labelAdd ("Exponent base-e Pixel Values");
454   GetDocument()->Modify (true);
455   GetDocument()->UpdateAllViews (this);
456 }
457
458 void
459 ImageFileView::OnAdd (wxCommandEvent& event)
460 {
461   std::vector<ImageFileDocument*> vecIF;
462   theApp->getCompatibleImages (GetDocument(), vecIF);
463   
464   if (vecIF.size() == 0) {
465     wxMessageBox ("There are no compatible image files open for comparision", "No comparison images");
466   } else {
467     DialogGetComparisonImage dialogGetCompare (getFrameForChild(), "Get Image to Add", vecIF, false);
468     
469     if (dialogGetCompare.ShowModal() == wxID_OK) {
470       ImageFile& rIF = GetDocument()->getImageFile();
471       ImageFileDocument* pRHSDoc = dialogGetCompare.getImageFileDocument();
472       const ImageFile& rRHSIF = pRHSDoc->getImageFile();
473       ImageFileDocument* pNewDoc = theApp->newImageDoc();
474       if (! pNewDoc) {
475         sys_error (ERR_SEVERE, "Unable to create image file");
476         return;
477       }
478       ImageFile& newImage = pNewDoc->getImageFile();  
479       newImage.setArraySize (rIF.nx(), rIF.ny());
480       rIF.addImages (rRHSIF, newImage);
481       std::ostringstream os;
482       os << "Add image " << GetDocument()->GetFirstView()->GetFrame()->GetTitle().c_str() << " and " 
483         << pRHSDoc->GetFirstView()->GetFrame()->GetTitle().c_str();
484       wxString s = GetDocument()->GetFirstView()->GetFrame()->GetTitle() + ": ";
485       newImage.labelsCopy (rIF, s.c_str());
486       s = pRHSDoc->GetFirstView()->GetFrame()->GetTitle() + ": ";
487       newImage.labelsCopy (rRHSIF, s.c_str());
488       newImage.labelAdd (os.str().c_str());
489       *theApp->getLog() << os.str().c_str() << "\n";
490       pNewDoc->Modify (true);
491       pNewDoc->UpdateAllViews (this);
492       pNewDoc->getView()->OnUpdate (this, NULL);
493       pNewDoc->getView()->getFrame()->Show(true);
494     }
495   }
496 }
497
498 void
499 ImageFileView::OnSubtract (wxCommandEvent& event)
500 {
501   std::vector<ImageFileDocument*> vecIF;
502   theApp->getCompatibleImages (GetDocument(), vecIF);
503   
504   if (vecIF.size() == 0) {
505     wxMessageBox ("There are no compatible image files open for comparision", "No comparison images");
506   } else {
507     DialogGetComparisonImage dialogGetCompare (getFrameForChild(), "Get Image to Subtract", vecIF, false);
508     
509     if (dialogGetCompare.ShowModal() == wxID_OK) {
510       ImageFile& rIF = GetDocument()->getImageFile();
511       ImageFileDocument* pRHSDoc = dialogGetCompare.getImageFileDocument();
512       const ImageFile& rRHSIF = pRHSDoc->getImageFile();
513       ImageFileDocument* pNewDoc = theApp->newImageDoc();
514       if (! pNewDoc) {
515         sys_error (ERR_SEVERE, "Unable to create image file");
516         return;
517       }
518       ImageFile& newImage = pNewDoc->getImageFile();  
519       newImage.setArraySize (rIF.nx(), rIF.ny());
520       rIF.subtractImages (rRHSIF, newImage);
521       std::ostringstream os;
522       os << "Subtract image " << GetDocument()->GetFirstView()->GetFrame()->GetTitle().c_str() << " and " 
523         << pRHSDoc->GetFirstView()->GetFrame()->GetTitle().c_str();
524       wxString s = GetDocument()->GetFirstView()->GetFrame()->GetTitle() + ": ";
525       newImage.labelsCopy (rIF, s.c_str());
526       s = pRHSDoc->GetFirstView()->GetFrame()->GetTitle() + ": ";
527       newImage.labelsCopy (rRHSIF, s.c_str());
528       newImage.labelAdd (os.str().c_str());
529       *theApp->getLog() << os.str().c_str() << "\n";
530       pNewDoc->Modify (true);
531       pNewDoc->UpdateAllViews (this);
532       pNewDoc->getView()->OnUpdate (this, NULL);
533       pNewDoc->getView()->getFrame()->Show(true);
534     }
535   }
536 }
537
538 void
539 ImageFileView::OnMultiply (wxCommandEvent& event)
540 {
541   std::vector<ImageFileDocument*> vecIF;
542   theApp->getCompatibleImages (GetDocument(), vecIF);
543   
544   if (vecIF.size() == 0) {
545     wxMessageBox ("There are no compatible image files open for comparision", "No comparison images");
546   } else {
547     DialogGetComparisonImage dialogGetCompare (getFrameForChild(), "Get Image to Multiply", vecIF, false);
548     
549     if (dialogGetCompare.ShowModal() == wxID_OK) {
550       ImageFile& rIF = GetDocument()->getImageFile();
551       ImageFileDocument* pRHSDoc = dialogGetCompare.getImageFileDocument();
552       const ImageFile& rRHSIF = pRHSDoc->getImageFile();
553       ImageFileDocument* pNewDoc = theApp->newImageDoc();
554       if (! pNewDoc) {
555         sys_error (ERR_SEVERE, "Unable to create image file");
556         return;
557       }
558       ImageFile& newImage = pNewDoc->getImageFile();  
559       newImage.setArraySize (rIF.nx(), rIF.ny());
560       rIF.multiplyImages (rRHSIF, newImage);
561       std::ostringstream os;
562       os << "Multiply image " << GetDocument()->GetFirstView()->GetFrame()->GetTitle().c_str() << " and " 
563         << pRHSDoc->GetFirstView()->GetFrame()->GetTitle().c_str();
564       wxString s = GetDocument()->GetFirstView()->GetFrame()->GetTitle() + ": ";
565       newImage.labelsCopy (rIF, s.c_str());
566       s = pRHSDoc->GetFirstView()->GetFrame()->GetTitle() + ": ";
567       newImage.labelsCopy (rRHSIF, s.c_str());
568       newImage.labelAdd (os.str().c_str());
569       *theApp->getLog() << os.str().c_str() << "\n";
570       pNewDoc->Modify (true);
571       pNewDoc->UpdateAllViews (this);
572       pNewDoc->getView()->OnUpdate (this, NULL);
573       pNewDoc->getView()->getFrame()->Show(true);
574     }
575   }
576 }
577
578 void
579 ImageFileView::OnDivide (wxCommandEvent& event)
580 {
581   std::vector<ImageFileDocument*> vecIF;
582   theApp->getCompatibleImages (GetDocument(), vecIF);
583   
584   if (vecIF.size() == 0) {
585     wxMessageBox ("There are no compatible image files open for comparision", "No comparison images");
586   } else {
587     DialogGetComparisonImage dialogGetCompare (getFrameForChild(), "Get Image to Divide", vecIF, false);
588     
589     if (dialogGetCompare.ShowModal() == wxID_OK) {
590       ImageFile& rIF = GetDocument()->getImageFile();
591       ImageFileDocument* pRHSDoc = dialogGetCompare.getImageFileDocument();
592       const ImageFile& rRHSIF = pRHSDoc->getImageFile();
593       ImageFileDocument* pNewDoc = theApp->newImageDoc();
594       if (! pNewDoc) {
595         sys_error (ERR_SEVERE, "Unable to create image file");
596         return;
597       }
598       ImageFile& newImage = pNewDoc->getImageFile();  
599       newImage.setArraySize (rIF.nx(), rIF.ny());
600       rIF.divideImages (rRHSIF, newImage);
601       std::ostringstream os;
602       os << "Divide image " << GetDocument()->GetFirstView()->GetFrame()->GetTitle().c_str() << " by " 
603         << pRHSDoc->GetFirstView()->GetFrame()->GetTitle().c_str();
604       wxString s = GetDocument()->GetFirstView()->GetFrame()->GetTitle() + ": ";
605       newImage.labelsCopy (rIF, s.c_str());
606       s = pRHSDoc->GetFirstView()->GetFrame()->GetTitle() + ": ";
607       newImage.labelsCopy (rRHSIF, s.c_str());
608       newImage.labelAdd (os.str().c_str());
609       *theApp->getLog() << os.str().c_str() << "\n";
610       pNewDoc->Modify (true);
611       pNewDoc->UpdateAllViews (this);
612       pNewDoc->getView()->OnUpdate (this, NULL);
613       pNewDoc->getView()->getFrame()->Show(true);
614     }
615   }
616 }
617
618
619 #ifdef HAVE_FFT
620 void
621 ImageFileView::OnFFT (wxCommandEvent& event)
622 {
623   ImageFile& rIF = GetDocument()->getImageFile();
624   rIF.fft (rIF);
625   rIF.labelAdd ("FFT Image");
626   m_bMinSpecified = false;
627   m_bMaxSpecified = false;
628   GetDocument()->Modify (true);
629   GetDocument()->UpdateAllViews (this);
630 }
631
632 void
633 ImageFileView::OnIFFT (wxCommandEvent& event)
634 {
635   ImageFile& rIF = GetDocument()->getImageFile();
636   rIF.ifft (rIF);
637   rIF.labelAdd ("IFFT Image");
638   m_bMinSpecified = false;
639   m_bMaxSpecified = false;
640   GetDocument()->Modify (true);
641   GetDocument()->UpdateAllViews (this);
642 }
643
644 void
645 ImageFileView::OnFFTRows (wxCommandEvent& event)
646 {
647   ImageFile& rIF = GetDocument()->getImageFile();
648   rIF.fftRows (rIF);
649   rIF.labelAdd ("FFT Rows");
650   m_bMinSpecified = false;
651   m_bMaxSpecified = false;
652   GetDocument()->Modify (true);
653   GetDocument()->UpdateAllViews (this);
654 }
655
656 void
657 ImageFileView::OnIFFTRows (wxCommandEvent& event)
658 {
659   ImageFile& rIF = GetDocument()->getImageFile();
660   rIF.ifftRows (rIF);
661   rIF.labelAdd ("IFFT Rows");
662   m_bMinSpecified = false;
663   m_bMaxSpecified = false;
664   GetDocument()->Modify (true);
665   GetDocument()->UpdateAllViews (this);
666 }
667
668 void
669 ImageFileView::OnFFTCols (wxCommandEvent& event)
670 {
671   ImageFile& rIF = GetDocument()->getImageFile();
672   rIF.fftCols (rIF);
673   rIF.labelAdd ("FFT Columns");
674   m_bMinSpecified = false;
675   m_bMaxSpecified = false;
676   GetDocument()->Modify (true);
677   GetDocument()->UpdateAllViews (this);
678 }
679
680 void
681 ImageFileView::OnIFFTCols (wxCommandEvent& event)
682 {
683   ImageFile& rIF = GetDocument()->getImageFile();
684   rIF.ifftCols (rIF);
685   rIF.labelAdd ("IFFT Columns");
686   m_bMinSpecified = false;
687   m_bMaxSpecified = false;
688   GetDocument()->Modify (true);
689   GetDocument()->UpdateAllViews (this);
690 }
691 #endif
692
693 void
694 ImageFileView::OnFourier (wxCommandEvent& event)
695 {
696   ImageFile& rIF = GetDocument()->getImageFile();
697   wxProgressDialog dlgProgress (wxString("Fourier"), wxString("Fourier Progress"), 1, getFrameForChild(), wxPD_APP_MODAL);
698   rIF.fourier (rIF);
699   rIF.labelAdd ("Fourier Image");
700   m_bMinSpecified = false;
701   m_bMaxSpecified = false;
702   GetDocument()->Modify (true);
703   GetDocument()->UpdateAllViews (this);
704 }
705
706 void
707 ImageFileView::OnInverseFourier (wxCommandEvent& event)
708 {
709   ImageFile& rIF = GetDocument()->getImageFile();
710   wxProgressDialog dlgProgress (wxString("Inverse Fourier"), wxString("Inverse Fourier Progress"), 1, getFrameForChild(), wxPD_APP_MODAL);
711   rIF.inverseFourier (rIF);
712   rIF.labelAdd ("Inverse Fourier Image");
713   m_bMinSpecified = false;
714   m_bMaxSpecified = false;
715   GetDocument()->Modify (true);
716   GetDocument()->UpdateAllViews (this);
717 }
718
719 void
720 ImageFileView::OnShuffleNaturalToFourierOrder (wxCommandEvent& event)
721 {
722   ImageFile& rIF = GetDocument()->getImageFile();
723   Fourier::shuffleNaturalToFourierOrder (rIF);
724   rIF.labelAdd ("Shuffle Natural To Fourier Order");
725   m_bMinSpecified = false;
726   m_bMaxSpecified = false;
727   GetDocument()->Modify (true);
728   GetDocument()->UpdateAllViews (this);
729 }
730
731 void
732 ImageFileView::OnShuffleFourierToNaturalOrder (wxCommandEvent& event)
733 {
734   ImageFile& rIF = GetDocument()->getImageFile();
735   Fourier::shuffleFourierToNaturalOrder (rIF);
736   rIF.labelAdd ("Shuffle Fourier To Natural Order");
737   m_bMinSpecified = false;
738   m_bMaxSpecified = false;
739   GetDocument()->Modify (true);
740   GetDocument()->UpdateAllViews (this);
741 }
742
743 void
744 ImageFileView::OnMagnitude (wxCommandEvent& event)
745 {
746   ImageFile& rIF = GetDocument()->getImageFile();
747   if (rIF.isComplex()) {
748     rIF.magnitude (rIF);
749     rIF.labelAdd ("Magnitude of complex-image");
750     m_bMinSpecified = false;
751     m_bMaxSpecified = false;
752     GetDocument()->Modify (true);
753     GetDocument()->UpdateAllViews (this);
754   }
755 }
756
757 void
758 ImageFileView::OnPhase (wxCommandEvent& event)
759 {
760   ImageFile& rIF = GetDocument()->getImageFile();
761   if (rIF.isComplex()) {
762     rIF.phase (rIF);
763     rIF.labelAdd ("Phase of complex-image");
764     m_bMinSpecified = false;
765     m_bMaxSpecified = false;
766     GetDocument()->Modify (true);
767     GetDocument()->UpdateAllViews (this);
768   }
769 }
770
771
772 ImageFileCanvas* 
773 ImageFileView::CreateCanvas (wxFrame* parent)
774 {
775   ImageFileCanvas* pCanvas;
776   int width, height;
777   parent->GetClientSize(&width, &height);
778   
779   pCanvas = new ImageFileCanvas (this, parent, wxPoint(0, 0), wxSize(width, height), 0);
780   
781   pCanvas->SetScrollbars(20, 20, 50, 50);
782   pCanvas->SetBackgroundColour(*wxWHITE);
783   pCanvas->Clear();
784   
785   return pCanvas;
786 }
787
788 #if CTSIM_MDI
789 wxDocMDIChildFrame*
790 #else
791 wxDocChildFrame*
792 #endif
793 ImageFileView::CreateChildFrame(wxDocument *doc, wxView *view)
794 {
795 #if CTSIM_MDI
796   wxDocMDIChildFrame* subframe = new wxDocMDIChildFrame (doc, view, theApp->getMainFrame(), -1, "ImageFile Frame", wxPoint(-1, -1), wxSize(0, 0), wxDEFAULT_FRAME_STYLE);
797 #else
798   wxDocChildFrame* subframe = new wxDocChildFrame (doc, view, theApp->getMainFrame(), -1, "ImageFile Frame", wxPoint(-1, -1), wxSize(0, 0), wxDEFAULT_FRAME_STYLE);
799 #endif
800   theApp->setIconForFrame (subframe);
801   
802   wxMenu *m_pFileMenu = new wxMenu;
803   
804   m_pFileMenu->Append(MAINMENU_FILE_CREATE_PHANTOM, "Cr&eate Phantom...\tCtrl-P");
805   m_pFileMenu->Append(MAINMENU_FILE_CREATE_FILTER, "Create &Filter...\tCtrl-F");
806   m_pFileMenu->Append(wxID_OPEN, "&Open...\tCtrl-O");
807   m_pFileMenu->Append(wxID_SAVE, "&Save\tCtrl-S");
808   m_pFileMenu->Append(wxID_SAVEAS, "Save &As...");
809   m_pFileMenu->Append(wxID_CLOSE, "&Close\tCtrl-W");
810   m_pFileMenu->Append(wxID_REVERT, "Re&vert");
811   
812   m_pFileMenu->AppendSeparator();
813   m_pFileMenu->Append(IFMENU_FILE_PROPERTIES, "P&roperties");
814   m_pFileMenu->Append(IFMENU_FILE_EXPORT, "&Export...");
815   
816   m_pFileMenu->AppendSeparator();
817   m_pFileMenu->Append(wxID_PRINT, "&Print...");
818   m_pFileMenu->Append(wxID_PRINT_SETUP, "Print &Setup...");
819   m_pFileMenu->Append(wxID_PREVIEW, "Print Preview");
820 #ifdef CTSIM_MDI
821   m_pFileMenu->AppendSeparator();
822   m_pFileMenu->Append(MAINMENU_FILE_EXIT, "E&xit");
823 #endif
824   GetDocumentManager()->FileHistoryAddFilesToMenu(m_pFileMenu);
825   GetDocumentManager()->FileHistoryUseMenu(m_pFileMenu);
826   
827   wxMenu *view_menu = new wxMenu;
828   view_menu->Append(IFMENU_VIEW_SCALE_MINMAX, "Display Scale S&et...\tCtrl-E");
829   view_menu->Append(IFMENU_VIEW_SCALE_AUTO, "Display Scale &Auto...\tCtrl-A");
830   view_menu->Append(IFMENU_VIEW_SCALE_FULL, "Display F&ull Scale\tCtrl-U");
831   
832   wxMenu* filter_menu = new wxMenu;
833   filter_menu->Append (IFMENU_FILTER_INVERTVALUES, "&Invert Values");
834   filter_menu->Append (IFMENU_FILTER_SQUARE, "&Square");
835   filter_menu->Append (IFMENU_FILTER_SQRT, "Square &Root");
836   filter_menu->Append (IFMENU_FILTER_LOG, "&Log");
837   filter_menu->Append (IFMENU_FILTER_EXP, "&Exp");
838   filter_menu->AppendSeparator();
839 #ifdef HAVE_FFT
840   filter_menu->Append (IFMENU_FILTER_FFT, "2D &FFT");
841   filter_menu->Append (IFMENU_FILTER_IFFT, "2D &IFFT");
842   filter_menu->Append (IFMENU_FILTER_FFT_ROWS, "FFT Rows");
843   filter_menu->Append (IFMENU_FILTER_IFFT_ROWS, "IFFT Rows");
844   filter_menu->Append (IFMENU_FILTER_FFT_COLS, "FFT Columns");
845   filter_menu->Append (IFMENU_FILTER_IFFT_COLS, "IFFT Columns");
846   filter_menu->Append (IFMENU_FILTER_FOURIER, "F&ourier");
847   filter_menu->Append (IFMENU_FILTER_INVERSE_FOURIER, "Inverse Fo&urier");
848 #else
849   filter_menu->Append (IFMENU_FILTER_FOURIER, "&Fourier");
850   filter_menu->Append (IFMENU_FILTER_INVERSE_FOURIER, "&Inverse Fourier");
851 #endif
852   filter_menu->Append (IFMENU_FILTER_SHUFFLEFOURIERTONATURALORDER, "S&huffle Fourier to Natural Order");
853   filter_menu->Append (IFMENU_FILTER_SHUFFLENATURALTOFOURIERORDER, "Shu&ffle Natural to Fourier Order");
854   filter_menu->Append (IFMENU_FILTER_MAGNITUDE, "&Magnitude");
855   filter_menu->Append (IFMENU_FILTER_PHASE, "&Phase");
856   
857   wxMenu* image_menu = new wxMenu;
858   image_menu->Append (IFMENU_IMAGE_ADD, "&Add...");
859   image_menu->Append (IFMENU_IMAGE_SUBTRACT, "&Subtract...");
860   image_menu->Append (IFMENU_IMAGE_MULTIPLY, "&Multiply...");
861   image_menu->Append (IFMENU_IMAGE_DIVIDE, "&Divide...");
862   image_menu->AppendSeparator();
863   image_menu->Append (IFMENU_IMAGE_SCALESIZE, "S&cale Size...");
864   
865   m_pMenuAnalyze = new wxMenu;
866   m_pMenuAnalyze->Append (IFMENU_PLOT_ROW, "Plot &Row");
867   m_pMenuAnalyze->Append (IFMENU_PLOT_COL, "Plot &Column");
868   m_pMenuAnalyze->Append (IFMENU_PLOT_HISTOGRAM, "Plot &Histogram");
869   m_pMenuAnalyze->AppendSeparator();
870   m_pMenuAnalyze->Append (IFMENU_PLOT_FFT_ROW, "Plot FFT Row");
871   m_pMenuAnalyze->Append (IFMENU_PLOT_FFT_COL, "Plot FFT Column");
872   m_pMenuAnalyze->AppendSeparator();
873   m_pMenuAnalyze->Append (IFMENU_COMPARE_IMAGES, "Compare &Images...");
874   m_pMenuAnalyze->Append (IFMENU_COMPARE_ROW, "Compare &Row");
875   m_pMenuAnalyze->Append (IFMENU_COMPARE_COL, "Compare &Column");
876   m_pMenuAnalyze->Enable (IFMENU_PLOT_ROW, false);
877   m_pMenuAnalyze->Enable (IFMENU_PLOT_COL, false);
878   m_pMenuAnalyze->Enable (IFMENU_COMPARE_ROW, false);
879   m_pMenuAnalyze->Enable (IFMENU_COMPARE_COL, false);
880   m_pMenuAnalyze->Enable (IFMENU_PLOT_FFT_ROW, false);
881   m_pMenuAnalyze->Enable (IFMENU_PLOT_FFT_COL, false);
882   
883   wxMenu *help_menu = new wxMenu;
884   help_menu->Append(MAINMENU_HELP_CONTENTS, "&Contents\tF1");
885   help_menu->Append(MAINMENU_HELP_TOPICS, "&Topics\tCtrl-H");
886   help_menu->Append(MAINMENU_HELP_ABOUT, "&About");
887   
888   wxMenuBar *menu_bar = new wxMenuBar;
889   
890   menu_bar->Append(m_pFileMenu, "&File");
891   menu_bar->Append(view_menu, "&View");
892   menu_bar->Append(image_menu, "&Image");
893   menu_bar->Append(filter_menu, "Fi&lter");
894   menu_bar->Append(m_pMenuAnalyze, "&Analyze");
895   menu_bar->Append(help_menu, "&Help");
896   
897   subframe->SetMenuBar(menu_bar);
898   
899   subframe->Centre(wxBOTH);
900   
901   wxAcceleratorEntry accelEntries[10];
902   accelEntries[0].Set (wxACCEL_CTRL, static_cast<int>('O'), wxID_OPEN);
903   accelEntries[1].Set (wxACCEL_CTRL, static_cast<int>('S'), wxID_SAVE);
904   accelEntries[2].Set (wxACCEL_CTRL, static_cast<int>('W'), wxID_CLOSE);
905   accelEntries[3].Set (wxACCEL_CTRL, static_cast<int>('H'), MAINMENU_HELP_TOPICS);
906   accelEntries[4].Set (wxACCEL_CTRL, static_cast<int>('P'), MAINMENU_FILE_CREATE_PHANTOM);
907   accelEntries[5].Set (wxACCEL_CTRL, static_cast<int>('F'), MAINMENU_FILE_CREATE_FILTER);
908   accelEntries[6].Set (wxACCEL_NORMAL, WXK_F1, MAINMENU_HELP_CONTENTS);
909   accelEntries[7].Set (wxACCEL_CTRL, static_cast<int>('A'), IFMENU_VIEW_SCALE_AUTO);
910   accelEntries[8].Set (wxACCEL_CTRL, static_cast<int>('U'), IFMENU_VIEW_SCALE_FULL);
911   accelEntries[9].Set (wxACCEL_CTRL, static_cast<int>('E'), IFMENU_VIEW_SCALE_MINMAX);
912   wxAcceleratorTable accelTable (10, accelEntries);
913   subframe->SetAcceleratorTable (accelTable);
914   
915   return subframe;
916 }
917
918
919 bool 
920 ImageFileView::OnCreate (wxDocument *doc, long WXUNUSED(flags) )
921 {
922   m_pFrame = CreateChildFrame(doc, this);
923   (m_pFrame);
924   
925   m_bMinSpecified = false;
926   m_bMaxSpecified = false;
927   m_dAutoScaleFactor = 1.;
928   
929   int width, height;
930   m_pFrame->GetClientSize (&width, &height);
931   m_pFrame->SetTitle("ImageFileView");
932   m_pCanvas = CreateCanvas (m_pFrame);
933   
934   int x, y;  // X requires a forced resize
935   m_pFrame->GetSize(&x, &y);
936   m_pFrame->SetSize(-1, -1, x, y);
937   m_pFrame->SetFocus();
938   m_pFrame->Show(true);
939   Activate(true);
940   
941   return true;
942 }
943
944 void 
945 ImageFileView::OnDraw (wxDC* dc)
946 {
947   wxSize sizeWindow = m_pFrame->GetClientSize();
948   wxSize sizeBest = m_pCanvas->GetBestSize();
949   if (sizeWindow.x > sizeBest.x || sizeWindow.y > sizeBest.y)
950     m_pFrame->SetClientSize (sizeBest);
951   
952   if (m_bitmap.Ok())
953     dc->DrawBitmap(m_bitmap, 0, 0, false);
954   
955   int xCursor, yCursor;
956   if (m_pCanvas->GetCurrentCursor (xCursor, yCursor))
957     m_pCanvas->DrawRubberBandCursor (*dc, xCursor, yCursor);
958 }
959
960
961 void 
962 ImageFileView::OnUpdate (wxView *WXUNUSED(sender), wxObject *WXUNUSED(hint) )
963 {
964   const ImageFile& rIF = GetDocument()->getImageFile();
965   ImageFileArrayConst v = rIF.getArray();
966   int nx = rIF.nx();
967   int ny = rIF.ny();
968   if (v != NULL && nx != 0 && ny != 0) {
969     if (! m_bMinSpecified || ! m_bMaxSpecified) {
970       double min, max;
971       rIF.getMinMax (min, max);
972       if (! m_bMinSpecified)
973         m_dMinPixel = min;
974       if (! m_bMaxSpecified)
975         m_dMaxPixel = max;
976     }
977     double scaleWidth = m_dMaxPixel - m_dMinPixel;
978     
979     unsigned char* imageData = new unsigned char [nx * ny * 3];
980     for (int ix = 0; ix < nx; ix++) {
981       for (int iy = 0; iy < ny; iy++) {
982         double scaleValue = ((v[ix][iy] - m_dMinPixel) / scaleWidth) * 255;
983         int intensity = static_cast<int>(scaleValue + 0.5);
984         intensity = clamp (intensity, 0, 255);
985         int baseAddr = ((ny - 1 - iy) * nx + ix) * 3;
986         imageData[baseAddr] = imageData[baseAddr+1] = imageData[baseAddr+2] = intensity;
987       }
988     }
989     wxImage image (nx, ny, imageData, true);
990     m_bitmap = image.ConvertToBitmap();
991     delete imageData;
992     int xSize = nx;
993     int ySize = ny;
994     ySize = clamp (ySize, 0, 800);
995     m_pFrame->SetClientSize (xSize, ySize);
996     m_pCanvas->SetScrollbars(20, 20, nx/20, ny/20);
997     m_pCanvas->SetBackgroundColour(*wxWHITE);
998   } 
999   
1000   if (m_pCanvas)
1001     m_pCanvas->Refresh();
1002 }
1003
1004 bool 
1005 ImageFileView::OnClose (bool deleteWindow)
1006 {
1007   //GetDocumentManager()->ActivateView (this, false, true);
1008   if (! GetDocument() || ! GetDocument()->Close())
1009     return false;
1010
1011   Activate (false);
1012   if (m_pCanvas) {
1013     m_pCanvas->setView(NULL);
1014     m_pCanvas = NULL;
1015   }
1016   wxString s(theApp->GetAppName());
1017   if (m_pFrame)
1018     m_pFrame->SetTitle(s);
1019   
1020   SetFrame(NULL);
1021   
1022   if (deleteWindow) {
1023     m_pFrame->Destroy();
1024     m_pFrame = NULL;
1025     if (GetDocument() && GetDocument()->getBadFileOpen())
1026       ::wxYield();  // wxWindows bug workaround
1027   }
1028   
1029   return true;
1030 }
1031
1032 void
1033 ImageFileView::OnExport (wxCommandEvent& event)
1034 {
1035   ImageFile& rIF = GetDocument()->getImageFile();
1036   ImageFileArrayConst v = rIF.getArray();
1037   int nx = rIF.nx();
1038   int ny = rIF.ny();
1039   if (v != NULL && nx != 0 && ny != 0) {
1040     if (! m_bMinSpecified || ! m_bMaxSpecified) {
1041       double min, max;
1042       rIF.getMinMax (min, max);
1043       if (! m_bMinSpecified)
1044         m_dMinPixel = min;
1045       if (! m_bMaxSpecified)
1046         m_dMaxPixel = max;
1047     }
1048     
1049     DialogExportParameters dialogExport (getFrameForChild(), m_iDefaultExportFormatID);
1050     if (dialogExport.ShowModal() == wxID_OK) {
1051       wxString strFormatName (dialogExport.getFormatName ());
1052       m_iDefaultExportFormatID = ImageFile::convertFormatNameToID (strFormatName.c_str());
1053       
1054       wxString strExt;
1055       wxString strWildcard;
1056       if (m_iDefaultExportFormatID == ImageFile::FORMAT_PGM || m_iDefaultExportFormatID == ImageFile::FORMAT_PGMASCII) {
1057         strExt = ".pgm";
1058         strWildcard = "PGM Files (*.pgm)|*.pgm";
1059       }
1060 #ifdef HAVE_PNG
1061       else if (m_iDefaultExportFormatID == ImageFile::FORMAT_PNG || m_iDefaultExportFormatID == ImageFile::FORMAT_PNG16) {
1062         strExt = ".png";
1063         strWildcard = "PNG Files (*.png)|*.png";
1064       }
1065 #endif
1066       
1067       const wxString& strFilename = wxFileSelector (wxString("Export Filename"), wxString(""), 
1068         wxString(""), strExt, strWildcard, wxOVERWRITE_PROMPT | wxHIDE_READONLY | wxSAVE);
1069       if (strFilename) {
1070         rIF.exportImage (strFormatName.c_str(), strFilename.c_str(), 1, 1, m_dMinPixel, m_dMaxPixel);
1071         *theApp->getLog() << "Exported file " << strFilename << "\n";
1072       }
1073     }
1074   }
1075 }
1076
1077 void
1078 ImageFileView::OnScaleSize (wxCommandEvent& event)
1079 {
1080   ImageFile& rIF = GetDocument()->getImageFile();
1081   unsigned int iOldNX = rIF.nx();
1082   unsigned int iOldNY = rIF.ny();
1083   
1084   DialogGetXYSize dialogGetXYSize (getFrameForChild(), "Set New X & Y Dimensions", iOldNX, iOldNY);
1085   if (dialogGetXYSize.ShowModal() == wxID_OK) {
1086     unsigned int iNewNX = dialogGetXYSize.getXSize();
1087     unsigned int iNewNY = dialogGetXYSize.getYSize();
1088     std::ostringstream os;
1089     os << "Scale Size from (" << iOldNX << "," << iOldNY << ") to (" << iNewNX << "," << iNewNY << ")";
1090     ImageFileDocument* pScaledDoc = theApp->newImageDoc();
1091     if (! pScaledDoc) {
1092       sys_error (ERR_SEVERE, "Unable to create image file");
1093       return;
1094     }
1095     ImageFile& rScaledIF = pScaledDoc->getImageFile();
1096     rScaledIF.setArraySize (iNewNX, iNewNY);
1097     rScaledIF.labelsCopy (rIF);
1098     rScaledIF.labelAdd (os.str().c_str());
1099     rIF.scaleImage (rScaledIF);
1100     *theApp->getLog() << os.str().c_str() << "\n";
1101     pScaledDoc->Modify (true);
1102     pScaledDoc->UpdateAllViews (this);
1103     pScaledDoc->getView()->OnUpdate (this, NULL);
1104     pScaledDoc->getView()->getFrame()->Show(true);
1105   }
1106 }
1107
1108 void
1109 ImageFileView::OnPlotRow (wxCommandEvent& event)
1110 {
1111   int xCursor, yCursor;
1112   if (! m_pCanvas->GetCurrentCursor (xCursor, yCursor)) {
1113     wxMessageBox ("No row selected. Please use left mouse button on image to select column","Error");
1114     return;
1115   }
1116   
1117   const ImageFile& rIF = GetDocument()->getImageFile();
1118   ImageFileArrayConst v = rIF.getArray();
1119   ImageFileArrayConst vImag = rIF.getImaginaryArray();
1120   int nx = rIF.nx();
1121   int ny = rIF.ny();
1122   
1123   if (v != NULL && yCursor < ny) {
1124     double* pX = new double [nx];
1125     double* pYReal = new double [nx];
1126     double *pYImag = NULL;
1127     double *pYMag = NULL;
1128     if (rIF.isComplex()) {
1129       pYImag = new double [nx];
1130       pYMag = new double [nx];
1131     }
1132     for (int i = 0; i < nx; i++) {
1133       pX[i] = i;
1134       pYReal[i] = v[i][yCursor];
1135       if (rIF.isComplex()) {
1136         pYImag[i] = vImag[i][yCursor];
1137         pYMag[i] = ::sqrt (v[i][yCursor] * v[i][yCursor] + vImag[i][yCursor] * vImag[i][yCursor]);
1138       }
1139     }
1140     PlotFileDocument* pPlotDoc = theApp->newPlotDoc();
1141     if (! pPlotDoc) {
1142       sys_error (ERR_SEVERE, "Internal error: unable to create Plot file");
1143     } else {
1144       PlotFile& rPlotFile = pPlotDoc->getPlotFile();
1145       std::ostringstream os;
1146       os << "Row " << yCursor;
1147       std::string title("title ");
1148       title += os.str();
1149       rPlotFile.addEzsetCommand (title.c_str());
1150       rPlotFile.addEzsetCommand ("xlabel Column");
1151       rPlotFile.addEzsetCommand ("ylabel Pixel Value");
1152       rPlotFile.addEzsetCommand ("lxfrac 0");
1153       rPlotFile.addEzsetCommand ("box");
1154       rPlotFile.addEzsetCommand ("grid");
1155       rPlotFile.addEzsetCommand ("curve 1");
1156       rPlotFile.addEzsetCommand ("color 1");
1157       if (rIF.isComplex()) {
1158         rPlotFile.addEzsetCommand ("dash 1");
1159         rPlotFile.addEzsetCommand ("curve 2");
1160         rPlotFile.addEzsetCommand ("color 4");
1161         rPlotFile.addEzsetCommand ("dash 3");
1162         rPlotFile.addEzsetCommand ("curve 3");
1163         rPlotFile.addEzsetCommand ("color 0");
1164         rPlotFile.addEzsetCommand ("solid");
1165         rPlotFile.setCurveSize (4, nx);
1166       } else
1167         rPlotFile.setCurveSize (2, nx);
1168       rPlotFile.addColumn (0, pX);
1169       rPlotFile.addColumn (1, pYReal); 
1170       if (rIF.isComplex()) {
1171         rPlotFile.addColumn (2, pYImag);
1172         rPlotFile.addColumn (3, pYMag);
1173       }
1174       for (unsigned int iL = 0; iL < rIF.nLabels(); iL++)
1175         rPlotFile.addDescription (rIF.labelGet(iL).getLabelString().c_str());
1176       os << " Plot of " << GetDocument()->GetFirstView()->GetFrame()->GetTitle().c_str();
1177       *theApp->getLog() << os.str().c_str() << "\n";
1178       rPlotFile.addDescription (os.str().c_str());
1179     }
1180     delete pX;
1181     delete pYReal;
1182     if (rIF.isComplex()) {
1183       delete pYImag;
1184       delete pYMag;
1185     }
1186     pPlotDoc->Modify (true);
1187     pPlotDoc->UpdateAllViews ();
1188     pPlotDoc->getView()->OnUpdate (this, NULL);
1189     pPlotDoc->getView()->getFrame()->Show(true);
1190   }
1191 }
1192
1193 void
1194 ImageFileView::OnPlotCol (wxCommandEvent& event)
1195 {
1196   int xCursor, yCursor;
1197   if (! m_pCanvas->GetCurrentCursor (xCursor, yCursor)) {
1198     wxMessageBox ("No column selected. Please use left mouse button on image to select column","Error");
1199     return;
1200   }
1201   
1202   const ImageFile& rIF = GetDocument()->getImageFile();
1203   ImageFileArrayConst v = rIF.getArray();
1204   ImageFileArrayConst vImag = rIF.getImaginaryArray();
1205   int nx = rIF.nx();
1206   int ny = rIF.ny();
1207   
1208   if (v != NULL && xCursor < nx) {
1209     double* pX = new double [ny];
1210     double* pYReal = new double [ny];
1211     double* pYImag = NULL;
1212     double* pYMag = NULL;
1213     if (rIF.isComplex()) {
1214       pYImag = new double [ny];
1215       pYMag = new double [ny];
1216     }
1217     for (int i = 0; i < ny; i++) {
1218       pX[i] = i;
1219       pYReal[i] = v[xCursor][i];
1220       if (rIF.isComplex()) {
1221         pYImag[i] = vImag[xCursor][i];
1222         pYMag[i] = ::sqrt (v[xCursor][i] * v[xCursor][i] + vImag[xCursor][i] * vImag[xCursor][i]);
1223       }
1224     }
1225     PlotFileDocument* pPlotDoc = theApp->newPlotDoc();
1226     if (! pPlotDoc) {
1227       sys_error (ERR_SEVERE, "Internal error: unable to create Plot file");
1228     } else {
1229       PlotFile& rPlotFile = pPlotDoc->getPlotFile();
1230       std::ostringstream os;
1231       os << "Column " << xCursor;
1232       std::string title("title ");
1233       title += os.str();
1234       rPlotFile.addEzsetCommand (title.c_str());
1235       rPlotFile.addEzsetCommand ("xlabel Row");
1236       rPlotFile.addEzsetCommand ("ylabel Pixel Value");
1237       rPlotFile.addEzsetCommand ("lxfrac 0");
1238       rPlotFile.addEzsetCommand ("box");
1239       rPlotFile.addEzsetCommand ("grid");
1240       rPlotFile.addEzsetCommand ("curve 1");
1241       rPlotFile.addEzsetCommand ("color 1");
1242       if (rIF.isComplex()) {
1243         rPlotFile.addEzsetCommand ("dash 1");
1244         rPlotFile.addEzsetCommand ("curve 2");
1245         rPlotFile.addEzsetCommand ("color 4");
1246         rPlotFile.addEzsetCommand ("dash 3");
1247         rPlotFile.addEzsetCommand ("curve 3");
1248         rPlotFile.addEzsetCommand ("color 0");
1249         rPlotFile.addEzsetCommand ("solid");
1250         rPlotFile.setCurveSize (4, ny);
1251       } else
1252         rPlotFile.setCurveSize (2, ny);
1253       rPlotFile.addColumn (0, pX);
1254       rPlotFile.addColumn (1, pYReal); 
1255       if (rIF.isComplex()) {
1256         rPlotFile.addColumn (2, pYImag);
1257         rPlotFile.addColumn (3, pYMag);
1258       }
1259       for (unsigned int iL = 0; iL < rIF.nLabels(); iL++)
1260         rPlotFile.addDescription (rIF.labelGet(iL).getLabelString().c_str());
1261       os << " Plot of " << GetDocument()->GetFirstView()->GetFrame()->GetTitle().c_str();
1262       *theApp->getLog() << os.str().c_str() << "\n";
1263       rPlotFile.addDescription (os.str().c_str());
1264     }
1265     delete pX;
1266     delete pYReal;
1267     if (rIF.isComplex()) {
1268       delete pYImag;
1269       delete pYMag;
1270     }
1271     pPlotDoc->Modify (true);
1272     pPlotDoc->UpdateAllViews ();
1273     pPlotDoc->getView()->OnUpdate (this, NULL);
1274     pPlotDoc->getView()->getFrame()->Show(true);
1275   }
1276 }
1277
1278 #ifdef HAVE_FFT
1279 void
1280 ImageFileView::OnPlotFFTRow (wxCommandEvent& event)
1281 {
1282   int xCursor, yCursor;
1283   if (! m_pCanvas->GetCurrentCursor (xCursor, yCursor)) {
1284     wxMessageBox ("No row selected. Please use left mouse button on image to select column","Error");
1285     return;
1286   }
1287   
1288   const ImageFile& rIF = GetDocument()->getImageFile();
1289   ImageFileArrayConst v = rIF.getArray();
1290   ImageFileArrayConst vImag = rIF.getImaginaryArray();
1291   int nx = rIF.nx();
1292   int ny = rIF.ny();
1293   
1294   if (v != NULL && yCursor < ny) {
1295     fftw_complex* pcIn = new fftw_complex [nx];
1296     
1297     int i;
1298     for (i = 0; i < nx; i++) {
1299       pcIn[i].re = v[i][yCursor];
1300       if (rIF.isComplex())
1301         pcIn[i].im = vImag[i][yCursor];
1302       else
1303         pcIn[i].im = 0;
1304     }
1305     
1306     fftw_plan plan = fftw_create_plan (nx, FFTW_FORWARD, FFTW_IN_PLACE);
1307     fftw_one (plan, pcIn, NULL);
1308     fftw_destroy_plan (plan);
1309     
1310     double* pX = new double [nx];
1311     double* pYReal = new double [nx];
1312     double* pYImag = new double [nx];
1313     double* pYMag = new double [nx];
1314     for (i = 0; i < nx; i++) {
1315       pX[i] = i;
1316       pYReal[i] = pcIn[i].re;
1317       pYImag[i] = pcIn[i].im;
1318       pYMag[i] = ::sqrt (pcIn[i].re * pcIn[i].re + pcIn[i].im * pcIn[i].im);
1319     }
1320     Fourier::shuffleFourierToNaturalOrder (pYReal, nx);
1321     Fourier::shuffleFourierToNaturalOrder (pYImag, nx);
1322     Fourier::shuffleFourierToNaturalOrder (pYMag, nx);
1323     
1324     PlotFileDocument* pPlotDoc = theApp->newPlotDoc();
1325     if (! pPlotDoc) {
1326       sys_error (ERR_SEVERE, "Internal error: unable to create Plot file");
1327     } else {
1328       PlotFile& rPlotFile = pPlotDoc->getPlotFile();
1329       std::ostringstream os;
1330       os << "Row " << yCursor;
1331       std::string title("title ");
1332       title += os.str();
1333       rPlotFile.addEzsetCommand (title.c_str());
1334       rPlotFile.addEzsetCommand ("xlabel Column");
1335       rPlotFile.addEzsetCommand ("ylabel Pixel Value");
1336       rPlotFile.addEzsetCommand ("lxfrac 0");
1337       rPlotFile.addEzsetCommand ("curve 1");
1338       rPlotFile.addEzsetCommand ("color 1");
1339       rPlotFile.addEzsetCommand ("dash 1");
1340       rPlotFile.addEzsetCommand ("curve 2");
1341       rPlotFile.addEzsetCommand ("color 4");
1342       rPlotFile.addEzsetCommand ("dash 3");
1343       rPlotFile.addEzsetCommand ("curve 3");
1344       rPlotFile.addEzsetCommand ("color 0");
1345       rPlotFile.addEzsetCommand ("solid");
1346       rPlotFile.addEzsetCommand ("box");
1347       rPlotFile.addEzsetCommand ("grid");
1348       rPlotFile.setCurveSize (4, nx);
1349       rPlotFile.addColumn (0, pX);
1350       rPlotFile.addColumn (1, pYReal);
1351       rPlotFile.addColumn (2, pYImag);
1352       rPlotFile.addColumn (3, pYMag);
1353       for (int iL = 0; iL < rIF.nLabels(); iL++)
1354         rPlotFile.addDescription (rIF.labelGet(iL).getLabelString().c_str());
1355       os << " FFT Plot of " << GetDocument()->GetFirstView()->GetFrame()->GetTitle().c_str();
1356       *theApp->getLog() << os.str().c_str() << "\n";
1357       rPlotFile.addDescription (os.str().c_str());
1358     }
1359     delete pX;
1360     delete pYReal;
1361     delete pYImag;
1362     delete pYMag;
1363     delete [] pcIn;
1364     
1365     pPlotDoc->Modify (true);
1366     pPlotDoc->UpdateAllViews ();
1367     pPlotDoc->getView()->OnUpdate (this, NULL);
1368     pPlotDoc->getView()->getFrame()->Show(true);
1369   }
1370 }
1371
1372 void
1373 ImageFileView::OnPlotFFTCol (wxCommandEvent& event)
1374 {
1375   int xCursor, yCursor;
1376   if (! m_pCanvas->GetCurrentCursor (xCursor, yCursor)) {
1377     wxMessageBox ("No column selected. Please use left mouse button on image to select column","Error");
1378     return;
1379   }
1380   
1381   const ImageFile& rIF = GetDocument()->getImageFile();
1382   ImageFileArrayConst v = rIF.getArray();
1383   ImageFileArrayConst vImag = rIF.getImaginaryArray();
1384   int nx = rIF.nx();
1385   int ny = rIF.ny();
1386   
1387   if (v != NULL && xCursor < nx) {
1388     fftw_complex* pcIn = new fftw_complex [ny];
1389     double *pdTemp = new double [ny];
1390     
1391     int i;
1392     for (i = 0; i < ny; i++)
1393       pdTemp[i] = v[xCursor][i];
1394     Fourier::shuffleNaturalToFourierOrder (pdTemp, ny);
1395     for (i = 0; i < ny; i++) 
1396       pcIn[i].re = pdTemp[i];
1397     
1398     for (i = 0; i < ny; i++) {
1399       if (rIF.isComplex())
1400         pdTemp[i] = vImag[xCursor][i];
1401       else
1402         pdTemp[i] = 0;
1403     }
1404     Fourier::shuffleNaturalToFourierOrder (pdTemp, ny);
1405     for (i = 0; i < ny; i++)
1406       pcIn[i].im = pdTemp[i];
1407     
1408     fftw_plan plan = fftw_create_plan (ny, FFTW_BACKWARD, FFTW_IN_PLACE);
1409     fftw_one (plan, pcIn, NULL);
1410     fftw_destroy_plan (plan);
1411     
1412     double* pX = new double [ny];
1413     double* pYReal = new double [ny];
1414     double* pYImag = new double [ny];
1415     double* pYMag = new double [ny];
1416     for (i = 0; i < ny; i++) {
1417       pX[i] = i;
1418       pYReal[i] = pcIn[i].re;
1419       pYImag[i] = pcIn[i].im;
1420       pYMag[i] = ::sqrt (pcIn[i].re * pcIn[i].re + pcIn[i].im * pcIn[i].im);
1421     }
1422     
1423     PlotFileDocument* pPlotDoc = theApp->newPlotDoc();
1424     if (! pPlotDoc) {
1425       sys_error (ERR_SEVERE, "Internal error: unable to create Plot file");
1426     } else {
1427       PlotFile& rPlotFile = pPlotDoc->getPlotFile();
1428       std::ostringstream os;
1429       os << "Column " << xCursor;
1430       std::string title("title ");
1431       title += os.str();
1432       rPlotFile.addEzsetCommand (title.c_str());
1433       rPlotFile.addEzsetCommand ("xlabel Column");
1434       rPlotFile.addEzsetCommand ("ylabel Pixel Value");
1435       rPlotFile.addEzsetCommand ("lxfrac 0");
1436       rPlotFile.addEzsetCommand ("curve 1");
1437       rPlotFile.addEzsetCommand ("color 1");
1438       rPlotFile.addEzsetCommand ("dash 1");
1439       rPlotFile.addEzsetCommand ("curve 2");
1440       rPlotFile.addEzsetCommand ("color 4");
1441       rPlotFile.addEzsetCommand ("dash 3");
1442       rPlotFile.addEzsetCommand ("curve 3");
1443       rPlotFile.addEzsetCommand ("color 0");
1444       rPlotFile.addEzsetCommand ("solid");
1445       rPlotFile.addEzsetCommand ("box");
1446       rPlotFile.addEzsetCommand ("grid");
1447       rPlotFile.setCurveSize (4, ny);
1448       rPlotFile.addColumn (0, pX);
1449       rPlotFile.addColumn (1, pYReal);
1450       rPlotFile.addColumn (2, pYImag);
1451       rPlotFile.addColumn (3, pYMag);
1452       for (int iL = 0; iL < rIF.nLabels(); iL++)
1453         rPlotFile.addDescription (rIF.labelGet(iL).getLabelString().c_str());
1454       os << " FFT Plot of " << GetDocument()->GetFirstView()->GetFrame()->GetTitle().c_str();
1455       *theApp->getLog() << os.str().c_str() << "\n";
1456       rPlotFile.addDescription (os.str().c_str());
1457     }
1458     delete pX;
1459     delete pYReal;
1460     delete pYImag;
1461     delete pYMag;
1462     delete pdTemp;
1463     delete [] pcIn;
1464     
1465     pPlotDoc->Modify (true);
1466     pPlotDoc->UpdateAllViews ();
1467     pPlotDoc->getView()->OnUpdate (this, NULL);
1468     pPlotDoc->getView()->getFrame()->Show(true);
1469   }
1470 }
1471 #endif
1472
1473 void
1474 ImageFileView::OnCompareCol (wxCommandEvent& event)
1475 {
1476   int xCursor, yCursor;
1477   if (! m_pCanvas->GetCurrentCursor (xCursor, yCursor)) {
1478     wxMessageBox ("No column selected. Please use left mouse button on image to select column","Error");
1479     return;
1480   }
1481   
1482   std::vector<ImageFileDocument*> vecIFDoc;
1483   theApp->getCompatibleImages (GetDocument(), vecIFDoc);
1484   if (vecIFDoc.size() == 0) {
1485     wxMessageBox ("No compatible images for Column Comparison", "Error");
1486     return;
1487   }
1488   DialogGetComparisonImage dialogGetCompare (getFrameForChild(), "Get Comparison Image", vecIFDoc, false);
1489   
1490   if (dialogGetCompare.ShowModal() == wxID_OK) {
1491     ImageFileDocument* pCompareDoc = dialogGetCompare.getImageFileDocument();
1492     const ImageFile& rIF = GetDocument()->getImageFile();
1493     const ImageFile& rCompareIF = pCompareDoc->getImageFile();
1494     
1495     ImageFileArrayConst v1 = rIF.getArray();
1496     ImageFileArrayConst v2 = rCompareIF.getArray();
1497     int nx = rIF.nx();
1498     int ny = rIF.ny();
1499     
1500     if (v1 != NULL && xCursor < nx) {
1501       double* pX = new double [ny];
1502       double* pY1 = new double [ny];
1503       double* pY2 = new double [ny];
1504       for (int i = 0; i < ny; i++) {
1505         pX[i] = i;
1506         pY1[i] = v1[xCursor][i];
1507         pY2[i] = v2[xCursor][i];
1508       }
1509       PlotFileDocument* pPlotDoc = theApp->newPlotDoc();
1510       if (! pPlotDoc) {
1511         sys_error (ERR_SEVERE, "Internal error: unable to create Plot file");
1512       } else {
1513         PlotFile& rPlotFile = pPlotDoc->getPlotFile();
1514         std::ostringstream os;
1515         os << "Column " << xCursor << " Comparison";
1516         std::string title("title ");
1517         title += os.str();
1518         rPlotFile.addEzsetCommand (title.c_str());
1519         rPlotFile.addEzsetCommand ("xlabel Row");
1520         rPlotFile.addEzsetCommand ("ylabel Pixel Value");
1521         rPlotFile.addEzsetCommand ("lxfrac 0");
1522         rPlotFile.addEzsetCommand ("curve 1");
1523         rPlotFile.addEzsetCommand ("color 2");
1524         rPlotFile.addEzsetCommand ("curve 2");
1525         rPlotFile.addEzsetCommand ("color 4");
1526         rPlotFile.addEzsetCommand ("dash 5");
1527         rPlotFile.addEzsetCommand ("box");
1528         rPlotFile.addEzsetCommand ("grid");
1529         rPlotFile.setCurveSize (3, ny);
1530         rPlotFile.addColumn (0, pX);
1531         rPlotFile.addColumn (1, pY1);
1532         rPlotFile.addColumn (2, pY2);
1533         
1534         unsigned int iL;
1535         for (iL = 0; iL < rIF.nLabels(); iL++) {
1536           std::string s = GetDocument()->GetFirstView()->GetFrame()->GetTitle().c_str();
1537           s += ": ";
1538           s += rIF.labelGet(iL).getLabelString();
1539           rPlotFile.addDescription (s.c_str());
1540         }
1541         for (iL = 0; iL < rCompareIF.nLabels(); iL++) {
1542           std::string s = pCompareDoc->GetFirstView()->GetFrame()->GetTitle().c_str();
1543           s += ": ";
1544           s += rCompareIF.labelGet(iL).getLabelString();
1545           rPlotFile.addDescription (s.c_str());
1546         }
1547         os << " Between " << GetDocument()->GetFirstView()->GetFrame()->GetTitle().c_str() << " and "
1548           << pCompareDoc->GetFirstView()->GetFrame()->GetTitle().c_str();
1549         *theApp->getLog() << os.str().c_str() << "\n";
1550         rPlotFile.addDescription (os.str().c_str());
1551       }
1552       delete pX;
1553       delete pY1;
1554       delete pY2;
1555       pPlotDoc->Modify (true);
1556       pPlotDoc->UpdateAllViews ();
1557       pPlotDoc->getView()->OnUpdate (this, NULL);
1558       pPlotDoc->getView()->getFrame()->Show(true);
1559     }
1560   }
1561 }
1562
1563 void
1564 ImageFileView::OnCompareRow (wxCommandEvent& event)
1565 {
1566   int xCursor, yCursor;
1567   if (! m_pCanvas->GetCurrentCursor (xCursor, yCursor)) {
1568     wxMessageBox ("No column selected. Please use left mouse button on image to select column","Error");
1569     return;
1570   }
1571   
1572   std::vector<ImageFileDocument*> vecIFDoc;
1573   theApp->getCompatibleImages (GetDocument(), vecIFDoc);
1574   
1575   if (vecIFDoc.size() == 0) {
1576     wxMessageBox ("No compatible images for Row Comparison", "Error");
1577     return;
1578   }
1579   
1580   DialogGetComparisonImage dialogGetCompare (getFrameForChild(), "Get Comparison Image", vecIFDoc, false);
1581   
1582   if (dialogGetCompare.ShowModal() == wxID_OK) {
1583     ImageFileDocument* pCompareDoc = dialogGetCompare.getImageFileDocument();
1584     const ImageFile& rIF = GetDocument()->getImageFile();
1585     const ImageFile& rCompareIF = pCompareDoc->getImageFile();
1586     
1587     ImageFileArrayConst v1 = rIF.getArray();
1588     ImageFileArrayConst v2 = rCompareIF.getArray();
1589     int nx = rIF.nx();
1590     int ny = rIF.ny();
1591     
1592     if (v1 != NULL && yCursor < ny) {
1593       double* pX = new double [nx];
1594       double* pY1 = new double [nx];
1595       double* pY2 = new double [nx];
1596       for (int i = 0; i < nx; i++) {
1597         pX[i] = i;
1598         pY1[i] = v1[i][yCursor];
1599         pY2[i] = v2[i][yCursor];
1600       }
1601       PlotFileDocument* pPlotDoc = theApp->newPlotDoc();
1602       if (! pPlotDoc) {
1603         sys_error (ERR_SEVERE, "Internal error: unable to create Plot file");
1604       } else {
1605         PlotFile& rPlotFile = pPlotDoc->getPlotFile();
1606         std::ostringstream os;
1607         os << "Row " << yCursor << " Comparison";
1608         std::string title("title ");
1609         title += os.str();
1610         rPlotFile.addEzsetCommand (title.c_str());
1611         rPlotFile.addEzsetCommand ("xlabel Column");
1612         rPlotFile.addEzsetCommand ("ylabel Pixel Value");
1613         rPlotFile.addEzsetCommand ("lxfrac 0");
1614         rPlotFile.addEzsetCommand ("curve 1");
1615         rPlotFile.addEzsetCommand ("color 2");
1616         rPlotFile.addEzsetCommand ("curve 2");
1617         rPlotFile.addEzsetCommand ("color 4");
1618         rPlotFile.addEzsetCommand ("dash 5");
1619         rPlotFile.addEzsetCommand ("box");
1620         rPlotFile.addEzsetCommand ("grid");
1621         rPlotFile.setCurveSize (3, nx);
1622         rPlotFile.addColumn (0, pX);
1623         rPlotFile.addColumn (1, pY1);
1624         rPlotFile.addColumn (2, pY2);
1625         unsigned int iL;
1626         for (iL = 0; iL < rIF.nLabels(); iL++) {
1627           std::string s = GetDocument()->GetFirstView()->GetFrame()->GetTitle().c_str();
1628           s += ": ";
1629           s += rIF.labelGet(iL).getLabelString();
1630           rPlotFile.addDescription (s.c_str());
1631         }
1632         for (iL = 0; iL < rCompareIF.nLabels(); iL++) {
1633           std::string s = pCompareDoc->GetFirstView()->GetFrame()->GetTitle().c_str();
1634           s += ": ";
1635           s += rCompareIF.labelGet(iL).getLabelString();
1636           rPlotFile.addDescription (s.c_str());
1637         }
1638         os << " Between " << GetDocument()->GetFirstView()->GetFrame()->GetTitle().c_str() << " and "
1639           << pCompareDoc->GetFirstView()->GetFrame()->GetTitle().c_str();
1640         *theApp->getLog() << os.str().c_str() << "\n";
1641         rPlotFile.addDescription (os.str().c_str());
1642       }
1643       delete pX;
1644       delete pY1;
1645       delete pY2;
1646       pPlotDoc->Modify (true);
1647       pPlotDoc->UpdateAllViews ();
1648       pPlotDoc->getView()->OnUpdate (this, NULL);
1649       pPlotDoc->getView()->getFrame()->Show(true);
1650     }
1651   }
1652 }
1653
1654 static int NUMBER_HISTOGRAM_BINS = 256;
1655
1656 void
1657 ImageFileView::OnPlotHistogram (wxCommandEvent& event)
1658
1659   const ImageFile& rIF = GetDocument()->getImageFile();
1660   ImageFileArrayConst v = rIF.getArray();
1661   int nx = rIF.nx();
1662   int ny = rIF.ny();
1663   
1664   if (v != NULL && nx > 0 && ny > 0) {
1665     PlotFileDocument* pPlotDoc = theApp->newPlotDoc();
1666     if (! pPlotDoc) {
1667       sys_error (ERR_SEVERE, "Internal error: unable to create Plot file");
1668       return;
1669     }
1670     
1671     double* pX = new double [NUMBER_HISTOGRAM_BINS];
1672     double* pY = new double [NUMBER_HISTOGRAM_BINS];
1673     double dMin, dMax;
1674     rIF.getMinMax (dMin, dMax);
1675     double dBinWidth = (dMax - dMin) / NUMBER_HISTOGRAM_BINS;
1676     
1677     for (int i = 0; i < NUMBER_HISTOGRAM_BINS; i++) {
1678       pX[i] = dMin + (i + 0.5) * dBinWidth;
1679       pY[i] = 0;
1680     }
1681     for (int ix = 0; ix < nx; ix++)
1682       for (int iy = 0; iy < ny; iy++) {
1683         int iBin = nearest<int> ((v[ix][iy] - dMin) / dBinWidth);
1684         if (iBin >= 0 && iBin < NUMBER_HISTOGRAM_BINS)
1685           pY[iBin] += 1;
1686       }
1687       
1688       PlotFile& rPlotFile = pPlotDoc->getPlotFile();
1689       std::ostringstream os;
1690       os << "Histogram";
1691       std::string title("title ");
1692       title += os.str();
1693       rPlotFile.addEzsetCommand (title.c_str());
1694       rPlotFile.addEzsetCommand ("xlabel Pixel Value");
1695       rPlotFile.addEzsetCommand ("ylabel Count");
1696       rPlotFile.addEzsetCommand ("box");
1697       rPlotFile.addEzsetCommand ("grid");
1698       rPlotFile.setCurveSize (2, NUMBER_HISTOGRAM_BINS);
1699       rPlotFile.addColumn (0, pX);
1700       rPlotFile.addColumn (1, pY);
1701       for (unsigned int iL = 0; iL < rIF.nLabels(); iL++) {
1702         std::string s = GetDocument()->GetFirstView()->GetFrame()->GetTitle().c_str();
1703         s += ": ";
1704         s += rIF.labelGet(iL).getLabelString();
1705         rPlotFile.addDescription (s.c_str());
1706       }
1707       os << " Plot of " << GetDocument()->GetFirstView()->GetFrame()->GetTitle().c_str();
1708       *theApp->getLog() << os.str().c_str() << "\n";
1709       rPlotFile.addDescription (os.str().c_str());
1710       delete pX;
1711       delete pY;
1712       pPlotDoc->Modify (true);
1713       pPlotDoc->UpdateAllViews ();
1714       pPlotDoc->getView()->OnUpdate (this, NULL);
1715       pPlotDoc->getView()->getFrame()->Show(true);
1716   }
1717 }
1718
1719
1720 // PhantomCanvas
1721
1722 PhantomCanvas::PhantomCanvas (PhantomFileView* v, wxFrame *frame, const wxPoint& pos, const wxSize& size, const long style)
1723 : wxScrolledWindow(frame, -1, pos, size, style)
1724 {
1725   m_pView = v;
1726 }
1727
1728 PhantomCanvas::~PhantomCanvas ()
1729 {
1730   m_pView = NULL;
1731 }
1732
1733 void 
1734 PhantomCanvas::OnDraw (wxDC& dc)
1735 {
1736   if (m_pView)
1737     m_pView->OnDraw(& dc);
1738 }
1739
1740
1741 // PhantomFileView
1742
1743 IMPLEMENT_DYNAMIC_CLASS(PhantomFileView, wxView)
1744
1745 BEGIN_EVENT_TABLE(PhantomFileView, wxView)
1746 EVT_MENU(PHMMENU_FILE_PROPERTIES, PhantomFileView::OnProperties)
1747 EVT_MENU(PHMMENU_PROCESS_RASTERIZE, PhantomFileView::OnRasterize)
1748 EVT_MENU(PHMMENU_PROCESS_PROJECTIONS, PhantomFileView::OnProjections)
1749 END_EVENT_TABLE()
1750
1751 PhantomFileView::PhantomFileView() 
1752 : wxView(), m_pFrame(NULL), m_pCanvas(NULL), m_pFileMenu(0)
1753 {
1754 #if defined(DEBUG) || defined(_DEBUG)
1755   m_iDefaultNDet = 165;
1756   m_iDefaultNView = 180;
1757 #else
1758   m_iDefaultNDet = 367;
1759   m_iDefaultNView = 320;
1760 #endif
1761   m_iDefaultNSample = 1;
1762   m_dDefaultRotation = 1;
1763   m_dDefaultFocalLength = 2;
1764   m_dDefaultFieldOfView = 1;
1765   m_iDefaultGeometry = Scanner::GEOMETRY_PARALLEL;
1766   m_iDefaultTrace = Trace::TRACE_NONE;
1767   
1768 #ifdef DEBUG 
1769   m_iDefaultRasterNX = 115;
1770   m_iDefaultRasterNY = 115;
1771   m_iDefaultRasterNSamples = 1;
1772 #else
1773   m_iDefaultRasterNX = 256;
1774   m_iDefaultRasterNY = 256;
1775   m_iDefaultRasterNSamples = 2;
1776 #endif
1777 }
1778
1779 PhantomFileView::~PhantomFileView()
1780 {
1781   GetDocumentManager()->FileHistoryRemoveMenu (m_pFileMenu);
1782   GetDocumentManager()->ActivateView(this, FALSE, TRUE);
1783 }
1784
1785 void
1786 PhantomFileView::OnProperties (wxCommandEvent& event)
1787 {
1788   const int idPhantom = GetDocument()->getPhantomID();
1789   const wxString& namePhantom = GetDocument()->getPhantomName();
1790   std::ostringstream os;
1791   os << "Phantom " << namePhantom.c_str() << " (" << idPhantom << ")" << "\n";
1792   const Phantom& rPhantom = GetDocument()->getPhantom();
1793   rPhantom.printDefinitions (os);
1794 #if DEBUG
1795   rPhantom.print (os);
1796 #endif
1797   *theApp->getLog() << ">>>>\n" << os.str().c_str() << "<<<<\n";
1798   wxMessageBox (os.str().c_str(), "Phantom Properties");
1799 }
1800
1801
1802 void
1803 PhantomFileView::OnProjections (wxCommandEvent& event)
1804 {
1805   DialogGetProjectionParameters dialogProjection (getFrameForChild(), m_iDefaultNDet, m_iDefaultNView, m_iDefaultNSample, m_dDefaultRotation, m_dDefaultFocalLength, m_dDefaultFieldOfView, m_iDefaultGeometry, m_iDefaultTrace);
1806   int retVal = dialogProjection.ShowModal();
1807   if (retVal == wxID_OK) {
1808     m_iDefaultNDet = dialogProjection.getNDet();
1809     m_iDefaultNView = dialogProjection.getNView();
1810     m_iDefaultNSample = dialogProjection.getNSamples();
1811     m_iDefaultTrace = dialogProjection.getTrace();
1812     m_dDefaultRotation = dialogProjection.getRotAngle();
1813     m_dDefaultFocalLength = dialogProjection.getFocalLengthRatio();
1814     m_dDefaultFieldOfView = dialogProjection.getFieldOfViewRatio();
1815     wxString sGeometry = dialogProjection.getGeometry();
1816     m_iDefaultGeometry = Scanner::convertGeometryNameToID (sGeometry.c_str());
1817     
1818     if (m_iDefaultNDet > 0 && m_iDefaultNView > 0 && sGeometry != "") {
1819       const Phantom& rPhantom = GetDocument()->getPhantom();
1820       Projections* pProj = new Projections;
1821       Scanner theScanner (rPhantom, sGeometry.c_str(), m_iDefaultNDet, m_iDefaultNView, m_iDefaultNSample, 
1822                           m_dDefaultRotation, m_dDefaultFocalLength, m_dDefaultFieldOfView);
1823       if (theScanner.fail()) {
1824         *theApp->getLog() << "Failed making scanner: " << theScanner.failMessage().c_str() << "\n";
1825         return;
1826       }
1827       pProj->initFromScanner (theScanner);
1828       m_dDefaultRotation /= PI;  // convert back to PI units
1829       
1830       Timer timer;
1831       if (m_iDefaultTrace > Trace::TRACE_CONSOLE) {
1832         ProjectionsDialog dialogProjections (theScanner, *pProj, rPhantom, m_iDefaultTrace, dynamic_cast<wxWindow*>(getFrameForChild()));
1833         for (int iView = 0; iView < pProj->nView(); iView++) {
1834           ::wxYield();
1835           if (dialogProjections.isCancelled() || ! dialogProjections.projectView (iView)) {
1836             delete pProj;
1837             return;
1838           }
1839           ::wxYield();
1840           while (dialogProjections.isPaused()) {
1841             ::wxYield();
1842             ::wxUsleep(50);
1843           }
1844         }
1845       } else {
1846         wxProgressDialog dlgProgress (wxString("Projection"), wxString("Projection Progress"), pProj->nView() + 1, getFrameForChild(), wxPD_CAN_ABORT);
1847         for (int i = 0; i < pProj->nView(); i++) {
1848           theScanner.collectProjections (*pProj, rPhantom, i, 1, true, m_iDefaultTrace);
1849           if (! dlgProgress.Update (i+1)) {
1850             delete pProj;
1851             return;
1852           }
1853         }
1854       }
1855       
1856       std::ostringstream os;
1857       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();
1858       pProj->setCalcTime (timer.timerEnd());
1859       pProj->setRemark (os.str());
1860       *theApp->getLog() << os.str().c_str() << "\n";
1861       
1862       ::wxYield();
1863       ProjectionFileDocument* pProjectionDoc = theApp->newProjectionDoc();
1864       if (! pProjectionDoc) {
1865         sys_error (ERR_SEVERE, "Unable to create projection document");
1866         return;
1867       }
1868       pProjectionDoc->setProjections (pProj);
1869       ProjectionFileView* projView = pProjectionDoc->getView();
1870       if (projView) {
1871         projView->OnUpdate (projView, NULL);
1872         if (projView->getCanvas())
1873               projView->getCanvas()->SetClientSize (m_iDefaultNDet, m_iDefaultNView);
1874         if (wxFrame* pFrame = projView->getFrame()) {
1875           pFrame->Show(true);
1876           pFrame->SetFocus();
1877           pFrame->Raise();
1878         }
1879         GetDocumentManager()->ActivateView (projView, true, false);
1880       }
1881       ::wxYield();
1882       pProjectionDoc-> Modify(true);
1883       pProjectionDoc->UpdateAllViews (this);
1884     }
1885   }
1886 }
1887
1888
1889 void
1890 PhantomFileView::OnRasterize (wxCommandEvent& event)
1891 {
1892   DialogGetRasterParameters dialogRaster (getFrameForChild(), m_iDefaultRasterNX, m_iDefaultRasterNY, m_iDefaultRasterNSamples);
1893   int retVal = dialogRaster.ShowModal();
1894   if (retVal == wxID_OK) {
1895     m_iDefaultRasterNX = dialogRaster.getXSize();
1896     m_iDefaultRasterNY  = dialogRaster.getYSize();
1897     m_iDefaultRasterNSamples = dialogRaster.getNSamples();
1898     if (m_iDefaultRasterNSamples < 1)
1899       m_iDefaultRasterNSamples = 1;
1900     if (m_iDefaultRasterNX > 0 && m_iDefaultRasterNY > 0) {
1901       const Phantom& rPhantom = GetDocument()->getPhantom();
1902
1903       ImageFile* pImageFile = new ImageFile;
1904       
1905       pImageFile->setArraySize (m_iDefaultRasterNX, m_iDefaultRasterNY);
1906       wxProgressDialog dlgProgress (wxString("Rasterize"), wxString("Rasterization Progress"), 
1907                                     pImageFile->nx() + 1, getFrameForChild(), wxPD_CAN_ABORT);
1908       Timer timer;
1909       for (unsigned int i = 0; i < pImageFile->nx(); i++) {
1910         rPhantom.convertToImagefile (*pImageFile, m_iDefaultRasterNSamples, Trace::TRACE_NONE, i, 1, true);
1911         if (! dlgProgress.Update (i+1)) {
1912           delete pImageFile;
1913           return;
1914         }
1915       }
1916
1917       ImageFileDocument* pRasterDoc = theApp->newImageDoc();
1918       if (! pRasterDoc) {
1919         sys_error (ERR_SEVERE, "Unable to create image file");
1920         return;
1921       }
1922       pRasterDoc->setImageFile (pImageFile);
1923
1924       pRasterDoc->Modify (true);
1925       pRasterDoc->UpdateAllViews (this);
1926       pRasterDoc->getView()->getFrame()->Show(true);
1927       std::ostringstream os;
1928       os << "Rasterize Phantom " << rPhantom.name() << ": XSize=" << m_iDefaultRasterNX << ", YSize=" 
1929         << m_iDefaultRasterNY << ", nSamples=" << m_iDefaultRasterNSamples;
1930       *theApp->getLog() << os.str().c_str() << "\n";
1931       pImageFile->labelAdd (os.str().c_str(), timer.timerEnd());
1932       ImageFileView* rasterView = pRasterDoc->getView();
1933       if (rasterView) {
1934         rasterView->getFrame()->SetFocus();
1935         rasterView->OnUpdate (rasterView, NULL);
1936       }
1937       
1938     }
1939   }
1940 }
1941
1942
1943 PhantomCanvas* 
1944 PhantomFileView::CreateCanvas (wxFrame *parent)
1945 {
1946   PhantomCanvas* pCanvas;
1947   int width, height;
1948   parent->GetClientSize(&width, &height);
1949   
1950   pCanvas = new PhantomCanvas (this, parent, wxPoint(0, 0), wxSize(width, height), 0);
1951   
1952   pCanvas->SetBackgroundColour(*wxWHITE);
1953   pCanvas->Clear();
1954   
1955   return pCanvas;
1956 }
1957
1958 #if CTSIM_MDI
1959 wxDocMDIChildFrame*
1960 #else
1961 wxDocChildFrame*
1962 #endif
1963 PhantomFileView::CreateChildFrame(wxDocument *doc, wxView *view)
1964 {
1965 #if CTSIM_MDI
1966   wxDocMDIChildFrame *subframe = new wxDocMDIChildFrame (doc, view, theApp->getMainFrame(), -1, "Phantom Frame", wxPoint(10, 10), wxSize(256, 256), wxDEFAULT_FRAME_STYLE);
1967 #else
1968   wxDocChildFrame *subframe = new wxDocChildFrame (doc, view, theApp->getMainFrame(), -1, "Phantom Frame", wxPoint(10, 10), wxSize(256, 256), wxDEFAULT_FRAME_STYLE);
1969 #endif
1970   theApp->setIconForFrame (subframe);
1971   
1972   wxMenu *m_pFileMenu = new wxMenu;
1973   
1974   m_pFileMenu->Append(MAINMENU_FILE_CREATE_PHANTOM, "Cr&eate Phantom...\tCtrl-P");
1975   m_pFileMenu->Append(MAINMENU_FILE_CREATE_FILTER, "Create &Filter...\tCtrl-F");
1976   m_pFileMenu->Append(wxID_OPEN, "&Open...\tCtrl-O");
1977   m_pFileMenu->Append(wxID_SAVEAS, "Save &As...");
1978   m_pFileMenu->Append(wxID_CLOSE, "&Close");
1979   
1980   m_pFileMenu->AppendSeparator();
1981   m_pFileMenu->Append(PHMMENU_FILE_PROPERTIES, "P&roperties");
1982   
1983   m_pFileMenu->AppendSeparator();
1984   m_pFileMenu->Append(wxID_PRINT, "&Print...");
1985   m_pFileMenu->Append(wxID_PRINT_SETUP, "Print &Setup...");
1986   m_pFileMenu->Append(wxID_PREVIEW, "Print Pre&view");
1987 #ifdef CTSIM_MDI
1988   m_pFileMenu->AppendSeparator();
1989   m_pFileMenu->Append(MAINMENU_FILE_EXIT, "E&xit");
1990 #endif
1991   GetDocumentManager()->FileHistoryAddFilesToMenu(m_pFileMenu);
1992   GetDocumentManager()->FileHistoryUseMenu(m_pFileMenu);
1993   
1994   wxMenu *process_menu = new wxMenu;
1995   process_menu->Append(PHMMENU_PROCESS_RASTERIZE, "&Rasterize...\tCtrl-R");
1996   process_menu->Append(PHMMENU_PROCESS_PROJECTIONS, "&Projections...\tCtrl-J");
1997   
1998   wxMenu *help_menu = new wxMenu;
1999   help_menu->Append(MAINMENU_HELP_CONTENTS, "&Contents\tF1");
2000   help_menu->Append(MAINMENU_HELP_TOPICS, "&Topics\tCtrl-H");
2001   help_menu->Append(MAINMENU_HELP_ABOUT, "&About");
2002   
2003   wxMenuBar *menu_bar = new wxMenuBar;
2004   
2005   menu_bar->Append(m_pFileMenu, "&File");
2006   menu_bar->Append(process_menu, "&Process");
2007   menu_bar->Append(help_menu, "&Help");
2008   
2009   subframe->SetMenuBar(menu_bar);
2010   subframe->Centre(wxBOTH);
2011   
2012   wxAcceleratorEntry accelEntries[8];
2013   accelEntries[0].Set (wxACCEL_CTRL, static_cast<int>('O'), wxID_OPEN);
2014   accelEntries[1].Set (wxACCEL_CTRL, static_cast<int>('S'), wxID_SAVE);
2015   accelEntries[2].Set (wxACCEL_CTRL, static_cast<int>('H'), MAINMENU_HELP_TOPICS);
2016   accelEntries[3].Set (wxACCEL_CTRL, static_cast<int>('P'), MAINMENU_FILE_CREATE_PHANTOM);
2017   accelEntries[4].Set (wxACCEL_CTRL, static_cast<int>('F'), MAINMENU_FILE_CREATE_FILTER);
2018   accelEntries[5].Set (wxACCEL_NORMAL, WXK_F1, MAINMENU_HELP_CONTENTS);
2019   accelEntries[6].Set (wxACCEL_CTRL, static_cast<int>('J'), PHMMENU_PROCESS_PROJECTIONS);
2020   accelEntries[7].Set (wxACCEL_CTRL, static_cast<int>('R'), PHMMENU_PROCESS_RASTERIZE);
2021   wxAcceleratorTable accelTable (8, accelEntries);
2022   subframe->SetAcceleratorTable (accelTable);
2023   
2024   return subframe;
2025 }
2026
2027
2028 bool 
2029 PhantomFileView::OnCreate(wxDocument *doc, long WXUNUSED(flags) )
2030 {
2031   m_pFrame = CreateChildFrame(doc, this);
2032   SetFrame(m_pFrame);
2033   
2034   int width, height;
2035   m_pFrame->GetClientSize(&width, &height);
2036   m_pFrame->SetTitle("PhantomFileView");
2037   m_pCanvas = CreateCanvas (m_pFrame);
2038   
2039 #ifdef __X__
2040   int x, y;  // X requires a forced resize
2041   m_pFrame->GetSize(&x, &y);
2042   m_pFrame->SetSize(-1, -1, x, y);
2043 #endif
2044   
2045   m_pFrame->Show(true);
2046   Activate(true);
2047   
2048   return true;
2049 }
2050
2051 void 
2052 PhantomFileView::OnUpdate (wxView *WXUNUSED(sender), wxObject *WXUNUSED(hint) )
2053 {
2054   if (m_pCanvas)
2055     m_pCanvas->Refresh();
2056 }
2057
2058 bool 
2059 PhantomFileView::OnClose (bool deleteWindow)
2060 {
2061   //GetDocumentManager()->ActivateView (this, false, true);
2062   if (! GetDocument() || ! GetDocument()->Close())
2063     return false;
2064   
2065   Activate(false);
2066   if (m_pCanvas) {
2067     m_pCanvas->setView(NULL);
2068     m_pCanvas = NULL;
2069   }
2070   wxString s(wxTheApp->GetAppName());
2071   if (m_pFrame)
2072     m_pFrame->SetTitle(s);
2073   
2074   SetFrame(NULL);
2075   
2076   if (deleteWindow) {
2077     delete m_pFrame;
2078     m_pFrame = NULL;
2079     if (GetDocument() && GetDocument()->getBadFileOpen())
2080       ::wxYield();  // wxWindows bug workaround
2081   }
2082   
2083   return true;
2084 }
2085
2086 void
2087 PhantomFileView::OnDraw (wxDC* dc)
2088 {
2089   int xsize, ysize;
2090   m_pCanvas->GetClientSize (&xsize, &ysize);
2091   SGPDriver driver (dc, xsize, ysize);
2092   SGP sgp (driver);
2093   const Phantom& rPhantom = GetDocument()->getPhantom();
2094   sgp.setColor (C_RED);
2095   rPhantom.show (sgp);
2096 }
2097
2098 // ProjectionCanvas
2099
2100 ProjectionFileCanvas::ProjectionFileCanvas (ProjectionFileView* v, wxFrame *frame, const wxPoint& pos, const wxSize& size, const long style)
2101 : wxScrolledWindow(frame, -1, pos, size, style)
2102 {
2103   m_pView = v;
2104 }
2105
2106 ProjectionFileCanvas::~ProjectionFileCanvas ()
2107 {
2108   m_pView = NULL;
2109 }
2110
2111 void 
2112 ProjectionFileCanvas::OnDraw(wxDC& dc)
2113 {
2114   if (m_pView)
2115     m_pView->OnDraw(& dc);
2116 }
2117
2118 wxSize
2119 ProjectionFileCanvas::GetBestSize () const
2120 {
2121   wxSize best (0, 0);
2122   if (m_pView) {
2123     Projections& rProj = m_pView->GetDocument()->getProjections();
2124     best.Set (rProj.nDet(), rProj.nView());
2125   }
2126   
2127   return best;
2128 }
2129
2130
2131 // ProjectionFileView
2132
2133 IMPLEMENT_DYNAMIC_CLASS(ProjectionFileView, wxView)
2134
2135 BEGIN_EVENT_TABLE(ProjectionFileView, wxView)
2136 EVT_MENU(PJMENU_FILE_PROPERTIES, ProjectionFileView::OnProperties)
2137 EVT_MENU(PJMENU_RECONSTRUCT_FBP, ProjectionFileView::OnReconstructFBP)
2138 EVT_MENU(PJMENU_CONVERT_POLAR, ProjectionFileView::OnConvertPolar)
2139 EVT_MENU(PJMENU_CONVERT_FFT_POLAR, ProjectionFileView::OnConvertFFTPolar)
2140 END_EVENT_TABLE()
2141
2142 ProjectionFileView::ProjectionFileView() 
2143 : wxView(), m_pCanvas(NULL), m_pFrame(NULL), m_pFileMenu(0)
2144 {
2145 #ifdef DEBUG
2146   m_iDefaultNX = 115;
2147   m_iDefaultNY = 115;
2148 #else
2149   m_iDefaultNX = 256;
2150   m_iDefaultNY = 256;
2151 #endif
2152   
2153   m_iDefaultFilter = SignalFilter::FILTER_ABS_BANDLIMIT;
2154   m_dDefaultFilterParam = 1.;
2155 #if HAVE_FFTW
2156   m_iDefaultFilterMethod = ProcessSignal::FILTER_METHOD_RFFTW;
2157   m_iDefaultFilterGeneration = ProcessSignal::FILTER_GENERATION_INVERSE_FOURIER;
2158 #else
2159   m_iDefaultFilterMethod = ProcessSignal::FILTER_METHOD_CONVOLUTION;
2160   m_iDefaultFilterGeneration = ProcessSignal::FILTER_GENERATION_DIRECT;
2161 #endif
2162   m_iDefaultZeropad = 1;
2163   m_iDefaultBackprojector = Backprojector::BPROJ_IDIFF3;
2164   m_iDefaultInterpolation = Backprojector::INTERP_LINEAR;
2165   m_iDefaultInterpParam = 1;
2166   m_iDefaultTrace = Trace::TRACE_NONE;
2167   
2168   m_iDefaultPolarNX = 256;
2169   m_iDefaultPolarNY = 256;
2170   m_iDefaultPolarInterpolation = Projections::POLAR_INTERP_BILINEAR;
2171   m_iDefaultPolarZeropad = 1;
2172 }
2173
2174 ProjectionFileView::~ProjectionFileView()
2175 {
2176   GetDocumentManager()->FileHistoryRemoveMenu (m_pFileMenu);
2177   GetDocumentManager()->ActivateView(this, FALSE, TRUE);;
2178 }
2179
2180 void
2181 ProjectionFileView::OnProperties (wxCommandEvent& event)
2182 {
2183   const Projections& rProj = GetDocument()->getProjections();
2184   std::ostringstream os;
2185   rProj.printScanInfo(os);
2186   *theApp->getLog() << ">>>>\n" << os.str().c_str() << "<<<<\n";
2187   wxMessageDialog dialogMsg (getFrameForChild(), os.str().c_str(), "Projection File Properties", wxOK | wxICON_INFORMATION);
2188   dialogMsg.ShowModal();
2189 }
2190
2191
2192 void
2193 ProjectionFileView::OnConvertPolar (wxCommandEvent& event)
2194 {
2195   Projections& rProj = GetDocument()->getProjections();
2196   DialogGetConvertPolarParameters dialogPolar (getFrameForChild(), "Convert Polar", m_iDefaultPolarNX, m_iDefaultPolarNY,
2197     m_iDefaultPolarInterpolation, -1);
2198   if (dialogPolar.ShowModal() == wxID_OK) {
2199     wxString strInterpolation (dialogPolar.getInterpolationName());
2200     m_iDefaultPolarNX = dialogPolar.getXSize();
2201     m_iDefaultPolarNY = dialogPolar.getYSize();
2202     ImageFileDocument* pPolarDoc = theApp->newImageDoc();
2203     ImageFile& rIF = pPolarDoc->getImageFile();
2204     if (! pPolarDoc) {
2205       sys_error (ERR_SEVERE, "Unable to create image file");
2206       return;
2207     }
2208     rIF.setArraySize (m_iDefaultPolarNX, m_iDefaultPolarNY);
2209     m_iDefaultPolarInterpolation = Projections::convertInterpNameToID (strInterpolation.c_str());
2210     rProj.convertPolar (rIF, m_iDefaultPolarInterpolation);
2211     rIF.labelAdd (rProj.getLabel().getLabelString().c_str(), rProj.calcTime());
2212     std::ostringstream os;
2213     os << "Convert projection file " << GetFrame()->GetTitle().c_str() << " to polar image: xSize=" 
2214       << m_iDefaultPolarNX << ", ySize=" << m_iDefaultPolarNY << ", interpolation=" 
2215       << strInterpolation.c_str();
2216     *theApp->getLog() << os.str().c_str() << "\n";
2217     rIF.labelAdd (os.str().c_str());
2218     pPolarDoc->Modify (true);
2219     pPolarDoc->UpdateAllViews ();
2220     pPolarDoc->getView()->OnUpdate (this, NULL);
2221     pPolarDoc->getView()->getFrame()->Show(true);
2222   }
2223 }
2224
2225 void
2226 ProjectionFileView::OnConvertFFTPolar (wxCommandEvent& event)
2227 {
2228   Projections& rProj = GetDocument()->getProjections();
2229   DialogGetConvertPolarParameters dialogPolar (getFrameForChild(), "Convert to FFT Polar", m_iDefaultPolarNX, m_iDefaultPolarNY,
2230     m_iDefaultPolarInterpolation, m_iDefaultPolarZeropad);
2231   if (dialogPolar.ShowModal() == wxID_OK) {
2232     wxString strInterpolation (dialogPolar.getInterpolationName());
2233     m_iDefaultPolarNX = dialogPolar.getXSize();
2234     m_iDefaultPolarNY = dialogPolar.getYSize();
2235     m_iDefaultPolarZeropad = dialogPolar.getZeropad();
2236     ImageFileDocument* pPolarDoc = theApp->newImageDoc();
2237     ImageFile& rIF = pPolarDoc->getImageFile();
2238     if (! pPolarDoc) {
2239       sys_error (ERR_SEVERE, "Unable to create image file");
2240       return;
2241     }
2242     rIF.setArraySize (m_iDefaultPolarNX, m_iDefaultPolarNY);
2243     m_iDefaultPolarInterpolation = Projections::convertInterpNameToID (strInterpolation.c_str());
2244     rProj.convertFFTPolar (rIF, m_iDefaultPolarInterpolation, m_iDefaultPolarZeropad);
2245     rIF.labelAdd (rProj.getLabel().getLabelString().c_str(), rProj.calcTime());
2246     std::ostringstream os;
2247     os << "Convert projection file " << GetFrame()->GetTitle().c_str() << " to FFT polar image: xSize=" 
2248       << m_iDefaultPolarNX << ", ySize=" << m_iDefaultPolarNY << ", interpolation=" 
2249       << strInterpolation.c_str() << ", zeropad=" << m_iDefaultPolarZeropad;
2250     *theApp->getLog() << os.str().c_str() << "\n";
2251     rIF.labelAdd (os.str().c_str());
2252     pPolarDoc->Modify (true);
2253     pPolarDoc->UpdateAllViews ();
2254     pPolarDoc->getView()->OnUpdate (this, NULL);
2255     pPolarDoc->getView()->getFrame()->Show(true);
2256   }
2257 }
2258
2259 void
2260 ProjectionFileView::OnReconstructFourier (wxCommandEvent& event)
2261 {
2262   wxMessageBox ("Fourier Reconstruction is not yet supported", "Unimplemented function");
2263 }
2264
2265 void
2266 ProjectionFileView::OnReconstructFBP (wxCommandEvent& event)
2267 {
2268   DialogGetReconstructionParameters dialogReconstruction (getFrameForChild(), m_iDefaultNX, m_iDefaultNY, m_iDefaultFilter, m_dDefaultFilterParam, m_iDefaultFilterMethod, m_iDefaultFilterGeneration, m_iDefaultZeropad, m_iDefaultInterpolation, m_iDefaultInterpParam, m_iDefaultBackprojector, m_iDefaultTrace);
2269   
2270   int retVal = dialogReconstruction.ShowModal();
2271   if (retVal == wxID_OK) {
2272     m_iDefaultNX = dialogReconstruction.getXSize();
2273     m_iDefaultNY = dialogReconstruction.getYSize();
2274     wxString optFilterName = dialogReconstruction.getFilterName();
2275     m_iDefaultFilter = SignalFilter::convertFilterNameToID (optFilterName.c_str());
2276     m_dDefaultFilterParam = dialogReconstruction.getFilterParam();
2277     wxString optFilterMethodName = dialogReconstruction.getFilterMethodName();
2278     m_iDefaultFilterMethod = ProcessSignal::convertFilterMethodNameToID(optFilterMethodName.c_str());
2279     m_iDefaultZeropad = dialogReconstruction.getZeropad();
2280     wxString optFilterGenerationName = dialogReconstruction.getFilterGenerationName();
2281     m_iDefaultFilterGeneration = ProcessSignal::convertFilterGenerationNameToID (optFilterGenerationName.c_str());
2282     wxString optInterpName = dialogReconstruction.getInterpName();
2283     m_iDefaultInterpolation = Backprojector::convertInterpNameToID (optInterpName.c_str());
2284     m_iDefaultInterpParam = dialogReconstruction.getInterpParam();
2285     wxString optBackprojectName = dialogReconstruction.getBackprojectName();
2286     m_iDefaultBackprojector = Backprojector::convertBackprojectNameToID (optBackprojectName.c_str());
2287     m_iDefaultTrace = dialogReconstruction.getTrace();
2288     if (m_iDefaultNX > 0 && m_iDefaultNY > 0) {
2289       const Projections& rProj = GetDocument()->getProjections();
2290
2291       ImageFile* pImageFile = new ImageFile;
2292       pImageFile->setArraySize (m_iDefaultNX, m_iDefaultNY);
2293       
2294       Reconstructor* pReconstructor = new Reconstructor (rProj, *pImageFile, optFilterName.c_str(), m_dDefaultFilterParam, optFilterMethodName.c_str(), m_iDefaultZeropad, optFilterGenerationName.c_str(), optInterpName.c_str(), m_iDefaultInterpParam, optBackprojectName.c_str(), m_iDefaultTrace);
2295       
2296       Timer timerRecon;
2297       if (m_iDefaultTrace > Trace::TRACE_CONSOLE) {
2298         ReconstructDialog* pDlgReconstruct = new ReconstructDialog (*pReconstructor, rProj, *pImageFile, m_iDefaultTrace, getFrameForChild());
2299         for (int iView = 0; iView < rProj.nView(); iView++) {
2300           ::wxYield();
2301           if (pDlgReconstruct->isCancelled() || ! pDlgReconstruct->reconstructView (iView)) {
2302             delete pDlgReconstruct;
2303             delete pReconstructor;
2304             delete pImageFile;
2305             return;
2306           }
2307           ::wxYield();
2308           while (pDlgReconstruct->isPaused()) {
2309             ::wxYield();
2310             ::wxUsleep(50);
2311           }
2312         }
2313         delete pDlgReconstruct;
2314       } else {
2315         wxProgressDialog dlgProgress (wxString("Reconstruction"), wxString("Reconstruction Progress"), rProj.nView() + 1, getFrameForChild(), wxPD_CAN_ABORT);
2316         for (int i = 0; i < rProj.nView(); i++) {
2317           pReconstructor->reconstructView (i, 1);
2318           if (! dlgProgress.Update (i + 1)) {
2319             delete pReconstructor;
2320             delete pImageFile;
2321             return;
2322           }
2323         }
2324       }
2325       delete pReconstructor;
2326       ImageFileDocument* pReconDoc = theApp->newImageDoc();
2327       if (! pReconDoc) {
2328         sys_error (ERR_SEVERE, "Unable to create image file");
2329         return;
2330       }
2331       pReconDoc->setImageFile (pImageFile);
2332       pReconDoc->Modify (true);
2333       pReconDoc->UpdateAllViews (this);
2334       if (ImageFileView* rasterView = pReconDoc->getView()) {
2335         rasterView->OnUpdate (rasterView, NULL);
2336         rasterView->getFrame()->SetFocus();
2337         rasterView->getFrame()->Show(true);
2338       }
2339       std::ostringstream os;
2340       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();
2341       *theApp->getLog() << os.str().c_str() << "\n";
2342       pImageFile->labelAdd (rProj.getLabel());
2343       pImageFile->labelAdd (os.str().c_str(), timerRecon.timerEnd());
2344     }
2345   }
2346 }
2347
2348
2349 ProjectionFileCanvas* 
2350 ProjectionFileView::CreateCanvas (wxFrame *parent)
2351 {
2352   ProjectionFileCanvas* pCanvas;
2353   int width, height;
2354   parent->GetClientSize(&width, &height);
2355   
2356   pCanvas = new ProjectionFileCanvas (this, parent, wxPoint(0, 0), wxSize(width, height), 0);
2357   
2358   pCanvas->SetScrollbars(20, 20, 50, 50);
2359   pCanvas->SetBackgroundColour(*wxWHITE);
2360   pCanvas->Clear();
2361   
2362   return pCanvas;
2363 }
2364
2365 #if CTSIM_MDI
2366 wxDocMDIChildFrame*
2367 #else
2368 wxDocChildFrame*
2369 #endif
2370 ProjectionFileView::CreateChildFrame(wxDocument *doc, wxView *view)
2371 {
2372 #ifdef CTSIM_MDI
2373   wxDocMDIChildFrame *subframe = new wxDocMDIChildFrame (doc, view, theApp->getMainFrame(), -1, "Projection Frame", wxPoint(10, 10), wxSize(0, 0), wxDEFAULT_FRAME_STYLE);
2374 #else
2375   wxDocChildFrame *subframe = new wxDocChildFrame (doc, view, theApp->getMainFrame(), -1, "Projection Frame", wxPoint(10, 10), wxSize(0, 0), wxDEFAULT_FRAME_STYLE);
2376 #endif
2377   theApp->setIconForFrame (subframe);
2378   
2379   wxMenu *m_pFileMenu = new wxMenu;
2380   
2381   m_pFileMenu->Append(MAINMENU_FILE_CREATE_PHANTOM, "Cr&eate Phantom...\tCtrl-P");
2382   m_pFileMenu->Append(MAINMENU_FILE_CREATE_FILTER, "Create &Filter...\tCtrl-F");
2383   m_pFileMenu->Append(wxID_OPEN, "&Open...\tCtrl-O");
2384   m_pFileMenu->Append(wxID_SAVE, "&Save\tCtrl-S");
2385   m_pFileMenu->Append(wxID_SAVEAS, "Save &As...");
2386   m_pFileMenu->Append(wxID_CLOSE, "&Close\tCtrl-W");
2387   
2388   m_pFileMenu->AppendSeparator();
2389   m_pFileMenu->Append(PJMENU_FILE_PROPERTIES, "P&roperties");
2390   
2391   m_pFileMenu->AppendSeparator();
2392   m_pFileMenu->Append(wxID_PRINT, "&Print...");
2393   m_pFileMenu->Append(wxID_PRINT_SETUP, "Print &Setup...");
2394   m_pFileMenu->Append(wxID_PREVIEW, "Print Pre&view");
2395 #ifdef CTSIM_MDI
2396   m_pFileMenu->AppendSeparator();
2397   m_pFileMenu->Append(MAINMENU_FILE_EXIT, "E&xit");
2398 #endif
2399   GetDocumentManager()->FileHistoryAddFilesToMenu(m_pFileMenu);
2400   GetDocumentManager()->FileHistoryUseMenu(m_pFileMenu);
2401   
2402   wxMenu *convert_menu = new wxMenu;
2403   convert_menu->Append (PJMENU_CONVERT_POLAR, "&Polar Image...\tCtrl-L");
2404   convert_menu->Append (PJMENU_CONVERT_FFT_POLAR, "&FFT->Polar Image...\tCtrl-I");
2405   
2406   wxMenu *reconstruct_menu = new wxMenu;
2407   reconstruct_menu->Append (PJMENU_RECONSTRUCT_FBP, "&Filtered Backprojection...\tCtrl-R", "Reconstruct image using filtered backprojection");
2408   reconstruct_menu->Append (PJMENU_RECONSTRUCT_FOURIER, "&Fourier...\tCtrl-E", "Reconstruct image using inverse Fourier");
2409   reconstruct_menu->Enable (PJMENU_RECONSTRUCT_FOURIER, false);
2410   
2411   wxMenu *help_menu = new wxMenu;
2412   help_menu->Append(MAINMENU_HELP_CONTENTS, "&Contents\tF1");
2413   help_menu->Append(MAINMENU_HELP_TOPICS, "&Topics\tCtrl-H");
2414   help_menu->Append(MAINMENU_HELP_ABOUT, "&About");
2415   
2416   wxMenuBar *menu_bar = new wxMenuBar;
2417   
2418   menu_bar->Append (m_pFileMenu, "&File");
2419   menu_bar->Append (convert_menu, "&Convert");
2420   menu_bar->Append (reconstruct_menu, "&Reconstruct");
2421   menu_bar->Append (help_menu, "&Help");
2422   
2423   subframe->SetMenuBar(menu_bar);  
2424   subframe->Centre(wxBOTH);
2425   
2426   wxAcceleratorEntry accelEntries[11];
2427   accelEntries[0].Set (wxACCEL_CTRL, static_cast<int>('O'), wxID_OPEN);
2428   accelEntries[1].Set (wxACCEL_CTRL, static_cast<int>('S'), wxID_SAVE);
2429   accelEntries[2].Set (wxACCEL_CTRL, static_cast<int>('W'), wxID_CLOSE);
2430   accelEntries[3].Set (wxACCEL_CTRL, static_cast<int>('H'), MAINMENU_HELP_TOPICS);
2431   accelEntries[4].Set (wxACCEL_CTRL, static_cast<int>('P'), MAINMENU_FILE_CREATE_PHANTOM);
2432   accelEntries[5].Set (wxACCEL_CTRL, static_cast<int>('F'), MAINMENU_FILE_CREATE_FILTER);
2433   accelEntries[6].Set (wxACCEL_NORMAL, WXK_F1, MAINMENU_HELP_CONTENTS);
2434   accelEntries[7].Set (wxACCEL_CTRL, static_cast<int>('L'), PJMENU_CONVERT_POLAR);
2435   accelEntries[8].Set (wxACCEL_CTRL, static_cast<int>('I'), PJMENU_CONVERT_FFT_POLAR);
2436   accelEntries[9].Set (wxACCEL_CTRL, static_cast<int>('R'), PJMENU_RECONSTRUCT_FBP);
2437   accelEntries[10].Set (wxACCEL_CTRL, static_cast<int>('E'), PJMENU_RECONSTRUCT_FOURIER);
2438   wxAcceleratorTable accelTable (11, accelEntries);
2439   subframe->SetAcceleratorTable (accelTable);
2440   
2441   return subframe;
2442 }
2443
2444
2445 bool 
2446 ProjectionFileView::OnCreate(wxDocument *doc, long WXUNUSED(flags) )
2447 {
2448   m_pFrame = CreateChildFrame(doc, this);
2449   SetFrame(m_pFrame);
2450   
2451   int width, height;
2452   m_pFrame->GetClientSize (&width, &height);
2453   m_pFrame->SetTitle ("ProjectionFileView");
2454   m_pCanvas = CreateCanvas (m_pFrame);
2455   
2456 #ifdef __X__
2457   int x, y;  // X requires a forced resize
2458   m_pFrame->GetSize(&x, &y);
2459   m_pFrame->SetSize(-1, -1, x, y);
2460 #endif
2461   
2462   m_pFrame->Show(true);
2463   Activate(true);
2464   
2465   return true;
2466 }
2467
2468 void 
2469 ProjectionFileView::OnDraw (wxDC* dc)
2470 {
2471   wxSize clientSize = m_pFrame->GetClientSize();
2472   wxSize bestSize = m_pCanvas->GetBestSize();
2473   
2474   if (clientSize.x > bestSize.x || clientSize.y > bestSize.y)
2475     m_pFrame->SetClientSize (bestSize);
2476   
2477   if (m_bitmap.Ok())
2478     dc->DrawBitmap (m_bitmap, 0, 0, false);
2479 }
2480
2481
2482 void 
2483 ProjectionFileView::OnUpdate (wxView *WXUNUSED(sender), wxObject *WXUNUSED(hint) )
2484 {
2485   const Projections& rProj = GetDocument()->getProjections();
2486   const int nDet = rProj.nDet();
2487   const int nView = rProj.nView();
2488   if (nDet != 0 && nView != 0) {
2489     const DetectorArray& detarray = rProj.getDetectorArray(0);
2490     const DetectorValue* detval = detarray.detValues();
2491     double min = detval[0];
2492     double max = detval[0];
2493     for (int iy = 0; iy < nView; iy++) {
2494       const DetectorArray& detarray = rProj.getDetectorArray(iy);
2495       const DetectorValue* detval = detarray.detValues();
2496       for (int ix = 0; ix < nDet; ix++) {
2497         if (min > detval[ix])
2498           min = detval[ix];
2499         else if (max < detval[ix])
2500           max = detval[ix];
2501       }
2502     }
2503     
2504     unsigned char* imageData = new unsigned char [nDet * nView * 3];
2505     double scale = (max - min) / 255;
2506     for (int iy2 = 0; iy2 < nView; iy2++) {
2507       const DetectorArray& detarray = rProj.getDetectorArray (iy2);
2508       const DetectorValue* detval = detarray.detValues();
2509       for (int ix = 0; ix < nDet; ix++) {
2510         int intensity = static_cast<int>(((detval[ix] - min) / scale) + 0.5);
2511         intensity = clamp(intensity, 0, 255);
2512         int baseAddr = (iy2 * nDet + ix) * 3;
2513         imageData[baseAddr] = imageData[baseAddr+1] = imageData[baseAddr+2] = intensity;
2514       }
2515     }
2516     wxImage image (nDet, nView, imageData, true);
2517     m_bitmap = image.ConvertToBitmap();
2518     delete imageData;
2519     int xSize = nDet;
2520     int ySize = nView;
2521     xSize = clamp (xSize, 0, 800);
2522     ySize = clamp (ySize, 0, 800);
2523     m_pFrame->SetClientSize (xSize, ySize);
2524     m_pCanvas->SetScrollbars (20, 20, nDet/20, nView/20);
2525   }
2526   
2527   if (m_pCanvas)
2528     m_pCanvas->Refresh();
2529 }
2530
2531 bool 
2532 ProjectionFileView::OnClose (bool deleteWindow)
2533 {
2534   //GetDocumentManager()->ActivateView (this, false, true);
2535   if (! GetDocument() || ! GetDocument()->Close())
2536     return false;
2537   
2538   Activate(false);
2539   if (m_pCanvas) {
2540         m_pCanvas->setView(NULL);
2541     m_pCanvas = NULL;
2542   }
2543   wxString s(wxTheApp->GetAppName());
2544   if (m_pFrame)
2545     m_pFrame->SetTitle(s);
2546   
2547   SetFrame(NULL);
2548   
2549   if (deleteWindow) {
2550     delete m_pFrame;
2551     m_pFrame = NULL;
2552     if (GetDocument() && GetDocument()->getBadFileOpen())
2553       ::wxYield();  // wxWindows bug workaround
2554   }
2555
2556   return true;
2557 }
2558
2559
2560
2561 // PlotFileCanvas
2562 PlotFileCanvas::PlotFileCanvas (PlotFileView* v, wxFrame *frame, const wxPoint& pos, const wxSize& size, const long style)
2563 : wxScrolledWindow(frame, -1, pos, size, style)
2564 {
2565   m_pView = v;
2566 }
2567
2568 PlotFileCanvas::~PlotFileCanvas ()
2569 {
2570   m_pView = NULL;
2571 }
2572
2573 void 
2574 PlotFileCanvas::OnDraw(wxDC& dc)
2575 {
2576   if (m_pView)
2577     m_pView->OnDraw(& dc);
2578 }
2579
2580
2581 // PlotFileView
2582
2583 IMPLEMENT_DYNAMIC_CLASS(PlotFileView, wxView)
2584
2585 BEGIN_EVENT_TABLE(PlotFileView, wxView)
2586 EVT_MENU(PJMENU_FILE_PROPERTIES, PlotFileView::OnProperties)
2587 EVT_MENU(PLOTMENU_VIEW_SCALE_MINMAX, PlotFileView::OnScaleMinMax)
2588 EVT_MENU(PLOTMENU_VIEW_SCALE_AUTO, PlotFileView::OnScaleAuto)
2589 EVT_MENU(PLOTMENU_VIEW_SCALE_FULL, PlotFileView::OnScaleFull)
2590 END_EVENT_TABLE()
2591
2592 PlotFileView::PlotFileView() 
2593 : wxView(), m_pFrame(NULL), m_pCanvas(NULL), m_pEZPlot(NULL), m_pFileMenu(0), m_bMinSpecified(false), m_bMaxSpecified(false)
2594 {
2595 }
2596
2597 PlotFileView::~PlotFileView()
2598 {
2599   if (m_pEZPlot)
2600     delete m_pEZPlot;
2601   
2602   GetDocumentManager()->FileHistoryRemoveMenu (m_pFileMenu);  
2603 }
2604
2605 void
2606 PlotFileView::OnProperties (wxCommandEvent& event)
2607 {
2608   const PlotFile& rPlot = GetDocument()->getPlotFile();
2609   std::ostringstream os;
2610   os << "Columns: " << rPlot.getNumColumns() << ", Records: " << rPlot.getNumRecords() << "\n";
2611   rPlot.printHeadersBrief (os);
2612   *theApp->getLog() << ">>>>\n" << os.str().c_str() << "<<<<<\n";
2613   wxMessageDialog dialogMsg (getFrameForChild(), os.str().c_str(), "Plot File Properties", wxOK | wxICON_INFORMATION);
2614   dialogMsg.ShowModal();
2615 }
2616
2617
2618 void 
2619 PlotFileView::OnScaleAuto (wxCommandEvent& event)
2620 {
2621   const PlotFile& rPlotFile = GetDocument()->getPlotFile();
2622   double min, max, mean, mode, median, stddev;
2623   rPlotFile.statistics (1, min, max, mean, mode, median, stddev);
2624   DialogAutoScaleParameters dialogAutoScale (getFrameForChild(), mean, mode, median, stddev, m_dAutoScaleFactor);
2625   int iRetVal = dialogAutoScale.ShowModal();
2626   if (iRetVal == wxID_OK) {
2627     m_bMinSpecified = true;
2628     m_bMaxSpecified = true;
2629     double dMin, dMax;
2630     if (dialogAutoScale.getMinMax (&dMin, &dMax)) {
2631       m_dMinPixel = dMin;
2632       m_dMaxPixel = dMax;
2633       m_dAutoScaleFactor = dialogAutoScale.getAutoScaleFactor();
2634       OnUpdate (this, NULL);
2635     }
2636   }
2637 }
2638
2639 void 
2640 PlotFileView::OnScaleMinMax (wxCommandEvent& event)
2641 {
2642   const PlotFile& rPlotFile = GetDocument()->getPlotFile();
2643   double min;
2644   double max;
2645   
2646   if (! m_bMinSpecified || ! m_bMaxSpecified) {
2647     if (! rPlotFile.getMinMax (1, min, max)) {
2648       *theApp->getLog() << "Error: unable to find Min/Max\n";
2649       return;
2650     }
2651   }
2652   
2653   if (m_bMinSpecified)
2654     min = m_dMinPixel;
2655   if (m_bMaxSpecified)
2656     max = m_dMaxPixel;
2657   
2658   DialogGetMinMax dialogMinMax (getFrameForChild(), "Set Y-axis Minimum & Maximum", min, max);
2659   int retVal = dialogMinMax.ShowModal();
2660   if (retVal == wxID_OK) {
2661     m_bMinSpecified = true;
2662     m_bMaxSpecified = true;
2663     m_dMinPixel = dialogMinMax.getMinimum();
2664     m_dMaxPixel = dialogMinMax.getMaximum();
2665     OnUpdate (this, NULL);
2666   }
2667 }
2668
2669 void 
2670 PlotFileView::OnScaleFull (wxCommandEvent& event)
2671 {
2672   if (m_bMinSpecified || m_bMaxSpecified) {
2673     m_bMinSpecified = false;
2674     m_bMaxSpecified = false;
2675     OnUpdate (this, NULL);
2676   }
2677 }
2678
2679
2680 PlotFileCanvas* 
2681 PlotFileView::CreateCanvas (wxFrame* parent)
2682 {
2683   PlotFileCanvas* pCanvas;
2684   int width, height;
2685   parent->GetClientSize(&width, &height);
2686   
2687   pCanvas = new PlotFileCanvas (this, parent, wxPoint(0, 0), wxSize(width, height), 0);
2688   
2689   pCanvas->SetBackgroundColour(*wxWHITE);
2690   pCanvas->Clear();
2691   
2692   return pCanvas;
2693 }
2694
2695 #if CTSIM_MDI
2696 wxDocMDIChildFrame*
2697 #else
2698 wxDocChildFrame*
2699 #endif
2700 PlotFileView::CreateChildFrame(wxDocument *doc, wxView *view)
2701 {
2702 #ifdef CTSIM_MDI
2703   wxDocMDIChildFrame *subframe = new wxDocMDIChildFrame (doc, view, theApp->getMainFrame(), -1, "Plot Frame", wxPoint(10, 10), wxSize(500, 300), wxDEFAULT_FRAME_STYLE);
2704 #else
2705   wxDocChildFrame *subframe = new wxDocChildFrame(doc, view, theApp->getMainFrame(), -1, "Plot Frame", wxPoint(10, 10), wxSize(500, 300), wxDEFAULT_FRAME_STYLE);
2706 #endif
2707   theApp->setIconForFrame (subframe);
2708   
2709   wxMenu *m_pFileMenu = new wxMenu;
2710   
2711   m_pFileMenu->Append(MAINMENU_FILE_CREATE_PHANTOM, "Cr&eate Phantom...\tCtrl-P");
2712   m_pFileMenu->Append(MAINMENU_FILE_CREATE_FILTER, "Create &Filter...\tCtrl-F");
2713   m_pFileMenu->Append(wxID_OPEN, "&Open...\tCtrl-O");
2714   m_pFileMenu->Append(wxID_SAVE, "&Save\tCtrl-S");
2715   m_pFileMenu->Append(wxID_SAVEAS, "Save &As...");
2716   m_pFileMenu->Append(wxID_CLOSE, "&Close\tCtrl-W");
2717   
2718   m_pFileMenu->AppendSeparator();
2719   m_pFileMenu->Append(PJMENU_FILE_PROPERTIES, "P&roperties");
2720   
2721   m_pFileMenu->AppendSeparator();
2722   m_pFileMenu->Append(wxID_PRINT, "&Print...");
2723   m_pFileMenu->Append(wxID_PRINT_SETUP, "Print &Setup...");
2724   m_pFileMenu->Append(wxID_PREVIEW, "Print Pre&view");
2725 #ifdef CTSIM_MDI
2726   m_pFileMenu->AppendSeparator();
2727   m_pFileMenu->Append(MAINMENU_FILE_EXIT, "E&xit");
2728 #endif
2729   GetDocumentManager()->FileHistoryAddFilesToMenu(m_pFileMenu);
2730   GetDocumentManager()->FileHistoryUseMenu(m_pFileMenu);
2731   
2732   wxMenu *view_menu = new wxMenu;
2733   view_menu->Append(PLOTMENU_VIEW_SCALE_MINMAX, "Display Scale &Set...\tCtrl-E");
2734   view_menu->Append(PLOTMENU_VIEW_SCALE_AUTO, "Display Scale &Auto...\tCtrl-A");
2735   view_menu->Append(PLOTMENU_VIEW_SCALE_FULL, "Display &Full Scale\tCtrl-U");
2736   
2737   wxMenu *help_menu = new wxMenu;
2738   help_menu->Append(MAINMENU_HELP_CONTENTS, "&Contents\tF1");
2739   help_menu->Append(MAINMENU_HELP_TOPICS, "&Topics\tCtrl-H");
2740   help_menu->Append(MAINMENU_HELP_ABOUT, "&About");
2741   
2742   wxMenuBar *menu_bar = new wxMenuBar;
2743   
2744   menu_bar->Append(m_pFileMenu, "&File");
2745   menu_bar->Append(view_menu, "&View");
2746   menu_bar->Append(help_menu, "&Help");
2747   
2748   subframe->SetMenuBar(menu_bar);
2749   subframe->Centre(wxBOTH);
2750   
2751   wxAcceleratorEntry accelEntries[10];
2752   accelEntries[0].Set (wxACCEL_CTRL, static_cast<int>('O'), wxID_OPEN);
2753   accelEntries[1].Set (wxACCEL_CTRL, static_cast<int>('S'), wxID_SAVE);
2754   accelEntries[2].Set (wxACCEL_CTRL, static_cast<int>('W'), wxID_CLOSE);
2755   accelEntries[3].Set (wxACCEL_CTRL, static_cast<int>('H'), MAINMENU_HELP_TOPICS);
2756   accelEntries[4].Set (wxACCEL_CTRL, static_cast<int>('P'), MAINMENU_FILE_CREATE_PHANTOM);
2757   accelEntries[5].Set (wxACCEL_CTRL, static_cast<int>('F'), MAINMENU_FILE_CREATE_FILTER);
2758   accelEntries[6].Set (wxACCEL_NORMAL, WXK_F1, MAINMENU_HELP_CONTENTS);
2759   accelEntries[7].Set (wxACCEL_CTRL, static_cast<int>('E'), PLOTMENU_VIEW_SCALE_MINMAX);
2760   accelEntries[8].Set (wxACCEL_CTRL, static_cast<int>('A'), PLOTMENU_VIEW_SCALE_AUTO);
2761   accelEntries[9].Set (wxACCEL_CTRL, static_cast<int>('U'), PLOTMENU_VIEW_SCALE_FULL);
2762   wxAcceleratorTable accelTable (10, accelEntries);
2763   subframe->SetAcceleratorTable (accelTable);
2764   
2765   return subframe;
2766 }
2767
2768
2769 bool 
2770 PlotFileView::OnCreate (wxDocument *doc, long WXUNUSED(flags) )
2771 {
2772   m_pFrame = CreateChildFrame(doc, this);
2773   SetFrame(m_pFrame);
2774   
2775   m_bMinSpecified = false;
2776   m_bMaxSpecified = false;
2777   m_dAutoScaleFactor = 1.;
2778   
2779   int width, height;
2780   m_pFrame->GetClientSize(&width, &height);
2781   m_pFrame->SetTitle ("Plot File");
2782   m_pCanvas = CreateCanvas (m_pFrame);
2783   
2784 #ifdef __X__
2785   int x, y;  // X requires a forced resize
2786   m_pFrame->GetSize(&x, &y);
2787   m_pFrame->SetSize(-1, -1, x, y);
2788 #endif
2789   
2790   m_pFrame->Show(true);
2791   Activate(true);
2792   
2793   return true;
2794 }
2795
2796 void 
2797 PlotFileView::OnDraw (wxDC* dc)
2798 {
2799   const PlotFile& rPlotFile = GetDocument()->getPlotFile();
2800   const int iNColumns = rPlotFile.getNumColumns();
2801   const int iNRecords = rPlotFile.getNumRecords();
2802   
2803   if (iNColumns > 0 && iNRecords > 0) {
2804     int xsize, ysize;
2805     m_pCanvas->GetClientSize (&xsize, &ysize);
2806     SGPDriver driver (dc, xsize, ysize);
2807     SGP sgp (driver);
2808     if (m_pEZPlot)
2809       m_pEZPlot->plot (&sgp);
2810   }
2811 }
2812
2813
2814 void 
2815 PlotFileView::OnUpdate (wxView *WXUNUSED(sender), wxObject *WXUNUSED(hint) )
2816 {
2817   const PlotFile& rPlotFile = GetDocument()->getPlotFile();
2818   const int iNColumns = rPlotFile.getNumColumns();
2819   const int iNRecords = rPlotFile.getNumRecords();
2820   
2821   if (iNColumns > 0 && iNRecords > 0) {
2822     if (m_pEZPlot)
2823       delete m_pEZPlot;
2824     m_pEZPlot = new EZPlot;
2825     
2826     for (unsigned int iEzset = 0; iEzset < rPlotFile.getNumEzsetCommands(); iEzset++)
2827       m_pEZPlot->ezset (rPlotFile.getEzsetCommand (iEzset));
2828     
2829     if (m_bMinSpecified) {
2830       std::ostringstream os;
2831       os << "ymin " << m_dMinPixel;
2832       m_pEZPlot->ezset (os.str());
2833     }
2834     
2835     if (m_bMaxSpecified) {
2836       std::ostringstream os;
2837       os << "ymax " << m_dMaxPixel;
2838       m_pEZPlot->ezset (os.str());
2839     }
2840     
2841     m_pEZPlot->ezset("box");
2842     m_pEZPlot->ezset("grid");
2843     
2844     double* pdXaxis = new double [iNRecords];
2845     rPlotFile.getColumn (0, pdXaxis);
2846     
2847     double* pdY = new double [iNRecords];
2848     for (int iCol = 1; iCol < iNColumns; iCol++) {
2849       rPlotFile.getColumn (iCol, pdY);
2850       m_pEZPlot->addCurve (pdXaxis, pdY, iNRecords);
2851     }
2852     
2853     delete pdXaxis;
2854     delete pdY;
2855   }
2856   
2857   if (m_pCanvas)
2858     m_pCanvas->Refresh();
2859 }
2860
2861 bool 
2862 PlotFileView::OnClose (bool deleteWindow)
2863 {
2864   //GetDocumentManager()->ActivateView (this, false, true);
2865   if (! GetDocument() || ! GetDocument()->Close())
2866     return false;
2867   
2868   Activate(false);
2869   if (m_pCanvas) {
2870     m_pCanvas->setView (NULL);
2871     m_pCanvas = NULL;
2872   }
2873   wxString s(wxTheApp->GetAppName());
2874   if (m_pFrame)
2875     m_pFrame->SetTitle(s);
2876   
2877   SetFrame(NULL);
2878   if (deleteWindow) {
2879     delete m_pFrame;
2880     m_pFrame = NULL;
2881     if (GetDocument() && GetDocument()->getBadFileOpen())
2882       ::wxYield();  // wxWindows bug workaround
2883  }
2884
2885   return true;
2886 }
2887
2888
2889 ////////////////////////////////////////////////////////////////
2890
2891
2892 IMPLEMENT_DYNAMIC_CLASS(TextFileView, wxView)
2893
2894 TextFileView::~TextFileView() 
2895 {
2896   GetDocumentManager()->FileHistoryRemoveMenu (m_pFileMenu);
2897   GetDocumentManager()->ActivateView(this, FALSE, TRUE);;
2898 }
2899
2900 bool TextFileView::OnCreate(wxDocument *doc, long WXUNUSED(flags) )
2901 {
2902   m_pFrame = CreateChildFrame(doc, this);
2903   SetFrame (m_pFrame);
2904   
2905   int width, height;
2906   m_pFrame->GetClientSize(&width, &height);
2907   m_pFrame->SetTitle("TextFile");
2908   m_pCanvas = new TextFileCanvas (this, m_pFrame, wxPoint(0, 0), wxSize(width, height), wxTE_MULTILINE | wxTE_READONLY);
2909   m_pFrame->SetTitle("Log");
2910   
2911 #ifdef __X__
2912   // X seems to require a forced resize
2913   int x, y;
2914   frame->GetSize(&x, &y);
2915   frame->SetSize(-1, -1, x, y);
2916 #endif
2917   
2918   m_pFrame->Show (true);
2919   Activate (true);
2920   
2921   return true;
2922 }
2923
2924 // Handled by wxTextWindow
2925 void TextFileView::OnDraw(wxDC *WXUNUSED(dc) )
2926 {
2927 }
2928
2929 void TextFileView::OnUpdate (wxView *WXUNUSED(sender), wxObject *WXUNUSED(hint) )
2930 {
2931 }
2932
2933 bool 
2934 TextFileView::OnClose (bool deleteWindow)
2935 {
2936   if (! theApp->getMainFrame()->getShuttingDown())
2937     return false;
2938   
2939   Activate(false);
2940   //GetDocumentManager()->ActivateView (this, false, true);
2941   if (! GetDocument() || ! GetDocument()->Close())
2942     return false;
2943   
2944   SetFrame(NULL);
2945   if (deleteWindow) {
2946     delete m_pFrame;
2947     m_pFrame = NULL;
2948     if (GetDocument() && GetDocument()->getBadFileOpen())
2949       ::wxYield();  // wxWindows bug workaround
2950   }
2951
2952   return TRUE;
2953 }
2954
2955 #if CTSIM_MDI
2956 wxDocMDIChildFrame*
2957 #else
2958 wxDocChildFrame*
2959 #endif
2960 TextFileView::CreateChildFrame (wxDocument *doc, wxView *view)
2961 {
2962 #if CTSIM_MDI
2963   wxDocMDIChildFrame* subframe = new wxDocMDIChildFrame (doc, view, theApp->getMainFrame(), -1, "TextFile Frame", wxPoint(-1, -1), wxSize(0,0), wxDEFAULT_FRAME_STYLE, "Log");
2964 #else
2965   wxDocChildFrame* subframe = new wxDocChildFrame (doc, view, theApp->getMainFrame(), -1, "TextFile Frame", wxPoint(-1, -1), wxSize(300, 150), wxDEFAULT_FRAME_STYLE, "Log");
2966 #endif
2967   theApp->setIconForFrame (subframe);
2968   
2969   wxMenu *m_pFileMenu = new wxMenu;
2970   
2971   m_pFileMenu->Append(MAINMENU_FILE_CREATE_PHANTOM, "Cr&eate Phantom...\tCtrl-P");
2972   m_pFileMenu->Append(MAINMENU_FILE_CREATE_FILTER, "Create &Filter...\tCtrl-F");
2973   m_pFileMenu->Append(wxID_OPEN, "&Open...\tCtrl-O");
2974   m_pFileMenu->Append(wxID_SAVE, "&Save\tCtrl-S");
2975   m_pFileMenu->Append(wxID_SAVEAS, "Save &As...");
2976   //  m_pFileMenu->Append(wxID_CLOSE, "&Close\tCtrl-W");
2977   
2978   m_pFileMenu->AppendSeparator();
2979   m_pFileMenu->Append(wxID_PRINT, "&Print...");
2980   m_pFileMenu->Append(wxID_PRINT_SETUP, "Print &Setup...");
2981   m_pFileMenu->Append(wxID_PREVIEW, "Print Pre&view");
2982 #ifdef CTSIM_MDI
2983   m_pFileMenu->AppendSeparator();
2984   m_pFileMenu->Append(MAINMENU_FILE_EXIT, "E&xit");
2985 #endif
2986   GetDocumentManager()->FileHistoryAddFilesToMenu(m_pFileMenu);
2987   GetDocumentManager()->FileHistoryUseMenu(m_pFileMenu);
2988   
2989   wxMenu *help_menu = new wxMenu;
2990   help_menu->Append(MAINMENU_HELP_CONTENTS, "&Contents\tF1");
2991   help_menu->Append(MAINMENU_HELP_TOPICS, "&Topics\tCtrl-H");
2992   help_menu->Append(MAINMENU_HELP_ABOUT, "&About");
2993   
2994   wxMenuBar *menu_bar = new wxMenuBar;
2995   
2996   menu_bar->Append(m_pFileMenu, "&File");
2997   menu_bar->Append(help_menu, "&Help");
2998   
2999   subframe->SetMenuBar(menu_bar);
3000   subframe->Centre(wxBOTH);
3001   
3002   wxAcceleratorEntry accelEntries[5];
3003   accelEntries[0].Set (wxACCEL_CTRL, static_cast<int>('O'), wxID_OPEN);
3004   accelEntries[1].Set (wxACCEL_CTRL, static_cast<int>('S'), wxID_SAVE);
3005   accelEntries[2].Set (wxACCEL_CTRL, static_cast<int>('W'), wxID_CLOSE);
3006   accelEntries[3].Set (wxACCEL_CTRL, static_cast<int>('H'), MAINMENU_HELP_TOPICS);
3007   accelEntries[4].Set (wxACCEL_NORMAL, WXK_F1, MAINMENU_HELP_CONTENTS);
3008   wxAcceleratorTable accelTable (5, accelEntries);
3009   subframe->SetAcceleratorTable (accelTable);
3010   
3011   return subframe;
3012 }
3013
3014
3015 // Define a constructor for my text subwindow
3016 TextFileCanvas::TextFileCanvas (TextFileView* v, wxFrame* frame, const wxPoint& pos, const wxSize& size, long style)
3017 : wxTextCtrl (frame, -1, "", pos, size, style), m_pView(v)
3018 {
3019 }
3020
3021 TextFileCanvas::~TextFileCanvas ()
3022 {
3023   m_pView = NULL;
3024 }