Fix all remaining compiler warnings
[ctsim.git] / src / views.cpp
1 /*****************************************************************************
2 ** FILE IDENTIFICATION
3 **
4 **   Name:          views.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-2009 Kevin Rosenberg
11 **
12 **  This program is free software; you can redistribute it and/or modify
13 **  it under the terms of the GNU General Public License (version 2) as
14 **  published by the Free Software Foundation.
15 **
16 **  This program is distributed in the hope that it will be useful,
17 **  but WITHOUT ANY WARRANTY; without even the implied warranty of
18 **  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 **  GNU General Public License for more details.
20 **
21 **  You should have received a copy of the GNU General Public License
22 **  along with this program; if not, write to the Free Software
23 **  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24 ******************************************************************************/
25
26 #include "wx/wxprec.h"
27 #ifndef WX_PRECOMP
28 #include "wx/wx.h"
29 #endif
30
31 #if !wxUSE_DOC_VIEW_ARCHITECTURE
32 #error You must set wxUSE_DOC_VIEW_ARCHITECTURE to 1 in setup.h!
33 #endif
34
35 #include "wx/image.h"
36 #include "wx/progdlg.h"
37 #include "wx/clipbrd.h"
38
39 #include "ct.h"
40 #include "ctsim.h"
41 #include "docs.h"
42 #include "views.h"
43 #include "dialogs.h"
44 #include "dlgprojections.h"
45 #include "dlgreconstruct.h"
46 #include "backprojectors.h"
47 #include "reconstruct.h"
48 #include "timer.h"
49 #include "threadproj.h"
50 #include "threadrecon.h"
51 #include "threadraster.h"
52
53 #if defined(MSVC) || HAVE_SSTREAM
54 #include <sstream>
55 #else
56 #include <sstream_subst>
57 #endif
58
59 // Used to reduce calls to progress bar update function
60 const short int ITER_PER_UPDATE = 10;
61
62 // ImageFileCanvas
63
64 BEGIN_EVENT_TABLE(ImageFileCanvas, wxScrolledWindow)
65 EVT_MOUSE_EVENTS(ImageFileCanvas::OnMouseEvent)
66 EVT_CHAR(ImageFileCanvas::OnChar)
67 END_EVENT_TABLE()
68
69
70 ImageFileCanvas::ImageFileCanvas (ImageFileView* v, wxFrame *frame, const wxPoint& pos, const wxSize& size, const long style)
71 : wxScrolledWindow(frame, -1, pos, size, style), m_pView(v), m_xCursor(-1), m_yCursor(-1)
72 {
73 }
74
75 ImageFileCanvas::~ImageFileCanvas()
76 {}
77
78 void
79 ImageFileCanvas::OnDraw(wxDC& dc)
80 {
81   if (m_pView)
82     m_pView->OnDraw(& dc);
83 }
84
85 void
86 ImageFileCanvas::DrawRubberBandCursor (wxDC& dc, int x, int y)
87 {
88   const ImageFile& rIF = m_pView->GetDocument()->getImageFile();
89   int nx = rIF.nx();
90   int ny = rIF.ny();
91
92   int yPt = ny - y - 1;
93   dc.SetLogicalFunction (wxINVERT);
94   dc.SetPen (*wxGREEN_PEN);
95   dc.DrawLine (0, yPt, nx, yPt);
96   dc.DrawLine (x, 0, x, ny);
97   dc.SetLogicalFunction (wxCOPY);
98 }
99
100 bool
101 ImageFileCanvas::GetCurrentCursor (int& x, int& y)
102 {
103   x = m_xCursor;
104   y = m_yCursor;
105
106   if (m_xCursor >= 0 && m_yCursor >= 0)
107     return true;
108   else
109     return false;
110 }
111
112 void
113 ImageFileCanvas::OnMouseEvent(wxMouseEvent& event)
114 {
115   if (! m_pView)
116     return;
117
118
119   wxClientDC dc(this);
120   PrepareDC(dc);
121
122   wxPoint pt(event.GetLogicalPosition(dc));
123
124   const ImageFileDocument* pIFDoc = m_pView->GetDocument();
125   if (! pIFDoc)
126     return;
127   const ImageFile& rIF = pIFDoc->getImageFile();
128   ImageFileArrayConst v = rIF.getArray();
129   int nx = rIF.nx();
130   int ny = rIF.ny();
131   const int yPt = ny - 1 - pt.y;
132   if (event.RightIsDown()) {
133     if (pt.x >= 0 && pt.x < nx && pt.y >= 0 && pt.y < ny) {
134       wxString os;
135       os << _T("Image value (") << pt.x << _T(",") << yPt << _T(") = ")
136          << v[pt.x][yPt];
137       if (rIF.isComplex()) {
138         double dImag = rIF.getImaginaryArray()[pt.x][yPt];
139         if (dImag < 0)
140           os << _T(" - ") << -dImag;
141         else
142           os << _T(" + ") << dImag;
143         os << _T("i\n");
144       } else
145         os << _T("\n");
146       *theApp->getLog() << os;
147     } else
148       *theApp->getLog() << _T("Mouse out of image range (") << pt.x << _T(",") << yPt << _T(")\n");
149   }
150   else if (event.LeftIsDown() || event.LeftUp() || event.RightUp()) {
151     if (pt.x >= 0 && pt.x < nx && pt.y >= 0 && pt.y < ny) {
152       if (m_xCursor >= 0 && m_yCursor >= 0) {
153         DrawRubberBandCursor (dc, m_xCursor, m_yCursor);
154       }
155       DrawRubberBandCursor (dc, pt.x, yPt);
156       m_xCursor = pt.x;
157       m_yCursor = yPt;
158       wxMenu* pMenu = m_pView->getMenuAnalyze();
159       if (pMenu && ! pMenu->IsEnabled(IFMENU_PLOT_ROW)) {
160         pMenu->Enable (IFMENU_PLOT_ROW, true);
161         pMenu->Enable (IFMENU_PLOT_COL, true);
162         pMenu->Enable (IFMENU_COMPARE_ROW, true);
163         pMenu->Enable (IFMENU_COMPARE_COL, true);
164         pMenu->Enable (IFMENU_PLOT_FFT_ROW, true);
165         pMenu->Enable (IFMENU_PLOT_FFT_COL, true);
166       }
167     } else
168       *theApp->getLog() << _T("Mouse out of image range (") << pt.x << _T(",") << yPt << _T(")\n");
169   }
170   if (event.LeftUp()) {
171     wxString os;
172     os << _T("Selected column ") << pt.x << _T(" , row ") << yPt << _T("\n");
173     *theApp->getLog() << os;
174   }
175 }
176
177 void
178 ImageFileCanvas::OnChar (wxKeyEvent& event)
179 {
180   if (event.GetKeyCode() == WXK_ESCAPE) {
181     m_xCursor = -1;
182     m_yCursor = -1;
183     if (m_pView)
184       m_pView->OnUpdate (NULL);
185   } else
186     event.Skip();
187 }
188
189 wxSize
190 ImageFileCanvas::GetBestSize() const
191 {
192   const int iMinX = 50;
193   const int iMinY = 20;
194   wxSize bestSize (iMinX,iMinY);
195
196   if (m_pView) {
197     const ImageFile& rIF = m_pView->GetDocument()->getImageFile();
198     bestSize.Set  (rIF.nx(), rIF.ny());
199   }
200
201   if (bestSize.x > 800)
202     bestSize.x = 800;
203   if (bestSize.y > 800)
204     bestSize.y = 800;
205
206   if (bestSize.y < iMinY)
207     bestSize.y = iMinY;
208   if (bestSize.x < iMinX)
209     bestSize.x = iMinX;
210
211   return bestSize;
212 }
213
214
215 // ImageFileView
216
217 IMPLEMENT_DYNAMIC_CLASS(ImageFileView, wxView)
218
219 BEGIN_EVENT_TABLE(ImageFileView, wxView)
220 EVT_MENU(IFMENU_FILE_EXPORT, ImageFileView::OnExport)
221 EVT_MENU(IFMENU_FILE_PROPERTIES, ImageFileView::OnProperties)
222 EVT_MENU(IFMENU_EDIT_COPY, ImageFileView::OnEditCopy)
223 EVT_MENU(IFMENU_EDIT_CUT, ImageFileView::OnEditCut)
224 EVT_MENU(IFMENU_EDIT_PASTE, ImageFileView::OnEditPaste)
225 EVT_MENU(IFMENU_VIEW_SCALE_MINMAX, ImageFileView::OnScaleMinMax)
226 EVT_MENU(IFMENU_VIEW_SCALE_AUTO, ImageFileView::OnScaleAuto)
227 EVT_MENU(IFMENU_VIEW_SCALE_FULL, ImageFileView::OnScaleFull)
228 EVT_MENU(IFMENU_COMPARE_IMAGES, ImageFileView::OnCompare)
229 EVT_MENU(IFMENU_COMPARE_ROW, ImageFileView::OnCompareRow)
230 EVT_MENU(IFMENU_COMPARE_COL, ImageFileView::OnCompareCol)
231 EVT_MENU(IFMENU_FILTER_INVERTVALUES, ImageFileView::OnInvertValues)
232 EVT_MENU(IFMENU_FILTER_SQUARE, ImageFileView::OnSquare)
233 EVT_MENU(IFMENU_FILTER_SQRT, ImageFileView::OnSquareRoot)
234 EVT_MENU(IFMENU_FILTER_LOG, ImageFileView::OnLog)
235 EVT_MENU(IFMENU_FILTER_EXP, ImageFileView::OnExp)
236 EVT_MENU(IFMENU_FILTER_FOURIER, ImageFileView::OnFourier)
237 EVT_MENU(IFMENU_FILTER_INVERSE_FOURIER, ImageFileView::OnInverseFourier)
238 EVT_MENU(IFMENU_FILTER_SHUFFLEFOURIERTONATURALORDER, ImageFileView::OnShuffleFourierToNaturalOrder)
239 EVT_MENU(IFMENU_FILTER_SHUFFLENATURALTOFOURIERORDER, ImageFileView::OnShuffleNaturalToFourierOrder)
240 EVT_MENU(IFMENU_IMAGE_ADD, ImageFileView::OnAdd)
241 EVT_MENU(IFMENU_IMAGE_SUBTRACT, ImageFileView::OnSubtract)
242 EVT_MENU(IFMENU_IMAGE_MULTIPLY, ImageFileView::OnMultiply)
243 EVT_MENU(IFMENU_IMAGE_DIVIDE, ImageFileView::OnDivide)
244 EVT_MENU(IFMENU_IMAGE_SCALESIZE, ImageFileView::OnScaleSize)
245 #if wxUSE_GLCANVAS
246 EVT_MENU(IFMENU_IMAGE_CONVERT3D, ImageFileView::OnConvert3d)
247 #endif
248 #ifdef HAVE_FFT
249 EVT_MENU(IFMENU_FILTER_FFT, ImageFileView::OnFFT)
250 EVT_MENU(IFMENU_FILTER_IFFT, ImageFileView::OnIFFT)
251 EVT_MENU(IFMENU_FILTER_FFT_ROWS, ImageFileView::OnFFTRows)
252 EVT_MENU(IFMENU_FILTER_IFFT_ROWS, ImageFileView::OnIFFTRows)
253 EVT_MENU(IFMENU_FILTER_FFT_COLS, ImageFileView::OnFFTCols)
254 EVT_MENU(IFMENU_FILTER_IFFT_COLS, ImageFileView::OnIFFTCols)
255 #endif
256 EVT_MENU(IFMENU_FILTER_MAGNITUDE, ImageFileView::OnMagnitude)
257 EVT_MENU(IFMENU_FILTER_PHASE, ImageFileView::OnPhase)
258 EVT_MENU(IFMENU_FILTER_REAL, ImageFileView::OnReal)
259 EVT_MENU(IFMENU_FILTER_IMAGINARY, ImageFileView::OnImaginary)
260 EVT_MENU(IFMENU_PLOT_ROW, ImageFileView::OnPlotRow)
261 EVT_MENU(IFMENU_PLOT_COL, ImageFileView::OnPlotCol)
262 #ifdef HAVE_FFT
263 EVT_MENU(IFMENU_PLOT_FFT_ROW, ImageFileView::OnPlotFFTRow)
264 EVT_MENU(IFMENU_PLOT_FFT_COL, ImageFileView::OnPlotFFTCol)
265 #endif
266 EVT_MENU(IFMENU_PLOT_HISTOGRAM, ImageFileView::OnPlotHistogram)
267 END_EVENT_TABLE()
268
269 ImageFileView::ImageFileView()
270   :  wxView(), m_pBitmap(0), m_pFrame(0), m_pCanvas(0), m_pFileMenu(0),
271      m_pFilterMenu(0), m_bMinSpecified(false), m_bMaxSpecified(false),
272      m_iDefaultExportFormatID(ImageFile::EXPORT_FORMAT_PNG)
273 {}
274
275 ImageFileView::~ImageFileView()
276 {
277   GetDocumentManager()->FileHistoryRemoveMenu (m_pFileMenu);
278   GetDocumentManager()->ActivateView(this, FALSE);
279 }
280
281
282 void
283 ImageFileView::OnProperties (wxCommandEvent& event)
284 {
285   const ImageFile& rIF = GetDocument()->getImageFile();
286   if (rIF.nx() == 0 || rIF.ny() == 0)
287     *theApp->getLog() << _T("Properties: empty imagefile\n");
288   else {
289     const std::string rFilename (m_pFrame->GetTitle().mb_str(wxConvUTF8));
290     std::ostringstream os;
291     double min, max, mean, mode, median, stddev;
292     rIF.statistics (rIF.getArray(), min, max, mean, mode, median, stddev);
293     os << "Filename: " << rFilename << "\n";
294     os << "Size: (" << rIF.nx() << "," << rIF.ny() << ")\n";
295     os << "Data type: ";
296     if (rIF.isComplex())
297       os << "Complex\n";
298     else
299       os << "Real\n";
300     os << "Minimum: "<<min<<"\nMaximum: "<<max<<"\nMean: "<<mean<<"\nMedian: "<<median<<"\nMode: "<<mode<<"\nStandard Deviation: "<<stddev << "\n";
301     if (rIF.isComplex()) {
302       rIF.statistics (rIF.getImaginaryArray(), min, max, mean, mode, median, stddev);
303       os << "Imaginary: min: "<<min<<"\nmax: "<<max<<"\nmean: "<<mean<<"\nmedian: "<<median<<"\nmode: "<<mode<<"\nstddev: "<<stddev << "\n";
304     }
305     if (rIF.nLabels() > 0) {
306       rIF.printLabelsBrief (os);
307     }
308     *theApp->getLog() << _T(">>>>\n") << wxConvUTF8.cMB2WX(os.str().c_str()) << _T("<<<<\n");
309     wxMessageDialog dialogMsg (getFrameForChild(), wxConvUTF8.cMB2WX(os.str().c_str()), _T("Imagefile Properties"), wxOK | wxICON_INFORMATION);
310     dialogMsg.ShowModal();
311   }
312   GetDocument()->Activate();
313 }
314
315 void
316 ImageFileView::OnScaleAuto (wxCommandEvent& event)
317 {
318   const ImageFile& rIF = GetDocument()->getImageFile();
319   double min, max, mean, mode, median, stddev;
320   rIF.statistics(min, max, mean, mode, median, stddev);
321   DialogAutoScaleParameters dialogAutoScale (getFrameForChild(), mean, mode, median, stddev, m_dAutoScaleFactor);
322   int iRetVal = dialogAutoScale.ShowModal();
323   if (iRetVal == wxID_OK) {
324     m_bMinSpecified = true;
325     m_bMaxSpecified = true;
326     double dMin, dMax;
327     if (dialogAutoScale.getMinMax (&dMin, &dMax)) {
328       m_dMinPixel = dMin;
329       m_dMaxPixel = dMax;
330       m_dAutoScaleFactor = dialogAutoScale.getAutoScaleFactor();
331       OnUpdate(this, NULL);
332       GetDocument()->UpdateAllViews (this);
333     }
334   }
335   GetDocument()->Activate();
336 }
337
338 void
339 ImageFileView::OnScaleMinMax (wxCommandEvent& event)
340 {
341   const ImageFile& rIF = GetDocument()->getImageFile();
342   double min, max;
343   if (! m_bMinSpecified || ! m_bMaxSpecified)
344     rIF.getMinMax (min, max);
345
346   if (m_bMinSpecified)
347     min = m_dMinPixel;
348   if (m_bMaxSpecified)
349     max = m_dMaxPixel;
350
351   DialogGetMinMax dialogMinMax (getFrameForChild(), _T("Set Image Minimum & Maximum"), min, max);
352   int retVal = dialogMinMax.ShowModal();
353   if (retVal == wxID_OK) {
354     m_bMinSpecified = true;
355     m_bMaxSpecified = true;
356     m_dMinPixel = dialogMinMax.getMinimum();
357     m_dMaxPixel = dialogMinMax.getMaximum();
358     OnUpdate(this, NULL);
359     GetDocument()->UpdateAllViews (this);
360   }
361   GetDocument()->Activate();
362 }
363
364 void
365 ImageFileView::OnScaleFull (wxCommandEvent& event)
366 {
367   if (m_bMinSpecified || m_bMaxSpecified) {
368     m_bMinSpecified = false;
369     m_bMaxSpecified = false;
370     OnUpdate(this, NULL);
371     GetDocument()->UpdateAllViews (this);
372   }
373   GetDocument()->Activate();
374 }
375
376 void
377 ImageFileView::OnCompare (wxCommandEvent& event)
378 {
379   std::vector<ImageFileDocument*> vecIF;
380   theApp->getCompatibleImages (GetDocument(), vecIF);
381
382   if (vecIF.size() == 0) {
383     wxMessageBox(_T("There are no compatible image files open for comparision"), _T("No comparison images"));
384   } else {
385     DialogGetComparisonImage dialogGetCompare(getFrameForChild(), _T("Get Comparison Image"), vecIF, true);
386
387     if (dialogGetCompare.ShowModal() == wxID_OK) {
388       const ImageFile& rIF = GetDocument()->getImageFile();
389       ImageFileDocument* pCompareDoc = dialogGetCompare.getImageFileDocument();
390       const ImageFile& rCompareIF = pCompareDoc->getImageFile();
391       std::ostringstream os;
392       double min, max, mean, mode, median, stddev;
393       rIF.statistics (min, max, mean, mode, median, stddev);
394       os << m_pFrame->GetTitle().mb_str(wxConvUTF8) << ": minimum=" << min << ", maximum=" << max << ", mean=" << mean << ", mode=" << mode << ", median=" << median << ", stddev=" << stddev << "\n";
395       rCompareIF.statistics (min, max, mean, mode, median, stddev);
396       os << dynamic_cast<wxFrame*>(pCompareDoc->GetFirstView()->GetFrame())->GetTitle().mb_str(wxConvUTF8) << ": minimum=" << min << ", maximum=" << max << ", mean=" << mean << ", mode=" << mode << ", median=" << median << ", stddev=" << stddev << "\n";
397       double d, r, e;
398       rIF.comparativeStatistics (rCompareIF, d, r, e);
399       os << "Comparative Statistics: d=" << d << ", r=" << r << ", e=" << e << "\n";
400       *theApp->getLog() << _T(">>>>\n") << wxConvUTF8.cMB2WX(os.str().c_str()) << _T("<<<<\n");
401       if (dialogGetCompare.getMakeDifferenceImage()) {
402         ImageFile* pDifferenceImage = new ImageFile;
403
404         pDifferenceImage->setArraySize (rIF.nx(), rIF.ny());
405         if (! rIF.subtractImages (rCompareIF, *pDifferenceImage)) {
406           *theApp->getLog() << _T("Unable to subtract images\n");
407           delete pDifferenceImage;
408           return;
409         }
410         ImageFileDocument* pDifferenceDoc = theApp->newImageDoc();
411         if (! pDifferenceDoc) {
412           sys_error (ERR_SEVERE, "Unable to create image file");
413           return;
414         }
415         pDifferenceDoc->setImageFile (pDifferenceImage);
416
417         wxString s = m_pFrame->GetTitle() + _T(": ");
418         pDifferenceImage->labelsCopy (rIF, s.mb_str(wxConvUTF8));
419         s = dynamic_cast<wxFrame*>(pCompareDoc->GetFirstView()->GetFrame())->GetTitle() + _T(": ");
420         pDifferenceImage->labelsCopy (rCompareIF, s.mb_str(wxConvUTF8));
421         std::ostringstream osLabel;
422         osLabel << "Compare image " << dynamic_cast<wxFrame*>(GetDocument()->GetFirstView()->GetFrame())->GetTitle().c_str()
423                 << " and " << dynamic_cast<wxFrame*>(pCompareDoc->GetFirstView()->GetFrame())->GetTitle().c_str() << ": "
424           << os.str().c_str();
425         pDifferenceImage->labelAdd (os.str().c_str());
426         if (theApp->getAskDeleteNewDocs())
427           pDifferenceDoc->Modify (true);
428         OnUpdate(this, NULL);
429         pDifferenceDoc->UpdateAllViews(this);
430         pDifferenceDoc->getView()->setInitialClientSize();
431         pDifferenceDoc->Activate();
432       }
433       wxMessageBox(wxConvUTF8.cMB2WX(os.str().c_str()), _T("Image Comparison"));
434     }
435   }
436 }
437
438 void
439 ImageFileView::OnInvertValues (wxCommandEvent& event)
440 {
441   ImageFile& rIF = GetDocument()->getImageFile();
442   rIF.invertPixelValues (rIF);
443   rIF.labelAdd ("Invert Pixel Values");
444   if (theApp->getAskDeleteNewDocs())
445     GetDocument()->Modify (true);
446   OnUpdate(this, NULL);
447   GetDocument()->UpdateAllViews (this);
448   GetDocument()->Activate();
449 }
450
451 void
452 ImageFileView::OnSquare (wxCommandEvent& event)
453 {
454   ImageFile& rIF = GetDocument()->getImageFile();
455   rIF.square (rIF);
456   rIF.labelAdd ("Square Pixel Values");
457   if (theApp->getAskDeleteNewDocs())
458     GetDocument()->Modify (true);
459   OnUpdate(this, NULL);
460   GetDocument()->UpdateAllViews (this);
461   GetDocument()->Activate();
462 }
463
464 void
465 ImageFileView::OnSquareRoot (wxCommandEvent& event)
466 {
467   ImageFile& rIF = GetDocument()->getImageFile();
468   rIF.sqrt (rIF);
469   rIF.labelAdd ("Square-root Pixel Values");
470   if (theApp->getAskDeleteNewDocs())
471     GetDocument()->Modify (true);
472   OnUpdate(this, NULL);
473   GetDocument()->UpdateAllViews (this);
474   GetDocument()->Activate();
475 }
476
477 void
478 ImageFileView::OnLog (wxCommandEvent& event)
479 {
480   ImageFile& rIF = GetDocument()->getImageFile();
481   rIF.log (rIF);
482   rIF.labelAdd ("Logrithm base-e Pixel Values");
483   if (theApp->getAskDeleteNewDocs())
484     GetDocument()->Modify (true);
485   OnUpdate(this, NULL);
486   GetDocument()->UpdateAllViews (this);
487   GetDocument()->Activate();
488 }
489
490 void
491 ImageFileView::OnExp (wxCommandEvent& event)
492 {
493   ImageFile& rIF = GetDocument()->getImageFile();
494   rIF.exp (rIF);
495   rIF.labelAdd ("Exponent base-e Pixel Values");
496   if (theApp->getAskDeleteNewDocs())
497     GetDocument()->Modify (true);
498   OnUpdate(this, NULL);
499   GetDocument()->UpdateAllViews (this);
500   GetDocument()->Activate();
501 }
502
503 void
504 ImageFileView::OnAdd (wxCommandEvent& event)
505 {
506   std::vector<ImageFileDocument*> vecIF;
507   theApp->getCompatibleImages (GetDocument(), vecIF);
508
509   if (vecIF.size() == 0) {
510     wxMessageBox (_T("There are no compatible image files open for comparision"), _T("No comparison images"));
511   } else {
512     DialogGetComparisonImage dialogGetCompare (getFrameForChild(), _T("Get Image to Add"), vecIF, false);
513
514     if (dialogGetCompare.ShowModal() == wxID_OK) {
515       ImageFile& rIF = GetDocument()->getImageFile();
516       ImageFileDocument* pRHSDoc = dialogGetCompare.getImageFileDocument();
517       const ImageFile& rRHSIF = pRHSDoc->getImageFile();
518       ImageFileDocument* pNewDoc = theApp->newImageDoc();
519       if (! pNewDoc) {
520         sys_error (ERR_SEVERE, "Unable to create image file");
521         return;
522       }
523       ImageFile& newImage = pNewDoc->getImageFile();
524       newImage.setArraySize (rIF.nx(), rIF.ny());
525       rIF.addImages (rRHSIF, newImage);
526       std::ostringstream os;
527       os << "Add image " << dynamic_cast<wxFrame*>(GetDocument()->GetFirstView()->GetFrame())->GetTitle().c_str() << " and "
528          << dynamic_cast<wxFrame*>(pRHSDoc->GetFirstView()->GetFrame())->GetTitle().c_str();
529       wxString s = dynamic_cast<wxFrame*>(GetDocument()->GetFirstView()->GetFrame())->GetTitle() + _T(": ");
530       newImage.labelsCopy (rIF, s.mb_str(wxConvUTF8));
531       s = dynamic_cast<wxFrame*>(pRHSDoc->GetFirstView()->GetFrame())->GetTitle() + _T(": ");
532       newImage.labelsCopy (rRHSIF, s.mb_str(wxConvUTF8));
533       newImage.labelAdd (os.str().c_str());
534       *theApp->getLog() << wxConvUTF8.cMB2WX(os.str().c_str()) << _T("\n");
535       if (theApp->getAskDeleteNewDocs())
536         pNewDoc->Modify (true);
537       OnUpdate(this, NULL);
538       pNewDoc->UpdateAllViews (this);
539       pNewDoc->getView()->setInitialClientSize();
540       pNewDoc->Activate();
541     }
542   }
543 }
544
545 void
546 ImageFileView::OnSubtract (wxCommandEvent& event)
547 {
548   std::vector<ImageFileDocument*> vecIF;
549   theApp->getCompatibleImages (GetDocument(), vecIF);
550
551   if (vecIF.size() == 0) {
552     wxMessageBox (_T("There are no compatible image files open for comparision"), _T("No comparison images"));
553   } else {
554     DialogGetComparisonImage dialogGetCompare (getFrameForChild(), _T("Get Image to Subtract"), vecIF, false);
555
556     if (dialogGetCompare.ShowModal() == wxID_OK) {
557       ImageFile& rIF = GetDocument()->getImageFile();
558       ImageFileDocument* pRHSDoc = dialogGetCompare.getImageFileDocument();
559       const ImageFile& rRHSIF = pRHSDoc->getImageFile();
560       ImageFileDocument* pNewDoc = theApp->newImageDoc();
561       if (! pNewDoc) {
562         sys_error (ERR_SEVERE, "Unable to create image file");
563         return;
564       }
565       ImageFile& newImage = pNewDoc->getImageFile();
566       newImage.setArraySize (rIF.nx(), rIF.ny());
567       rIF.subtractImages (rRHSIF, newImage);
568       std::ostringstream os;
569       os << "Subtract image " << dynamic_cast<wxFrame*>(GetDocument()->GetFirstView()->GetFrame())->GetTitle().c_str() << " and "
570          << dynamic_cast<wxFrame*>(pRHSDoc->GetFirstView()->GetFrame())->GetTitle().c_str();
571       wxString s = dynamic_cast<wxFrame*>(GetDocument()->GetFirstView()->GetFrame())->GetTitle() + _T(": ");
572       newImage.labelsCopy (rIF, s.mb_str(wxConvUTF8));
573       s = dynamic_cast<wxFrame*>(pRHSDoc->GetFirstView()->GetFrame())->GetTitle() + _T(": ");
574       newImage.labelsCopy (rRHSIF, s.mb_str(wxConvUTF8));
575       newImage.labelAdd (os.str().c_str());
576       *theApp->getLog() << wxConvUTF8.cMB2WX(os.str().c_str()) << _T("\n");
577       if (theApp->getAskDeleteNewDocs())
578         pNewDoc->Modify (true);
579       OnUpdate(this, NULL);
580       pNewDoc->UpdateAllViews (this);
581       pNewDoc->getView()->setInitialClientSize();
582       pNewDoc->Activate();
583     }
584   }
585 }
586
587 void
588 ImageFileView::OnMultiply (wxCommandEvent& event)
589 {
590   std::vector<ImageFileDocument*> vecIF;
591   theApp->getCompatibleImages (GetDocument(), vecIF);
592
593   if (vecIF.size() == 0) {
594     wxMessageBox (_T("There are no compatible image files open for comparision"), _T("No comparison images"));
595   } else {
596     DialogGetComparisonImage dialogGetCompare (getFrameForChild(), _T("Get Image to Multiply"), vecIF, false);
597
598     if (dialogGetCompare.ShowModal() == wxID_OK) {
599       ImageFile& rIF = GetDocument()->getImageFile();
600       ImageFileDocument* pRHSDoc = dialogGetCompare.getImageFileDocument();
601       const ImageFile& rRHSIF = pRHSDoc->getImageFile();
602       ImageFileDocument* pNewDoc = theApp->newImageDoc();
603       if (! pNewDoc) {
604         sys_error (ERR_SEVERE, "Unable to create image file");
605         return;
606       }
607       ImageFile& newImage = pNewDoc->getImageFile();
608       newImage.setArraySize (rIF.nx(), rIF.ny());
609       rIF.multiplyImages (rRHSIF, newImage);
610       std::ostringstream os;
611       os << "Multiply image " << dynamic_cast<wxFrame*>(GetDocument()->GetFirstView()->GetFrame())->GetTitle().c_str() << " and "
612          << dynamic_cast<wxFrame*>(pRHSDoc->GetFirstView()->GetFrame())->GetTitle().c_str();
613       wxString s = dynamic_cast<wxFrame*>(GetDocument()->GetFirstView()->GetFrame())->GetTitle() + _T(": ");
614       newImage.labelsCopy (rIF, s.mb_str(wxConvUTF8));
615       s = dynamic_cast<wxFrame*>(pRHSDoc->GetFirstView()->GetFrame())->GetTitle() + _T(": ");
616       newImage.labelsCopy (rRHSIF, s.mb_str(wxConvUTF8));
617       newImage.labelAdd (os.str().c_str());
618       *theApp->getLog() << wxConvUTF8.cMB2WX(os.str().c_str()) << _T("\n");
619       if (theApp->getAskDeleteNewDocs())
620         pNewDoc->Modify (true);
621       OnUpdate(this, NULL);
622       pNewDoc->UpdateAllViews (this);
623       pNewDoc->getView()->setInitialClientSize();
624       pNewDoc->Activate();
625     }
626   }
627 }
628
629 void
630 ImageFileView::OnDivide (wxCommandEvent& event)
631 {
632   std::vector<ImageFileDocument*> vecIF;
633   theApp->getCompatibleImages (GetDocument(), vecIF);
634
635   if (vecIF.size() == 0) {
636     wxMessageBox (_T("There are no compatible image files open for comparision"), _T("No comparison images"));
637   } else {
638     DialogGetComparisonImage dialogGetCompare (getFrameForChild(), _T("Get Image to Divide"), vecIF, false);
639
640     if (dialogGetCompare.ShowModal() == wxID_OK) {
641       ImageFile& rIF = GetDocument()->getImageFile();
642       ImageFileDocument* pRHSDoc = dialogGetCompare.getImageFileDocument();
643       const ImageFile& rRHSIF = pRHSDoc->getImageFile();
644       ImageFileDocument* pNewDoc = theApp->newImageDoc();
645       if (! pNewDoc) {
646         sys_error (ERR_SEVERE, "Unable to create image file");
647         return;
648       }
649       ImageFile& newImage = pNewDoc->getImageFile();
650       newImage.setArraySize (rIF.nx(), rIF.ny());
651       rIF.divideImages (rRHSIF, newImage);
652       std::ostringstream os;
653       os << "Divide image " << dynamic_cast<wxFrame*>(GetDocument()->GetFirstView()->GetFrame())->GetTitle().c_str() << " by "
654          << dynamic_cast<wxFrame*>(pRHSDoc->GetFirstView()->GetFrame())->GetTitle().c_str();
655       wxString s = dynamic_cast<wxFrame*>(GetDocument()->GetFirstView()->GetFrame())->GetTitle() + _T(": ");
656       newImage.labelsCopy (rIF, s.mb_str(wxConvUTF8));
657       s = dynamic_cast<wxFrame*>(pRHSDoc->GetFirstView()->GetFrame())->GetTitle() + _T(": ");
658       newImage.labelsCopy (rRHSIF, s.mb_str(wxConvUTF8));
659       newImage.labelAdd (os.str().c_str());
660       *theApp->getLog() << wxConvUTF8.cMB2WX(os.str().c_str()) << _T("\n");
661       if (theApp->getAskDeleteNewDocs())
662         pNewDoc->Modify (true);
663       OnUpdate(this, NULL);
664       pNewDoc->UpdateAllViews (this);
665       pNewDoc->getView()->setInitialClientSize();
666       pNewDoc->Activate();
667     }
668   }
669 }
670
671
672 #ifdef HAVE_FFT
673 void
674 ImageFileView::OnFFT (wxCommandEvent& event)
675 {
676   ImageFile& rIF = GetDocument()->getImageFile();
677   rIF.fft (rIF);
678   rIF.labelAdd ("FFT Image");
679   m_bMinSpecified = false;
680   m_bMaxSpecified = false;
681   if (theApp->getAskDeleteNewDocs())
682     GetDocument()->Modify (true);
683   OnUpdate(this, NULL);
684   GetDocument()->UpdateAllViews (this);
685   GetDocument()->Activate();
686 }
687
688 void
689 ImageFileView::OnIFFT (wxCommandEvent& event)
690 {
691   ImageFile& rIF = GetDocument()->getImageFile();
692   rIF.ifft (rIF);
693   rIF.labelAdd ("IFFT Image");
694   m_bMinSpecified = false;
695   m_bMaxSpecified = false;
696   if (theApp->getAskDeleteNewDocs())
697     GetDocument()->Modify (true);
698   OnUpdate(this, NULL);
699   GetDocument()->UpdateAllViews (this);
700   GetDocument()->Activate();
701 }
702
703 void
704 ImageFileView::OnFFTRows (wxCommandEvent& event)
705 {
706   ImageFile& rIF = GetDocument()->getImageFile();
707   rIF.fftRows (rIF);
708   rIF.labelAdd ("FFT Rows");
709   m_bMinSpecified = false;
710   m_bMaxSpecified = false;
711   if (theApp->getAskDeleteNewDocs())
712     GetDocument()->Modify (true);
713   OnUpdate(this, NULL);
714   GetDocument()->UpdateAllViews (this);
715   GetDocument()->Activate();
716 }
717
718 void
719 ImageFileView::OnIFFTRows (wxCommandEvent& event)
720 {
721   ImageFile& rIF = GetDocument()->getImageFile();
722   rIF.ifftRows (rIF);
723   rIF.labelAdd ("IFFT Rows");
724   m_bMinSpecified = false;
725   m_bMaxSpecified = false;
726   if (theApp->getAskDeleteNewDocs())
727     GetDocument()->Modify (true);
728   OnUpdate(this, NULL);
729   GetDocument()->UpdateAllViews (this);
730   GetDocument()->Activate();
731 }
732
733 void
734 ImageFileView::OnFFTCols (wxCommandEvent& event)
735 {
736   ImageFile& rIF = GetDocument()->getImageFile();
737   rIF.fftCols (rIF);
738   rIF.labelAdd ("FFT Columns");
739   m_bMinSpecified = false;
740   m_bMaxSpecified = false;
741   if (theApp->getAskDeleteNewDocs())
742     GetDocument()->Modify (true);
743   OnUpdate(this, NULL);
744   GetDocument()->UpdateAllViews (this);
745   GetDocument()->Activate();
746 }
747
748 void
749 ImageFileView::OnIFFTCols (wxCommandEvent& event)
750 {
751   ImageFile& rIF = GetDocument()->getImageFile();
752   rIF.ifftCols (rIF);
753   rIF.labelAdd ("IFFT Columns");
754   m_bMinSpecified = false;
755   m_bMaxSpecified = false;
756   if (theApp->getAskDeleteNewDocs())
757     GetDocument()->Modify (true);
758   OnUpdate(this, NULL);
759   GetDocument()->UpdateAllViews (this);
760   GetDocument()->Activate();
761 }
762 #endif
763
764 void
765 ImageFileView::OnFourier (wxCommandEvent& event)
766 {
767   ImageFile& rIF = GetDocument()->getImageFile();
768   wxProgressDialog dlgProgress (_T("Fourier"), _T("Fourier Progress"), 1, getFrameForChild(), wxPD_APP_MODAL);
769   rIF.fourier (rIF);
770   rIF.labelAdd ("Fourier Image");
771   m_bMinSpecified = false;
772   m_bMaxSpecified = false;
773   if (theApp->getAskDeleteNewDocs())
774     GetDocument()->Modify (true);
775   OnUpdate(this, NULL);
776   GetDocument()->UpdateAllViews (this);
777   GetDocument()->Activate();
778 }
779
780 void
781 ImageFileView::OnInverseFourier (wxCommandEvent& event)
782 {
783   ImageFile& rIF = GetDocument()->getImageFile();
784   wxProgressDialog dlgProgress (_T("Inverse Fourier"), _T("Inverse Fourier Progress"), 1, getFrameForChild(), wxPD_APP_MODAL);
785   rIF.inverseFourier (rIF);
786   rIF.labelAdd ("Inverse Fourier Image");
787   m_bMinSpecified = false;
788   m_bMaxSpecified = false;
789   if (theApp->getAskDeleteNewDocs())
790     GetDocument()->Modify (true);
791   OnUpdate(this, NULL);
792   GetDocument()->UpdateAllViews (this);
793   GetDocument()->Activate();
794 }
795
796 void
797 ImageFileView::OnShuffleNaturalToFourierOrder (wxCommandEvent& event)
798 {
799   ImageFile& rIF = GetDocument()->getImageFile();
800   Fourier::shuffleNaturalToFourierOrder (rIF);
801   rIF.labelAdd ("Shuffle Natural To Fourier Order");
802   m_bMinSpecified = false;
803   m_bMaxSpecified = false;
804   if (theApp->getAskDeleteNewDocs())
805     GetDocument()->Modify (true);
806   OnUpdate(this, NULL);
807   GetDocument()->UpdateAllViews (this);
808   GetDocument()->Activate();
809 }
810
811 void
812 ImageFileView::OnShuffleFourierToNaturalOrder (wxCommandEvent& event)
813 {
814   ImageFile& rIF = GetDocument()->getImageFile();
815   Fourier::shuffleFourierToNaturalOrder (rIF);
816   rIF.labelAdd ("Shuffle Fourier To Natural Order");
817   m_bMinSpecified = false;
818   m_bMaxSpecified = false;
819   if (theApp->getAskDeleteNewDocs())
820     GetDocument()->Modify (true);
821   OnUpdate(this, NULL);
822   GetDocument()->UpdateAllViews (this);
823   GetDocument()->Activate();
824 }
825
826 void
827 ImageFileView::OnMagnitude (wxCommandEvent& event)
828 {
829   ImageFile& rIF = GetDocument()->getImageFile();
830   rIF.magnitude (rIF);
831   rIF.labelAdd ("Magnitude");
832   m_bMinSpecified = false;
833   m_bMaxSpecified = false;
834   if (theApp->getAskDeleteNewDocs())
835     GetDocument()->Modify (true);
836   OnUpdate(this, NULL);
837   GetDocument()->UpdateAllViews (this);
838   GetDocument()->Activate();
839 }
840
841 void
842 ImageFileView::OnPhase (wxCommandEvent& event)
843 {
844   ImageFile& rIF = GetDocument()->getImageFile();
845   if (rIF.isComplex()) {
846     rIF.phase (rIF);
847     rIF.labelAdd ("Phase of complex-image");
848     m_bMinSpecified = false;
849     m_bMaxSpecified = false;
850     if (theApp->getAskDeleteNewDocs())
851       GetDocument()->Modify (true);
852     OnUpdate(this, NULL);
853     GetDocument()->UpdateAllViews (this);
854   }
855   GetDocument()->Activate();
856 }
857
858 void
859 ImageFileView::OnReal (wxCommandEvent& event)
860 {
861   ImageFile& rIF = GetDocument()->getImageFile();
862   if (rIF.isComplex()) {
863     rIF.real (rIF);
864     rIF.labelAdd ("Real component of complex-image");
865     m_bMinSpecified = false;
866     m_bMaxSpecified = false;
867     if (theApp->getAskDeleteNewDocs())
868       GetDocument()->Modify (true);
869     OnUpdate(this, NULL);
870     GetDocument()->UpdateAllViews (this);
871   }
872   GetDocument()->Activate();
873 }
874
875 void
876 ImageFileView::OnImaginary (wxCommandEvent& event)
877 {
878   ImageFile& rIF = GetDocument()->getImageFile();
879   if (rIF.isComplex()) {
880     rIF.imaginary (rIF);
881     rIF.labelAdd ("Imaginary component of complex-image");
882     m_bMinSpecified = false;
883     m_bMaxSpecified = false;
884     if (theApp->getAskDeleteNewDocs())
885       GetDocument()->Modify (true);
886     OnUpdate(this, NULL);
887     GetDocument()->UpdateAllViews (this);
888   }
889   GetDocument()->Activate();
890 }
891
892
893 ImageFileCanvas*
894 ImageFileView::CreateCanvas (wxFrame* parent)
895 {
896   ImageFileCanvas* pCanvas = new ImageFileCanvas (this, parent, wxPoint(-1,-1),
897                                                   wxSize(-1,-1), 0);
898   pCanvas->SetBackgroundColour(*wxWHITE);
899   pCanvas->ClearBackground();
900
901   return pCanvas;
902 }
903
904 #if CTSIM_MDI
905 wxDocMDIChildFrame*
906 #else
907 wxDocChildFrame*
908 #endif
909 ImageFileView::CreateChildFrame(wxDocument *doc, wxView *view)
910 {
911 #if CTSIM_MDI
912   wxDocMDIChildFrame* subframe = new wxDocMDIChildFrame (doc, view, theApp->getMainFrame(), -1, _T("ImageFile Frame"), wxPoint(-1,-1), wxSize(-1,-1), wxDEFAULT_FRAME_STYLE);
913 #else
914   wxDocChildFrame* subframe = new wxDocChildFrame (doc, view, theApp->getMainFrame(), -1, _T("ImageFile Frame"), wxPoint(-1,-1), wxSize(-1,-1), wxDEFAULT_FRAME_STYLE);
915 #endif
916   theApp->setIconForFrame (subframe);
917
918   m_pFileMenu = new wxMenu;
919   m_pFileMenu->Append(MAINMENU_FILE_CREATE_PHANTOM, _T("Cr&eate Phantom...\tCtrl-P"));
920   m_pFileMenu->Append(MAINMENU_FILE_CREATE_FILTER, _T("Create &Filter...\tCtrl-F"));
921   m_pFileMenu->Append(wxID_OPEN, _T("&Open...\tCtrl-O"));
922   m_pFileMenu->Append(wxID_SAVE, _T("&Save\tCtrl-S"));
923   m_pFileMenu->Append(wxID_SAVEAS, _T("Save &As..."));
924   m_pFileMenu->Append(wxID_CLOSE, _T("&Close\tCtrl-W"));
925   m_pFileMenu->Append(wxID_REVERT, _T("Re&vert"));
926
927   m_pFileMenu->AppendSeparator();
928   m_pFileMenu->Append(IFMENU_FILE_PROPERTIES, _T("P&roperties\tCtrl-I"));
929   m_pFileMenu->Append(IFMENU_FILE_EXPORT, _T("Expor&t..."));
930
931   m_pFileMenu->AppendSeparator();
932   m_pFileMenu->Append(wxID_PRINT, _T("&Print..."));
933   m_pFileMenu->Append(wxID_PRINT_SETUP, _T("Print &Setup..."));
934   m_pFileMenu->Append(wxID_PREVIEW, _T("Print Preview"));
935   m_pFileMenu->AppendSeparator();
936   m_pFileMenu->Append(MAINMENU_IMPORT, _T("&Import...\tCtrl-M"));
937   m_pFileMenu->AppendSeparator();
938   m_pFileMenu->Append (MAINMENU_FILE_PREFERENCES, _T("Prefere&nces..."));
939   m_pFileMenu->Append(MAINMENU_FILE_EXIT, _T("E&xit"));
940   GetDocumentManager()->FileHistoryAddFilesToMenu(m_pFileMenu);
941   GetDocumentManager()->FileHistoryUseMenu(m_pFileMenu);
942
943   wxMenu* edit_menu = new wxMenu;
944   edit_menu->Append(IFMENU_EDIT_COPY, _T("Copy\tCtrl-C"));
945   edit_menu->Append(IFMENU_EDIT_CUT, _T("Cut\tCtrl-X"));
946   edit_menu->Append(IFMENU_EDIT_PASTE, _T("Paste\tCtrl-V"));
947
948   wxMenu *view_menu = new wxMenu;
949   view_menu->Append(IFMENU_VIEW_SCALE_MINMAX, _T("Display Scale S&et...\tCtrl-E"));
950   view_menu->Append(IFMENU_VIEW_SCALE_AUTO, _T("Display Scale &Auto...\tCtrl-A"));
951   view_menu->Append(IFMENU_VIEW_SCALE_FULL, _T("Display F&ull Scale\tCtrl-U"));
952
953   m_pFilterMenu = new wxMenu;
954   m_pFilterMenu->Append (IFMENU_FILTER_INVERTVALUES, _T("In&vert Values"));
955   m_pFilterMenu->Append (IFMENU_FILTER_SQUARE, _T("&Square"));
956   m_pFilterMenu->Append (IFMENU_FILTER_SQRT, _T("Square &Root"));
957   m_pFilterMenu->Append (IFMENU_FILTER_LOG, _T("&Log"));
958   m_pFilterMenu->Append (IFMENU_FILTER_EXP, _T("E&xp"));
959   m_pFilterMenu->AppendSeparator();
960 #ifdef HAVE_FFT
961   m_pFilterMenu->Append (IFMENU_FILTER_FFT, _T("2-D &FFT\tCtrl-2"));
962   m_pFilterMenu->Append (IFMENU_FILTER_IFFT, _T("2-D &IFFT\tAlt-2"));
963   m_pFilterMenu->Append (IFMENU_FILTER_FFT_ROWS, _T("FFT Rows"));
964   m_pFilterMenu->Append (IFMENU_FILTER_IFFT_ROWS, _T("IFFT Rows"));
965   m_pFilterMenu->Append (IFMENU_FILTER_FFT_COLS, _T("FFT Columns"));
966   m_pFilterMenu->Append (IFMENU_FILTER_IFFT_COLS, _T("IFFT Columns"));
967   m_pFilterMenu->Append (IFMENU_FILTER_FOURIER, _T("2-D F&ourier"));
968   m_pFilterMenu->Append (IFMENU_FILTER_INVERSE_FOURIER, _T("2-D Inverse Fo&urier"));
969 #else
970   m_pFilterMenu->Append (IFMENU_FILTER_FOURIER, _T("&Fourier"));
971   m_pFilterMenu->Append (IFMENU_FILTER_INVERSE_FOURIER, _T("&Inverse Fourier"));
972 #endif
973   m_pFilterMenu->Append (IFMENU_FILTER_SHUFFLEFOURIERTONATURALORDER, _T("Shuffl&e Fourier to Natural Order"));
974   m_pFilterMenu->Append (IFMENU_FILTER_SHUFFLENATURALTOFOURIERORDER, _T("Shuffle &Natural to Fourier Order"));
975   m_pFilterMenu->AppendSeparator();
976   m_pFilterMenu->Append (IFMENU_FILTER_MAGNITUDE, _T("&Magnitude"));
977   m_pFilterMenu->Append (IFMENU_FILTER_PHASE, _T("&Phase"));
978   m_pFilterMenu->Append (IFMENU_FILTER_REAL, _T("Re&al"));
979   m_pFilterMenu->Append (IFMENU_FILTER_IMAGINARY, _T("Ima&ginary"));
980
981   wxMenu* image_menu = new wxMenu;
982   image_menu->Append (IFMENU_IMAGE_ADD, _T("&Add..."));
983   image_menu->Append (IFMENU_IMAGE_SUBTRACT, _T("&Subtract..."));
984   image_menu->Append (IFMENU_IMAGE_MULTIPLY, _T("&Multiply..."));
985   image_menu->Append (IFMENU_IMAGE_DIVIDE, _T("&Divide..."));
986   image_menu->AppendSeparator();
987   image_menu->Append (IFMENU_IMAGE_SCALESIZE, _T("S&cale Size..."));
988 #if wxUSE_GLCANVAS
989   image_menu->Append (IFMENU_IMAGE_CONVERT3D, _T("Convert &3-D\tCtrl-3"));
990 #endif
991
992   m_pMenuAnalyze = new wxMenu;
993   m_pMenuAnalyze->Append (IFMENU_PLOT_ROW, _T("Plot &Row"));
994   m_pMenuAnalyze->Append (IFMENU_PLOT_COL, _T("Plot &Column"));
995   m_pMenuAnalyze->Append (IFMENU_PLOT_HISTOGRAM, _T("Plot &Histogram"));
996   m_pMenuAnalyze->AppendSeparator();
997   m_pMenuAnalyze->Append (IFMENU_PLOT_FFT_ROW, _T("P&lot FFT Row"));
998   m_pMenuAnalyze->Append (IFMENU_PLOT_FFT_COL, _T("Plo&t FFT Column"));
999   m_pMenuAnalyze->AppendSeparator();
1000   m_pMenuAnalyze->Append (IFMENU_COMPARE_IMAGES, _T("Compare &Images..."));
1001   m_pMenuAnalyze->Append (IFMENU_COMPARE_ROW, _T("Compare Ro&w"));
1002   m_pMenuAnalyze->Append (IFMENU_COMPARE_COL, _T("Compare Colu&mn"));
1003   m_pMenuAnalyze->Enable (IFMENU_PLOT_ROW, false);
1004   m_pMenuAnalyze->Enable (IFMENU_PLOT_COL, false);
1005   m_pMenuAnalyze->Enable (IFMENU_COMPARE_ROW, false);
1006   m_pMenuAnalyze->Enable (IFMENU_COMPARE_COL, false);
1007   m_pMenuAnalyze->Enable (IFMENU_PLOT_FFT_ROW, false);
1008   m_pMenuAnalyze->Enable (IFMENU_PLOT_FFT_COL, false);
1009
1010   wxMenu *help_menu = new wxMenu;
1011   help_menu->Append(MAINMENU_HELP_CONTENTS, _T("&Contents\tF1"));
1012   help_menu->Append (MAINMENU_HELP_TIPS, _T("&Tips"));
1013   help_menu->Append (IDH_QUICKSTART, _T("&Quick Start"));
1014   help_menu->Append(MAINMENU_HELP_ABOUT, _T("&About"));
1015
1016   wxMenuBar *menu_bar = new wxMenuBar;
1017
1018   menu_bar->Append(m_pFileMenu, _T("&File"));
1019   menu_bar->Append(edit_menu, _T("&Edit"));
1020   menu_bar->Append(view_menu, _T("&View"));
1021   menu_bar->Append(image_menu, _T("&Image"));
1022   menu_bar->Append(m_pFilterMenu, _T("Fi&lter"));
1023   menu_bar->Append(m_pMenuAnalyze, _T("&Analyze"));
1024   menu_bar->Append(help_menu, _T("&Help"));
1025
1026   subframe->SetMenuBar(menu_bar);
1027
1028   subframe->Centre(wxBOTH);
1029
1030   wxAcceleratorEntry accelEntries[10];
1031   accelEntries[0].Set (wxACCEL_CTRL, static_cast<int>('A'), IFMENU_VIEW_SCALE_AUTO);
1032   accelEntries[1].Set (wxACCEL_CTRL, static_cast<int>('U'), IFMENU_VIEW_SCALE_FULL);
1033   accelEntries[2].Set (wxACCEL_CTRL, static_cast<int>('E'), IFMENU_VIEW_SCALE_MINMAX);
1034   accelEntries[3].Set (wxACCEL_CTRL, static_cast<int>('I'), IFMENU_FILE_PROPERTIES);
1035   accelEntries[4].Set (wxACCEL_CTRL, static_cast<int>('C'), IFMENU_EDIT_COPY);
1036   accelEntries[5].Set (wxACCEL_CTRL, static_cast<int>('X'), IFMENU_EDIT_CUT);
1037   accelEntries[6].Set (wxACCEL_CTRL, static_cast<int>('V'), IFMENU_EDIT_PASTE);
1038   int iEntry = 7;
1039 #ifdef HAVE_FFT
1040   accelEntries[iEntry++].Set (wxACCEL_CTRL, static_cast<int>('2'), IFMENU_FILTER_FFT);
1041   accelEntries[iEntry++].Set (wxACCEL_ALT,  static_cast<int>('2'), IFMENU_FILTER_IFFT);
1042 #endif
1043 #if wxUSE_GLCANVAS
1044   accelEntries[iEntry++].Set (wxACCEL_CTRL, static_cast<int>('3'), IFMENU_IMAGE_CONVERT3D);
1045 #endif
1046
1047   wxAcceleratorTable accelTable (iEntry, accelEntries);
1048   subframe->SetAcceleratorTable (accelTable);
1049
1050   return subframe;
1051 }
1052
1053
1054 bool
1055 ImageFileView::OnCreate (wxDocument *doc, long WXUNUSED(flags) )
1056 {
1057   m_bMinSpecified = false;
1058   m_bMaxSpecified = false;
1059   m_dAutoScaleFactor = 1.;
1060
1061   m_pFrame = CreateChildFrame(doc, this);
1062   SetFrame (m_pFrame);
1063   m_pCanvas = CreateCanvas (m_pFrame);
1064   m_pFrame->SetClientSize (m_pCanvas->GetBestSize());
1065   m_pCanvas->SetClientSize (m_pCanvas->GetBestSize());
1066   m_pFrame->SetTitle(doc->GetFilename());
1067
1068   m_pFrame->Show(true);
1069   Activate(true);
1070
1071   return true;
1072 }
1073
1074 void
1075 ImageFileView::setInitialClientSize ()
1076 {
1077   if (m_pFrame && m_pCanvas) {
1078     wxSize bestSize = m_pCanvas->GetBestSize();
1079
1080     m_pFrame->SetClientSize (bestSize);
1081     m_pFrame->Show (true);
1082     m_pFrame->SetFocus();
1083   }
1084 }
1085
1086 void
1087 ImageFileView::OnDraw (wxDC* dc)
1088 {
1089   if (m_pBitmap && m_pBitmap->Ok()) {
1090 #ifdef DEBUG
1091     *theApp->getLog() << _T("Drawing bitmap\n");
1092 #endif
1093     dc->DrawBitmap(*m_pBitmap, 0, 0, false);
1094   }
1095
1096   int xCursor, yCursor;
1097   if (m_pCanvas->GetCurrentCursor (xCursor, yCursor))
1098     m_pCanvas->DrawRubberBandCursor (*dc, xCursor, yCursor);
1099 }
1100
1101
1102 void
1103 ImageFileView::OnUpdate (wxView *WXUNUSED(sender), wxObject *WXUNUSED(hint) )
1104 {
1105   const ImageFile& rIF = GetDocument()->getImageFile();
1106   if (m_pFilterMenu && rIF.isComplex()) {
1107     m_pFilterMenu->Enable(IFMENU_FILTER_REAL, true);
1108     m_pFilterMenu->Enable(IFMENU_FILTER_IMAGINARY, true);
1109     m_pFilterMenu->Enable(IFMENU_FILTER_PHASE, true);
1110   } else {
1111     m_pFilterMenu->Enable(IFMENU_FILTER_REAL, false);
1112     m_pFilterMenu->Enable(IFMENU_FILTER_IMAGINARY, false);
1113     m_pFilterMenu->Enable(IFMENU_FILTER_PHASE, false);
1114   }
1115   ImageFileArrayConst v = rIF.getArray();
1116   int nx = rIF.nx();
1117   int ny = rIF.ny();
1118   if (v != NULL && nx != 0 && ny != 0) {
1119     if (! m_bMinSpecified || ! m_bMaxSpecified) {
1120       double min, max;
1121       rIF.getMinMax (min, max);
1122       if (! m_bMinSpecified)
1123         m_dMinPixel = min;
1124       if (! m_bMaxSpecified)
1125         m_dMaxPixel = max;
1126     }
1127     double scaleWidth = m_dMaxPixel - m_dMinPixel;
1128
1129     unsigned char* imageData = new unsigned char [nx * ny * 3];
1130     if (! imageData) {
1131       sys_error (ERR_SEVERE, "Unable to allocate memory for Image display");
1132       return;
1133     }
1134     for (int ix = 0; ix < nx; ix++) {
1135       for (int iy = 0; iy < ny; iy++) {
1136         double scaleValue = ((v[ix][iy] - m_dMinPixel) / scaleWidth) * 255;
1137         int intensity = static_cast<int>(scaleValue + 0.5);
1138         intensity = clamp (intensity, 0, 255);
1139         int baseAddr = ((ny - 1 - iy) * nx + ix) * 3;
1140         imageData[baseAddr] = imageData[baseAddr+1] = imageData[baseAddr+2] = intensity;
1141       }
1142     }
1143     wxImage image (nx, ny, imageData, true);
1144     if (m_pBitmap) {
1145       delete m_pBitmap;
1146       m_pBitmap = NULL;
1147     }
1148 #ifdef DEBUG
1149     *theApp->getLog() << _T("Making new bitmap\n");
1150 #endif
1151     m_pBitmap = new wxBitmap (image);
1152     delete imageData;
1153     m_pCanvas->SetScrollbars(20, 20, nx/20, ny/20);
1154     m_pCanvas->SetBackgroundColour(*wxWHITE);
1155   }
1156
1157   if (m_pCanvas)
1158     m_pCanvas->Refresh();
1159 }
1160
1161 bool
1162 ImageFileView::OnClose (bool deleteWindow)
1163 {
1164   if (! GetDocument() || ! GetDocument()->Close())
1165     return false;
1166
1167   Activate (false);
1168   if (m_pCanvas) {
1169     m_pCanvas->setView(NULL);
1170     m_pCanvas = NULL;
1171   }
1172   wxString s(theApp->GetAppName());
1173   if (m_pFrame)
1174     m_pFrame->SetTitle(s);
1175
1176   SetFrame(NULL);
1177
1178   if (deleteWindow) {
1179     delete m_pFrame;
1180     m_pFrame = NULL;
1181     if (GetDocument() && GetDocument()->getBadFileOpen())
1182       ::wxYield();  // wxWindows bug workaround
1183   }
1184
1185   return true;
1186 }
1187
1188 void
1189 ImageFileView::OnEditCopy (wxCommandEvent& event)
1190 {
1191   wxBitmapDataObject *pBitmapObject = new wxBitmapDataObject;
1192
1193   if (m_pBitmap)
1194     pBitmapObject->SetBitmap (*m_pBitmap);
1195
1196   if (wxTheClipboard->Open()) {
1197     wxTheClipboard->SetData (pBitmapObject);
1198     wxTheClipboard->Close();
1199   }
1200 }
1201
1202 void
1203 ImageFileView::OnEditCut (wxCommandEvent& event)
1204 {
1205   OnEditCopy (event);
1206   ImageFile& rIF = GetDocument()->getImageFile();
1207   int nx = rIF.nx();
1208   int ny = rIF.ny();
1209   ImageFile* pIF = new ImageFile (nx, ny);
1210   pIF->arrayDataClear();
1211   GetDocument()->setImageFile (pIF); // deletes old IF
1212   OnUpdate(this, NULL);
1213   GetDocument()->UpdateAllViews();
1214   if (theApp->getAskDeleteNewDocs())
1215     GetDocument()->Modify (true);
1216 }
1217
1218 void
1219 ImageFileView::OnEditPaste (wxCommandEvent& event)
1220 {
1221   ImageFile& rIF = GetDocument()->getImageFile();
1222
1223   if (wxTheClipboard->Open()) {
1224     wxBitmap bitmap;
1225     if (wxTheClipboard->IsSupported (wxDF_BITMAP)) {
1226       wxBitmapDataObject bitmapObject;
1227       wxTheClipboard->GetData (bitmapObject);
1228       bitmap = bitmapObject.GetBitmap ();
1229     }
1230     wxTheClipboard->Close();
1231
1232     int nx = rIF.nx();
1233     int ny = rIF.ny();
1234     bool bMonochrome = false;
1235
1236     if (bitmap.Ok() == true && bitmap.GetWidth() == nx && bitmap.GetHeight() == ny) {
1237       wxImage image (bitmap.ConvertToImage());
1238       double dScale3 = 3 * 255;
1239       unsigned char* pixels = image.GetData();
1240       ImageFileArray v = rIF.getArray();
1241       for (unsigned int ix = 0; ix < rIF.nx(); ix++) {
1242         for (unsigned int iy = 0; iy < rIF.ny(); iy++) {
1243           unsigned int iBase = 3 * (iy * nx + ix);
1244           if (ix == 0 && iy == 0 && (pixels[iBase] == pixels[iBase+1] && pixels[iBase+1] == pixels[iBase+2]))
1245             bMonochrome = true;
1246           if (bMonochrome) {
1247             v[ix][ny - 1 - iy] = (pixels[iBase]+pixels[iBase+1]+pixels[iBase+2]) / dScale3;
1248           } else {
1249             double dR = pixels[iBase] / 255.;
1250             double dG = pixels[iBase+1] / 255.;
1251             double dB = pixels[iBase+2] / 255.;
1252             v[ix][ny - 1 - iy] = ImageFile::colorToGrayscale (dR, dG, dB);
1253           }
1254         }
1255       }
1256       OnUpdate(this, NULL);
1257       GetDocument()->UpdateAllViews();
1258       if (theApp->getAskDeleteNewDocs())
1259         GetDocument()->Modify(true);
1260     }
1261   }
1262 }
1263
1264 void
1265 ImageFileView::OnExport (wxCommandEvent& event)
1266 {
1267   ImageFile& rIF = GetDocument()->getImageFile();
1268   ImageFileArrayConst v = rIF.getArray();
1269   int nx = rIF.nx();
1270   int ny = rIF.ny();
1271   if (v != NULL && nx != 0 && ny != 0) {
1272     if (! m_bMinSpecified || ! m_bMaxSpecified) {
1273       double min, max;
1274       rIF.getMinMax (min, max);
1275       if (! m_bMinSpecified)
1276         m_dMinPixel = min;
1277       if (! m_bMaxSpecified)
1278         m_dMaxPixel = max;
1279     }
1280
1281     DialogExportParameters dialogExport (getFrameForChild(), m_iDefaultExportFormatID);
1282     if (dialogExport.ShowModal() == wxID_OK) {
1283       wxString strFormatName (dialogExport.getFormatName (), wxConvUTF8);
1284       m_iDefaultExportFormatID = ImageFile::convertExportFormatNameToID (strFormatName.mb_str(wxConvUTF8));
1285
1286       wxString strExt;
1287       wxString strWildcard;
1288       if (m_iDefaultExportFormatID == ImageFile::EXPORT_FORMAT_PGM || m_iDefaultExportFormatID == ImageFile::EXPORT_FORMAT_PGMASCII) {
1289         strExt = _T(".pgm");
1290         strWildcard = _T("PGM Files (*.pgm)|*.pgm");
1291       }
1292 #ifdef HAVE_PNG
1293       else if (m_iDefaultExportFormatID == ImageFile::EXPORT_FORMAT_PNG || m_iDefaultExportFormatID == ImageFile::EXPORT_FORMAT_PNG16) {
1294         strExt = _T(".png");
1295         strWildcard = _T("PNG Files (*.png)|*.png");
1296       }
1297 #endif
1298 #ifdef HAVE_CTN_DICOM
1299       else if (m_iDefaultExportFormatID == ImageFile::EXPORT_FORMAT_DICOM) {
1300         strExt = _T("");
1301         strWildcard = _T("DICOM Files (*.*)|*.*");
1302       }
1303 #endif
1304       else if (m_iDefaultExportFormatID == ImageFile::EXPORT_FORMAT_TEXT) {
1305         strExt = _T(".txt");
1306         strWildcard = _T("Text (*.txt)|*.txt");
1307       }
1308       else {
1309         strExt = _T("");
1310         strWildcard = _T("Miscellaneous (*.*)|*.*");
1311       }
1312
1313       const wxString& strFilename = wxFileSelector (_T("Export Filename"), _T(""),
1314                                                     _T(""), strExt, strWildcard, wxFD_OVERWRITE_PROMPT | wxFD_SAVE);
1315       if (! strFilename.empty()) {
1316         rIF.exportImage (strFormatName.mb_str(wxConvUTF8), strFilename.mb_str(wxConvUTF8), 1, 1, m_dMinPixel, m_dMaxPixel);
1317         *theApp->getLog() << _T("Exported file ") << strFilename << _T("\n");
1318       }
1319     }
1320   }
1321 }
1322
1323 void
1324 ImageFileView::OnScaleSize (wxCommandEvent& event)
1325 {
1326   ImageFile& rIF = GetDocument()->getImageFile();
1327   unsigned int iOldNX = rIF.nx();
1328   unsigned int iOldNY = rIF.ny();
1329
1330   DialogGetXYSize dialogGetXYSize (getFrameForChild(), _T("Set New X & Y Dimensions"), iOldNX, iOldNY);
1331   if (dialogGetXYSize.ShowModal() == wxID_OK) {
1332     unsigned int iNewNX = dialogGetXYSize.getXSize();
1333     unsigned int iNewNY = dialogGetXYSize.getYSize();
1334     std::ostringstream os;
1335     os << "Scale Size from (" << iOldNX << "," << iOldNY << ") to (" << iNewNX << "," << iNewNY << ")";
1336     ImageFileDocument* pScaledDoc = theApp->newImageDoc();
1337     if (! pScaledDoc) {
1338       sys_error (ERR_SEVERE, "Unable to create image file");
1339       return;
1340     }
1341     ImageFile& rScaledIF = pScaledDoc->getImageFile();
1342     rScaledIF.setArraySize (iNewNX, iNewNY);
1343     rScaledIF.labelsCopy (rIF);
1344     rScaledIF.labelAdd (os.str().c_str());
1345     rIF.scaleImage (rScaledIF);
1346     *theApp->getLog() << wxConvUTF8.cMB2WX(os.str().c_str()) << _T("\n");
1347     if (theApp->getAskDeleteNewDocs())
1348       pScaledDoc->Modify (true);
1349     OnUpdate(this, NULL);
1350     pScaledDoc->UpdateAllViews (this);
1351     pScaledDoc->getView()->setInitialClientSize();
1352     pScaledDoc->Activate();
1353   }
1354 }
1355
1356 #if wxUSE_GLCANVAS
1357 void
1358 ImageFileView::OnConvert3d (wxCommandEvent& event)
1359 {
1360   ImageFile& rIF = GetDocument()->getImageFile();
1361   Graph3dFileDocument* pGraph3d = theApp->newGraph3dDoc();
1362   pGraph3d->getView()->getFrame()->Show (false);
1363   pGraph3d->setBadFileOpen();
1364   pGraph3d->createFromImageFile (rIF);
1365   pGraph3d->UpdateAllViews();
1366   pGraph3d->getView()->getFrame()->Show (true);
1367   pGraph3d->getView()->Activate(true);
1368   ::wxYield();
1369   pGraph3d->getView()->getCanvas()->SetFocus();
1370 }
1371 #endif
1372
1373 void
1374 ImageFileView::OnPlotRow (wxCommandEvent& event)
1375 {
1376   int xCursor, yCursor;
1377   if (! m_pCanvas->GetCurrentCursor (xCursor, yCursor)) {
1378     wxMessageBox (_T("No row selected. Please use left mouse button on image to select column"),_T("Error"));
1379     return;
1380   }
1381
1382   const ImageFile& rIF = GetDocument()->getImageFile();
1383   ImageFileArrayConst v = rIF.getArray();
1384   ImageFileArrayConst vImag = rIF.getImaginaryArray();
1385   int nx = rIF.nx();
1386   int ny = rIF.ny();
1387
1388   if (v != NULL && yCursor < ny) {
1389     double* pX = new double [nx];
1390     double* pYReal = new double [nx];
1391     double *pYImag = NULL;
1392     double *pYMag = NULL;
1393     if (rIF.isComplex()) {
1394       pYImag = new double [nx];
1395       pYMag = new double [nx];
1396     }
1397     for (int i = 0; i < nx; i++) {
1398       pX[i] = i;
1399       pYReal[i] = v[i][yCursor];
1400       if (rIF.isComplex()) {
1401         pYImag[i] = vImag[i][yCursor];
1402         pYMag[i] = ::sqrt (v[i][yCursor] * v[i][yCursor] + vImag[i][yCursor] * vImag[i][yCursor]);
1403       }
1404     }
1405     PlotFileDocument* pPlotDoc = theApp->newPlotDoc();
1406     if (! pPlotDoc) {
1407       sys_error (ERR_SEVERE, "Internal error: unable to create Plot file");
1408     } else {
1409       PlotFile& rPlotFile = pPlotDoc->getPlotFile();
1410       std::ostringstream os;
1411       os << "Row " << yCursor;
1412       std::string title("title ");
1413       title += os.str();
1414       rPlotFile.addEzsetCommand (title.c_str());
1415       rPlotFile.addEzsetCommand ("xlabel Column");
1416       rPlotFile.addEzsetCommand ("ylabel Pixel Value");
1417       rPlotFile.addEzsetCommand ("lxfrac 0");
1418       rPlotFile.addEzsetCommand ("box");
1419       rPlotFile.addEzsetCommand ("grid");
1420       rPlotFile.addEzsetCommand ("curve 1");
1421       rPlotFile.addEzsetCommand ("color 1");
1422       if (rIF.isComplex()) {
1423         rPlotFile.addEzsetCommand ("dash 1");
1424         rPlotFile.addEzsetCommand ("curve 2");
1425         rPlotFile.addEzsetCommand ("color 4");
1426         rPlotFile.addEzsetCommand ("dash 3");
1427         rPlotFile.addEzsetCommand ("curve 3");
1428         rPlotFile.addEzsetCommand ("color 0");
1429         rPlotFile.addEzsetCommand ("solid");
1430         rPlotFile.setCurveSize (4, nx);
1431       } else
1432         rPlotFile.setCurveSize (2, nx);
1433       rPlotFile.addColumn (0, pX);
1434       rPlotFile.addColumn (1, pYReal);
1435       if (rIF.isComplex()) {
1436         rPlotFile.addColumn (2, pYImag);
1437         rPlotFile.addColumn (3, pYMag);
1438       }
1439       for (unsigned int iL = 0; iL < rIF.nLabels(); iL++)
1440         rPlotFile.addDescription (rIF.labelGet(iL).getLabelString().c_str());
1441       os << ": plot of " << wxConvUTF8.cWX2MB(dynamic_cast<wxFrame*>(GetDocument()->GetFirstView()->GetFrame())->GetTitle().c_str());
1442       *theApp->getLog() << wxConvUTF8.cMB2WX(os.str().c_str()) << _T("\n");
1443       rPlotFile.addDescription (os.str().c_str());
1444     }
1445     delete pX;
1446     delete pYReal;
1447     if (rIF.isComplex()) {
1448       delete pYImag;
1449       delete pYMag;
1450     }
1451     if (theApp->getAskDeleteNewDocs())
1452       pPlotDoc->Modify (true);
1453     pPlotDoc->getView()->getFrame()->Show(true);
1454     pPlotDoc->UpdateAllViews ();
1455     pPlotDoc->Activate();
1456   }
1457 }
1458
1459 void
1460 ImageFileView::OnPlotCol (wxCommandEvent& event)
1461 {
1462   int xCursor, yCursor;
1463   if (! m_pCanvas->GetCurrentCursor (xCursor, yCursor)) {
1464     wxMessageBox (_T("No column selected. Please use left mouse button on image to select column"),_T("Error"));
1465     return;
1466   }
1467
1468   const ImageFile& rIF = GetDocument()->getImageFile();
1469   ImageFileArrayConst v = rIF.getArray();
1470   ImageFileArrayConst vImag = rIF.getImaginaryArray();
1471   int nx = rIF.nx();
1472   int ny = rIF.ny();
1473
1474   if (v != NULL && xCursor < nx) {
1475     double* const pX = new double [ny];
1476     double* const pYReal = new double [ny];
1477     double* pYImag = NULL;
1478     double* pYMag = NULL;
1479     if (rIF.isComplex()) {
1480       pYImag = new double [ny];
1481       pYMag = new double [ny];
1482     }
1483     for (int i = 0; i < ny; i++) {
1484       pX[i] = i;
1485       pYReal[i] = v[xCursor][i];
1486       if (rIF.isComplex()) {
1487         pYImag[i] = vImag[xCursor][i];
1488         pYMag[i] = ::sqrt (v[xCursor][i] * v[xCursor][i] + vImag[xCursor][i] * vImag[xCursor][i]);
1489       }
1490     }
1491     PlotFileDocument* pPlotDoc = theApp->newPlotDoc();
1492     if (! pPlotDoc) {
1493       sys_error (ERR_SEVERE, "Internal error: unable to create Plot file");
1494     } else {
1495       PlotFile& rPlotFile = pPlotDoc->getPlotFile();
1496       std::ostringstream os;
1497       os << "Column " << xCursor;
1498       std::string title("title ");
1499       title += os.str();
1500       rPlotFile.addEzsetCommand (title.c_str());
1501       rPlotFile.addEzsetCommand ("xlabel Row");
1502       rPlotFile.addEzsetCommand ("ylabel Pixel Value");
1503       rPlotFile.addEzsetCommand ("lxfrac 0");
1504       rPlotFile.addEzsetCommand ("box");
1505       rPlotFile.addEzsetCommand ("grid");
1506       rPlotFile.addEzsetCommand ("curve 1");
1507       rPlotFile.addEzsetCommand ("color 1");
1508       if (rIF.isComplex()) {
1509         rPlotFile.addEzsetCommand ("dash 1");
1510         rPlotFile.addEzsetCommand ("curve 2");
1511         rPlotFile.addEzsetCommand ("color 4");
1512         rPlotFile.addEzsetCommand ("dash 3");
1513         rPlotFile.addEzsetCommand ("curve 3");
1514         rPlotFile.addEzsetCommand ("color 0");
1515         rPlotFile.addEzsetCommand ("solid");
1516         rPlotFile.setCurveSize (4, ny);
1517       } else
1518         rPlotFile.setCurveSize (2, ny);
1519       rPlotFile.addColumn (0, pX);
1520       rPlotFile.addColumn (1, pYReal);
1521       if (rIF.isComplex()) {
1522         rPlotFile.addColumn (2, pYImag);
1523         rPlotFile.addColumn (3, pYMag);
1524       }
1525       for (unsigned int iL = 0; iL < rIF.nLabels(); iL++)
1526         rPlotFile.addDescription (rIF.labelGet(iL).getLabelString().c_str());
1527       os << " : plot of " << wxConvUTF8.cWX2MB(dynamic_cast<wxFrame*>(GetDocument()->GetFirstView()->GetFrame())->GetTitle().c_str());
1528       *theApp->getLog() << wxConvUTF8.cMB2WX(os.str().c_str()) << _T("\n");
1529       rPlotFile.addDescription (os.str().c_str());
1530     }
1531     delete pX;
1532     delete pYReal;
1533     if (rIF.isComplex()) {
1534       delete pYImag;
1535       delete pYMag;
1536     }
1537     if (theApp->getAskDeleteNewDocs())
1538       pPlotDoc->Modify (true);
1539     pPlotDoc->getView()->getFrame()->Show(true);
1540     pPlotDoc->UpdateAllViews ();
1541     pPlotDoc->Activate();
1542   }
1543 }
1544
1545 #ifdef HAVE_FFT
1546 void
1547 ImageFileView::OnPlotFFTRow (wxCommandEvent& event)
1548 {
1549   int xCursor, yCursor;
1550   if (! m_pCanvas->GetCurrentCursor (xCursor, yCursor)) {
1551     wxMessageBox (_T("No row selected. Please use left mouse button on image to select column"),_T("Error"));
1552     return;
1553   }
1554
1555   const ImageFile& rIF = GetDocument()->getImageFile();
1556   ImageFileArrayConst v = rIF.getArray();
1557   ImageFileArrayConst vImag = rIF.getImaginaryArray();
1558   int nx = rIF.nx();
1559   int ny = rIF.ny();
1560
1561   if (v != NULL && yCursor < ny) {
1562     fftw_complex* pcIn = static_cast<fftw_complex*>(fftw_malloc (sizeof(fftw_complex) * nx));
1563
1564     int i;
1565     for (i = 0; i < nx; i++) {
1566       pcIn[i][0] = v[i][yCursor];
1567       if (rIF.isComplex())
1568         pcIn[i][1] = vImag[i][yCursor];
1569       else
1570         pcIn[i][1] = 0;
1571     }
1572
1573     fftw_plan plan = fftw_plan_dft_1d (nx, pcIn, pcIn, FFTW_FORWARD, FFTW_ESTIMATE);
1574     fftw_execute (plan);
1575     fftw_destroy_plan (plan);
1576
1577     double* pX = new double [nx];
1578     double* pYReal = new double [nx];
1579     double* pYImag = new double [nx];
1580     double* pYMag = new double [nx];
1581     for (i = 0; i < nx; i++) {
1582       pX[i] = i;
1583       pYReal[i] = pcIn[i][0] / nx;
1584       pYImag[i] = pcIn[i][1] / nx;
1585       pYMag[i] = ::sqrt (pcIn[i][0] * pcIn[i][0] + pcIn[i][1] * pcIn[i][1]);
1586     }
1587     Fourier::shuffleFourierToNaturalOrder (pYReal, nx);
1588     Fourier::shuffleFourierToNaturalOrder (pYImag, nx);
1589     Fourier::shuffleFourierToNaturalOrder (pYMag, nx);
1590
1591     PlotFileDocument* pPlotDoc = theApp->newPlotDoc();
1592     if (! pPlotDoc) {
1593       sys_error (ERR_SEVERE, "Internal error: unable to create Plot file");
1594     } else {
1595       PlotFile& rPlotFile = pPlotDoc->getPlotFile();
1596       std::ostringstream os;
1597       os << "Row " << yCursor;
1598       std::string title("title ");
1599       title += os.str();
1600       rPlotFile.addEzsetCommand (title.c_str());
1601       rPlotFile.addEzsetCommand ("xlabel Column");
1602       rPlotFile.addEzsetCommand ("ylabel Pixel Value");
1603       rPlotFile.addEzsetCommand ("lxfrac 0");
1604       rPlotFile.addEzsetCommand ("curve 1");
1605       rPlotFile.addEzsetCommand ("color 1");
1606       rPlotFile.addEzsetCommand ("dash 1");
1607       rPlotFile.addEzsetCommand ("curve 2");
1608       rPlotFile.addEzsetCommand ("color 4");
1609       rPlotFile.addEzsetCommand ("dash 3");
1610       rPlotFile.addEzsetCommand ("curve 3");
1611       rPlotFile.addEzsetCommand ("color 0");
1612       rPlotFile.addEzsetCommand ("solid");
1613       rPlotFile.addEzsetCommand ("box");
1614       rPlotFile.addEzsetCommand ("grid");
1615       rPlotFile.setCurveSize (4, nx);
1616       rPlotFile.addColumn (0, pX);
1617       rPlotFile.addColumn (1, pYReal);
1618       rPlotFile.addColumn (2, pYImag);
1619       rPlotFile.addColumn (3, pYMag);
1620       for (unsigned int iL = 0; iL < rIF.nLabels(); iL++)
1621         rPlotFile.addDescription (rIF.labelGet(iL).getLabelString().c_str());
1622       os << ": FFT plot of " << wxConvUTF8.cWX2MB(dynamic_cast<wxFrame*>(GetDocument()->GetFirstView()->GetFrame())->GetTitle().c_str());
1623       *theApp->getLog() << wxConvUTF8.cMB2WX(os.str().c_str()) << _T("\n");
1624       rPlotFile.addDescription (os.str().c_str());
1625     }
1626     delete pX;
1627     delete pYReal;
1628     delete pYImag;
1629     delete pYMag;
1630     fftw_free(pcIn);
1631
1632     if (theApp->getAskDeleteNewDocs())
1633       pPlotDoc->Modify (true);
1634     pPlotDoc->getView()->getFrame()->Show(true);
1635     pPlotDoc->UpdateAllViews ();
1636     pPlotDoc->Activate();
1637   }
1638 }
1639
1640 void
1641 ImageFileView::OnPlotFFTCol (wxCommandEvent& event)
1642 {
1643   int xCursor, yCursor;
1644   if (! m_pCanvas->GetCurrentCursor (xCursor, yCursor)) {
1645     wxMessageBox (_T("No column selected. Please use left mouse button on image to select column"),_T("Error"));
1646     return;
1647   }
1648
1649   const ImageFile& rIF = GetDocument()->getImageFile();
1650   ImageFileArrayConst v = rIF.getArray();
1651   ImageFileArrayConst vImag = rIF.getImaginaryArray();
1652   int nx = rIF.nx();
1653   int ny = rIF.ny();
1654
1655   if (v != NULL && xCursor < nx) {
1656     fftw_complex* pcIn = new fftw_complex [ny];
1657     double *pdTemp = new double [ny];
1658
1659     int i;
1660     for (i = 0; i < ny; i++)
1661       pdTemp[i] = v[xCursor][i];
1662     Fourier::shuffleNaturalToFourierOrder (pdTemp, ny);
1663     for (i = 0; i < ny; i++)
1664       pcIn[i][0] = pdTemp[i];
1665
1666     for (i = 0; i < ny; i++) {
1667       if (rIF.isComplex())
1668         pdTemp[i] = vImag[xCursor][i];
1669       else
1670         pdTemp[i] = 0;
1671     }
1672     Fourier::shuffleNaturalToFourierOrder (pdTemp, ny);
1673     for (i = 0; i < ny; i++)
1674       pcIn[i][1] = pdTemp[i];
1675
1676     fftw_plan plan = fftw_plan_dft_1d (ny, pcIn, pcIn, FFTW_BACKWARD, FFTW_ESTIMATE);
1677     fftw_execute (plan);
1678     fftw_destroy_plan (plan);
1679
1680     double* pX = new double [ny];
1681     double* pYReal = new double [ny];
1682     double* pYImag = new double [ny];
1683     double* pYMag = new double [ny];
1684     for (i = 0; i < ny; i++) {
1685       pX[i] = i;
1686       pYReal[i] = pcIn[i][0] / ny;
1687       pYImag[i] = pcIn[i][1] / ny;
1688       pYMag[i] = ::sqrt (pcIn[i][0] * pcIn[i][0] + pcIn[i][1] * pcIn[i][1]);
1689     }
1690
1691     PlotFileDocument* pPlotDoc = theApp->newPlotDoc();
1692     if (! pPlotDoc) {
1693       sys_error (ERR_SEVERE, "Internal error: unable to create Plot file");
1694     } else {
1695       PlotFile& rPlotFile = pPlotDoc->getPlotFile();
1696       std::ostringstream os;
1697       os << "Column " << xCursor;
1698       std::string title("title ");
1699       title += os.str();
1700       rPlotFile.addEzsetCommand (title.c_str());
1701       rPlotFile.addEzsetCommand ("xlabel Column");
1702       rPlotFile.addEzsetCommand ("ylabel Pixel Value");
1703       rPlotFile.addEzsetCommand ("lxfrac 0");
1704       rPlotFile.addEzsetCommand ("curve 1");
1705       rPlotFile.addEzsetCommand ("color 1");
1706       rPlotFile.addEzsetCommand ("dash 1");
1707       rPlotFile.addEzsetCommand ("curve 2");
1708       rPlotFile.addEzsetCommand ("color 4");
1709       rPlotFile.addEzsetCommand ("dash 3");
1710       rPlotFile.addEzsetCommand ("curve 3");
1711       rPlotFile.addEzsetCommand ("color 0");
1712       rPlotFile.addEzsetCommand ("solid");
1713       rPlotFile.addEzsetCommand ("box");
1714       rPlotFile.addEzsetCommand ("grid");
1715       rPlotFile.setCurveSize (4, ny);
1716       rPlotFile.addColumn (0, pX);
1717       rPlotFile.addColumn (1, pYReal);
1718       rPlotFile.addColumn (2, pYImag);
1719       rPlotFile.addColumn (3, pYMag);
1720       for (unsigned int iL = 0; iL < rIF.nLabels(); iL++)
1721         rPlotFile.addDescription (rIF.labelGet(iL).getLabelString().c_str());
1722       os << ": FFT plot of " << wxConvUTF8.cWX2MB(dynamic_cast<wxFrame*>(GetDocument()->GetFirstView()->GetFrame())->GetTitle().c_str());
1723       *theApp->getLog() << wxConvUTF8.cMB2WX(os.str().c_str()) << _T("\n");
1724       rPlotFile.addDescription (os.str().c_str());
1725     }
1726     delete pX;
1727     delete pYReal;
1728     delete pYImag;
1729     delete pYMag;
1730     delete pdTemp;
1731     delete [] pcIn;
1732
1733     if (theApp->getAskDeleteNewDocs())
1734       pPlotDoc->Modify (true);
1735     pPlotDoc->getView()->getFrame()->Show(true);
1736     pPlotDoc->UpdateAllViews ();
1737     pPlotDoc->Activate();
1738   }
1739 }
1740 #endif
1741
1742 void
1743 ImageFileView::OnCompareCol (wxCommandEvent& event)
1744 {
1745   int xCursor, yCursor;
1746   if (! m_pCanvas->GetCurrentCursor (xCursor, yCursor)) {
1747     wxMessageBox (_T("No column selected. Please use left mouse button on image to select column"),_T("Error"));
1748     return;
1749   }
1750
1751   std::vector<ImageFileDocument*> vecIFDoc;
1752   theApp->getCompatibleImages (GetDocument(), vecIFDoc);
1753   if (vecIFDoc.size() == 0) {
1754     wxMessageBox (_T("No compatible images for Column Comparison"), _T("Error"));
1755     return;
1756   }
1757   DialogGetComparisonImage dialogGetCompare (getFrameForChild(), _T("Get Comparison Image"), vecIFDoc, false);
1758
1759   if (dialogGetCompare.ShowModal() == wxID_OK) {
1760     ImageFileDocument* pCompareDoc = dialogGetCompare.getImageFileDocument();
1761     const ImageFile& rIF = GetDocument()->getImageFile();
1762     const ImageFile& rCompareIF = pCompareDoc->getImageFile();
1763
1764     ImageFileArrayConst v1 = rIF.getArray();
1765     ImageFileArrayConst v2 = rCompareIF.getArray();
1766     int nx = rIF.nx();
1767     int ny = rIF.ny();
1768
1769     if (v1 != NULL && xCursor < nx) {
1770       double* pX = new double [ny];
1771       double* pY1 = new double [ny];
1772       double* pY2 = new double [ny];
1773       for (int i = 0; i < ny; i++) {
1774         pX[i] = i;
1775         pY1[i] = v1[xCursor][i];
1776         pY2[i] = v2[xCursor][i];
1777       }
1778       PlotFileDocument* pPlotDoc = theApp->newPlotDoc();
1779       if (! pPlotDoc) {
1780         sys_error (ERR_SEVERE, "Internal error: unable to create Plot file");
1781       } else {
1782         PlotFile& rPlotFile = pPlotDoc->getPlotFile();
1783         std::ostringstream os;
1784         os << "Column " << xCursor << ": Comparison";
1785         std::string title("title ");
1786         title += os.str();
1787         rPlotFile.addEzsetCommand (title.c_str());
1788         rPlotFile.addEzsetCommand ("xlabel Row");
1789         rPlotFile.addEzsetCommand ("ylabel Pixel Value");
1790         rPlotFile.addEzsetCommand ("lxfrac 0");
1791         rPlotFile.addEzsetCommand ("curve 1");
1792         rPlotFile.addEzsetCommand ("color 2");
1793         rPlotFile.addEzsetCommand ("curve 2");
1794         rPlotFile.addEzsetCommand ("color 4");
1795         rPlotFile.addEzsetCommand ("dash 5");
1796         rPlotFile.addEzsetCommand ("box");
1797         rPlotFile.addEzsetCommand ("grid");
1798         rPlotFile.setCurveSize (3, ny);
1799         rPlotFile.addColumn (0, pX);
1800         rPlotFile.addColumn (1, pY1);
1801         rPlotFile.addColumn (2, pY2);
1802
1803         unsigned int iL;
1804         for (iL = 0; iL < rIF.nLabels(); iL++) {
1805           std::ostringstream os;
1806           os << dynamic_cast<wxFrame*>(GetDocument()->GetFirstView()->GetFrame())->GetTitle().c_str();
1807           os << ": " << rIF.labelGet(iL).getLabelString();
1808           rPlotFile.addDescription (os.str().c_str());
1809         }
1810         for (iL = 0; iL < rCompareIF.nLabels(); iL++) {
1811           std::ostringstream os;
1812           os << dynamic_cast<wxFrame*>(pCompareDoc->GetFirstView()->GetFrame())->GetTitle().mb_str(wxConvUTF8);
1813           os << ": ";
1814           os << rCompareIF.labelGet(iL).getLabelString();
1815           rPlotFile.addDescription (os.str().c_str());
1816         }
1817         os << " between " << dynamic_cast<wxFrame*>(GetDocument()->GetFirstView()->GetFrame())->GetTitle().mb_str(wxConvUTF8) << " and "
1818            << dynamic_cast<wxFrame*>(pCompareDoc->GetFirstView()->GetFrame())->GetTitle().mb_str(wxConvUTF8);
1819         *theApp->getLog() << wxConvUTF8.cMB2WX(os.str().c_str()) << _T("\n");
1820         rPlotFile.addDescription (os.str().c_str());
1821       }
1822       delete pX;
1823       delete pY1;
1824       delete pY2;
1825       if (theApp->getAskDeleteNewDocs())
1826         pPlotDoc->Modify (true);
1827       pPlotDoc->getView()->getFrame()->Show(true);
1828       pPlotDoc->UpdateAllViews ();
1829       pPlotDoc->Activate();
1830     }
1831   }
1832 }
1833
1834 void
1835 ImageFileView::OnCompareRow (wxCommandEvent& event)
1836 {
1837   int xCursor, yCursor;
1838   if (! m_pCanvas->GetCurrentCursor (xCursor, yCursor)) {
1839     wxMessageBox (_T("No column selected. Please use left mouse button on image to select column"),_T("Error"));
1840     return;
1841   }
1842
1843   std::vector<ImageFileDocument*> vecIFDoc;
1844   theApp->getCompatibleImages (GetDocument(), vecIFDoc);
1845
1846   if (vecIFDoc.size() == 0) {
1847     wxMessageBox (_T("No compatible images for Row Comparison"), _T("Error"));
1848     return;
1849   }
1850
1851   DialogGetComparisonImage dialogGetCompare (getFrameForChild(), _T("Get Comparison Image"), vecIFDoc, false);
1852
1853   if (dialogGetCompare.ShowModal() == wxID_OK) {
1854     ImageFileDocument* pCompareDoc = dialogGetCompare.getImageFileDocument();
1855     const ImageFile& rIF = GetDocument()->getImageFile();
1856     const ImageFile& rCompareIF = pCompareDoc->getImageFile();
1857
1858     ImageFileArrayConst v1 = rIF.getArray();
1859     ImageFileArrayConst v2 = rCompareIF.getArray();
1860     int nx = rIF.nx();
1861     int ny = rIF.ny();
1862
1863     if (v1 != NULL && yCursor < ny) {
1864       double* pX = new double [nx];
1865       double* pY1 = new double [nx];
1866       double* pY2 = new double [nx];
1867       for (int i = 0; i < nx; i++) {
1868         pX[i] = i;
1869         pY1[i] = v1[i][yCursor];
1870         pY2[i] = v2[i][yCursor];
1871       }
1872       PlotFileDocument* pPlotDoc = theApp->newPlotDoc();
1873       if (! pPlotDoc) {
1874         sys_error (ERR_SEVERE, "Internal error: unable to create Plot file");
1875       } else {
1876         PlotFile& rPlotFile = pPlotDoc->getPlotFile();
1877         std::ostringstream os;
1878         os << "Row " << yCursor << ": Comparison";
1879         std::string title("title ");
1880         title += os.str();
1881         rPlotFile.addEzsetCommand (title.c_str());
1882         rPlotFile.addEzsetCommand ("xlabel Column");
1883         rPlotFile.addEzsetCommand ("ylabel Pixel Value");
1884         rPlotFile.addEzsetCommand ("lxfrac 0");
1885         rPlotFile.addEzsetCommand ("curve 1");
1886         rPlotFile.addEzsetCommand ("color 2");
1887         rPlotFile.addEzsetCommand ("curve 2");
1888         rPlotFile.addEzsetCommand ("color 4");
1889         rPlotFile.addEzsetCommand ("dash 5");
1890         rPlotFile.addEzsetCommand ("box");
1891         rPlotFile.addEzsetCommand ("grid");
1892         rPlotFile.setCurveSize (3, nx);
1893         rPlotFile.addColumn (0, pX);
1894         rPlotFile.addColumn (1, pY1);
1895         rPlotFile.addColumn (2, pY2);
1896         unsigned int iL;
1897         for (iL = 0; iL < rIF.nLabels(); iL++) {
1898           std::ostringstream os;
1899           os << dynamic_cast<wxFrame*>(GetDocument()->GetFirstView()->GetFrame())->GetTitle().mb_str(wxConvUTF8);
1900           os << ": ";
1901           os << rIF.labelGet(iL).getLabelString();
1902           rPlotFile.addDescription (os.str().c_str());
1903         }
1904         for (iL = 0; iL < rCompareIF.nLabels(); iL++) {
1905           std::ostringstream os;
1906           os << dynamic_cast<wxFrame*>(pCompareDoc->GetFirstView()->GetFrame())->GetTitle().mb_str(wxConvUTF8) << ": "
1907              << rCompareIF.labelGet(iL).getLabelString();
1908           rPlotFile.addDescription (os.str().c_str());
1909         }
1910         os << " between " << dynamic_cast<wxFrame*>(GetDocument()->GetFirstView()->GetFrame())->GetTitle().mb_str(wxConvUTF8) << " and "
1911            << dynamic_cast<wxFrame*>(pCompareDoc->GetFirstView()->GetFrame())->GetTitle().mb_str(wxConvUTF8);
1912         *theApp->getLog() << wxConvUTF8.cMB2WX(os.str().c_str()) << _T("\n");
1913         rPlotFile.addDescription (os.str().c_str());
1914       }
1915       delete pX;
1916       delete pY1;
1917       delete pY2;
1918       if (theApp->getAskDeleteNewDocs())
1919         pPlotDoc->Modify (true);
1920       pPlotDoc->getView()->getFrame()->Show(true);
1921       pPlotDoc->UpdateAllViews ();
1922       pPlotDoc->Activate();
1923     }
1924   }
1925 }
1926
1927 static int NUMBER_HISTOGRAM_BINS = 256;
1928
1929 void
1930 ImageFileView::OnPlotHistogram (wxCommandEvent& event)
1931 {
1932   const ImageFile& rIF = GetDocument()->getImageFile();
1933   ImageFileArrayConst v = rIF.getArray();
1934   int nx = rIF.nx();
1935   int ny = rIF.ny();
1936
1937   if (v != NULL && nx > 0 && ny > 0) {
1938     PlotFileDocument* pPlotDoc = theApp->newPlotDoc();
1939     if (! pPlotDoc) {
1940       sys_error (ERR_SEVERE, "Internal error: unable to create Plot file");
1941       return;
1942     }
1943
1944     double* pX = new double [NUMBER_HISTOGRAM_BINS];
1945     double* pY = new double [NUMBER_HISTOGRAM_BINS];
1946     double dMin, dMax;
1947     rIF.getMinMax (dMin, dMax);
1948     double dBinWidth = (dMax - dMin) / NUMBER_HISTOGRAM_BINS;
1949
1950     for (int i = 0; i < NUMBER_HISTOGRAM_BINS; i++) {
1951       pX[i] = dMin + (i + 0.5) * dBinWidth;
1952       pY[i] = 0;
1953     }
1954     for (int ix = 0; ix < nx; ix++) {
1955       for (int iy = 0; iy < ny; iy++) {
1956         int iBin = nearest<int> ((v[ix][iy] - dMin) / dBinWidth);
1957         if (iBin >= 0 && iBin < NUMBER_HISTOGRAM_BINS)
1958           pY[iBin] += 1;
1959       }
1960     }
1961     PlotFile& rPlotFile = pPlotDoc->getPlotFile();
1962     std::ostringstream os;
1963     os << "Histogram";
1964     std::string title("title ");
1965     title += os.str();
1966     rPlotFile.addEzsetCommand (title.c_str());
1967     rPlotFile.addEzsetCommand ("xlabel Pixel Value");
1968     rPlotFile.addEzsetCommand ("ylabel Count");
1969     rPlotFile.addEzsetCommand ("box");
1970     rPlotFile.addEzsetCommand ("grid");
1971     rPlotFile.setCurveSize (2, NUMBER_HISTOGRAM_BINS);
1972     rPlotFile.addColumn (0, pX);
1973     rPlotFile.addColumn (1, pY);
1974     for (unsigned int iL = 0; iL < rIF.nLabels(); iL++) {
1975       std::ostringstream os;
1976       os << dynamic_cast<wxFrame*>(GetDocument()->GetFirstView()->GetFrame())->GetTitle().mb_str(wxConvUTF8);
1977       os << ": " << rIF.labelGet(iL).getLabelString();
1978       rPlotFile.addDescription (os.str().c_str());
1979     }
1980     os << " plot of " << wxConvUTF8.cWX2MB(dynamic_cast<wxFrame*>(GetDocument()->GetFirstView()->GetFrame())->GetTitle().c_str());
1981     *theApp->getLog() << wxConvUTF8.cMB2WX(os.str().c_str()) << _T("\n");
1982     rPlotFile.addDescription (os.str().c_str());
1983     delete pX;
1984     delete pY;
1985     if (theApp->getAskDeleteNewDocs())
1986       pPlotDoc->Modify (true);
1987     pPlotDoc->getView()->getFrame()->Show(true);
1988     pPlotDoc->UpdateAllViews ();
1989     pPlotDoc->Activate();
1990   }
1991 }
1992
1993
1994 // PhantomCanvas
1995
1996 PhantomCanvas::PhantomCanvas (PhantomFileView* v, wxFrame *frame, const wxPoint& pos, const wxSize& size, const long style)
1997   : wxScrolledWindow(frame, -1, pos, size, style), m_pView(v)
1998 {
1999 }
2000
2001 PhantomCanvas::~PhantomCanvas ()
2002 {
2003   m_pView = NULL;
2004 }
2005
2006 void
2007 PhantomCanvas::OnDraw (wxDC& dc)
2008 {
2009   if (m_pView)
2010     m_pView->OnDraw(& dc);
2011 }
2012
2013 wxSize
2014 PhantomCanvas::GetBestSize() const
2015 {
2016   if (! m_pView)
2017     return wxSize(0,0);
2018
2019   int xSize, ySize;
2020   theApp->getMainFrame()->GetClientSize (&xSize, &ySize);
2021   xSize = maxValue<int> (xSize, ySize);
2022 #ifdef CTSIM_MDI
2023   ySize = xSize = (xSize / 4);
2024 #else
2025   xSize = ySize = static_cast<int>(ySize * .7);
2026 #endif
2027
2028   return wxSize (xSize, ySize);
2029 }
2030
2031
2032
2033 // PhantomFileView
2034
2035 IMPLEMENT_DYNAMIC_CLASS(PhantomFileView, wxView)
2036
2037 BEGIN_EVENT_TABLE(PhantomFileView, wxView)
2038 EVT_MENU(PHMMENU_FILE_PROPERTIES, PhantomFileView::OnProperties)
2039 EVT_MENU(PHMMENU_PROCESS_RASTERIZE, PhantomFileView::OnRasterize)
2040 EVT_MENU(PHMMENU_PROCESS_PROJECTIONS, PhantomFileView::OnProjections)
2041 END_EVENT_TABLE()
2042
2043 PhantomFileView::PhantomFileView()
2044 : wxView(), m_pFrame(NULL), m_pCanvas(NULL), m_pFileMenu(0)
2045 {
2046 #if defined(DEBUG) || defined(_DEBUG)
2047   m_iDefaultNDet = 165;
2048   m_iDefaultNView = 180;
2049   m_iDefaultNSample = 1;
2050 #else
2051   m_iDefaultNDet = 367;
2052   m_iDefaultNView = 320;
2053   m_iDefaultNSample = 2;
2054 #endif
2055   m_iDefaultOffsetView = 0;
2056   m_dDefaultRotation = 1;
2057   m_dDefaultFocalLength = 2;
2058   m_dDefaultCenterDetectorLength = 2;
2059   m_dDefaultViewRatio = 1;
2060   m_dDefaultScanRatio = 1;
2061   m_iDefaultGeometry = Scanner::GEOMETRY_PARALLEL;
2062   m_iDefaultTrace = Trace::TRACE_NONE;
2063
2064 #ifdef DEBUG
2065   m_iDefaultRasterNX = 115;
2066   m_iDefaultRasterNY = 115;
2067   m_iDefaultRasterNSamples = 1;
2068 #else
2069   m_iDefaultRasterNX = 256;
2070   m_iDefaultRasterNY = 256;
2071   m_iDefaultRasterNSamples = 2;
2072 #endif
2073   m_dDefaultRasterViewRatio = 1;
2074 }
2075
2076 PhantomFileView::~PhantomFileView()
2077 {
2078   GetDocumentManager()->FileHistoryRemoveMenu (m_pFileMenu);
2079   GetDocumentManager()->ActivateView(this, FALSE);
2080 }
2081
2082 void
2083 PhantomFileView::OnProperties (wxCommandEvent& event)
2084 {
2085   const int idPhantom = GetDocument()->getPhantomID();
2086   const wxString& namePhantom = GetDocument()->getPhantomName();
2087   std::ostringstream os;
2088   os << "Phantom " << namePhantom.c_str() << " (" << idPhantom << ")" << "\n";
2089   const Phantom& rPhantom = GetDocument()->getPhantom();
2090   rPhantom.printDefinitions (os);
2091 #if DEBUG
2092   rPhantom.print (os);
2093 #endif
2094   *theApp->getLog() << _T(">>>>\n") << wxConvUTF8.cMB2WX(os.str().c_str()) << _T("<<<<\n");
2095   wxMessageBox (wxConvUTF8.cMB2WX(os.str().c_str()), _T("Phantom Properties"));
2096   GetDocument()->Activate();
2097 }
2098
2099
2100 void
2101 PhantomFileView::OnProjections (wxCommandEvent& event)
2102 {
2103   DialogGetProjectionParameters dialogProjection (getFrameForChild(),
2104     m_iDefaultNDet, m_iDefaultNView, m_iDefaultOffsetView, m_iDefaultNSample, m_dDefaultRotation,
2105     m_dDefaultFocalLength, m_dDefaultCenterDetectorLength, m_dDefaultViewRatio, m_dDefaultScanRatio,
2106     m_iDefaultGeometry, m_iDefaultTrace);
2107   int retVal = dialogProjection.ShowModal();
2108   if (retVal != wxID_OK)
2109     return;
2110
2111   m_iDefaultNDet = dialogProjection.getNDet();
2112   m_iDefaultNView = dialogProjection.getNView();
2113   m_iDefaultOffsetView = dialogProjection.getOffsetView();
2114   m_iDefaultNSample = dialogProjection.getNSamples();
2115   m_iDefaultTrace = dialogProjection.getTrace();
2116   m_dDefaultRotation = dialogProjection.getRotAngle();
2117   m_dDefaultFocalLength = dialogProjection.getFocalLengthRatio();
2118   m_dDefaultCenterDetectorLength = dialogProjection.getCenterDetectorLengthRatio();
2119   m_dDefaultViewRatio = dialogProjection.getViewRatio();
2120   m_dDefaultScanRatio = dialogProjection.getScanRatio();
2121   wxString sGeometry (dialogProjection.getGeometry(), wxConvUTF8);
2122   m_iDefaultGeometry = Scanner::convertGeometryNameToID (sGeometry.mb_str(wxConvUTF8));
2123   double dRotationRadians = m_dDefaultRotation;
2124   m_dDefaultRotation /= TWOPI;  // convert back to fraction of a circle
2125
2126   if (m_iDefaultNDet <= 0 || m_iDefaultNView <= 0 || sGeometry == _T(""))
2127     return;
2128
2129   const Phantom& rPhantom = GetDocument()->getPhantom();
2130   Scanner theScanner (rPhantom, sGeometry.mb_str(wxConvUTF8), m_iDefaultNDet, m_iDefaultNView, m_iDefaultOffsetView, m_iDefaultNSample,
2131     dRotationRadians, m_dDefaultFocalLength, m_dDefaultCenterDetectorLength, m_dDefaultViewRatio, m_dDefaultScanRatio);
2132   if (theScanner.fail()) {
2133     wxString msg = _T("Failed making scanner\n");
2134     msg += wxConvUTF8.cMB2WX(theScanner.failMessage().c_str());
2135     *theApp->getLog() << msg << _T("\n");
2136     wxMessageBox (msg, _T("Error"));
2137     return;
2138   }
2139
2140   std::ostringstream os;
2141   os << "Projections for " << rPhantom.name().c_str()
2142      << ": nDet=" << m_iDefaultNDet
2143      << ", nView=" << m_iDefaultNView
2144      << ", gantry offset=" << m_iDefaultOffsetView
2145      << ", nSamples=" << m_iDefaultNSample
2146      << ", RotAngle=" << m_dDefaultRotation
2147      << ", FocalLengthRatio=" << m_dDefaultFocalLength
2148      << ", CenterDetectorLengthRatio=" << m_dDefaultCenterDetectorLength
2149      << ", ViewRatio=" << m_dDefaultViewRatio
2150      << ", ScanRatio=" << m_dDefaultScanRatio
2151      << ", Geometry=" << sGeometry.mb_str(wxConvUTF8)
2152      << ", FanBeamAngle=" << convertRadiansToDegrees (theScanner.fanBeamAngle());
2153
2154   Timer timer;
2155   Projections* pProj = NULL;
2156   if (m_iDefaultTrace > Trace::TRACE_CONSOLE) {
2157     pProj = new Projections;
2158     pProj->initFromScanner (theScanner);
2159
2160     ProjectionsDialog dialogProjections (theScanner, *pProj, rPhantom, m_iDefaultTrace, dynamic_cast<wxWindow*>(getFrameForChild()));
2161     for (int iView = 0; iView < pProj->nView(); iView++) {
2162       ::wxYield();
2163       if (dialogProjections.isCancelled() || ! dialogProjections.projectView (iView)) {
2164         delete pProj;
2165         return;
2166       }
2167       ::wxYield();
2168       while (dialogProjections.isPaused()) {
2169         ::wxYield();
2170         ::wxMilliSleep(50);
2171       }
2172     }
2173   } else {
2174 #if HAVE_WXTHREADS
2175     if (theApp->getUseBackgroundTasks()) {
2176       ProjectorSupervisorThread* pProjector = new ProjectorSupervisorThread
2177         (this, m_iDefaultNDet, m_iDefaultNView, m_iDefaultOffsetView, 
2178          sGeometry.mb_str(wxConvUTF8), m_iDefaultNSample, dRotationRadians,
2179          m_dDefaultFocalLength, m_dDefaultCenterDetectorLength, m_dDefaultViewRatio, 
2180          m_dDefaultScanRatio, wxConvUTF8.cMB2WX(os.str().c_str()));
2181       if (pProjector->Create() != wxTHREAD_NO_ERROR) {
2182         sys_error (ERR_SEVERE, "Error creating projector thread");
2183         delete pProjector;
2184         return;
2185       }
2186       pProjector->SetPriority(60);
2187       pProjector->Run();
2188       return;
2189     } else
2190 #endif // HAVE_WXTHREADS
2191     {
2192       pProj = new Projections;
2193       pProj->initFromScanner (theScanner);
2194       wxProgressDialog dlgProgress (_T("Projection"), _T("Projection Progress"), pProj->nView() + 1, getFrameForChild(), wxPD_CAN_ABORT );
2195       for (int i = 0; i < pProj->nView(); i++) {
2196         //theScanner.collectProjections (*pProj, rPhantom, i, 1, true, m_iDefaultTrace);
2197         theScanner.collectProjections (*pProj, rPhantom, i, 1, theScanner.offsetView(), true, m_iDefaultTrace);
2198         if ((i + 1) % ITER_PER_UPDATE == 0)
2199           if (! dlgProgress.Update (i+1)) {
2200             delete pProj;
2201             return;
2202           }
2203       }
2204     }
2205   }
2206
2207   *theApp->getLog() << wxConvUTF8.cMB2WX(os.str().c_str()) << _T("\n");
2208   pProj->setRemark (os.str());
2209   pProj->setCalcTime (timer.timerEnd());
2210
2211   ProjectionFileDocument* pProjectionDoc = theApp->newProjectionDoc();
2212   if (! pProjectionDoc) {
2213     sys_error (ERR_SEVERE, "Unable to create projection document");
2214     return;
2215   }
2216   pProjectionDoc->setProjections (pProj);
2217   if (theApp->getAskDeleteNewDocs())
2218     pProjectionDoc-> Modify(true);
2219   OnUpdate(this, NULL);
2220   pProjectionDoc->UpdateAllViews (this);
2221   pProjectionDoc->getView()->setInitialClientSize();
2222   pProjectionDoc->Activate();
2223 }
2224 void
2225 PhantomFileView::OnRasterize (wxCommandEvent& event)
2226 {
2227   DialogGetRasterParameters dialogRaster (getFrameForChild(), m_iDefaultRasterNX, m_iDefaultRasterNY,
2228     m_iDefaultRasterNSamples, m_dDefaultRasterViewRatio);
2229   int retVal = dialogRaster.ShowModal();
2230   if (retVal != wxID_OK)
2231     return;
2232
2233   m_iDefaultRasterNX = dialogRaster.getXSize();
2234   m_iDefaultRasterNY  = dialogRaster.getYSize();
2235   m_iDefaultRasterNSamples = dialogRaster.getNSamples();
2236   m_dDefaultRasterViewRatio = dialogRaster.getViewRatio();
2237   if (m_iDefaultRasterNSamples < 1)
2238     m_iDefaultRasterNSamples = 1;
2239   if (m_dDefaultRasterViewRatio < 0)
2240     m_dDefaultRasterViewRatio = 0;
2241   if (m_iDefaultRasterNX <= 0 || m_iDefaultRasterNY <= 0)
2242     return;
2243
2244   const Phantom& rPhantom = GetDocument()->getPhantom();
2245   std::ostringstream os;
2246   os << "Rasterize Phantom " << rPhantom.name() << ": XSize=" << m_iDefaultRasterNX << ", YSize="
2247     << m_iDefaultRasterNY << ", ViewRatio=" << m_dDefaultRasterViewRatio << ", nSamples="
2248     << m_iDefaultRasterNSamples;;
2249
2250 #if HAVE_WXTHREADS
2251   if (theApp->getUseBackgroundTasks()) {
2252     RasterizerSupervisorThread* pThread = new RasterizerSupervisorThread
2253       (this, m_iDefaultRasterNX, m_iDefaultRasterNY,
2254        m_iDefaultRasterNSamples, m_dDefaultRasterViewRatio, 
2255        wxConvUTF8.cMB2WX(os.str().c_str()));
2256     if (pThread->Create() != wxTHREAD_NO_ERROR) {
2257       *theApp->getLog() << _T("Error creating rasterizer thread\n");
2258       return;
2259     }
2260     pThread->SetPriority (60);
2261     pThread->Run();
2262   } else
2263 #endif
2264   {
2265     ImageFile* pImageFile = new ImageFile (m_iDefaultRasterNX, m_iDefaultRasterNY);
2266
2267     wxProgressDialog dlgProgress (_T("Rasterize"),
2268                                   _T("Rasterization Progress"),
2269                                   pImageFile->nx() + 1,
2270                                   getFrameForChild(),
2271                                   wxPD_CAN_ABORT );
2272     Timer timer;
2273     for (unsigned int i = 0; i < pImageFile->nx(); i++) {
2274       rPhantom.convertToImagefile (*pImageFile, m_dDefaultRasterViewRatio,
2275                                    m_iDefaultRasterNSamples, Trace::TRACE_NONE,
2276                                    i, 1, true);
2277       if ((i + 1) % ITER_PER_UPDATE == 0)
2278         if (! dlgProgress.Update (i+1)) {
2279           delete pImageFile;
2280           return;
2281         }
2282     }
2283
2284     ImageFileDocument* pRasterDoc = theApp->newImageDoc();
2285     if (! pRasterDoc) {
2286       sys_error (ERR_SEVERE, "Unable to create image file");
2287       return;
2288     }
2289     pRasterDoc->setImageFile (pImageFile);
2290     if (theApp->getAskDeleteNewDocs())
2291       pRasterDoc->Modify (true);
2292     *theApp->getLog() << wxConvUTF8.cMB2WX(os.str().c_str()) << _T("\n");
2293     pImageFile->labelAdd (os.str().c_str(), timer.timerEnd());
2294
2295     pRasterDoc->UpdateAllViews(this);
2296     pRasterDoc->getView()->setInitialClientSize();
2297     pRasterDoc->Activate();
2298   }
2299 }
2300
2301
2302 PhantomCanvas*
2303 PhantomFileView::CreateCanvas (wxFrame *parent)
2304 {
2305   PhantomCanvas* pCanvas =
2306     new PhantomCanvas (this, parent, wxPoint(-1,-1),
2307                        wxSize(-1,-1), wxFULL_REPAINT_ON_RESIZE);
2308   pCanvas->SetBackgroundColour(*wxWHITE);
2309   pCanvas->ClearBackground();
2310   return pCanvas;
2311 }
2312
2313 #if CTSIM_MDI
2314 wxDocMDIChildFrame*
2315 #else
2316 wxDocChildFrame*
2317 #endif
2318 PhantomFileView::CreateChildFrame(wxDocument *doc, wxView *view)
2319 {
2320 #if CTSIM_MDI
2321   wxDocMDIChildFrame *subframe = new wxDocMDIChildFrame (doc, view, theApp->getMainFrame(), -1, _T("Phantom Frame"), wxPoint(-1,-1), wxSize(-1,-1), wxDEFAULT_FRAME_STYLE);
2322 #else
2323   wxDocChildFrame *subframe = new wxDocChildFrame (doc, view, theApp->getMainFrame(), -1, _T("Phantom Frame"), wxPoint(-1,-1), wxSize(-1,-1), wxDEFAULT_FRAME_STYLE);
2324 #endif
2325   theApp->setIconForFrame (subframe);
2326
2327   m_pFileMenu = new wxMenu;
2328
2329   m_pFileMenu->Append(MAINMENU_FILE_CREATE_PHANTOM, _T("Cr&eate Phantom...\tCtrl-P"));
2330   m_pFileMenu->Append(MAINMENU_FILE_CREATE_FILTER, _T("Create &Filter...\tCtrl-F"));
2331   m_pFileMenu->Append(wxID_OPEN, _T("&Open...\tCtrl-O"));
2332   m_pFileMenu->Append(wxID_SAVEAS, _T("Save &As..."));
2333   m_pFileMenu->Append(wxID_CLOSE, _T("&Close"));
2334
2335   m_pFileMenu->AppendSeparator();
2336   m_pFileMenu->Append(PHMMENU_FILE_PROPERTIES, _T("P&roperties\tCtrl-I"));
2337
2338   m_pFileMenu->AppendSeparator();
2339   m_pFileMenu->Append(wxID_PRINT, _T("&Print..."));
2340   m_pFileMenu->Append(wxID_PRINT_SETUP, _T("Print &Setup..."));
2341   m_pFileMenu->Append(wxID_PREVIEW, _T("Print Pre&view"));
2342   m_pFileMenu->AppendSeparator();
2343   m_pFileMenu->Append(MAINMENU_IMPORT, _T("&Import...\tCtrl-M"));
2344   m_pFileMenu->AppendSeparator();
2345   m_pFileMenu->Append (MAINMENU_FILE_PREFERENCES, _T("Prefere&nces..."));
2346   m_pFileMenu->Append(MAINMENU_FILE_EXIT, _T("E&xit"));
2347   GetDocumentManager()->FileHistoryAddFilesToMenu(m_pFileMenu);
2348   GetDocumentManager()->FileHistoryUseMenu(m_pFileMenu);
2349
2350   wxMenu *process_menu = new wxMenu;
2351   process_menu->Append(PHMMENU_PROCESS_RASTERIZE, _T("&Rasterize...\tCtrl-R"));
2352   process_menu->Append(PHMMENU_PROCESS_PROJECTIONS, _T("&Projections...\tCtrl-J"));
2353
2354   wxMenu *help_menu = new wxMenu;
2355   help_menu->Append(MAINMENU_HELP_CONTENTS, _T("&Contents\tF1"));
2356   help_menu->Append (MAINMENU_HELP_TIPS, _T("&Tips"));
2357   help_menu->Append (IDH_QUICKSTART, _T("&Quick Start"));
2358   help_menu->Append(MAINMENU_HELP_ABOUT, _T("&About"));
2359
2360   wxMenuBar *menu_bar = new wxMenuBar;
2361
2362   menu_bar->Append(m_pFileMenu, _T("&File"));
2363   menu_bar->Append(process_menu, _T("&Process"));
2364   menu_bar->Append(help_menu, _T("&Help"));
2365
2366   subframe->SetMenuBar(menu_bar);
2367   subframe->Centre(wxBOTH);
2368
2369   wxAcceleratorEntry accelEntries[3];
2370   accelEntries[0].Set (wxACCEL_CTRL, static_cast<int>('J'), PHMMENU_PROCESS_PROJECTIONS);
2371   accelEntries[1].Set (wxACCEL_CTRL, static_cast<int>('R'), PHMMENU_PROCESS_RASTERIZE);
2372   accelEntries[2].Set (wxACCEL_CTRL, static_cast<int>('I'), PHMMENU_FILE_PROPERTIES);
2373   wxAcceleratorTable accelTable (3, accelEntries);
2374   subframe->SetAcceleratorTable (accelTable);
2375
2376   return subframe;
2377 }
2378
2379
2380 bool
2381 PhantomFileView::OnCreate(wxDocument *doc, long WXUNUSED(flags) )
2382 {
2383   m_pFrame = CreateChildFrame(doc, this);
2384   SetFrame(m_pFrame);
2385   m_pCanvas = CreateCanvas (m_pFrame);
2386   m_pFrame->SetClientSize (m_pCanvas->GetBestSize());
2387   m_pCanvas->SetClientSize (m_pCanvas->GetBestSize());
2388   m_pFrame->SetTitle (doc->GetFilename());
2389
2390   m_pFrame->Show(true);
2391   Activate(true);
2392
2393   return true;
2394 }
2395
2396 void
2397 PhantomFileView::OnUpdate (wxView *WXUNUSED(sender), wxObject *WXUNUSED(hint) )
2398 {
2399   if (m_pCanvas)
2400     m_pCanvas->Refresh();
2401 }
2402
2403 bool
2404 PhantomFileView::OnClose (bool deleteWindow)
2405 {
2406   if (! GetDocument() || ! GetDocument()->Close())
2407     return false;
2408
2409   Activate(false);
2410   if (m_pCanvas) {
2411     m_pCanvas->setView(NULL);
2412     m_pCanvas = NULL;
2413   }
2414   wxString s(wxTheApp->GetAppName());
2415   if (m_pFrame)
2416     m_pFrame->SetTitle(s);
2417
2418   SetFrame(NULL);
2419
2420   if (deleteWindow) {
2421     delete m_pFrame;
2422     m_pFrame = NULL;
2423     if (GetDocument() && GetDocument()->getBadFileOpen())
2424       ::wxYield();  // wxWindows bug workaround
2425   }
2426
2427   return true;
2428 }
2429
2430 void
2431 PhantomFileView::OnDraw (wxDC* dc)
2432 {
2433   int xsize, ysize;
2434   m_pCanvas->GetClientSize (&xsize, &ysize);
2435   SGPDriver driver (dc, xsize, ysize);
2436   SGP sgp (driver);
2437   const Phantom& rPhantom = GetDocument()->getPhantom();
2438   sgp.setColor (C_RED);
2439   rPhantom.show (sgp);
2440 }
2441
2442 // ProjectionCanvas
2443
2444 ProjectionFileCanvas::ProjectionFileCanvas (ProjectionFileView* v, wxFrame *frame, const wxPoint& pos, const wxSize& size, const long style)
2445 : wxScrolledWindow(frame, -1, pos, size, style)
2446 {
2447   m_pView = v;
2448 }
2449
2450 ProjectionFileCanvas::~ProjectionFileCanvas ()
2451 {
2452   m_pView = NULL;
2453 }
2454
2455 void
2456 ProjectionFileCanvas::OnDraw(wxDC& dc)
2457 {
2458   if (m_pView)
2459     m_pView->OnDraw(& dc);
2460 }
2461
2462 wxSize
2463 ProjectionFileCanvas::GetBestSize () const
2464 {
2465   const int iMinX = 50;
2466   const int iMinY = 20;
2467   wxSize bestSize (iMinX,iMinY);
2468
2469   if (m_pView) {
2470     Projections& rProj = m_pView->GetDocument()->getProjections();
2471     bestSize.Set (rProj.nDet(), rProj.nView());
2472   }
2473
2474   if (bestSize.x > 800)
2475     bestSize.x = 800;
2476   if (bestSize.y > 800)
2477     bestSize.y = 800;
2478
2479   if (bestSize.x < iMinX)
2480     bestSize.x = iMinX;
2481   if (bestSize.y < iMinY)
2482     bestSize.y = iMinY;
2483
2484   return bestSize;
2485 }
2486
2487
2488 // ProjectionFileView
2489
2490 IMPLEMENT_DYNAMIC_CLASS(ProjectionFileView, wxView)
2491
2492 BEGIN_EVENT_TABLE(ProjectionFileView, wxView)
2493 EVT_MENU(PJMENU_FILE_PROPERTIES, ProjectionFileView::OnProperties)
2494 EVT_MENU(PJMENU_RECONSTRUCT_FBP, ProjectionFileView::OnReconstructFBP)
2495 EVT_MENU(PJMENU_RECONSTRUCT_FBP_REBIN, ProjectionFileView::OnReconstructFBPRebin)
2496 EVT_MENU(PJMENU_RECONSTRUCT_FOURIER, ProjectionFileView::OnReconstructFourier)
2497 EVT_MENU(PJMENU_CONVERT_RECTANGULAR, ProjectionFileView::OnConvertRectangular)
2498 EVT_MENU(PJMENU_CONVERT_POLAR, ProjectionFileView::OnConvertPolar)
2499 EVT_MENU(PJMENU_CONVERT_FFT_POLAR, ProjectionFileView::OnConvertFFTPolar)
2500 EVT_MENU(PJMENU_CONVERT_PARALLEL, ProjectionFileView::OnConvertParallel)
2501 EVT_MENU(PJMENU_PLOT_TTHETA_SAMPLING, ProjectionFileView::OnPlotTThetaSampling)
2502 EVT_MENU(PJMENU_PLOT_HISTOGRAM, ProjectionFileView::OnPlotHistogram)
2503   // EVT_MENU(PJMENU_ARTIFACT_REDUCTION, ProjectionFileView::OnArtifactReduction)
2504 END_EVENT_TABLE()
2505
2506
2507 ProjectionFileView::ProjectionFileView()
2508   : wxView(), m_pBitmap(0), m_pFrame(0), m_pCanvas(0), m_pFileMenu(0)
2509 {
2510 #ifdef DEBUG
2511   m_iDefaultNX = 115;
2512   m_iDefaultNY = 115;
2513 #else
2514   m_iDefaultNX = 256;
2515   m_iDefaultNY = 256;
2516 #endif
2517
2518   m_iDefaultFilter = SignalFilter::FILTER_ABS_BANDLIMIT;
2519   m_dDefaultFilterParam = 1.;
2520 #if HAVE_FFTW
2521   m_iDefaultFilterMethod = ProcessSignal::FILTER_METHOD_RFFTW;
2522   m_iDefaultFilterGeneration = ProcessSignal::FILTER_GENERATION_INVERSE_FOURIER;
2523 #else
2524   m_iDefaultFilterMethod = ProcessSignal::FILTER_METHOD_CONVOLUTION;
2525   m_iDefaultFilterGeneration = ProcessSignal::FILTER_GENERATION_DIRECT;
2526 #endif
2527   m_iDefaultZeropad = 2;
2528   m_iDefaultBackprojector = Backprojector::BPROJ_IDIFF;
2529   m_iDefaultInterpolation = Backprojector::INTERP_LINEAR;
2530   m_iDefaultInterpParam = 1;
2531   m_iDefaultTrace = Trace::TRACE_NONE;
2532
2533   m_iDefaultPolarNX = 256;
2534   m_iDefaultPolarNY = 256;
2535   m_iDefaultPolarInterpolation = Projections::POLAR_INTERP_BILINEAR;
2536   m_iDefaultPolarZeropad = 2;
2537 }
2538
2539 ProjectionFileView::~ProjectionFileView()
2540 {
2541   GetDocumentManager()->FileHistoryRemoveMenu (m_pFileMenu);
2542   GetDocumentManager()->ActivateView(this, FALSE);;
2543 }
2544
2545 void
2546 ProjectionFileView::OnProperties (wxCommandEvent& event)
2547 {
2548   const Projections& rProj = GetDocument()->getProjections();
2549   std::ostringstream os;
2550   rProj.printScanInfo(os);
2551   *theApp->getLog() << _T(">>>>\n") << wxConvUTF8.cMB2WX(os.str().c_str()) << _T("<<<<\n");
2552   wxMessageDialog dialogMsg (getFrameForChild(), wxConvUTF8.cMB2WX(os.str().c_str()), _T("Projection File Properties"), wxOK | wxICON_INFORMATION);
2553   dialogMsg.ShowModal();
2554   GetDocument()->Activate();
2555 }
2556
2557
2558 void
2559 ProjectionFileView::OnConvertRectangular (wxCommandEvent& event)
2560 {
2561   Projections& rProj = GetDocument()->getProjections();
2562
2563   int nDet = rProj.nDet();
2564   int nView = rProj.nView();
2565   ImageFile* pIF = new ImageFile (nDet, nView);
2566   ImageFileArray v = pIF->getArray();
2567   for (int iv = 0; iv < nView; iv++) {
2568     DetectorValue* detval = rProj.getDetectorArray(iv).detValues();
2569
2570     for (int id = 0; id < nDet; id++)
2571       v[id][iv] = detval[id];
2572   }
2573
2574   ImageFileDocument* pRectDoc = theApp->newImageDoc ();
2575   if (! pRectDoc) {
2576     sys_error (ERR_SEVERE, "Unable to create image file");
2577     return;
2578   }
2579   pRectDoc->setImageFile (pIF);
2580   pIF->labelAdd (rProj.getLabel().getLabelString().c_str(), rProj.calcTime());
2581   std::ostringstream os;
2582   os << "Convert projection file " << getFrame()->GetTitle().c_str() << " to rectangular image";
2583   *theApp->getLog() << wxConvUTF8.cMB2WX(os.str().c_str()) << _T("\n");
2584   pIF->labelAdd (os.str().c_str());
2585   if (theApp->getAskDeleteNewDocs())
2586     pRectDoc->Modify (true);
2587   pRectDoc->UpdateAllViews();
2588   pRectDoc->getView()->setInitialClientSize();
2589   pRectDoc->Activate();
2590 }
2591
2592 void
2593 ProjectionFileView::OnConvertPolar (wxCommandEvent& event)
2594 {
2595   Projections& rProj = GetDocument()->getProjections();
2596   DialogGetConvertPolarParameters dialogPolar (getFrameForChild(), _T("Convert Polar"), m_iDefaultPolarNX, m_iDefaultPolarNY,
2597     m_iDefaultPolarInterpolation, -1, IDH_DLG_POLAR);
2598   if (dialogPolar.ShowModal() == wxID_OK) {
2599     wxProgressDialog dlgProgress (_T("Convert Polar"), _T("Conversion Progress"), 1, getFrameForChild(), wxPD_APP_MODAL);
2600     wxString strInterpolation (dialogPolar.getInterpolationName(), wxConvUTF8);
2601     m_iDefaultPolarNX = dialogPolar.getXSize();
2602     m_iDefaultPolarNY = dialogPolar.getYSize();
2603     ImageFile* pIF = new ImageFile (m_iDefaultPolarNX, m_iDefaultPolarNY);
2604     m_iDefaultPolarInterpolation = Projections::convertInterpNameToID (strInterpolation.mb_str(wxConvUTF8));
2605
2606     if (! rProj.convertPolar (*pIF, m_iDefaultPolarInterpolation)) {
2607       delete pIF;
2608       *theApp->getLog() << _T("Error converting to Polar\n");
2609       return;
2610     }
2611
2612     ImageFileDocument* pPolarDoc = theApp->newImageDoc();
2613     if (! pPolarDoc) {
2614       sys_error (ERR_SEVERE, "Unable to create image file");
2615       return;
2616     }
2617     pPolarDoc->setImageFile (pIF);
2618     pIF->labelAdd (rProj.getLabel().getLabelString().c_str(), rProj.calcTime());
2619     std::ostringstream os;
2620     os << "Convert projection file " << getFrame()->GetTitle().c_str() << " to polar image: xSize="
2621       << m_iDefaultPolarNX << ", ySize=" << m_iDefaultPolarNY << ", interpolation="
2622       << strInterpolation.c_str();
2623     *theApp->getLog() << wxConvUTF8.cMB2WX(os.str().c_str()) << _T("\n");
2624     pIF->labelAdd (os.str().c_str());
2625     if (theApp->getAskDeleteNewDocs())
2626       pPolarDoc->Modify (true);
2627     pPolarDoc->UpdateAllViews ();
2628     pPolarDoc->getView()->setInitialClientSize();
2629     pPolarDoc->Activate();
2630   }
2631 }
2632
2633 void
2634 ProjectionFileView::OnConvertFFTPolar (wxCommandEvent& event)
2635 {
2636   Projections& rProj = GetDocument()->getProjections();
2637   DialogGetConvertPolarParameters dialogPolar (getFrameForChild(), _T("Convert to FFT Polar"), m_iDefaultPolarNX, m_iDefaultPolarNY,
2638     m_iDefaultPolarInterpolation, m_iDefaultPolarZeropad, IDH_DLG_FFT_POLAR);
2639   if (dialogPolar.ShowModal() == wxID_OK) {
2640     wxProgressDialog dlgProgress (_T("Convert FFT Polar"), _T("Conversion Progress"), 1, getFrameForChild(), wxPD_APP_MODAL);
2641     wxString strInterpolation (dialogPolar.getInterpolationName(), wxConvUTF8);
2642     m_iDefaultPolarNX = dialogPolar.getXSize();
2643     m_iDefaultPolarNY = dialogPolar.getYSize();
2644     m_iDefaultPolarZeropad = dialogPolar.getZeropad();
2645     ImageFile* pIF = new ImageFile (m_iDefaultPolarNX, m_iDefaultPolarNY);
2646
2647     m_iDefaultPolarInterpolation = Projections::convertInterpNameToID (strInterpolation.mb_str(wxConvUTF8));
2648     if (! rProj.convertFFTPolar (*pIF, m_iDefaultPolarInterpolation, m_iDefaultPolarZeropad)) {
2649       delete pIF;
2650       *theApp->getLog() << _T("Error converting to polar\n");
2651       return;
2652     }
2653     ImageFileDocument* pPolarDoc = theApp->newImageDoc();
2654     if (! pPolarDoc) {
2655       sys_error (ERR_SEVERE, "Unable to create image file");
2656       return;
2657     }
2658     pPolarDoc->setImageFile (pIF);
2659     pIF->labelAdd (rProj.getLabel().getLabelString().c_str(), rProj.calcTime());
2660     std::ostringstream os;
2661     os << "Convert projection file " << getFrame()->GetTitle().c_str() << " to FFT polar image: xSize="
2662       << m_iDefaultPolarNX << ", ySize=" << m_iDefaultPolarNY << ", interpolation="
2663       << strInterpolation.c_str() << ", zeropad=" << m_iDefaultPolarZeropad;
2664     *theApp->getLog() << wxConvUTF8.cMB2WX(os.str().c_str()) << _T("\n");
2665     pIF->labelAdd (os.str().c_str());
2666     if (theApp->getAskDeleteNewDocs())
2667       pPolarDoc->Modify (true);
2668     pPolarDoc->UpdateAllViews (this);
2669     pPolarDoc->getView()->setInitialClientSize();
2670     pPolarDoc->Activate();
2671   }
2672 }
2673
2674 void
2675 ProjectionFileView::OnPlotTThetaSampling (wxCommandEvent& event)
2676 {
2677   DialogGetThetaRange dlgTheta (this->getFrame(), ParallelRaysums::THETA_RANGE_UNCONSTRAINED);
2678   if (dlgTheta.ShowModal() != wxID_OK)
2679     return;
2680
2681   int iThetaRange = dlgTheta.getThetaRange();
2682
2683   Projections& rProj = GetDocument()->getProjections();
2684   ParallelRaysums parallel (&rProj, iThetaRange);
2685   PlotFileDocument* pPlotDoc = theApp->newPlotDoc();
2686   PlotFile& rPlot = pPlotDoc->getPlotFile();
2687   ParallelRaysums::CoordinateContainer& coordContainer = parallel.getCoordinates();
2688   double* pdT = new double [parallel.getNumCoordinates()];
2689   double* pdTheta = new double [parallel.getNumCoordinates()];
2690
2691   for (int i = 0; i < parallel.getNumCoordinates(); i++) {
2692     pdT[i] = coordContainer[i]->m_dT;
2693     pdTheta[i] = coordContainer[i]->m_dTheta;
2694   }
2695   rPlot.setCurveSize (2, parallel.getNumCoordinates(), true);
2696   rPlot.addEzsetCommand ("title T-Theta Sampling");
2697   rPlot.addEzsetCommand ("xlabel T");
2698   rPlot.addEzsetCommand ("ylabel Theta");
2699   rPlot.addEzsetCommand ("curve 1");
2700   if (rProj.nDet() < 50 && rProj.nView() < 50)
2701     rPlot.addEzsetCommand ("symbol 1"); // x symbol
2702   else
2703     rPlot.addEzsetCommand ("symbol 6"); // point symbol
2704   rPlot.addEzsetCommand ("noline");
2705   rPlot.addColumn (0, pdT);
2706   rPlot.addColumn (1, pdTheta);
2707   delete pdT;
2708   delete pdTheta;
2709   if (theApp->getAskDeleteNewDocs())
2710     pPlotDoc->Modify (true);
2711   pPlotDoc->getView()->getFrame()->Show(true);
2712   pPlotDoc->UpdateAllViews ();
2713   pPlotDoc->Activate();
2714 }
2715
2716
2717 void
2718 ProjectionFileView::OnPlotHistogram (wxCommandEvent& event)
2719 {
2720   Projections& rProj = GetDocument()->getProjections();
2721   int nDet = rProj.nDet();
2722   int nView = rProj.nView();
2723
2724   if (nDet < 1 || nView < 1)
2725     return;
2726
2727   PlotFileDocument* pPlotDoc = theApp->newPlotDoc();
2728   if (! pPlotDoc) {
2729     sys_error (ERR_SEVERE, "Internal error: unable to create Plot file");
2730     return;
2731   }
2732
2733   DetectorValue* pdDetval = rProj.getDetectorArray(0).detValues();
2734   double dMin = pdDetval[0], dMax = pdDetval[0];
2735
2736   for (int iv = 0; iv < nView; iv++) {
2737     pdDetval = rProj.getDetectorArray(iv).detValues();
2738     for (int id = 0; id < nDet; id++) {
2739       double dV = pdDetval[id];
2740       if (dV < dMin)
2741         dMin = dV;
2742       else if (dV > dMax)
2743         dMax = dV;
2744     }
2745   }
2746
2747   double* pX = new double [NUMBER_HISTOGRAM_BINS];
2748   double* pY = new double [NUMBER_HISTOGRAM_BINS];
2749   double dBinWidth = (dMax - dMin) / NUMBER_HISTOGRAM_BINS;
2750
2751   for (int i = 0; i < NUMBER_HISTOGRAM_BINS; i++) {
2752     pX[i] = dMin + (i + 0.5) * dBinWidth;
2753     pY[i] = 0;
2754   }
2755   for (int j = 0; j < nView; j++) {
2756     pdDetval = rProj.getDetectorArray(j).detValues();
2757     for (int id = 0; id < nDet; id++) {
2758       int iBin = nearest<int> ((pdDetval[id] - dMin) / dBinWidth);
2759       if (iBin >= 0 && iBin < NUMBER_HISTOGRAM_BINS)
2760         pY[iBin] += 1;
2761     }
2762   }
2763   PlotFile& rPlotFile = pPlotDoc->getPlotFile();
2764   std::ostringstream os;
2765   os << "Histogram";
2766   std::string title("title ");
2767   title += os.str();
2768   rPlotFile.addEzsetCommand (title.c_str());
2769   rPlotFile.addEzsetCommand ("xlabel Detector Value");
2770   rPlotFile.addEzsetCommand ("ylabel Count");
2771   rPlotFile.addEzsetCommand ("box");
2772   rPlotFile.addEzsetCommand ("grid");
2773   rPlotFile.setCurveSize (2, NUMBER_HISTOGRAM_BINS);
2774   rPlotFile.addColumn (0, pX);
2775   rPlotFile.addColumn (1, pY);
2776   rPlotFile.addDescription (rProj.remark());
2777   os << " plot of " << dynamic_cast<wxFrame*>(GetDocument()->GetFirstView()->GetFrame())->GetTitle().c_str();
2778   *theApp->getLog() << wxConvUTF8.cMB2WX(os.str().c_str()) << _T("\n");
2779   rPlotFile.addDescription (os.str().c_str());
2780   delete pX;
2781   delete pY;
2782   if (theApp->getAskDeleteNewDocs())
2783     pPlotDoc->Modify (true);
2784   pPlotDoc->getView()->getFrame()->Show(true);
2785   pPlotDoc->UpdateAllViews ();
2786   pPlotDoc->Activate();
2787 }
2788
2789
2790 void
2791 ProjectionFileView::OnConvertParallel (wxCommandEvent& event)
2792 {
2793   Projections& rProj = GetDocument()->getProjections();
2794   if (rProj.geometry() == Scanner::GEOMETRY_PARALLEL) {
2795     wxMessageBox (_T("Projections are already parallel"), _T("Error"));
2796     return;
2797   }
2798   wxProgressDialog dlgProgress (_T("Convert to Parallel"), _T("Conversion Progress"), 1, getFrameForChild(), wxPD_APP_MODAL);
2799   Projections* pProjNew = rProj.interpolateToParallel();
2800   ProjectionFileDocument* pProjDocNew = theApp->newProjectionDoc();
2801   pProjDocNew->setProjections (pProjNew);
2802
2803   if (ProjectionFileView* projView = pProjDocNew->getView()) {
2804     projView->OnUpdate (projView, NULL);
2805     if (projView->getCanvas())
2806       projView->getCanvas()->SetClientSize (pProjNew->nDet(), pProjNew->nView());
2807     if (wxFrame* pFrame = projView->getFrame()) {
2808       pFrame->Show(true);
2809       pFrame->SetFocus();
2810       pFrame->Raise();
2811     }
2812     GetDocumentManager()->ActivateView (projView, true);
2813   }
2814   if (theApp->getAskDeleteNewDocs())
2815     pProjDocNew-> Modify(true);
2816   pProjDocNew->UpdateAllViews (this);
2817   pProjDocNew->getView()->setInitialClientSize();
2818   pProjDocNew->Activate();
2819 }
2820
2821 void
2822 ProjectionFileView::OnReconstructFourier (wxCommandEvent& event)
2823 {
2824   Projections& rProj = GetDocument()->getProjections();
2825   DialogGetConvertPolarParameters dialogPolar (getFrameForChild(), _T("Fourier Reconstruction"), m_iDefaultPolarNX, m_iDefaultPolarNY,
2826     m_iDefaultPolarInterpolation, m_iDefaultPolarZeropad, IDH_DLG_RECON_FOURIER);
2827   if (dialogPolar.ShowModal() == wxID_OK) {
2828     wxProgressDialog dlgProgress (_T("Reconstruction Fourier"), _T("Reconstruction Progress"), 1, getFrameForChild(), wxPD_APP_MODAL);
2829     wxString strInterpolation (dialogPolar.getInterpolationName(), wxConvUTF8);
2830     m_iDefaultPolarNX = dialogPolar.getXSize();
2831     m_iDefaultPolarNY = dialogPolar.getYSize();
2832     m_iDefaultPolarZeropad = dialogPolar.getZeropad();
2833     ImageFile* pIF = new ImageFile (m_iDefaultPolarNX, m_iDefaultPolarNY);
2834
2835     m_iDefaultPolarInterpolation = Projections::convertInterpNameToID (strInterpolation.mb_str(wxConvUTF8));
2836     if (! rProj.convertFFTPolar (*pIF, m_iDefaultPolarInterpolation, m_iDefaultPolarZeropad)) {
2837       delete pIF;
2838       *theApp->getLog() << _T("Error converting to polar\n");
2839       return;
2840     }
2841 #ifdef HAVE_FFT
2842     pIF->ifft(*pIF);
2843 #endif
2844     pIF->magnitude(*pIF);
2845     Fourier::shuffleFourierToNaturalOrder (*pIF);
2846
2847     ImageFileDocument* pPolarDoc = theApp->newImageDoc();
2848     if (! pPolarDoc) {
2849       sys_error (ERR_SEVERE, "Unable to create image file");
2850       return;
2851     }
2852     pPolarDoc->setImageFile (pIF);
2853     pIF->labelAdd (rProj.getLabel().getLabelString().c_str(), rProj.calcTime());
2854     std::ostringstream os;
2855     os << "Reconstruct Fourier " << getFrame()->GetTitle().mb_str(wxConvUTF8) << ": xSize="
2856       << m_iDefaultPolarNX << ", ySize=" << m_iDefaultPolarNY << ", interpolation="
2857       << strInterpolation.mb_str(wxConvUTF8) << ", zeropad=" << m_iDefaultPolarZeropad;
2858     *theApp->getLog() << wxConvUTF8.cMB2WX(os.str().c_str()) << _T("\n");
2859     pIF->labelAdd (os.str().c_str());
2860     if (theApp->getAskDeleteNewDocs())
2861       pPolarDoc->Modify (true);
2862     pPolarDoc->UpdateAllViews ();
2863     pPolarDoc->getView()->setInitialClientSize();
2864     pPolarDoc->Activate();
2865   }
2866 }
2867
2868 void
2869 ProjectionFileView::OnReconstructFBPRebin (wxCommandEvent& event)
2870 {
2871   Projections& rProj = GetDocument()->getProjections();
2872   doReconstructFBP (rProj, true);
2873 }
2874
2875 void
2876 ProjectionFileView::OnReconstructFBP (wxCommandEvent& event)
2877 {
2878   Projections& rProj = GetDocument()->getProjections();
2879   doReconstructFBP (rProj, false);
2880 }
2881
2882 void
2883 ProjectionFileView::doReconstructFBP (const Projections& rProj, bool bRebinToParallel)
2884 {
2885   ReconstructionROI defaultROI;
2886   defaultROI.m_dXMin = -rProj.phmLen() / 2;
2887   defaultROI.m_dXMax = defaultROI.m_dXMin + rProj.phmLen();
2888   defaultROI.m_dYMin = -rProj.phmLen() / 2;
2889   defaultROI.m_dYMax = defaultROI.m_dYMin + rProj.phmLen();
2890
2891   DialogGetReconstructionParameters dialogReconstruction (getFrameForChild(), m_iDefaultNX, m_iDefaultNY,
2892     m_iDefaultFilter, m_dDefaultFilterParam, m_iDefaultFilterMethod, m_iDefaultFilterGeneration,
2893     m_iDefaultZeropad, m_iDefaultInterpolation, m_iDefaultInterpParam, m_iDefaultBackprojector,
2894     m_iDefaultTrace,  &defaultROI);
2895
2896   int retVal = dialogReconstruction.ShowModal();
2897   if (retVal != wxID_OK)
2898     return;
2899
2900   m_iDefaultNX = dialogReconstruction.getXSize();
2901   m_iDefaultNY = dialogReconstruction.getYSize();
2902   wxString optFilterName (dialogReconstruction.getFilterName(), wxConvUTF8);
2903   m_iDefaultFilter = SignalFilter::convertFilterNameToID (optFilterName.mb_str(wxConvUTF8));
2904   m_dDefaultFilterParam = dialogReconstruction.getFilterParam();
2905   wxString optFilterMethodName (dialogReconstruction.getFilterMethodName(), wxConvUTF8);
2906   m_iDefaultFilterMethod = ProcessSignal::convertFilterMethodNameToID(optFilterMethodName.mb_str(wxConvUTF8));
2907   m_iDefaultZeropad = dialogReconstruction.getZeropad();
2908   wxString optFilterGenerationName (dialogReconstruction.getFilterGenerationName(), wxConvUTF8);
2909   m_iDefaultFilterGeneration = ProcessSignal::convertFilterGenerationNameToID (optFilterGenerationName.mb_str(wxConvUTF8));
2910   wxString optInterpName (dialogReconstruction.getInterpName(), wxConvUTF8);
2911   m_iDefaultInterpolation = Backprojector::convertInterpNameToID (optInterpName.mb_str(wxConvUTF8));
2912   m_iDefaultInterpParam = dialogReconstruction.getInterpParam();
2913   wxString optBackprojectName (dialogReconstruction.getBackprojectName(), wxConvUTF8);
2914   m_iDefaultBackprojector = Backprojector::convertBackprojectNameToID (optBackprojectName.mb_str(wxConvUTF8));
2915   m_iDefaultTrace = dialogReconstruction.getTrace();
2916   dialogReconstruction.getROI (&defaultROI);
2917
2918   if (m_iDefaultNX <= 0 && m_iDefaultNY <= 0)
2919     return;
2920
2921   std::ostringstream os;
2922   os << "Reconstruct " << rProj.getFilename() << ": xSize=" << m_iDefaultNX << ", ySize=" << m_iDefaultNY << ", Filter=" << optFilterName.mb_str(wxConvUTF8) << ", FilterParam=" << m_dDefaultFilterParam << ", FilterMethod=" << optFilterMethodName.mb_str(wxConvUTF8) << ", FilterGeneration=" << optFilterGenerationName.mb_str(wxConvUTF8) << ", Zeropad=" << m_iDefaultZeropad << ", Interpolation=" << optInterpName.mb_str(wxConvUTF8) << ", InterpolationParam=" << m_iDefaultInterpParam << ", Backprojection=" << optBackprojectName.mb_str(wxConvUTF8);
2923   if (bRebinToParallel)
2924     os << "; Interpolate to Parallel";
2925
2926   Timer timerRecon;
2927   ImageFile* pImageFile = NULL;
2928   if (m_iDefaultTrace > Trace::TRACE_CONSOLE) {
2929     pImageFile = new ImageFile (m_iDefaultNX, m_iDefaultNY);
2930     Reconstructor* pReconstructor = new Reconstructor (rProj, *pImageFile, optFilterName.mb_str(wxConvUTF8),
2931       m_dDefaultFilterParam, optFilterMethodName.mb_str(wxConvUTF8), m_iDefaultZeropad, optFilterGenerationName.mb_str(wxConvUTF8),
2932       optInterpName.mb_str(wxConvUTF8), m_iDefaultInterpParam, optBackprojectName.mb_str(wxConvUTF8), m_iDefaultTrace,
2933       &defaultROI, bRebinToParallel);
2934
2935     ReconstructDialog* pDlgReconstruct = new ReconstructDialog (*pReconstructor, rProj, *pImageFile, m_iDefaultTrace, getFrameForChild());
2936     for (int iView = 0; iView < rProj.nView(); iView++) {
2937       ::wxYield();
2938       if (pDlgReconstruct->isCancelled() || ! pDlgReconstruct->reconstructView (iView, true)) {
2939         delete pDlgReconstruct;
2940         delete pReconstructor;
2941         return;
2942       }
2943       ::wxYield();
2944       ::wxYield();
2945       while (pDlgReconstruct->isPaused()) {
2946         ::wxYield();
2947         ::wxMilliSleep(50);
2948       }
2949     }
2950     pReconstructor->postProcessing();
2951     delete pDlgReconstruct;
2952     delete pReconstructor;
2953   } else {
2954 #if HAVE_WXTHREADS
2955     if (theApp->getUseBackgroundTasks()) {
2956       ReconstructorSupervisorThread* pReconstructor = new ReconstructorSupervisorThread
2957         (this, m_iDefaultNX, m_iDefaultNY, optFilterName.mb_str(wxConvUTF8), 
2958          m_dDefaultFilterParam, optFilterMethodName.mb_str(wxConvUTF8),
2959          m_iDefaultZeropad, optFilterGenerationName.mb_str(wxConvUTF8), 
2960          optInterpName.mb_str(wxConvUTF8), m_iDefaultInterpParam,
2961          optBackprojectName.mb_str(wxConvUTF8), 
2962          wxConvUTF8.cMB2WX(os.str().c_str()), &defaultROI, bRebinToParallel);
2963       if (pReconstructor->Create() != wxTHREAD_NO_ERROR) {
2964         sys_error (ERR_SEVERE, "Error creating reconstructor thread");
2965         delete pReconstructor;
2966         return;
2967       }
2968       pReconstructor->SetPriority (60);
2969       pReconstructor->Run();
2970       return;
2971     } else
2972 #endif
2973     {
2974       pImageFile = new ImageFile (m_iDefaultNX, m_iDefaultNY);
2975       wxProgressDialog dlgProgress (_T("Reconstruction"), _T("Reconstruction Progress"), rProj.nView() + 1, getFrameForChild(), wxPD_CAN_ABORT );
2976       Reconstructor* pReconstructor = new Reconstructor (rProj, *pImageFile, optFilterName.mb_str(wxConvUTF8),
2977         m_dDefaultFilterParam, optFilterMethodName.mb_str(wxConvUTF8), m_iDefaultZeropad, optFilterGenerationName.mb_str(wxConvUTF8),
2978         optInterpName.mb_str(wxConvUTF8), m_iDefaultInterpParam, optBackprojectName.mb_str(wxConvUTF8), m_iDefaultTrace,
2979         &defaultROI, bRebinToParallel);
2980
2981       for (int iView = 0; iView < rProj.nView(); iView++) {
2982         pReconstructor->reconstructView (iView, 1);
2983         if ((iView + 1) % ITER_PER_UPDATE == 0)
2984           if (! dlgProgress.Update (iView + 1)) {
2985             delete pReconstructor;
2986             return; // don't make new window, thread will do this
2987           }
2988       }
2989       pReconstructor->postProcessing();
2990       delete pReconstructor;
2991     }
2992   }
2993   ImageFileDocument* pReconDoc = theApp->newImageDoc();
2994   if (! pReconDoc) {
2995     sys_error (ERR_SEVERE, "Unable to create image file");
2996     return;
2997   }
2998   *theApp->getLog() << wxConvUTF8.cMB2WX(os.str().c_str()) << _T("\n");
2999   pImageFile->labelAdd (rProj.getLabel());
3000   pImageFile->labelAdd (os.str().c_str(), timerRecon.timerEnd());
3001
3002   pReconDoc->setImageFile (pImageFile);
3003   if (theApp->getAskDeleteNewDocs())
3004     pReconDoc->Modify (true);
3005   pReconDoc->UpdateAllViews();
3006   pReconDoc->getView()->setInitialClientSize();
3007   pReconDoc->Activate();
3008 }
3009
3010
3011 void
3012 ProjectionFileView::OnArtifactReduction (wxCommandEvent& event)
3013 {
3014 }
3015
3016
3017 ProjectionFileCanvas*
3018 ProjectionFileView::CreateCanvas (wxFrame *parent)
3019 {
3020   ProjectionFileCanvas* pCanvas;
3021   int width, height;
3022   parent->GetClientSize(&width, &height);
3023
3024   pCanvas = new ProjectionFileCanvas (this, parent, wxPoint(-1,-1), wxSize(width, height), 0);
3025
3026   pCanvas->SetScrollbars(20, 20, 50, 50);
3027   pCanvas->SetBackgroundColour(*wxWHITE);
3028   pCanvas->ClearBackground();
3029
3030   return pCanvas;
3031 }
3032
3033 #if CTSIM_MDI
3034 wxDocMDIChildFrame*
3035 #else
3036 wxDocChildFrame*
3037 #endif
3038 ProjectionFileView::CreateChildFrame(wxDocument *doc, wxView *view)
3039 {
3040 #ifdef CTSIM_MDI
3041   wxDocMDIChildFrame *subframe = new wxDocMDIChildFrame (doc, view, theApp->getMainFrame(), -1, _T("Projection Frame"), wxPoint(-1,-1), wxSize(-1,-1), wxDEFAULT_FRAME_STYLE);
3042 #else
3043   wxDocChildFrame *subframe = new wxDocChildFrame (doc, view, theApp->getMainFrame(), -1, _T("Projection Frame"), wxPoint(-1,-1), wxSize(-1,-1), wxDEFAULT_FRAME_STYLE);
3044 #endif
3045   theApp->setIconForFrame (subframe);
3046
3047   m_pFileMenu = new wxMenu;
3048
3049   m_pFileMenu->Append(MAINMENU_FILE_CREATE_PHANTOM, _T("Cr&eate Phantom...\tCtrl-P"));
3050   m_pFileMenu->Append(MAINMENU_FILE_CREATE_FILTER, _T("Create &Filter...\tCtrl-F"));
3051   m_pFileMenu->Append(wxID_OPEN, _T("&Open...\tCtrl-O"));
3052   m_pFileMenu->Append(wxID_SAVE, _T("&Save\tCtrl-S"));
3053   m_pFileMenu->Append(wxID_SAVEAS, _T("Save &As..."));
3054   m_pFileMenu->Append(wxID_CLOSE, _T("&Close\tCtrl-W"));
3055
3056   m_pFileMenu->AppendSeparator();
3057   m_pFileMenu->Append(PJMENU_FILE_PROPERTIES, _T("P&roperties\tCtrl-I"));
3058
3059   m_pFileMenu->AppendSeparator();
3060   m_pFileMenu->Append(wxID_PRINT, _T("&Print..."));
3061   m_pFileMenu->Append(wxID_PRINT_SETUP, _T("Print &Setup..."));
3062   m_pFileMenu->Append(wxID_PREVIEW, _T("Print Pre&view"));
3063   m_pFileMenu->AppendSeparator();
3064   m_pFileMenu->Append(MAINMENU_IMPORT, _T("&Import...\tCtrl-M"));
3065   m_pFileMenu->AppendSeparator();
3066   m_pFileMenu->Append (MAINMENU_FILE_PREFERENCES, _T("Prefere&nces..."));
3067   m_pFileMenu->Append(MAINMENU_FILE_EXIT, _T("E&xit"));
3068   GetDocumentManager()->FileHistoryAddFilesToMenu(m_pFileMenu);
3069   GetDocumentManager()->FileHistoryUseMenu(m_pFileMenu);
3070
3071   m_pConvertMenu = new wxMenu;
3072   m_pConvertMenu->Append (PJMENU_CONVERT_RECTANGULAR, _T("&Rectangular Image"));
3073   m_pConvertMenu->Append (PJMENU_CONVERT_POLAR, _T("&Polar Image...\tCtrl-L"));
3074   m_pConvertMenu->Append (PJMENU_CONVERT_FFT_POLAR, _T("FF&T->Polar Image...\tCtrl-T"));
3075   m_pConvertMenu->AppendSeparator();
3076   m_pConvertMenu->Append (PJMENU_CONVERT_PARALLEL, _T("&Interpolate to Parallel"));
3077
3078   //  wxMenu* filter_menu = new wxMenu;
3079   //  filter_menu->Append (PJMENU_ARTIFACT_REDUCTION, _T("&Artifact Reduction"));
3080
3081   wxMenu* analyze_menu = new wxMenu;
3082   analyze_menu->Append (PJMENU_PLOT_HISTOGRAM, _T("&Plot Histogram"));
3083   analyze_menu->Append (PJMENU_PLOT_TTHETA_SAMPLING, _T("Plot T-T&heta Sampling...\tCtrl-H"));
3084
3085   m_pReconstructMenu = new wxMenu;
3086   m_pReconstructMenu->Append (PJMENU_RECONSTRUCT_FBP, _T("&Filtered Backprojection...\tCtrl-R"), _T("Reconstruct image using filtered backprojection"));
3087   m_pReconstructMenu->Append (PJMENU_RECONSTRUCT_FBP_REBIN, _T("Filtered &Backprojection (Rebin to Parallel)...\tCtrl-B"), _T("Reconstruct image using filtered backprojection"));
3088   m_pReconstructMenu->Append (PJMENU_RECONSTRUCT_FOURIER, _T("&Inverse Fourier...\tCtrl-E"), _T("Direct inverse Fourier"));
3089
3090   wxMenu *help_menu = new wxMenu;
3091   help_menu->Append(MAINMENU_HELP_CONTENTS, _T("&Contents\tF1"));
3092   help_menu->Append (MAINMENU_HELP_TIPS, _T("&Tips"));
3093   help_menu->Append (IDH_QUICKSTART, _T("&Quick Start"));
3094   help_menu->Append(MAINMENU_HELP_ABOUT, _T("&About"));
3095
3096   wxMenuBar *menu_bar = new wxMenuBar;
3097
3098   menu_bar->Append (m_pFileMenu, _T("&File"));
3099   menu_bar->Append (m_pConvertMenu, _T("&Convert"));
3100   //  menu_bar->Append (filter_menu, _T("Fi&lter"));
3101   menu_bar->Append (analyze_menu, _T("&Analyze"));
3102   menu_bar->Append (m_pReconstructMenu, _T("&Reconstruct"));
3103   menu_bar->Append (help_menu, _T("&Help"));
3104
3105   subframe->SetMenuBar(menu_bar);
3106   subframe->Centre(wxBOTH);
3107
3108   wxAcceleratorEntry accelEntries[7];
3109   accelEntries[0].Set (wxACCEL_CTRL, static_cast<int>('L'), PJMENU_CONVERT_POLAR);
3110   accelEntries[1].Set (wxACCEL_CTRL, static_cast<int>('T'), PJMENU_CONVERT_FFT_POLAR);
3111   accelEntries[2].Set (wxACCEL_CTRL, static_cast<int>('R'), PJMENU_RECONSTRUCT_FBP);
3112   accelEntries[3].Set (wxACCEL_CTRL, static_cast<int>('B'), PJMENU_RECONSTRUCT_FBP_REBIN);
3113   accelEntries[4].Set (wxACCEL_CTRL, static_cast<int>('E'), PJMENU_RECONSTRUCT_FOURIER);
3114   accelEntries[5].Set (wxACCEL_CTRL, static_cast<int>('I'), PJMENU_FILE_PROPERTIES);
3115   accelEntries[6].Set (wxACCEL_CTRL, static_cast<int>('H'), PJMENU_PLOT_TTHETA_SAMPLING);
3116   wxAcceleratorTable accelTable (7, accelEntries);
3117   subframe->SetAcceleratorTable (accelTable);
3118
3119   return subframe;
3120 }
3121
3122
3123 bool
3124 ProjectionFileView::OnCreate(wxDocument *doc, long WXUNUSED(flags) )
3125 {
3126   m_pFrame = CreateChildFrame(doc, this);
3127   SetFrame(m_pFrame);
3128   m_pCanvas = CreateCanvas (m_pFrame);
3129   m_pFrame->SetClientSize (m_pCanvas->GetBestSize());
3130   m_pCanvas->SetClientSize (m_pCanvas->GetBestSize());
3131   m_pFrame->SetTitle (doc->GetFilename());
3132
3133   m_pFrame->Show(true);
3134   Activate(true);
3135
3136   return true;
3137 }
3138
3139 void
3140 ProjectionFileView::OnDraw (wxDC* dc)
3141 {
3142   if (m_pBitmap && m_pBitmap->Ok())
3143     dc->DrawBitmap (*m_pBitmap, 0, 0, false);
3144 }
3145
3146
3147 void
3148 ProjectionFileView::setInitialClientSize ()
3149 {
3150   if (m_pFrame && m_pCanvas) {
3151     wxSize bestSize = m_pCanvas->GetBestSize();
3152
3153     m_pFrame->SetClientSize (bestSize);
3154     m_pFrame->Show (true);
3155     m_pFrame->SetFocus();
3156   }
3157 }
3158
3159 void
3160 ProjectionFileView::OnUpdate (wxView *WXUNUSED(sender), wxObject *WXUNUSED(hint) )
3161 {
3162   const Projections& rProj = GetDocument()->getProjections();
3163   const int nDet = rProj.nDet();
3164   const int nView = rProj.nView();
3165   if (rProj.geometry() == Scanner::GEOMETRY_PARALLEL) {
3166     m_pReconstructMenu->Enable (PJMENU_RECONSTRUCT_FBP_REBIN, false);
3167     m_pConvertMenu->Enable (PJMENU_CONVERT_PARALLEL, false);
3168   } else {
3169     m_pReconstructMenu->Enable (PJMENU_RECONSTRUCT_FBP_REBIN, true);
3170     m_pConvertMenu->Enable (PJMENU_CONVERT_PARALLEL, true);
3171   }
3172
3173   if (nDet != 0 && nView != 0) {
3174     const DetectorArray& detarray = rProj.getDetectorArray(0);
3175     const DetectorValue* detval = detarray.detValues();
3176     double min = detval[0];
3177     double max = detval[0];
3178     for (int iy = 0; iy < nView; iy++) {
3179       const DetectorArray& detarray = rProj.getDetectorArray(iy);
3180       const DetectorValue* detval = detarray.detValues();
3181       for (int ix = 0; ix < nDet; ix++) {
3182         if (min > detval[ix])
3183           min = detval[ix];
3184         else if (max < detval[ix])
3185           max = detval[ix];
3186       }
3187     }
3188
3189     unsigned char* imageData = new unsigned char [nDet * nView * 3];
3190     if (! imageData) {
3191       sys_error (ERR_SEVERE, "Unable to allocate memory for image display");
3192       return;
3193     }
3194     double scale = (max - min) / 255;
3195     for (int iy2 = 0; iy2 < nView; iy2++) {
3196       const DetectorArray& detarray = rProj.getDetectorArray (iy2);
3197       const DetectorValue* detval = detarray.detValues();
3198       for (int ix = 0; ix < nDet; ix++) {
3199         int intensity = static_cast<int>(((detval[ix] - min) / scale) + 0.5);
3200         intensity = clamp(intensity, 0, 255);
3201         int baseAddr = (iy2 * nDet + ix) * 3;
3202         imageData[baseAddr] = imageData[baseAddr+1] = imageData[baseAddr+2] = intensity;
3203       }
3204     }
3205     wxImage image (nDet, nView, imageData, true);
3206     if (m_pBitmap) {
3207       delete m_pBitmap;
3208       m_pBitmap = NULL;
3209     }
3210     m_pBitmap = new wxBitmap (image);
3211     delete imageData;
3212   }
3213
3214     m_pCanvas->SetScrollbars(20, 20, nDet/20, nView/20);
3215     m_pCanvas->SetBackgroundColour(*wxWHITE);
3216
3217     if (m_pCanvas)
3218       m_pCanvas->Refresh();
3219 }
3220
3221 bool
3222 ProjectionFileView::OnClose (bool deleteWindow)
3223 {
3224   //GetDocumentManager()->ActivateView (this, false);
3225   if (! GetDocument() || ! GetDocument()->Close())
3226     return false;
3227
3228   Activate(false);
3229   if (m_pCanvas) {
3230         m_pCanvas->setView(NULL);
3231     m_pCanvas = NULL;
3232   }
3233   wxString s(wxTheApp->GetAppName());
3234   if (m_pFrame)
3235     m_pFrame->SetTitle(s);
3236
3237   SetFrame(NULL);
3238
3239   if (deleteWindow) {
3240     delete m_pFrame;
3241     m_pFrame = NULL;
3242     if (GetDocument() && GetDocument()->getBadFileOpen())
3243       ::wxYield();  // wxWindows bug workaround
3244   }
3245
3246   return true;
3247 }
3248
3249
3250
3251 // PlotFileCanvas
3252 PlotFileCanvas::PlotFileCanvas (PlotFileView* v, wxFrame *frame,
3253                                 const wxPoint& pos, const wxSize& size,
3254                                 const long style)
3255   : wxScrolledWindow(frame, -1, pos, size, style), m_pView(v)
3256 {
3257 }
3258
3259 PlotFileCanvas::~PlotFileCanvas ()
3260 {
3261 }
3262
3263 wxSize
3264 PlotFileCanvas::GetBestSize() const
3265 {
3266   return wxSize (500, 300);
3267 }
3268
3269
3270 void
3271 PlotFileCanvas::OnDraw(wxDC& dc)
3272 {
3273   if (m_pView)
3274     m_pView->OnDraw(& dc);
3275 }
3276
3277
3278 // PlotFileView
3279
3280 IMPLEMENT_DYNAMIC_CLASS(PlotFileView, wxView)
3281
3282 BEGIN_EVENT_TABLE(PlotFileView, wxView)
3283 EVT_MENU(PLOTMENU_FILE_PROPERTIES, PlotFileView::OnProperties)
3284 EVT_MENU(PLOTMENU_VIEW_SCALE_MINMAX, PlotFileView::OnScaleMinMax)
3285 EVT_MENU(PLOTMENU_VIEW_SCALE_AUTO, PlotFileView::OnScaleAuto)
3286 EVT_MENU(PLOTMENU_VIEW_SCALE_FULL, PlotFileView::OnScaleFull)
3287 END_EVENT_TABLE()
3288
3289 PlotFileView::PlotFileView()
3290 : wxView(), m_pFrame(0), m_pCanvas(0), m_pEZPlot(0), m_pFileMenu(0),
3291   m_bMinSpecified(false), m_bMaxSpecified(false)
3292 {
3293 }
3294
3295 PlotFileView::~PlotFileView()
3296 {
3297   if (m_pEZPlot)
3298     delete m_pEZPlot;
3299
3300   GetDocumentManager()->FileHistoryRemoveMenu (m_pFileMenu);
3301   GetDocumentManager()->ActivateView(this, FALSE);
3302 }
3303
3304 void
3305 PlotFileView::OnProperties (wxCommandEvent& event)
3306 {
3307   const PlotFile& rPlot = GetDocument()->getPlotFile();
3308   std::ostringstream os;
3309   os << "Columns: " << rPlot.getNumColumns() << ", Records: " << rPlot.getNumRecords() << "\n";
3310   rPlot.printHeadersBrief (os);
3311   *theApp->getLog() << _T(">>>>\n") << wxConvUTF8.cMB2WX(os.str().c_str()) << _T("<<<<<\n");
3312   wxMessageDialog dialogMsg (getFrameForChild(), wxConvUTF8.cMB2WX(os.str().c_str()), _T("Plot File Properties"), 
3313                              wxOK | wxICON_INFORMATION);
3314   dialogMsg.ShowModal();
3315   GetDocument()->Activate();
3316 }
3317
3318
3319 void
3320 PlotFileView::OnScaleAuto (wxCommandEvent& event)
3321 {
3322   const PlotFile& rPlotFile = GetDocument()->getPlotFile();
3323   double min, max, mean, mode, median, stddev;
3324   rPlotFile.statistics (1, min, max, mean, mode, median, stddev);
3325   DialogAutoScaleParameters dialogAutoScale (getFrameForChild(), mean, mode, median, stddev, m_dAutoScaleFactor);
3326   int iRetVal = dialogAutoScale.ShowModal();
3327   if (iRetVal == wxID_OK) {
3328     m_bMinSpecified = true;
3329     m_bMaxSpecified = true;
3330     double dMin, dMax;
3331     if (dialogAutoScale.getMinMax (&dMin, &dMax)) {
3332       m_dMinPixel = dMin;
3333       m_dMaxPixel = dMax;
3334       m_dAutoScaleFactor = dialogAutoScale.getAutoScaleFactor();
3335       OnUpdate (this, NULL);
3336     }
3337   }
3338   GetDocument()->Activate();
3339 }
3340
3341 void
3342 PlotFileView::OnScaleMinMax (wxCommandEvent& event)
3343 {
3344   const PlotFile& rPlotFile = GetDocument()->getPlotFile();
3345   double min, max;
3346
3347   if (! m_bMinSpecified || ! m_bMaxSpecified) {
3348     if (! rPlotFile.getMinMax (1, min, max)) {
3349       *theApp->getLog() << _T("Error: unable to find Min/Max\n");
3350       return;
3351     }
3352   }
3353
3354   if (m_bMinSpecified)
3355     min = m_dMinPixel;
3356   if (m_bMaxSpecified)
3357     max = m_dMaxPixel;
3358
3359   DialogGetMinMax dialogMinMax (getFrameForChild(), _T("Set Y-axis Minimum & Maximum"), min, max);
3360   int retVal = dialogMinMax.ShowModal();
3361   if (retVal == wxID_OK) {
3362     m_bMinSpecified = true;
3363     m_bMaxSpecified = true;
3364     m_dMinPixel = dialogMinMax.getMinimum();
3365     m_dMaxPixel = dialogMinMax.getMaximum();
3366     OnUpdate (this, NULL);
3367   }
3368   GetDocument()->Activate();
3369 }
3370
3371 void
3372 PlotFileView::OnScaleFull (wxCommandEvent& event)
3373 {
3374   m_bMinSpecified = false;
3375   m_bMaxSpecified = false;
3376   OnUpdate (this, NULL);
3377   GetDocument()->Activate();
3378 }
3379
3380
3381 PlotFileCanvas*
3382 PlotFileView::CreateCanvas (wxFrame* parent)
3383 {
3384   PlotFileCanvas* pCanvas;
3385
3386   pCanvas = new PlotFileCanvas (this, parent, wxPoint(-1,-1), wxSize(-1,-1),
3387                                 wxFULL_REPAINT_ON_RESIZE);
3388   pCanvas->SetBackgroundColour(*wxWHITE);
3389   pCanvas->ClearBackground();
3390
3391   return pCanvas;
3392 }
3393
3394 #if CTSIM_MDI
3395 wxDocMDIChildFrame*
3396 #else
3397 wxDocChildFrame*
3398 #endif
3399 PlotFileView::CreateChildFrame(wxDocument *doc, wxView *view)
3400 {
3401 #ifdef CTSIM_MDI
3402   wxDocMDIChildFrame *subframe = new wxDocMDIChildFrame (doc, view, theApp->getMainFrame(), -1, _T("Plot Frame"), wxPoint(-1,-1), wxSize(-1,-1), wxDEFAULT_FRAME_STYLE);
3403 #else
3404   wxDocChildFrame *subframe = new wxDocChildFrame(doc, view, theApp->getMainFrame(), -1, _T("Plot Frame"), wxPoint(-1,-1), wxSize(-1,-1), wxDEFAULT_FRAME_STYLE);
3405 #endif
3406   theApp->setIconForFrame (subframe);
3407
3408   m_pFileMenu = new wxMenu;
3409
3410   m_pFileMenu->Append(MAINMENU_FILE_CREATE_PHANTOM, _T("Cr&eate Phantom...\tCtrl-P"));
3411   m_pFileMenu->Append(MAINMENU_FILE_CREATE_FILTER, _T("Create &Filter...\tCtrl-F"));
3412   m_pFileMenu->Append(wxID_OPEN, _T("&Open...\tCtrl-O"));
3413   m_pFileMenu->Append(wxID_SAVE, _T("&Save\tCtrl-S"));
3414   m_pFileMenu->Append(wxID_SAVEAS, _T("Save &As..."));
3415   m_pFileMenu->Append(wxID_CLOSE, _T("&Close\tCtrl-W"));
3416
3417   m_pFileMenu->AppendSeparator();
3418   m_pFileMenu->Append(PLOTMENU_FILE_PROPERTIES, _T("P&roperties\tCtrl-I"));
3419
3420   m_pFileMenu->AppendSeparator();
3421   m_pFileMenu->Append(wxID_PRINT, _T("&Print..."));
3422   m_pFileMenu->Append(wxID_PRINT_SETUP, _T("Print &Setup..."));
3423   m_pFileMenu->Append(wxID_PREVIEW, _T("Print Pre&view"));
3424   m_pFileMenu->AppendSeparator();
3425   m_pFileMenu->Append(MAINMENU_IMPORT, _T("&Import...\tCtrl-M"));
3426   m_pFileMenu->AppendSeparator();
3427   m_pFileMenu->Append (MAINMENU_FILE_PREFERENCES, _T("Prefere&nces..."));
3428   m_pFileMenu->Append(MAINMENU_FILE_EXIT, _T("E&xit"));
3429   GetDocumentManager()->FileHistoryAddFilesToMenu(m_pFileMenu);
3430   GetDocumentManager()->FileHistoryUseMenu(m_pFileMenu);
3431
3432   wxMenu *view_menu = new wxMenu;
3433   view_menu->Append(PLOTMENU_VIEW_SCALE_MINMAX, _T("Display Scale &Set...\tCtrl-E"));
3434   view_menu->Append(PLOTMENU_VIEW_SCALE_AUTO, _T("Display Scale &Auto...\tCtrl-A"));
3435   view_menu->Append(PLOTMENU_VIEW_SCALE_FULL, _T("Display &Full Scale\tCtrl-U"));
3436
3437   wxMenu *help_menu = new wxMenu;
3438   help_menu->Append(MAINMENU_HELP_CONTENTS, _T("&Contents\tF1"));
3439   help_menu->Append (MAINMENU_HELP_TIPS, _T("&Tips"));
3440   help_menu->Append (IDH_QUICKSTART, _T("&Quick Start"));
3441   help_menu->Append(MAINMENU_HELP_ABOUT, _T("&About"));
3442
3443   wxMenuBar *menu_bar = new wxMenuBar;
3444
3445   menu_bar->Append(m_pFileMenu, _T("&File"));
3446   menu_bar->Append(view_menu, _T("&View"));
3447   menu_bar->Append(help_menu, _T("&Help"));
3448
3449   subframe->SetMenuBar(menu_bar);
3450   subframe->Centre(wxBOTH);
3451
3452   wxAcceleratorEntry accelEntries[4];
3453   accelEntries[0].Set (wxACCEL_CTRL, static_cast<int>('E'), PLOTMENU_VIEW_SCALE_MINMAX);
3454   accelEntries[1].Set (wxACCEL_CTRL, static_cast<int>('A'), PLOTMENU_VIEW_SCALE_AUTO);
3455   accelEntries[2].Set (wxACCEL_CTRL, static_cast<int>('U'), PLOTMENU_VIEW_SCALE_FULL);
3456   accelEntries[3].Set (wxACCEL_CTRL, static_cast<int>('I'), PLOTMENU_FILE_PROPERTIES);
3457   wxAcceleratorTable accelTable (4, accelEntries);
3458   subframe->SetAcceleratorTable (accelTable);
3459
3460   return subframe;
3461 }
3462
3463
3464 bool
3465 PlotFileView::OnCreate (wxDocument *doc, long WXUNUSED(flags) )
3466 {
3467   m_bMinSpecified = false;
3468   m_bMaxSpecified = false;
3469   m_dAutoScaleFactor = 1.;
3470
3471   m_pFrame = CreateChildFrame(doc, this);
3472   SetFrame(m_pFrame);
3473   m_pCanvas = CreateCanvas (m_pFrame);
3474   m_pFrame->SetClientSize (m_pCanvas->GetBestSize());
3475   m_pCanvas->SetClientSize (m_pCanvas->GetBestSize());
3476   m_pFrame->SetTitle (_T("test"));
3477   *theApp->getLog() << _T("Plot doc name: ") << doc->GetDocumentName() << _T("\n");
3478   *theApp->getLog() << _T("Plot file name: ") << doc->GetFilename() << _T("\n");
3479   m_pFrame->SetTitle (doc->GetFilename());
3480
3481   m_pFrame->Show(true);
3482   Activate(true);
3483
3484   return true;
3485 }
3486
3487 void
3488 PlotFileView::setInitialClientSize ()
3489 {
3490   if (m_pFrame && m_pCanvas) {
3491     wxSize bestSize = m_pCanvas->GetBestSize();
3492
3493     m_pFrame->SetClientSize (bestSize);
3494     m_pFrame->Show (true);
3495     m_pFrame->SetFocus();
3496   }
3497 }
3498
3499
3500 void
3501 PlotFileView::OnDraw (wxDC* dc)
3502 {
3503   const PlotFile& rPlotFile = GetDocument()->getPlotFile();
3504   const int iNColumns = rPlotFile.getNumColumns();
3505   const int iNRecords = rPlotFile.getNumRecords();
3506
3507   if (iNColumns > 0 && iNRecords > 0) {
3508     int xsize, ysize;
3509     m_pCanvas->GetClientSize (&xsize, &ysize);
3510     SGPDriver driver (dc, xsize, ysize);
3511     SGP sgp (driver);
3512     if (m_pEZPlot)
3513       m_pEZPlot->plot (&sgp);
3514   }
3515 }
3516
3517
3518 void
3519 PlotFileView::OnUpdate (wxView *WXUNUSED(sender), wxObject *WXUNUSED(hint) )
3520 {
3521   const PlotFile& rPlotFile = GetDocument()->getPlotFile();
3522   const int iNColumns = rPlotFile.getNumColumns();
3523   const int iNRecords = rPlotFile.getNumRecords();
3524   const bool bScatterPlot = rPlotFile.getIsScatterPlot();
3525
3526   if (iNColumns > 0 && iNRecords > 0) {
3527     if (m_pEZPlot)
3528       delete m_pEZPlot;
3529     m_pEZPlot = new EZPlot;
3530
3531     for (unsigned int iEzset = 0; iEzset < rPlotFile.getNumEzsetCommands(); iEzset++)
3532       m_pEZPlot->ezset (rPlotFile.getEzsetCommand (iEzset));
3533
3534     if (m_bMinSpecified) {
3535       std::ostringstream os;
3536       os << "ymin " << m_dMinPixel;
3537       m_pEZPlot->ezset (os.str());
3538     }
3539
3540     if (m_bMaxSpecified) {
3541       std::ostringstream os;
3542       os << "ymax " << m_dMaxPixel;
3543       m_pEZPlot->ezset (os.str());
3544     }
3545
3546     m_pEZPlot->ezset("box");
3547     m_pEZPlot->ezset("grid");
3548
3549     double* pdX = new double [iNRecords];
3550     double* pdY = new double [iNRecords];
3551     if (! bScatterPlot) {
3552       rPlotFile.getColumn (0, pdX);
3553
3554       for (int iCol = 1; iCol < iNColumns; iCol++) {
3555         rPlotFile.getColumn (iCol, pdY);
3556         m_pEZPlot->addCurve (pdX, pdY, iNRecords);
3557       }
3558     } else {
3559       rPlotFile.getColumn (0, pdX);
3560       rPlotFile.getColumn (1, pdY);
3561       m_pEZPlot->addCurve (pdX, pdY, iNRecords);
3562     }
3563     delete pdX;
3564     delete pdY;
3565   }
3566
3567   if (m_pCanvas) {
3568     m_pCanvas->Refresh();
3569   }
3570 }
3571
3572 bool
3573 PlotFileView::OnClose (bool deleteWindow)
3574 {
3575   if (! GetDocument() || ! GetDocument()->Close())
3576     return false;
3577
3578   Activate(false);
3579   if (m_pCanvas) {
3580     m_pCanvas->setView (NULL);
3581     m_pCanvas = NULL;
3582   }
3583   wxString s(wxTheApp->GetAppName());
3584   if (m_pFrame)
3585     m_pFrame->SetTitle(s);
3586
3587   SetFrame(NULL);
3588   if (deleteWindow) {
3589     delete m_pFrame;
3590     m_pFrame = NULL;
3591     if (GetDocument() && GetDocument()->getBadFileOpen())
3592       ::wxYield();  // wxWindows bug workaround
3593   }
3594
3595   return true;
3596 }
3597
3598
3599 ////////////////////////////////////////////////////////////////
3600
3601
3602 IMPLEMENT_DYNAMIC_CLASS(TextFileView, wxView)
3603
3604 TextFileView::~TextFileView()
3605 {
3606   GetDocumentManager()->FileHistoryRemoveMenu (m_pFileMenu);
3607   GetDocumentManager()->ActivateView(this, FALSE);;
3608 }
3609
3610 bool TextFileView::OnCreate(wxDocument *doc, long WXUNUSED(flags) )
3611 {
3612   m_pFrame = CreateChildFrame(doc, this);
3613   SetFrame (m_pFrame);
3614
3615   int width, height;
3616   m_pFrame->GetClientSize(&width, &height);
3617   m_pFrame->SetTitle(doc->GetFilename());
3618   m_pCanvas = new TextFileCanvas (this, m_pFrame, wxPoint(-1,-1), wxSize(width, height), wxTE_MULTILINE | wxTE_READONLY);
3619
3620   m_pFrame->Show (true);
3621   Activate (true);
3622
3623   return true;
3624 }
3625
3626 // Handled by wxTextWindow
3627 void TextFileView::OnDraw(wxDC *WXUNUSED(dc) )
3628 {
3629 }
3630
3631 void TextFileView::OnUpdate (wxView *WXUNUSED(sender), wxObject *WXUNUSED(hint) )
3632 {
3633 }
3634
3635 bool
3636 TextFileView::OnClose (bool deleteWindow)
3637 {
3638   if (! theApp->getMainFrame()->getShuttingDown())
3639     return false;
3640
3641   Activate(false);
3642   //GetDocumentManager()->ActivateView (this, false);
3643   if (! GetDocument() || ! GetDocument()->Close())
3644     return false;
3645
3646   SetFrame(NULL);
3647   if (deleteWindow) {
3648     delete m_pFrame;
3649     m_pFrame = NULL;
3650     if (GetDocument() && GetDocument()->getBadFileOpen())
3651       ::wxYield();  // wxWindows bug workaround
3652   }
3653
3654   return TRUE;
3655 }
3656
3657 #if CTSIM_MDI
3658 wxDocMDIChildFrame*
3659 #else
3660 wxDocChildFrame*
3661 #endif
3662 TextFileView::CreateChildFrame (wxDocument *doc, wxView *view)
3663 {
3664 #if CTSIM_MDI
3665   wxDocMDIChildFrame* subframe = new wxDocMDIChildFrame (doc, view, theApp->getMainFrame(), -1, _T("TextFile Frame"), wxPoint(-1, -1), wxSize(-1,-1), wxDEFAULT_FRAME_STYLE, _T("Log"));
3666 #else
3667   wxDocChildFrame* subframe = new wxDocChildFrame (doc, view, theApp->getMainFrame(), -1, _T("TextFile Frame"), wxPoint(-1, -1), wxSize(300, 150), wxDEFAULT_FRAME_STYLE, _T("Log"));
3668 #endif
3669   theApp->setIconForFrame (subframe);
3670
3671   m_pFileMenu = new wxMenu;
3672
3673   m_pFileMenu->Append(MAINMENU_FILE_CREATE_PHANTOM, _T("Cr&eate Phantom...\tCtrl-P"));
3674   m_pFileMenu->Append(MAINMENU_FILE_CREATE_FILTER, _T("Create &Filter...\tCtrl-F"));
3675   m_pFileMenu->Append(wxID_OPEN, _T("&Open...\tCtrl-O"));
3676   m_pFileMenu->Append(wxID_SAVE, _T("&Save\tCtrl-S"));
3677   m_pFileMenu->Append(wxID_SAVEAS, _T("Save &As..."));
3678   //  m_pFileMenu->Append(wxID_CLOSE, _T("&Close\tCtrl-W"));
3679
3680   m_pFileMenu->AppendSeparator();
3681   m_pFileMenu->Append(wxID_PRINT, _T("&Print..."));
3682   m_pFileMenu->Append(wxID_PRINT_SETUP, _T("Print &Setup..."));
3683   m_pFileMenu->Append(wxID_PREVIEW, _T("Print Pre&view"));
3684   m_pFileMenu->AppendSeparator();
3685   m_pFileMenu->Append(MAINMENU_IMPORT, _T("&Import...\tCtrl-M"));
3686   m_pFileMenu->AppendSeparator();
3687   m_pFileMenu->Append (MAINMENU_FILE_PREFERENCES, _T("Prefere&nces..."));
3688   m_pFileMenu->Append(MAINMENU_FILE_EXIT, _T("E&xit"));
3689   GetDocumentManager()->FileHistoryAddFilesToMenu(m_pFileMenu);
3690   GetDocumentManager()->FileHistoryUseMenu(m_pFileMenu);
3691
3692   wxMenu *help_menu = new wxMenu;
3693   help_menu->Append(MAINMENU_HELP_CONTENTS, _T("&Contents\tF1"));
3694   help_menu->Append (MAINMENU_HELP_TIPS, _T("&Tips"));
3695   help_menu->Append (IDH_QUICKSTART, _T("&Quick Start"));
3696   help_menu->Append(MAINMENU_HELP_ABOUT, _T("&About"));
3697
3698   wxMenuBar *menu_bar = new wxMenuBar;
3699
3700   menu_bar->Append(m_pFileMenu, _T("&File"));
3701   menu_bar->Append(help_menu, _T("&Help"));
3702
3703   subframe->SetMenuBar(menu_bar);
3704   subframe->Centre(wxBOTH);
3705
3706   return subframe;
3707 }
3708
3709
3710 // Define a constructor for my text subwindow
3711 TextFileCanvas::TextFileCanvas (TextFileView* v, wxFrame* frame, const wxPoint& pos, const wxSize& size, long style)
3712   : wxTextCtrl (frame, -1, _T(""), pos, size, style), m_pView(v)
3713 {
3714 }
3715
3716 TextFileCanvas::~TextFileCanvas ()
3717 {
3718   m_pView = NULL;
3719 }
3720
3721 wxSize
3722 TextFileCanvas::GetBestSize() const
3723 {
3724   int xSize, ySize;
3725   theApp->getMainFrame()->GetClientSize (&xSize, &ySize);
3726   xSize = maxValue<int> (xSize, ySize);
3727 #ifdef CTSIM_MDI
3728   ySize = xSize = (xSize / 4);
3729 #else
3730   ySize = xSize;
3731 #endif
3732   return wxSize (xSize, ySize);
3733 }