690ac223bccdd3226680817ab0287ba62ebefb62
[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(_T("ImageFileView"));
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 #if WXWIN_COMPATIBILITY_2_4
1314       const wxString& strFilename = wxFileSelector (_T("Export Filename"), _T(""),
1315         _T(""), strExt, strWildcard, wxOVERWRITE_PROMPT | wxHIDE_READONLY | wxSAVE);
1316 #else
1317       const wxString& strFilename = wxFileSelector (_T("Export Filename"), _T(""),
1318         _T(""), strExt, strWildcard, wxOVERWRITE_PROMPT | wxSAVE);
1319 #endif
1320       if (strFilename) {
1321         rIF.exportImage (strFormatName.mb_str(wxConvUTF8), strFilename.mb_str(wxConvUTF8), 1, 1, m_dMinPixel, m_dMaxPixel);
1322         *theApp->getLog() << _T("Exported file ") << strFilename << _T("\n");
1323       }
1324     }
1325   }
1326 }
1327
1328 void
1329 ImageFileView::OnScaleSize (wxCommandEvent& event)
1330 {
1331   ImageFile& rIF = GetDocument()->getImageFile();
1332   unsigned int iOldNX = rIF.nx();
1333   unsigned int iOldNY = rIF.ny();
1334
1335   DialogGetXYSize dialogGetXYSize (getFrameForChild(), _T("Set New X & Y Dimensions"), iOldNX, iOldNY);
1336   if (dialogGetXYSize.ShowModal() == wxID_OK) {
1337     unsigned int iNewNX = dialogGetXYSize.getXSize();
1338     unsigned int iNewNY = dialogGetXYSize.getYSize();
1339     std::ostringstream os;
1340     os << "Scale Size from (" << iOldNX << "," << iOldNY << ") to (" << iNewNX << "," << iNewNY << ")";
1341     ImageFileDocument* pScaledDoc = theApp->newImageDoc();
1342     if (! pScaledDoc) {
1343       sys_error (ERR_SEVERE, "Unable to create image file");
1344       return;
1345     }
1346     ImageFile& rScaledIF = pScaledDoc->getImageFile();
1347     rScaledIF.setArraySize (iNewNX, iNewNY);
1348     rScaledIF.labelsCopy (rIF);
1349     rScaledIF.labelAdd (os.str().c_str());
1350     rIF.scaleImage (rScaledIF);
1351     *theApp->getLog() << wxConvUTF8.cMB2WX(os.str().c_str()) << _T("\n");
1352     if (theApp->getAskDeleteNewDocs())
1353       pScaledDoc->Modify (true);
1354     OnUpdate(this, NULL);
1355     pScaledDoc->UpdateAllViews (this);
1356     pScaledDoc->getView()->setInitialClientSize();
1357     pScaledDoc->Activate();
1358   }
1359 }
1360
1361 #if wxUSE_GLCANVAS
1362 void
1363 ImageFileView::OnConvert3d (wxCommandEvent& event)
1364 {
1365   ImageFile& rIF = GetDocument()->getImageFile();
1366   Graph3dFileDocument* pGraph3d = theApp->newGraph3dDoc();
1367   pGraph3d->getView()->getFrame()->Show (false);
1368   pGraph3d->setBadFileOpen();
1369   pGraph3d->createFromImageFile (rIF);
1370   pGraph3d->UpdateAllViews();
1371   pGraph3d->getView()->getFrame()->Show (true);
1372   pGraph3d->getView()->Activate(true);
1373   ::wxYield();
1374   pGraph3d->getView()->getCanvas()->SetFocus();
1375 }
1376 #endif
1377
1378 void
1379 ImageFileView::OnPlotRow (wxCommandEvent& event)
1380 {
1381   int xCursor, yCursor;
1382   if (! m_pCanvas->GetCurrentCursor (xCursor, yCursor)) {
1383     wxMessageBox (_T("No row selected. Please use left mouse button on image to select column"),_T("Error"));
1384     return;
1385   }
1386
1387   const ImageFile& rIF = GetDocument()->getImageFile();
1388   ImageFileArrayConst v = rIF.getArray();
1389   ImageFileArrayConst vImag = rIF.getImaginaryArray();
1390   int nx = rIF.nx();
1391   int ny = rIF.ny();
1392
1393   if (v != NULL && yCursor < ny) {
1394     double* pX = new double [nx];
1395     double* pYReal = new double [nx];
1396     double *pYImag = NULL;
1397     double *pYMag = NULL;
1398     if (rIF.isComplex()) {
1399       pYImag = new double [nx];
1400       pYMag = new double [nx];
1401     }
1402     for (int i = 0; i < nx; i++) {
1403       pX[i] = i;
1404       pYReal[i] = v[i][yCursor];
1405       if (rIF.isComplex()) {
1406         pYImag[i] = vImag[i][yCursor];
1407         pYMag[i] = ::sqrt (v[i][yCursor] * v[i][yCursor] + vImag[i][yCursor] * vImag[i][yCursor]);
1408       }
1409     }
1410     PlotFileDocument* pPlotDoc = theApp->newPlotDoc();
1411     if (! pPlotDoc) {
1412       sys_error (ERR_SEVERE, "Internal error: unable to create Plot file");
1413     } else {
1414       PlotFile& rPlotFile = pPlotDoc->getPlotFile();
1415       std::ostringstream os;
1416       os << "Row " << yCursor << ": ";
1417       std::string title("title ");
1418       title += os.str();
1419       rPlotFile.addEzsetCommand (title.c_str());
1420       rPlotFile.addEzsetCommand ("xlabel Column");
1421       rPlotFile.addEzsetCommand ("ylabel Pixel Value");
1422       rPlotFile.addEzsetCommand ("lxfrac 0");
1423       rPlotFile.addEzsetCommand ("box");
1424       rPlotFile.addEzsetCommand ("grid");
1425       rPlotFile.addEzsetCommand ("curve 1");
1426       rPlotFile.addEzsetCommand ("color 1");
1427       if (rIF.isComplex()) {
1428         rPlotFile.addEzsetCommand ("dash 1");
1429         rPlotFile.addEzsetCommand ("curve 2");
1430         rPlotFile.addEzsetCommand ("color 4");
1431         rPlotFile.addEzsetCommand ("dash 3");
1432         rPlotFile.addEzsetCommand ("curve 3");
1433         rPlotFile.addEzsetCommand ("color 0");
1434         rPlotFile.addEzsetCommand ("solid");
1435         rPlotFile.setCurveSize (4, nx);
1436       } else
1437         rPlotFile.setCurveSize (2, nx);
1438       rPlotFile.addColumn (0, pX);
1439       rPlotFile.addColumn (1, pYReal);
1440       if (rIF.isComplex()) {
1441         rPlotFile.addColumn (2, pYImag);
1442         rPlotFile.addColumn (3, pYMag);
1443       }
1444       for (unsigned int iL = 0; iL < rIF.nLabels(); iL++)
1445         rPlotFile.addDescription (rIF.labelGet(iL).getLabelString().c_str());
1446       os << " Plot of " << wxConvUTF8.cWX2MB(dynamic_cast<wxFrame*>(GetDocument()->GetFirstView()->GetFrame())->GetTitle().c_str());
1447       *theApp->getLog() << wxConvUTF8.cMB2WX(os.str().c_str()) << _T("\n");
1448       rPlotFile.addDescription (os.str().c_str());
1449     }
1450     delete pX;
1451     delete pYReal;
1452     if (rIF.isComplex()) {
1453       delete pYImag;
1454       delete pYMag;
1455     }
1456     if (theApp->getAskDeleteNewDocs())
1457       pPlotDoc->Modify (true);
1458     pPlotDoc->getView()->getFrame()->Show(true);
1459     pPlotDoc->UpdateAllViews ();
1460     pPlotDoc->Activate();
1461   }
1462 }
1463
1464 void
1465 ImageFileView::OnPlotCol (wxCommandEvent& event)
1466 {
1467   int xCursor, yCursor;
1468   if (! m_pCanvas->GetCurrentCursor (xCursor, yCursor)) {
1469     wxMessageBox (_T("No column selected. Please use left mouse button on image to select column"),_T("Error"));
1470     return;
1471   }
1472
1473   const ImageFile& rIF = GetDocument()->getImageFile();
1474   ImageFileArrayConst v = rIF.getArray();
1475   ImageFileArrayConst vImag = rIF.getImaginaryArray();
1476   int nx = rIF.nx();
1477   int ny = rIF.ny();
1478
1479   if (v != NULL && xCursor < nx) {
1480     double* pX = new double [ny];
1481     double* pYReal = new double [ny];
1482     double* pYImag = NULL;
1483     double* pYMag = NULL;
1484     if (rIF.isComplex()) {
1485       pYImag = new double [ny];
1486       pYMag = new double [ny];
1487     }
1488     for (int i = 0; i < ny; i++) {
1489       pX[i] = i;
1490       pYReal[i] = v[xCursor][i];
1491       if (rIF.isComplex()) {
1492         pYImag[i] = vImag[xCursor][i];
1493         pYMag[i] = ::sqrt (v[xCursor][i] * v[xCursor][i] + vImag[xCursor][i] * vImag[xCursor][i]);
1494       }
1495     }
1496     PlotFileDocument* pPlotDoc = theApp->newPlotDoc();
1497     if (! pPlotDoc) {
1498       sys_error (ERR_SEVERE, "Internal error: unable to create Plot file");
1499     } else {
1500       PlotFile& rPlotFile = pPlotDoc->getPlotFile();
1501       std::ostringstream os;
1502       os << "Column " << xCursor << ": ";
1503       std::string title("title ");
1504       title += os.str();
1505       rPlotFile.addEzsetCommand (title.c_str());
1506       rPlotFile.addEzsetCommand ("xlabel Row");
1507       rPlotFile.addEzsetCommand ("ylabel Pixel Value");
1508       rPlotFile.addEzsetCommand ("lxfrac 0");
1509       rPlotFile.addEzsetCommand ("box");
1510       rPlotFile.addEzsetCommand ("grid");
1511       rPlotFile.addEzsetCommand ("curve 1");
1512       rPlotFile.addEzsetCommand ("color 1");
1513       if (rIF.isComplex()) {
1514         rPlotFile.addEzsetCommand ("dash 1");
1515         rPlotFile.addEzsetCommand ("curve 2");
1516         rPlotFile.addEzsetCommand ("color 4");
1517         rPlotFile.addEzsetCommand ("dash 3");
1518         rPlotFile.addEzsetCommand ("curve 3");
1519         rPlotFile.addEzsetCommand ("color 0");
1520         rPlotFile.addEzsetCommand ("solid");
1521         rPlotFile.setCurveSize (4, ny);
1522       } else
1523         rPlotFile.setCurveSize (2, ny);
1524       rPlotFile.addColumn (0, pX);
1525       rPlotFile.addColumn (1, pYReal);
1526       if (rIF.isComplex()) {
1527         rPlotFile.addColumn (2, pYImag);
1528         rPlotFile.addColumn (3, pYMag);
1529       }
1530       for (unsigned int iL = 0; iL < rIF.nLabels(); iL++)
1531         rPlotFile.addDescription (rIF.labelGet(iL).getLabelString().c_str());
1532       os << " Plot of " << wxConvUTF8.cWX2MB(dynamic_cast<wxFrame*>(GetDocument()->GetFirstView()->GetFrame())->GetTitle().c_str());
1533       *theApp->getLog() << wxConvUTF8.cMB2WX(os.str().c_str()) << _T("\n");
1534       rPlotFile.addDescription (os.str().c_str());
1535     }
1536     delete pX;
1537     delete pYReal;
1538     if (rIF.isComplex()) {
1539       delete pYImag;
1540       delete pYMag;
1541     }
1542     if (theApp->getAskDeleteNewDocs())
1543       pPlotDoc->Modify (true);
1544     pPlotDoc->getView()->getFrame()->Show(true);
1545     pPlotDoc->UpdateAllViews ();
1546     pPlotDoc->Activate();
1547   }
1548 }
1549
1550 #ifdef HAVE_FFT
1551 void
1552 ImageFileView::OnPlotFFTRow (wxCommandEvent& event)
1553 {
1554   int xCursor, yCursor;
1555   if (! m_pCanvas->GetCurrentCursor (xCursor, yCursor)) {
1556     wxMessageBox (_T("No row selected. Please use left mouse button on image to select column"),_T("Error"));
1557     return;
1558   }
1559
1560   const ImageFile& rIF = GetDocument()->getImageFile();
1561   ImageFileArrayConst v = rIF.getArray();
1562   ImageFileArrayConst vImag = rIF.getImaginaryArray();
1563   int nx = rIF.nx();
1564   int ny = rIF.ny();
1565
1566   if (v != NULL && yCursor < ny) {
1567     fftw_complex* pcIn = static_cast<fftw_complex*>(fftw_malloc (sizeof(fftw_complex) * nx));
1568
1569     int i;
1570     for (i = 0; i < nx; i++) {
1571       pcIn[i][0] = v[i][yCursor];
1572       if (rIF.isComplex())
1573         pcIn[i][1] = vImag[i][yCursor];
1574       else
1575         pcIn[i][1] = 0;
1576     }
1577
1578     fftw_plan plan = fftw_plan_dft_1d (nx, pcIn, pcIn, FFTW_FORWARD, FFTW_ESTIMATE);
1579     fftw_execute (plan);
1580     fftw_destroy_plan (plan);
1581
1582     double* pX = new double [nx];
1583     double* pYReal = new double [nx];
1584     double* pYImag = new double [nx];
1585     double* pYMag = new double [nx];
1586     for (i = 0; i < nx; i++) {
1587       pX[i] = i;
1588       pYReal[i] = pcIn[i][0] / nx;
1589       pYImag[i] = pcIn[i][1] / nx;
1590       pYMag[i] = ::sqrt (pcIn[i][0] * pcIn[i][0] + pcIn[i][1] * pcIn[i][1]);
1591     }
1592     Fourier::shuffleFourierToNaturalOrder (pYReal, nx);
1593     Fourier::shuffleFourierToNaturalOrder (pYImag, nx);
1594     Fourier::shuffleFourierToNaturalOrder (pYMag, nx);
1595
1596     PlotFileDocument* pPlotDoc = theApp->newPlotDoc();
1597     if (! pPlotDoc) {
1598       sys_error (ERR_SEVERE, "Internal error: unable to create Plot file");
1599     } else {
1600       PlotFile& rPlotFile = pPlotDoc->getPlotFile();
1601       std::ostringstream os;
1602       os << "Row " << yCursor;
1603       std::string title("title ");
1604       title += os.str();
1605       rPlotFile.addEzsetCommand (title.c_str());
1606       rPlotFile.addEzsetCommand ("xlabel Column");
1607       rPlotFile.addEzsetCommand ("ylabel Pixel Value");
1608       rPlotFile.addEzsetCommand ("lxfrac 0");
1609       rPlotFile.addEzsetCommand ("curve 1");
1610       rPlotFile.addEzsetCommand ("color 1");
1611       rPlotFile.addEzsetCommand ("dash 1");
1612       rPlotFile.addEzsetCommand ("curve 2");
1613       rPlotFile.addEzsetCommand ("color 4");
1614       rPlotFile.addEzsetCommand ("dash 3");
1615       rPlotFile.addEzsetCommand ("curve 3");
1616       rPlotFile.addEzsetCommand ("color 0");
1617       rPlotFile.addEzsetCommand ("solid");
1618       rPlotFile.addEzsetCommand ("box");
1619       rPlotFile.addEzsetCommand ("grid");
1620       rPlotFile.setCurveSize (4, nx);
1621       rPlotFile.addColumn (0, pX);
1622       rPlotFile.addColumn (1, pYReal);
1623       rPlotFile.addColumn (2, pYImag);
1624       rPlotFile.addColumn (3, pYMag);
1625       for (unsigned int iL = 0; iL < rIF.nLabels(); iL++)
1626         rPlotFile.addDescription (rIF.labelGet(iL).getLabelString().c_str());
1627       os << " FFT Plot of " << dynamic_cast<wxFrame*>(GetDocument()->GetFirstView()->GetFrame())->GetTitle().c_str();
1628       *theApp->getLog() << wxConvUTF8.cMB2WX(os.str().c_str()) << _T("\n");
1629       rPlotFile.addDescription (os.str().c_str());
1630     }
1631     delete pX;
1632     delete pYReal;
1633     delete pYImag;
1634     delete pYMag;
1635     fftw_free(pcIn);
1636
1637     if (theApp->getAskDeleteNewDocs())
1638       pPlotDoc->Modify (true);
1639     pPlotDoc->getView()->getFrame()->Show(true);
1640     pPlotDoc->UpdateAllViews ();
1641     pPlotDoc->Activate();
1642   }
1643 }
1644
1645 void
1646 ImageFileView::OnPlotFFTCol (wxCommandEvent& event)
1647 {
1648   int xCursor, yCursor;
1649   if (! m_pCanvas->GetCurrentCursor (xCursor, yCursor)) {
1650     wxMessageBox (_T("No column selected. Please use left mouse button on image to select column"),_T("Error"));
1651     return;
1652   }
1653
1654   const ImageFile& rIF = GetDocument()->getImageFile();
1655   ImageFileArrayConst v = rIF.getArray();
1656   ImageFileArrayConst vImag = rIF.getImaginaryArray();
1657   int nx = rIF.nx();
1658   int ny = rIF.ny();
1659
1660   if (v != NULL && xCursor < nx) {
1661     fftw_complex* pcIn = new fftw_complex [ny];
1662     double *pdTemp = new double [ny];
1663
1664     int i;
1665     for (i = 0; i < ny; i++)
1666       pdTemp[i] = v[xCursor][i];
1667     Fourier::shuffleNaturalToFourierOrder (pdTemp, ny);
1668     for (i = 0; i < ny; i++)
1669       pcIn[i][0] = pdTemp[i];
1670
1671     for (i = 0; i < ny; i++) {
1672       if (rIF.isComplex())
1673         pdTemp[i] = vImag[xCursor][i];
1674       else
1675         pdTemp[i] = 0;
1676     }
1677     Fourier::shuffleNaturalToFourierOrder (pdTemp, ny);
1678     for (i = 0; i < ny; i++)
1679       pcIn[i][1] = pdTemp[i];
1680
1681     fftw_plan plan = fftw_plan_dft_1d (ny, pcIn, pcIn, FFTW_BACKWARD, FFTW_ESTIMATE);
1682     fftw_execute (plan);
1683     fftw_destroy_plan (plan);
1684
1685     double* pX = new double [ny];
1686     double* pYReal = new double [ny];
1687     double* pYImag = new double [ny];
1688     double* pYMag = new double [ny];
1689     for (i = 0; i < ny; i++) {
1690       pX[i] = i;
1691       pYReal[i] = pcIn[i][0] / ny;
1692       pYImag[i] = pcIn[i][1] / ny;
1693       pYMag[i] = ::sqrt (pcIn[i][0] * pcIn[i][0] + pcIn[i][1] * pcIn[i][1]);
1694     }
1695
1696     PlotFileDocument* pPlotDoc = theApp->newPlotDoc();
1697     if (! pPlotDoc) {
1698       sys_error (ERR_SEVERE, "Internal error: unable to create Plot file");
1699     } else {
1700       PlotFile& rPlotFile = pPlotDoc->getPlotFile();
1701       std::ostringstream os;
1702       os << "Column " << xCursor;
1703       std::string title("title ");
1704       title += os.str();
1705       rPlotFile.addEzsetCommand (title.c_str());
1706       rPlotFile.addEzsetCommand ("xlabel Column");
1707       rPlotFile.addEzsetCommand ("ylabel Pixel Value");
1708       rPlotFile.addEzsetCommand ("lxfrac 0");
1709       rPlotFile.addEzsetCommand ("curve 1");
1710       rPlotFile.addEzsetCommand ("color 1");
1711       rPlotFile.addEzsetCommand ("dash 1");
1712       rPlotFile.addEzsetCommand ("curve 2");
1713       rPlotFile.addEzsetCommand ("color 4");
1714       rPlotFile.addEzsetCommand ("dash 3");
1715       rPlotFile.addEzsetCommand ("curve 3");
1716       rPlotFile.addEzsetCommand ("color 0");
1717       rPlotFile.addEzsetCommand ("solid");
1718       rPlotFile.addEzsetCommand ("box");
1719       rPlotFile.addEzsetCommand ("grid");
1720       rPlotFile.setCurveSize (4, ny);
1721       rPlotFile.addColumn (0, pX);
1722       rPlotFile.addColumn (1, pYReal);
1723       rPlotFile.addColumn (2, pYImag);
1724       rPlotFile.addColumn (3, pYMag);
1725       for (unsigned int iL = 0; iL < rIF.nLabels(); iL++)
1726         rPlotFile.addDescription (rIF.labelGet(iL).getLabelString().c_str());
1727       os << " FFT Plot of " << dynamic_cast<wxFrame*>(GetDocument()->GetFirstView()->GetFrame())->GetTitle().c_str();
1728       *theApp->getLog() << wxConvUTF8.cMB2WX(os.str().c_str()) << _T("\n");
1729       rPlotFile.addDescription (os.str().c_str());
1730     }
1731     delete pX;
1732     delete pYReal;
1733     delete pYImag;
1734     delete pYMag;
1735     delete pdTemp;
1736     delete [] pcIn;
1737
1738     if (theApp->getAskDeleteNewDocs())
1739       pPlotDoc->Modify (true);
1740     pPlotDoc->getView()->getFrame()->Show(true);
1741     pPlotDoc->UpdateAllViews ();
1742     pPlotDoc->Activate();
1743   }
1744 }
1745 #endif
1746
1747 void
1748 ImageFileView::OnCompareCol (wxCommandEvent& event)
1749 {
1750   int xCursor, yCursor;
1751   if (! m_pCanvas->GetCurrentCursor (xCursor, yCursor)) {
1752     wxMessageBox (_T("No column selected. Please use left mouse button on image to select column"),_T("Error"));
1753     return;
1754   }
1755
1756   std::vector<ImageFileDocument*> vecIFDoc;
1757   theApp->getCompatibleImages (GetDocument(), vecIFDoc);
1758   if (vecIFDoc.size() == 0) {
1759     wxMessageBox (_T("No compatible images for Column Comparison"), _T("Error"));
1760     return;
1761   }
1762   DialogGetComparisonImage dialogGetCompare (getFrameForChild(), _T("Get Comparison Image"), vecIFDoc, false);
1763
1764   if (dialogGetCompare.ShowModal() == wxID_OK) {
1765     ImageFileDocument* pCompareDoc = dialogGetCompare.getImageFileDocument();
1766     const ImageFile& rIF = GetDocument()->getImageFile();
1767     const ImageFile& rCompareIF = pCompareDoc->getImageFile();
1768
1769     ImageFileArrayConst v1 = rIF.getArray();
1770     ImageFileArrayConst v2 = rCompareIF.getArray();
1771     int nx = rIF.nx();
1772     int ny = rIF.ny();
1773
1774     if (v1 != NULL && xCursor < nx) {
1775       double* pX = new double [ny];
1776       double* pY1 = new double [ny];
1777       double* pY2 = new double [ny];
1778       for (int i = 0; i < ny; i++) {
1779         pX[i] = i;
1780         pY1[i] = v1[xCursor][i];
1781         pY2[i] = v2[xCursor][i];
1782       }
1783       PlotFileDocument* pPlotDoc = theApp->newPlotDoc();
1784       if (! pPlotDoc) {
1785         sys_error (ERR_SEVERE, "Internal error: unable to create Plot file");
1786       } else {
1787         PlotFile& rPlotFile = pPlotDoc->getPlotFile();
1788         std::ostringstream os;
1789         os << "Column " << xCursor << " Comparison";
1790         std::string title("title ");
1791         title += os.str();
1792         rPlotFile.addEzsetCommand (title.c_str());
1793         rPlotFile.addEzsetCommand ("xlabel Row");
1794         rPlotFile.addEzsetCommand ("ylabel Pixel Value");
1795         rPlotFile.addEzsetCommand ("lxfrac 0");
1796         rPlotFile.addEzsetCommand ("curve 1");
1797         rPlotFile.addEzsetCommand ("color 2");
1798         rPlotFile.addEzsetCommand ("curve 2");
1799         rPlotFile.addEzsetCommand ("color 4");
1800         rPlotFile.addEzsetCommand ("dash 5");
1801         rPlotFile.addEzsetCommand ("box");
1802         rPlotFile.addEzsetCommand ("grid");
1803         rPlotFile.setCurveSize (3, ny);
1804         rPlotFile.addColumn (0, pX);
1805         rPlotFile.addColumn (1, pY1);
1806         rPlotFile.addColumn (2, pY2);
1807
1808         unsigned int iL;
1809         for (iL = 0; iL < rIF.nLabels(); iL++) {
1810           std::ostringstream os;
1811           os << dynamic_cast<wxFrame*>(GetDocument()->GetFirstView()->GetFrame())->GetTitle().mb_str(wxConvUTF8);
1812           os << ": " << rIF.labelGet(iL).getLabelString();
1813           rPlotFile.addDescription (os.str().c_str());
1814         }
1815         for (iL = 0; iL < rCompareIF.nLabels(); iL++) {
1816           std::ostringstream os;
1817           os << dynamic_cast<wxFrame*>(pCompareDoc->GetFirstView()->GetFrame())->GetTitle().mb_str(wxConvUTF8);
1818           os << ": ";
1819           os << rCompareIF.labelGet(iL).getLabelString();
1820           rPlotFile.addDescription (os.str().c_str());
1821         }
1822         os << " Between " << dynamic_cast<wxFrame*>(GetDocument()->GetFirstView()->GetFrame())->GetTitle().c_str() << " and "
1823            << dynamic_cast<wxFrame*>(pCompareDoc->GetFirstView()->GetFrame())->GetTitle().c_str();
1824         *theApp->getLog() << wxConvUTF8.cMB2WX(os.str().c_str()) << _T("\n");
1825         rPlotFile.addDescription (os.str().c_str());
1826       }
1827       delete pX;
1828       delete pY1;
1829       delete pY2;
1830       if (theApp->getAskDeleteNewDocs())
1831         pPlotDoc->Modify (true);
1832       pPlotDoc->getView()->getFrame()->Show(true);
1833       pPlotDoc->UpdateAllViews ();
1834       pPlotDoc->Activate();
1835     }
1836   }
1837 }
1838
1839 void
1840 ImageFileView::OnCompareRow (wxCommandEvent& event)
1841 {
1842   int xCursor, yCursor;
1843   if (! m_pCanvas->GetCurrentCursor (xCursor, yCursor)) {
1844     wxMessageBox (_T("No column selected. Please use left mouse button on image to select column"),_T("Error"));
1845     return;
1846   }
1847
1848   std::vector<ImageFileDocument*> vecIFDoc;
1849   theApp->getCompatibleImages (GetDocument(), vecIFDoc);
1850
1851   if (vecIFDoc.size() == 0) {
1852     wxMessageBox (_T("No compatible images for Row Comparison"), _T("Error"));
1853     return;
1854   }
1855
1856   DialogGetComparisonImage dialogGetCompare (getFrameForChild(), _T("Get Comparison Image"), vecIFDoc, false);
1857
1858   if (dialogGetCompare.ShowModal() == wxID_OK) {
1859     ImageFileDocument* pCompareDoc = dialogGetCompare.getImageFileDocument();
1860     const ImageFile& rIF = GetDocument()->getImageFile();
1861     const ImageFile& rCompareIF = pCompareDoc->getImageFile();
1862
1863     ImageFileArrayConst v1 = rIF.getArray();
1864     ImageFileArrayConst v2 = rCompareIF.getArray();
1865     int nx = rIF.nx();
1866     int ny = rIF.ny();
1867
1868     if (v1 != NULL && yCursor < ny) {
1869       double* pX = new double [nx];
1870       double* pY1 = new double [nx];
1871       double* pY2 = new double [nx];
1872       for (int i = 0; i < nx; i++) {
1873         pX[i] = i;
1874         pY1[i] = v1[i][yCursor];
1875         pY2[i] = v2[i][yCursor];
1876       }
1877       PlotFileDocument* pPlotDoc = theApp->newPlotDoc();
1878       if (! pPlotDoc) {
1879         sys_error (ERR_SEVERE, "Internal error: unable to create Plot file");
1880       } else {
1881         PlotFile& rPlotFile = pPlotDoc->getPlotFile();
1882         std::ostringstream os;
1883         os << "Row " << yCursor << " Comparison";
1884         std::string title("title ");
1885         title += os.str();
1886         rPlotFile.addEzsetCommand (title.c_str());
1887         rPlotFile.addEzsetCommand ("xlabel Column");
1888         rPlotFile.addEzsetCommand ("ylabel Pixel Value");
1889         rPlotFile.addEzsetCommand ("lxfrac 0");
1890         rPlotFile.addEzsetCommand ("curve 1");
1891         rPlotFile.addEzsetCommand ("color 2");
1892         rPlotFile.addEzsetCommand ("curve 2");
1893         rPlotFile.addEzsetCommand ("color 4");
1894         rPlotFile.addEzsetCommand ("dash 5");
1895         rPlotFile.addEzsetCommand ("box");
1896         rPlotFile.addEzsetCommand ("grid");
1897         rPlotFile.setCurveSize (3, nx);
1898         rPlotFile.addColumn (0, pX);
1899         rPlotFile.addColumn (1, pY1);
1900         rPlotFile.addColumn (2, pY2);
1901         unsigned int iL;
1902         for (iL = 0; iL < rIF.nLabels(); iL++) {
1903           std::ostringstream os;
1904           os << dynamic_cast<wxFrame*>(GetDocument()->GetFirstView()->GetFrame())->GetTitle().mb_str(wxConvUTF8);
1905           os << ": ";
1906           os << rIF.labelGet(iL).getLabelString();
1907           rPlotFile.addDescription (os.str().c_str());
1908         }
1909         for (iL = 0; iL < rCompareIF.nLabels(); iL++) {
1910           std::ostringstream os;
1911           os << dynamic_cast<wxFrame*>(pCompareDoc->GetFirstView()->GetFrame())->GetTitle().mb_str(wxConvUTF8) << ": "
1912              << rCompareIF.labelGet(iL).getLabelString();
1913           rPlotFile.addDescription (os.str().c_str());
1914         }
1915         os << " Between " << dynamic_cast<wxFrame*>(GetDocument()->GetFirstView()->GetFrame())->GetTitle().mb_str(wxConvUTF8) << " and "
1916            << dynamic_cast<wxFrame*>(pCompareDoc->GetFirstView()->GetFrame())->GetTitle().c_str();
1917         *theApp->getLog() << wxConvUTF8.cMB2WX(os.str().c_str()) << _T("\n");
1918         rPlotFile.addDescription (os.str().c_str());
1919       }
1920       delete pX;
1921       delete pY1;
1922       delete pY2;
1923       if (theApp->getAskDeleteNewDocs())
1924         pPlotDoc->Modify (true);
1925       pPlotDoc->getView()->getFrame()->Show(true);
1926       pPlotDoc->UpdateAllViews ();
1927       pPlotDoc->Activate();
1928     }
1929   }
1930 }
1931
1932 static int NUMBER_HISTOGRAM_BINS = 256;
1933
1934 void
1935 ImageFileView::OnPlotHistogram (wxCommandEvent& event)
1936 {
1937   const ImageFile& rIF = GetDocument()->getImageFile();
1938   ImageFileArrayConst v = rIF.getArray();
1939   int nx = rIF.nx();
1940   int ny = rIF.ny();
1941
1942   if (v != NULL && nx > 0 && ny > 0) {
1943     PlotFileDocument* pPlotDoc = theApp->newPlotDoc();
1944     if (! pPlotDoc) {
1945       sys_error (ERR_SEVERE, "Internal error: unable to create Plot file");
1946       return;
1947     }
1948
1949     double* pX = new double [NUMBER_HISTOGRAM_BINS];
1950     double* pY = new double [NUMBER_HISTOGRAM_BINS];
1951     double dMin, dMax;
1952     rIF.getMinMax (dMin, dMax);
1953     double dBinWidth = (dMax - dMin) / NUMBER_HISTOGRAM_BINS;
1954
1955     for (int i = 0; i < NUMBER_HISTOGRAM_BINS; i++) {
1956       pX[i] = dMin + (i + 0.5) * dBinWidth;
1957       pY[i] = 0;
1958     }
1959     for (int ix = 0; ix < nx; ix++)
1960       for (int iy = 0; iy < ny; iy++) {
1961         int iBin = nearest<int> ((v[ix][iy] - dMin) / dBinWidth);
1962         if (iBin >= 0 && iBin < NUMBER_HISTOGRAM_BINS)
1963           pY[iBin] += 1;
1964       }
1965
1966       PlotFile& rPlotFile = pPlotDoc->getPlotFile();
1967       std::ostringstream os;
1968       os << "Histogram ";
1969       std::string title("title ");
1970       title += os.str();
1971       rPlotFile.addEzsetCommand (title.c_str());
1972       rPlotFile.addEzsetCommand ("xlabel Pixel Value");
1973       rPlotFile.addEzsetCommand ("ylabel Count");
1974       rPlotFile.addEzsetCommand ("box");
1975       rPlotFile.addEzsetCommand ("grid");
1976       rPlotFile.setCurveSize (2, NUMBER_HISTOGRAM_BINS);
1977       rPlotFile.addColumn (0, pX);
1978       rPlotFile.addColumn (1, pY);
1979       for (unsigned int iL = 0; iL < rIF.nLabels(); iL++) {
1980         std::ostringstream os;
1981         os << dynamic_cast<wxFrame*>(GetDocument()->GetFirstView()->GetFrame())->GetTitle().mb_str(wxConvUTF8);
1982         os << ": " << rIF.labelGet(iL).getLabelString();
1983         rPlotFile.addDescription (os.str().c_str());
1984       }
1985       os << " Plot of " << wxConvUTF8.cWX2MB(dynamic_cast<wxFrame*>(GetDocument()->GetFirstView()->GetFrame())->GetTitle().c_str());
1986       *theApp->getLog() << wxConvUTF8.cMB2WX(os.str().c_str()) << _T("\n");
1987       rPlotFile.addDescription (os.str().c_str());
1988       delete pX;
1989       delete pY;
1990       if (theApp->getAskDeleteNewDocs())
1991         pPlotDoc->Modify (true);
1992       pPlotDoc->getView()->getFrame()->Show(true);
1993       pPlotDoc->UpdateAllViews ();
1994       pPlotDoc->Activate();
1995   }
1996 }
1997
1998
1999 // PhantomCanvas
2000
2001 PhantomCanvas::PhantomCanvas (PhantomFileView* v, wxFrame *frame, const wxPoint& pos, const wxSize& size, const long style)
2002   : wxScrolledWindow(frame, -1, pos, size, style), m_pView(v)
2003 {
2004 }
2005
2006 PhantomCanvas::~PhantomCanvas ()
2007 {
2008   m_pView = NULL;
2009 }
2010
2011 void
2012 PhantomCanvas::OnDraw (wxDC& dc)
2013 {
2014   if (m_pView)
2015     m_pView->OnDraw(& dc);
2016 }
2017
2018 wxSize
2019 PhantomCanvas::GetBestSize() const
2020 {
2021   if (! m_pView)
2022     return wxSize(0,0);
2023
2024   int xSize, ySize;
2025   theApp->getMainFrame()->GetClientSize (&xSize, &ySize);
2026   xSize = maxValue<int> (xSize, ySize);
2027 #ifdef CTSIM_MDI
2028   ySize = xSize = (xSize / 4);
2029 #else
2030   xSize = ySize = static_cast<int>(ySize * .7);
2031 #endif
2032
2033   return wxSize (xSize, ySize);
2034 }
2035
2036
2037
2038 // PhantomFileView
2039
2040 IMPLEMENT_DYNAMIC_CLASS(PhantomFileView, wxView)
2041
2042 BEGIN_EVENT_TABLE(PhantomFileView, wxView)
2043 EVT_MENU(PHMMENU_FILE_PROPERTIES, PhantomFileView::OnProperties)
2044 EVT_MENU(PHMMENU_PROCESS_RASTERIZE, PhantomFileView::OnRasterize)
2045 EVT_MENU(PHMMENU_PROCESS_PROJECTIONS, PhantomFileView::OnProjections)
2046 END_EVENT_TABLE()
2047
2048 PhantomFileView::PhantomFileView()
2049 : wxView(), m_pFrame(NULL), m_pCanvas(NULL), m_pFileMenu(0)
2050 {
2051 #if defined(DEBUG) || defined(_DEBUG)
2052   m_iDefaultNDet = 165;
2053   m_iDefaultNView = 180;
2054   m_iDefaultNSample = 1;
2055 #else
2056   m_iDefaultNDet = 367;
2057   m_iDefaultNView = 320;
2058   m_iDefaultNSample = 2;
2059 #endif
2060   m_iDefaultOffsetView = 0;
2061   m_dDefaultRotation = 1;
2062   m_dDefaultFocalLength = 2;
2063   m_dDefaultCenterDetectorLength = 2;
2064   m_dDefaultViewRatio = 1;
2065   m_dDefaultScanRatio = 1;
2066   m_iDefaultGeometry = Scanner::GEOMETRY_PARALLEL;
2067   m_iDefaultTrace = Trace::TRACE_NONE;
2068
2069 #ifdef DEBUG
2070   m_iDefaultRasterNX = 115;
2071   m_iDefaultRasterNY = 115;
2072   m_iDefaultRasterNSamples = 1;
2073 #else
2074   m_iDefaultRasterNX = 256;
2075   m_iDefaultRasterNY = 256;
2076   m_iDefaultRasterNSamples = 2;
2077 #endif
2078   m_dDefaultRasterViewRatio = 1;
2079 }
2080
2081 PhantomFileView::~PhantomFileView()
2082 {
2083   GetDocumentManager()->FileHistoryRemoveMenu (m_pFileMenu);
2084   GetDocumentManager()->ActivateView(this, FALSE);
2085 }
2086
2087 void
2088 PhantomFileView::OnProperties (wxCommandEvent& event)
2089 {
2090   const int idPhantom = GetDocument()->getPhantomID();
2091   const wxString& namePhantom = GetDocument()->getPhantomName();
2092   std::ostringstream os;
2093   os << "Phantom " << namePhantom.c_str() << " (" << idPhantom << ")" << "\n";
2094   const Phantom& rPhantom = GetDocument()->getPhantom();
2095   rPhantom.printDefinitions (os);
2096 #if DEBUG
2097   rPhantom.print (os);
2098 #endif
2099   *theApp->getLog() << _T(">>>>\n") << wxConvUTF8.cMB2WX(os.str().c_str()) << _T("<<<<\n");
2100   wxMessageBox (wxConvUTF8.cMB2WX(os.str().c_str()), _T("Phantom Properties"));
2101   GetDocument()->Activate();
2102 }
2103
2104
2105 void
2106 PhantomFileView::OnProjections (wxCommandEvent& event)
2107 {
2108   DialogGetProjectionParameters dialogProjection (getFrameForChild(),
2109     m_iDefaultNDet, m_iDefaultNView, m_iDefaultOffsetView, m_iDefaultNSample, m_dDefaultRotation,
2110     m_dDefaultFocalLength, m_dDefaultCenterDetectorLength, m_dDefaultViewRatio, m_dDefaultScanRatio,
2111     m_iDefaultGeometry, m_iDefaultTrace);
2112   int retVal = dialogProjection.ShowModal();
2113   if (retVal != wxID_OK)
2114     return;
2115
2116   m_iDefaultNDet = dialogProjection.getNDet();
2117   m_iDefaultNView = dialogProjection.getNView();
2118   m_iDefaultOffsetView = dialogProjection.getOffsetView();
2119   m_iDefaultNSample = dialogProjection.getNSamples();
2120   m_iDefaultTrace = dialogProjection.getTrace();
2121   m_dDefaultRotation = dialogProjection.getRotAngle();
2122   m_dDefaultFocalLength = dialogProjection.getFocalLengthRatio();
2123   m_dDefaultCenterDetectorLength = dialogProjection.getCenterDetectorLengthRatio();
2124   m_dDefaultViewRatio = dialogProjection.getViewRatio();
2125   m_dDefaultScanRatio = dialogProjection.getScanRatio();
2126   wxString sGeometry (dialogProjection.getGeometry(), wxConvUTF8);
2127   m_iDefaultGeometry = Scanner::convertGeometryNameToID (sGeometry.mb_str(wxConvUTF8));
2128   double dRotationRadians = m_dDefaultRotation;
2129   m_dDefaultRotation /= TWOPI;  // convert back to fraction of a circle
2130
2131   if (m_iDefaultNDet <= 0 || m_iDefaultNView <= 0 || sGeometry == _T(""))
2132     return;
2133
2134   const Phantom& rPhantom = GetDocument()->getPhantom();
2135   Scanner theScanner (rPhantom, sGeometry.mb_str(wxConvUTF8), m_iDefaultNDet, m_iDefaultNView, m_iDefaultOffsetView, m_iDefaultNSample,
2136     dRotationRadians, m_dDefaultFocalLength, m_dDefaultCenterDetectorLength, m_dDefaultViewRatio, m_dDefaultScanRatio);
2137   if (theScanner.fail()) {
2138     wxString msg = _T("Failed making scanner\n");
2139     msg += wxConvUTF8.cMB2WX(theScanner.failMessage().c_str());
2140     *theApp->getLog() << msg << _T("\n");
2141     wxMessageBox (msg, _T("Error"));
2142     return;
2143   }
2144
2145   std::ostringstream os;
2146   os << "Projections for " << rPhantom.name().c_str()
2147      << ": nDet=" << m_iDefaultNDet
2148      << ", nView=" << m_iDefaultNView
2149      << ", gantry offset=" << m_iDefaultOffsetView
2150      << ", nSamples=" << m_iDefaultNSample
2151      << ", RotAngle=" << m_dDefaultRotation
2152      << ", FocalLengthRatio=" << m_dDefaultFocalLength
2153      << ", CenterDetectorLengthRatio=" << m_dDefaultCenterDetectorLength
2154      << ", ViewRatio=" << m_dDefaultViewRatio
2155      << ", ScanRatio=" << m_dDefaultScanRatio
2156      << ", Geometry=" << sGeometry.mb_str(wxConvUTF8)
2157      << ", FanBeamAngle=" << convertRadiansToDegrees (theScanner.fanBeamAngle());
2158
2159   Timer timer;
2160   Projections* pProj = NULL;
2161   if (m_iDefaultTrace > Trace::TRACE_CONSOLE) {
2162     pProj = new Projections;
2163     pProj->initFromScanner (theScanner);
2164
2165     ProjectionsDialog dialogProjections (theScanner, *pProj, rPhantom, m_iDefaultTrace, dynamic_cast<wxWindow*>(getFrameForChild()));
2166     for (int iView = 0; iView < pProj->nView(); iView++) {
2167       ::wxYield();
2168       if (dialogProjections.isCancelled() || ! dialogProjections.projectView (iView)) {
2169         delete pProj;
2170         return;
2171       }
2172       ::wxYield();
2173       while (dialogProjections.isPaused()) {
2174         ::wxYield();
2175         ::wxMilliSleep(50);
2176       }
2177     }
2178   } else {
2179 #if HAVE_WXTHREADS
2180     if (theApp->getUseBackgroundTasks()) {
2181       ProjectorSupervisorThread* pProjector = new ProjectorSupervisorThread
2182         (this, m_iDefaultNDet, m_iDefaultNView, m_iDefaultOffsetView, 
2183          sGeometry.mb_str(wxConvUTF8), m_iDefaultNSample, dRotationRadians,
2184          m_dDefaultFocalLength, m_dDefaultCenterDetectorLength, m_dDefaultViewRatio, 
2185          m_dDefaultScanRatio, wxConvUTF8.cMB2WX(os.str().c_str()));
2186       if (pProjector->Create() != wxTHREAD_NO_ERROR) {
2187         sys_error (ERR_SEVERE, "Error creating projector thread");
2188         delete pProjector;
2189         return;
2190       }
2191       pProjector->SetPriority(60);
2192       pProjector->Run();
2193       return;
2194     } else
2195 #endif // HAVE_WXTHREADS
2196     {
2197       pProj = new Projections;
2198       pProj->initFromScanner (theScanner);
2199       wxProgressDialog dlgProgress (_T("Projection"), _T("Projection Progress"), pProj->nView() + 1, getFrameForChild(), wxPD_CAN_ABORT );
2200       for (int i = 0; i < pProj->nView(); i++) {
2201         //theScanner.collectProjections (*pProj, rPhantom, i, 1, true, m_iDefaultTrace);
2202         theScanner.collectProjections (*pProj, rPhantom, i, 1, theScanner.offsetView(), true, m_iDefaultTrace);
2203         if ((i + 1) % ITER_PER_UPDATE == 0)
2204           if (! dlgProgress.Update (i+1)) {
2205             delete pProj;
2206             return;
2207           }
2208       }
2209     }
2210   }
2211
2212   *theApp->getLog() << wxConvUTF8.cMB2WX(os.str().c_str()) << _T("\n");
2213   pProj->setRemark (os.str());
2214   pProj->setCalcTime (timer.timerEnd());
2215
2216   ProjectionFileDocument* pProjectionDoc = theApp->newProjectionDoc();
2217   if (! pProjectionDoc) {
2218     sys_error (ERR_SEVERE, "Unable to create projection document");
2219     return;
2220   }
2221   pProjectionDoc->setProjections (pProj);
2222   if (theApp->getAskDeleteNewDocs())
2223     pProjectionDoc-> Modify(true);
2224   OnUpdate(this, NULL);
2225   pProjectionDoc->UpdateAllViews (this);
2226   pProjectionDoc->getView()->setInitialClientSize();
2227   pProjectionDoc->Activate();
2228 }
2229 void
2230 PhantomFileView::OnRasterize (wxCommandEvent& event)
2231 {
2232   DialogGetRasterParameters dialogRaster (getFrameForChild(), m_iDefaultRasterNX, m_iDefaultRasterNY,
2233     m_iDefaultRasterNSamples, m_dDefaultRasterViewRatio);
2234   int retVal = dialogRaster.ShowModal();
2235   if (retVal != wxID_OK)
2236     return;
2237
2238   m_iDefaultRasterNX = dialogRaster.getXSize();
2239   m_iDefaultRasterNY  = dialogRaster.getYSize();
2240   m_iDefaultRasterNSamples = dialogRaster.getNSamples();
2241   m_dDefaultRasterViewRatio = dialogRaster.getViewRatio();
2242   if (m_iDefaultRasterNSamples < 1)
2243     m_iDefaultRasterNSamples = 1;
2244   if (m_dDefaultRasterViewRatio < 0)
2245     m_dDefaultRasterViewRatio = 0;
2246   if (m_iDefaultRasterNX <= 0 || m_iDefaultRasterNY <= 0)
2247     return;
2248
2249   const Phantom& rPhantom = GetDocument()->getPhantom();
2250   std::ostringstream os;
2251   os << "Rasterize Phantom " << rPhantom.name() << ": XSize=" << m_iDefaultRasterNX << ", YSize="
2252     << m_iDefaultRasterNY << ", ViewRatio=" << m_dDefaultRasterViewRatio << ", nSamples="
2253     << m_iDefaultRasterNSamples;;
2254
2255 #if HAVE_WXTHREADS
2256   if (theApp->getUseBackgroundTasks()) {
2257     RasterizerSupervisorThread* pThread = new RasterizerSupervisorThread
2258       (this, m_iDefaultRasterNX, m_iDefaultRasterNY,
2259        m_iDefaultRasterNSamples, m_dDefaultRasterViewRatio, 
2260        wxConvUTF8.cMB2WX(os.str().c_str()));
2261     if (pThread->Create() != wxTHREAD_NO_ERROR) {
2262       *theApp->getLog() << _T("Error creating rasterizer thread\n");
2263       return;
2264     }
2265     pThread->SetPriority (60);
2266     pThread->Run();
2267   } else
2268 #endif
2269   {
2270     ImageFile* pImageFile = new ImageFile (m_iDefaultRasterNX, m_iDefaultRasterNY);
2271
2272     wxProgressDialog dlgProgress (_T("Rasterize"),
2273                                   _T("Rasterization Progress"),
2274                                   pImageFile->nx() + 1,
2275                                   getFrameForChild(),
2276                                   wxPD_CAN_ABORT );
2277     Timer timer;
2278     for (unsigned int i = 0; i < pImageFile->nx(); i++) {
2279       rPhantom.convertToImagefile (*pImageFile, m_dDefaultRasterViewRatio,
2280                                    m_iDefaultRasterNSamples, Trace::TRACE_NONE,
2281                                    i, 1, true);
2282       if ((i + 1) % ITER_PER_UPDATE == 0)
2283         if (! dlgProgress.Update (i+1)) {
2284           delete pImageFile;
2285           return;
2286         }
2287     }
2288
2289     ImageFileDocument* pRasterDoc = theApp->newImageDoc();
2290     if (! pRasterDoc) {
2291       sys_error (ERR_SEVERE, "Unable to create image file");
2292       return;
2293     }
2294     pRasterDoc->setImageFile (pImageFile);
2295     if (theApp->getAskDeleteNewDocs())
2296       pRasterDoc->Modify (true);
2297     *theApp->getLog() << wxConvUTF8.cMB2WX(os.str().c_str()) << _T("\n");
2298     pImageFile->labelAdd (os.str().c_str(), timer.timerEnd());
2299
2300     pRasterDoc->UpdateAllViews(this);
2301     pRasterDoc->getView()->setInitialClientSize();
2302     pRasterDoc->Activate();
2303   }
2304 }
2305
2306
2307 PhantomCanvas*
2308 PhantomFileView::CreateCanvas (wxFrame *parent)
2309 {
2310   PhantomCanvas* pCanvas =
2311     new PhantomCanvas (this, parent, wxPoint(-1,-1),
2312                        wxSize(-1,-1), wxFULL_REPAINT_ON_RESIZE);
2313   pCanvas->SetBackgroundColour(*wxWHITE);
2314   pCanvas->ClearBackground();
2315
2316   return pCanvas;
2317 }
2318
2319 #if CTSIM_MDI
2320 wxDocMDIChildFrame*
2321 #else
2322 wxDocChildFrame*
2323 #endif
2324 PhantomFileView::CreateChildFrame(wxDocument *doc, wxView *view)
2325 {
2326 #if CTSIM_MDI
2327   wxDocMDIChildFrame *subframe = new wxDocMDIChildFrame (doc, view, theApp->getMainFrame(), -1, _T("Phantom Frame"), wxPoint(-1,-1), wxSize(-1,-1), wxDEFAULT_FRAME_STYLE);
2328 #else
2329   wxDocChildFrame *subframe = new wxDocChildFrame (doc, view, theApp->getMainFrame(), -1, _T("Phantom Frame"), wxPoint(-1,-1), wxSize(-1,-1), wxDEFAULT_FRAME_STYLE);
2330 #endif
2331   theApp->setIconForFrame (subframe);
2332
2333   m_pFileMenu = new wxMenu;
2334
2335   m_pFileMenu->Append(MAINMENU_FILE_CREATE_PHANTOM, _T("Cr&eate Phantom...\tCtrl-P"));
2336   m_pFileMenu->Append(MAINMENU_FILE_CREATE_FILTER, _T("Create &Filter...\tCtrl-F"));
2337   m_pFileMenu->Append(wxID_OPEN, _T("&Open...\tCtrl-O"));
2338   m_pFileMenu->Append(wxID_SAVEAS, _T("Save &As..."));
2339   m_pFileMenu->Append(wxID_CLOSE, _T("&Close"));
2340
2341   m_pFileMenu->AppendSeparator();
2342   m_pFileMenu->Append(PHMMENU_FILE_PROPERTIES, _T("P&roperties\tCtrl-I"));
2343
2344   m_pFileMenu->AppendSeparator();
2345   m_pFileMenu->Append(wxID_PRINT, _T("&Print..."));
2346   m_pFileMenu->Append(wxID_PRINT_SETUP, _T("Print &Setup..."));
2347   m_pFileMenu->Append(wxID_PREVIEW, _T("Print Pre&view"));
2348   m_pFileMenu->AppendSeparator();
2349   m_pFileMenu->Append(MAINMENU_IMPORT, _T("&Import...\tCtrl-M"));
2350   m_pFileMenu->AppendSeparator();
2351   m_pFileMenu->Append (MAINMENU_FILE_PREFERENCES, _T("Prefere&nces..."));
2352   m_pFileMenu->Append(MAINMENU_FILE_EXIT, _T("E&xit"));
2353   GetDocumentManager()->FileHistoryAddFilesToMenu(m_pFileMenu);
2354   GetDocumentManager()->FileHistoryUseMenu(m_pFileMenu);
2355
2356   wxMenu *process_menu = new wxMenu;
2357   process_menu->Append(PHMMENU_PROCESS_RASTERIZE, _T("&Rasterize...\tCtrl-R"));
2358   process_menu->Append(PHMMENU_PROCESS_PROJECTIONS, _T("&Projections...\tCtrl-J"));
2359
2360   wxMenu *help_menu = new wxMenu;
2361   help_menu->Append(MAINMENU_HELP_CONTENTS, _T("&Contents\tF1"));
2362   help_menu->Append (MAINMENU_HELP_TIPS, _T("&Tips"));
2363   help_menu->Append (IDH_QUICKSTART, _T("&Quick Start"));
2364   help_menu->Append(MAINMENU_HELP_ABOUT, _T("&About"));
2365
2366   wxMenuBar *menu_bar = new wxMenuBar;
2367
2368   menu_bar->Append(m_pFileMenu, _T("&File"));
2369   menu_bar->Append(process_menu, _T("&Process"));
2370   menu_bar->Append(help_menu, _T("&Help"));
2371
2372   subframe->SetMenuBar(menu_bar);
2373   subframe->Centre(wxBOTH);
2374
2375   wxAcceleratorEntry accelEntries[3];
2376   accelEntries[0].Set (wxACCEL_CTRL, static_cast<int>('J'), PHMMENU_PROCESS_PROJECTIONS);
2377   accelEntries[1].Set (wxACCEL_CTRL, static_cast<int>('R'), PHMMENU_PROCESS_RASTERIZE);
2378   accelEntries[2].Set (wxACCEL_CTRL, static_cast<int>('I'), PHMMENU_FILE_PROPERTIES);
2379   wxAcceleratorTable accelTable (3, accelEntries);
2380   subframe->SetAcceleratorTable (accelTable);
2381
2382   return subframe;
2383 }
2384
2385
2386 bool
2387 PhantomFileView::OnCreate(wxDocument *doc, long WXUNUSED(flags) )
2388 {
2389   m_pFrame = CreateChildFrame(doc, this);
2390   SetFrame(m_pFrame);
2391   m_pCanvas = CreateCanvas (m_pFrame);
2392   m_pFrame->SetClientSize (m_pCanvas->GetBestSize());
2393   m_pCanvas->SetClientSize (m_pCanvas->GetBestSize());
2394   m_pFrame->SetTitle (_T("PhantomFileView"));
2395
2396   m_pFrame->Show(true);
2397   Activate(true);
2398
2399   return true;
2400 }
2401
2402 void
2403 PhantomFileView::OnUpdate (wxView *WXUNUSED(sender), wxObject *WXUNUSED(hint) )
2404 {
2405   if (m_pCanvas)
2406     m_pCanvas->Refresh();
2407 }
2408
2409 bool
2410 PhantomFileView::OnClose (bool deleteWindow)
2411 {
2412   if (! GetDocument() || ! GetDocument()->Close())
2413     return false;
2414
2415   Activate(false);
2416   if (m_pCanvas) {
2417     m_pCanvas->setView(NULL);
2418     m_pCanvas = NULL;
2419   }
2420   wxString s(wxTheApp->GetAppName());
2421   if (m_pFrame)
2422     m_pFrame->SetTitle(s);
2423
2424   SetFrame(NULL);
2425
2426   if (deleteWindow) {
2427     delete m_pFrame;
2428     m_pFrame = NULL;
2429     if (GetDocument() && GetDocument()->getBadFileOpen())
2430       ::wxYield();  // wxWindows bug workaround
2431   }
2432
2433   return true;
2434 }
2435
2436 void
2437 PhantomFileView::OnDraw (wxDC* dc)
2438 {
2439   int xsize, ysize;
2440   m_pCanvas->GetClientSize (&xsize, &ysize);
2441   SGPDriver driver (dc, xsize, ysize);
2442   SGP sgp (driver);
2443   const Phantom& rPhantom = GetDocument()->getPhantom();
2444   sgp.setColor (C_RED);
2445   rPhantom.show (sgp);
2446 }
2447
2448 // ProjectionCanvas
2449
2450 ProjectionFileCanvas::ProjectionFileCanvas (ProjectionFileView* v, wxFrame *frame, const wxPoint& pos, const wxSize& size, const long style)
2451 : wxScrolledWindow(frame, -1, pos, size, style)
2452 {
2453   m_pView = v;
2454 }
2455
2456 ProjectionFileCanvas::~ProjectionFileCanvas ()
2457 {
2458   m_pView = NULL;
2459 }
2460
2461 void
2462 ProjectionFileCanvas::OnDraw(wxDC& dc)
2463 {
2464   if (m_pView)
2465     m_pView->OnDraw(& dc);
2466 }
2467
2468 wxSize
2469 ProjectionFileCanvas::GetBestSize () const
2470 {
2471   const int iMinX = 50;
2472   const int iMinY = 20;
2473   wxSize bestSize (iMinX,iMinY);
2474
2475   if (m_pView) {
2476     Projections& rProj = m_pView->GetDocument()->getProjections();
2477     bestSize.Set (rProj.nDet(), rProj.nView());
2478   }
2479
2480   if (bestSize.x > 800)
2481     bestSize.x = 800;
2482   if (bestSize.y > 800)
2483     bestSize.y = 800;
2484
2485   if (bestSize.x < iMinX)
2486     bestSize.x = iMinX;
2487   if (bestSize.y < iMinY)
2488     bestSize.y = iMinY;
2489
2490   return bestSize;
2491 }
2492
2493
2494 // ProjectionFileView
2495
2496 IMPLEMENT_DYNAMIC_CLASS(ProjectionFileView, wxView)
2497
2498 BEGIN_EVENT_TABLE(ProjectionFileView, wxView)
2499 EVT_MENU(PJMENU_FILE_PROPERTIES, ProjectionFileView::OnProperties)
2500 EVT_MENU(PJMENU_RECONSTRUCT_FBP, ProjectionFileView::OnReconstructFBP)
2501 EVT_MENU(PJMENU_RECONSTRUCT_FBP_REBIN, ProjectionFileView::OnReconstructFBPRebin)
2502 EVT_MENU(PJMENU_RECONSTRUCT_FOURIER, ProjectionFileView::OnReconstructFourier)
2503 EVT_MENU(PJMENU_CONVERT_RECTANGULAR, ProjectionFileView::OnConvertRectangular)
2504 EVT_MENU(PJMENU_CONVERT_POLAR, ProjectionFileView::OnConvertPolar)
2505 EVT_MENU(PJMENU_CONVERT_FFT_POLAR, ProjectionFileView::OnConvertFFTPolar)
2506 EVT_MENU(PJMENU_CONVERT_PARALLEL, ProjectionFileView::OnConvertParallel)
2507 EVT_MENU(PJMENU_PLOT_TTHETA_SAMPLING, ProjectionFileView::OnPlotTThetaSampling)
2508 EVT_MENU(PJMENU_PLOT_HISTOGRAM, ProjectionFileView::OnPlotHistogram)
2509   // EVT_MENU(PJMENU_ARTIFACT_REDUCTION, ProjectionFileView::OnArtifactReduction)
2510 END_EVENT_TABLE()
2511
2512
2513 ProjectionFileView::ProjectionFileView()
2514   : wxView(), m_pBitmap(0), m_pFrame(0), m_pCanvas(0), m_pFileMenu(0)
2515 {
2516 #ifdef DEBUG
2517   m_iDefaultNX = 115;
2518   m_iDefaultNY = 115;
2519 #else
2520   m_iDefaultNX = 256;
2521   m_iDefaultNY = 256;
2522 #endif
2523
2524   m_iDefaultFilter = SignalFilter::FILTER_ABS_BANDLIMIT;
2525   m_dDefaultFilterParam = 1.;
2526 #if HAVE_FFTW
2527   m_iDefaultFilterMethod = ProcessSignal::FILTER_METHOD_RFFTW;
2528   m_iDefaultFilterGeneration = ProcessSignal::FILTER_GENERATION_INVERSE_FOURIER;
2529 #else
2530   m_iDefaultFilterMethod = ProcessSignal::FILTER_METHOD_CONVOLUTION;
2531   m_iDefaultFilterGeneration = ProcessSignal::FILTER_GENERATION_DIRECT;
2532 #endif
2533   m_iDefaultZeropad = 2;
2534   m_iDefaultBackprojector = Backprojector::BPROJ_IDIFF;
2535   m_iDefaultInterpolation = Backprojector::INTERP_LINEAR;
2536   m_iDefaultInterpParam = 1;
2537   m_iDefaultTrace = Trace::TRACE_NONE;
2538
2539   m_iDefaultPolarNX = 256;
2540   m_iDefaultPolarNY = 256;
2541   m_iDefaultPolarInterpolation = Projections::POLAR_INTERP_BILINEAR;
2542   m_iDefaultPolarZeropad = 2;
2543 }
2544
2545 ProjectionFileView::~ProjectionFileView()
2546 {
2547   GetDocumentManager()->FileHistoryRemoveMenu (m_pFileMenu);
2548   GetDocumentManager()->ActivateView(this, FALSE);;
2549 }
2550
2551 void
2552 ProjectionFileView::OnProperties (wxCommandEvent& event)
2553 {
2554   const Projections& rProj = GetDocument()->getProjections();
2555   std::ostringstream os;
2556   rProj.printScanInfo(os);
2557   *theApp->getLog() << _T(">>>>\n") << wxConvUTF8.cMB2WX(os.str().c_str()) << _T("<<<<\n");
2558   wxMessageDialog dialogMsg (getFrameForChild(), wxConvUTF8.cMB2WX(os.str().c_str()), _T("Projection File Properties"), wxOK | wxICON_INFORMATION);
2559   dialogMsg.ShowModal();
2560   GetDocument()->Activate();
2561 }
2562
2563
2564 void
2565 ProjectionFileView::OnConvertRectangular (wxCommandEvent& event)
2566 {
2567   Projections& rProj = GetDocument()->getProjections();
2568
2569   int nDet = rProj.nDet();
2570   int nView = rProj.nView();
2571   ImageFile* pIF = new ImageFile (nDet, nView);
2572   ImageFileArray v = pIF->getArray();
2573   for (int iv = 0; iv < nView; iv++) {
2574     DetectorValue* detval = rProj.getDetectorArray(iv).detValues();
2575
2576     for (int id = 0; id < nDet; id++)
2577       v[id][iv] = detval[id];
2578   }
2579
2580   ImageFileDocument* pRectDoc = theApp->newImageDoc ();
2581   if (! pRectDoc) {
2582     sys_error (ERR_SEVERE, "Unable to create image file");
2583     return;
2584   }
2585   pRectDoc->setImageFile (pIF);
2586   pIF->labelAdd (rProj.getLabel().getLabelString().c_str(), rProj.calcTime());
2587   std::ostringstream os;
2588   os << "Convert projection file " << getFrame()->GetTitle().c_str() << " to rectangular image";
2589   *theApp->getLog() << wxConvUTF8.cMB2WX(os.str().c_str()) << _T("\n");
2590   pIF->labelAdd (os.str().c_str());
2591   if (theApp->getAskDeleteNewDocs())
2592     pRectDoc->Modify (true);
2593   pRectDoc->UpdateAllViews();
2594   pRectDoc->getView()->setInitialClientSize();
2595   pRectDoc->Activate();
2596 }
2597
2598 void
2599 ProjectionFileView::OnConvertPolar (wxCommandEvent& event)
2600 {
2601   Projections& rProj = GetDocument()->getProjections();
2602   DialogGetConvertPolarParameters dialogPolar (getFrameForChild(), _T("Convert Polar"), m_iDefaultPolarNX, m_iDefaultPolarNY,
2603     m_iDefaultPolarInterpolation, -1, IDH_DLG_POLAR);
2604   if (dialogPolar.ShowModal() == wxID_OK) {
2605     wxProgressDialog dlgProgress (_T("Convert Polar"), _T("Conversion Progress"), 1, getFrameForChild(), wxPD_APP_MODAL);
2606     wxString strInterpolation (dialogPolar.getInterpolationName(), wxConvUTF8);
2607     m_iDefaultPolarNX = dialogPolar.getXSize();
2608     m_iDefaultPolarNY = dialogPolar.getYSize();
2609     ImageFile* pIF = new ImageFile (m_iDefaultPolarNX, m_iDefaultPolarNY);
2610     m_iDefaultPolarInterpolation = Projections::convertInterpNameToID (strInterpolation.mb_str(wxConvUTF8));
2611
2612     if (! rProj.convertPolar (*pIF, m_iDefaultPolarInterpolation)) {
2613       delete pIF;
2614       *theApp->getLog() << _T("Error converting to Polar\n");
2615       return;
2616     }
2617
2618     ImageFileDocument* pPolarDoc = theApp->newImageDoc();
2619     if (! pPolarDoc) {
2620       sys_error (ERR_SEVERE, "Unable to create image file");
2621       return;
2622     }
2623     pPolarDoc->setImageFile (pIF);
2624     pIF->labelAdd (rProj.getLabel().getLabelString().c_str(), rProj.calcTime());
2625     std::ostringstream os;
2626     os << "Convert projection file " << getFrame()->GetTitle().c_str() << " to polar image: xSize="
2627       << m_iDefaultPolarNX << ", ySize=" << m_iDefaultPolarNY << ", interpolation="
2628       << strInterpolation.c_str();
2629     *theApp->getLog() << wxConvUTF8.cMB2WX(os.str().c_str()) << _T("\n");
2630     pIF->labelAdd (os.str().c_str());
2631     if (theApp->getAskDeleteNewDocs())
2632       pPolarDoc->Modify (true);
2633     pPolarDoc->UpdateAllViews ();
2634     pPolarDoc->getView()->setInitialClientSize();
2635     pPolarDoc->Activate();
2636   }
2637 }
2638
2639 void
2640 ProjectionFileView::OnConvertFFTPolar (wxCommandEvent& event)
2641 {
2642   Projections& rProj = GetDocument()->getProjections();
2643   DialogGetConvertPolarParameters dialogPolar (getFrameForChild(), _T("Convert to FFT Polar"), m_iDefaultPolarNX, m_iDefaultPolarNY,
2644     m_iDefaultPolarInterpolation, m_iDefaultPolarZeropad, IDH_DLG_FFT_POLAR);
2645   if (dialogPolar.ShowModal() == wxID_OK) {
2646     wxProgressDialog dlgProgress (_T("Convert FFT Polar"), _T("Conversion Progress"), 1, getFrameForChild(), wxPD_APP_MODAL);
2647     wxString strInterpolation (dialogPolar.getInterpolationName(), wxConvUTF8);
2648     m_iDefaultPolarNX = dialogPolar.getXSize();
2649     m_iDefaultPolarNY = dialogPolar.getYSize();
2650     m_iDefaultPolarZeropad = dialogPolar.getZeropad();
2651     ImageFile* pIF = new ImageFile (m_iDefaultPolarNX, m_iDefaultPolarNY);
2652
2653     m_iDefaultPolarInterpolation = Projections::convertInterpNameToID (strInterpolation.mb_str(wxConvUTF8));
2654     if (! rProj.convertFFTPolar (*pIF, m_iDefaultPolarInterpolation, m_iDefaultPolarZeropad)) {
2655       delete pIF;
2656       *theApp->getLog() << _T("Error converting to polar\n");
2657       return;
2658     }
2659     ImageFileDocument* pPolarDoc = theApp->newImageDoc();
2660     if (! pPolarDoc) {
2661       sys_error (ERR_SEVERE, "Unable to create image file");
2662       return;
2663     }
2664     pPolarDoc->setImageFile (pIF);
2665     pIF->labelAdd (rProj.getLabel().getLabelString().c_str(), rProj.calcTime());
2666     std::ostringstream os;
2667     os << "Convert projection file " << getFrame()->GetTitle().c_str() << " to FFT polar image: xSize="
2668       << m_iDefaultPolarNX << ", ySize=" << m_iDefaultPolarNY << ", interpolation="
2669       << strInterpolation.c_str() << ", zeropad=" << m_iDefaultPolarZeropad;
2670     *theApp->getLog() << wxConvUTF8.cMB2WX(os.str().c_str()) << _T("\n");
2671     pIF->labelAdd (os.str().c_str());
2672     if (theApp->getAskDeleteNewDocs())
2673       pPolarDoc->Modify (true);
2674     pPolarDoc->UpdateAllViews (this);
2675     pPolarDoc->getView()->setInitialClientSize();
2676     pPolarDoc->Activate();
2677   }
2678 }
2679
2680 void
2681 ProjectionFileView::OnPlotTThetaSampling (wxCommandEvent& event)
2682 {
2683   DialogGetThetaRange dlgTheta (this->getFrame(), ParallelRaysums::THETA_RANGE_UNCONSTRAINED);
2684   if (dlgTheta.ShowModal() != wxID_OK)
2685     return;
2686
2687   int iThetaRange = dlgTheta.getThetaRange();
2688
2689   Projections& rProj = GetDocument()->getProjections();
2690   ParallelRaysums parallel (&rProj, iThetaRange);
2691   PlotFileDocument* pPlotDoc = theApp->newPlotDoc();
2692   PlotFile& rPlot = pPlotDoc->getPlotFile();
2693   ParallelRaysums::CoordinateContainer& coordContainer = parallel.getCoordinates();
2694   double* pdT = new double [parallel.getNumCoordinates()];
2695   double* pdTheta = new double [parallel.getNumCoordinates()];
2696
2697   for (int i = 0; i < parallel.getNumCoordinates(); i++) {
2698     pdT[i] = coordContainer[i]->m_dT;
2699     pdTheta[i] = coordContainer[i]->m_dTheta;
2700   }
2701   rPlot.setCurveSize (2, parallel.getNumCoordinates(), true);
2702   rPlot.addEzsetCommand ("title T-Theta Sampling");
2703   rPlot.addEzsetCommand ("xlabel T");
2704   rPlot.addEzsetCommand ("ylabel Theta");
2705   rPlot.addEzsetCommand ("curve 1");
2706   if (rProj.nDet() < 50 && rProj.nView() < 50)
2707     rPlot.addEzsetCommand ("symbol 1"); // x symbol
2708   else
2709     rPlot.addEzsetCommand ("symbol 6"); // point symbol
2710   rPlot.addEzsetCommand ("noline");
2711   rPlot.addColumn (0, pdT);
2712   rPlot.addColumn (1, pdTheta);
2713   delete pdT;
2714   delete pdTheta;
2715   if (theApp->getAskDeleteNewDocs())
2716     pPlotDoc->Modify (true);
2717   pPlotDoc->getView()->getFrame()->Show(true);
2718   pPlotDoc->UpdateAllViews ();
2719   pPlotDoc->Activate();
2720 }
2721
2722
2723 void
2724 ProjectionFileView::OnPlotHistogram (wxCommandEvent& event)
2725 {
2726   Projections& rProj = GetDocument()->getProjections();
2727   int nDet = rProj.nDet();
2728   int nView = rProj.nView();
2729
2730   if (nDet < 1 || nView < 1)
2731     return;
2732
2733   PlotFileDocument* pPlotDoc = theApp->newPlotDoc();
2734   if (! pPlotDoc) {
2735     sys_error (ERR_SEVERE, "Internal error: unable to create Plot file");
2736     return;
2737   }
2738
2739   DetectorValue* pdDetval = rProj.getDetectorArray(0).detValues();
2740   double dMin = pdDetval[0], dMax = pdDetval[0];
2741
2742   for (int iv = 0; iv < nView; iv++) {
2743     pdDetval = rProj.getDetectorArray(iv).detValues();
2744     for (int id = 0; id < nDet; id++) {
2745       double dV = pdDetval[id];
2746       if (dV < dMin)
2747         dMin = dV;
2748       else if (dV > dMax)
2749         dMax = dV;
2750     }
2751   }
2752
2753   double* pX = new double [NUMBER_HISTOGRAM_BINS];
2754   double* pY = new double [NUMBER_HISTOGRAM_BINS];
2755   double dBinWidth = (dMax - dMin) / NUMBER_HISTOGRAM_BINS;
2756
2757   for (int i = 0; i < NUMBER_HISTOGRAM_BINS; i++) {
2758     pX[i] = dMin + (i + 0.5) * dBinWidth;
2759     pY[i] = 0;
2760   }
2761   for (int j = 0; j < nView; j++) {
2762     pdDetval = rProj.getDetectorArray(j).detValues();
2763     for (int id = 0; id < nDet; id++) {
2764       int iBin = nearest<int> ((pdDetval[id] - dMin) / dBinWidth);
2765       if (iBin >= 0 && iBin < NUMBER_HISTOGRAM_BINS)
2766         pY[iBin] += 1;
2767     }
2768   }
2769   PlotFile& rPlotFile = pPlotDoc->getPlotFile();
2770   std::ostringstream os;
2771   os << "Histogram";
2772   std::string title("title ");
2773   title += os.str();
2774   rPlotFile.addEzsetCommand (title.c_str());
2775   rPlotFile.addEzsetCommand ("xlabel Detector Value");
2776   rPlotFile.addEzsetCommand ("ylabel Count");
2777   rPlotFile.addEzsetCommand ("box");
2778   rPlotFile.addEzsetCommand ("grid");
2779   rPlotFile.setCurveSize (2, NUMBER_HISTOGRAM_BINS);
2780   rPlotFile.addColumn (0, pX);
2781   rPlotFile.addColumn (1, pY);
2782   rPlotFile.addDescription (rProj.remark());
2783   os << " plot of " << dynamic_cast<wxFrame*>(GetDocument()->GetFirstView()->GetFrame())->GetTitle().c_str();
2784   *theApp->getLog() << wxConvUTF8.cMB2WX(os.str().c_str()) << _T("\n");
2785   rPlotFile.addDescription (os.str().c_str());
2786   delete pX;
2787   delete pY;
2788   if (theApp->getAskDeleteNewDocs())
2789     pPlotDoc->Modify (true);
2790   pPlotDoc->getView()->getFrame()->Show(true);
2791   pPlotDoc->UpdateAllViews ();
2792   pPlotDoc->Activate();
2793 }
2794
2795
2796 void
2797 ProjectionFileView::OnConvertParallel (wxCommandEvent& event)
2798 {
2799   Projections& rProj = GetDocument()->getProjections();
2800   if (rProj.geometry() == Scanner::GEOMETRY_PARALLEL) {
2801     wxMessageBox (_T("Projections are already parallel"), _T("Error"));
2802     return;
2803   }
2804   wxProgressDialog dlgProgress (_T("Convert to Parallel"), _T("Conversion Progress"), 1, getFrameForChild(), wxPD_APP_MODAL);
2805   Projections* pProjNew = rProj.interpolateToParallel();
2806   ProjectionFileDocument* pProjDocNew = theApp->newProjectionDoc();
2807   pProjDocNew->setProjections (pProjNew);
2808
2809   if (ProjectionFileView* projView = pProjDocNew->getView()) {
2810     projView->OnUpdate (projView, NULL);
2811     if (projView->getCanvas())
2812       projView->getCanvas()->SetClientSize (pProjNew->nDet(), pProjNew->nView());
2813     if (wxFrame* pFrame = projView->getFrame()) {
2814       pFrame->Show(true);
2815       pFrame->SetFocus();
2816       pFrame->Raise();
2817     }
2818     GetDocumentManager()->ActivateView (projView, true);
2819   }
2820   if (theApp->getAskDeleteNewDocs())
2821     pProjDocNew-> Modify(true);
2822   pProjDocNew->UpdateAllViews (this);
2823   pProjDocNew->getView()->setInitialClientSize();
2824   pProjDocNew->Activate();
2825 }
2826
2827 void
2828 ProjectionFileView::OnReconstructFourier (wxCommandEvent& event)
2829 {
2830   Projections& rProj = GetDocument()->getProjections();
2831   DialogGetConvertPolarParameters dialogPolar (getFrameForChild(), _T("Fourier Reconstruction"), m_iDefaultPolarNX, m_iDefaultPolarNY,
2832     m_iDefaultPolarInterpolation, m_iDefaultPolarZeropad, IDH_DLG_RECON_FOURIER);
2833   if (dialogPolar.ShowModal() == wxID_OK) {
2834     wxProgressDialog dlgProgress (_T("Reconstruction Fourier"), _T("Reconstruction Progress"), 1, getFrameForChild(), wxPD_APP_MODAL);
2835     wxString strInterpolation (dialogPolar.getInterpolationName(), wxConvUTF8);
2836     m_iDefaultPolarNX = dialogPolar.getXSize();
2837     m_iDefaultPolarNY = dialogPolar.getYSize();
2838     m_iDefaultPolarZeropad = dialogPolar.getZeropad();
2839     ImageFile* pIF = new ImageFile (m_iDefaultPolarNX, m_iDefaultPolarNY);
2840
2841     m_iDefaultPolarInterpolation = Projections::convertInterpNameToID (strInterpolation.mb_str(wxConvUTF8));
2842     if (! rProj.convertFFTPolar (*pIF, m_iDefaultPolarInterpolation, m_iDefaultPolarZeropad)) {
2843       delete pIF;
2844       *theApp->getLog() << _T("Error converting to polar\n");
2845       return;
2846     }
2847 #ifdef HAVE_FFT
2848     pIF->ifft(*pIF);
2849 #endif
2850     pIF->magnitude(*pIF);
2851     Fourier::shuffleFourierToNaturalOrder (*pIF);
2852
2853     ImageFileDocument* pPolarDoc = theApp->newImageDoc();
2854     if (! pPolarDoc) {
2855       sys_error (ERR_SEVERE, "Unable to create image file");
2856       return;
2857     }
2858     pPolarDoc->setImageFile (pIF);
2859     pIF->labelAdd (rProj.getLabel().getLabelString().c_str(), rProj.calcTime());
2860     std::ostringstream os;
2861     os << "Reconstruct Fourier " << getFrame()->GetTitle().mb_str(wxConvUTF8) << ": xSize="
2862       << m_iDefaultPolarNX << ", ySize=" << m_iDefaultPolarNY << ", interpolation="
2863       << strInterpolation.mb_str(wxConvUTF8) << ", zeropad=" << m_iDefaultPolarZeropad;
2864     *theApp->getLog() << wxConvUTF8.cMB2WX(os.str().c_str()) << _T("\n");
2865     pIF->labelAdd (os.str().c_str());
2866     if (theApp->getAskDeleteNewDocs())
2867       pPolarDoc->Modify (true);
2868     pPolarDoc->UpdateAllViews ();
2869     pPolarDoc->getView()->setInitialClientSize();
2870     pPolarDoc->Activate();
2871   }
2872 }
2873
2874 void
2875 ProjectionFileView::OnReconstructFBPRebin (wxCommandEvent& event)
2876 {
2877   Projections& rProj = GetDocument()->getProjections();
2878   doReconstructFBP (rProj, true);
2879 }
2880
2881 void
2882 ProjectionFileView::OnReconstructFBP (wxCommandEvent& event)
2883 {
2884   Projections& rProj = GetDocument()->getProjections();
2885   doReconstructFBP (rProj, false);
2886 }
2887
2888 void
2889 ProjectionFileView::doReconstructFBP (const Projections& rProj, bool bRebinToParallel)
2890 {
2891   ReconstructionROI defaultROI;
2892   defaultROI.m_dXMin = -rProj.phmLen() / 2;
2893   defaultROI.m_dXMax = defaultROI.m_dXMin + rProj.phmLen();
2894   defaultROI.m_dYMin = -rProj.phmLen() / 2;
2895   defaultROI.m_dYMax = defaultROI.m_dYMin + rProj.phmLen();
2896
2897   DialogGetReconstructionParameters dialogReconstruction (getFrameForChild(), m_iDefaultNX, m_iDefaultNY,
2898     m_iDefaultFilter, m_dDefaultFilterParam, m_iDefaultFilterMethod, m_iDefaultFilterGeneration,
2899     m_iDefaultZeropad, m_iDefaultInterpolation, m_iDefaultInterpParam, m_iDefaultBackprojector,
2900     m_iDefaultTrace,  &defaultROI);
2901
2902   int retVal = dialogReconstruction.ShowModal();
2903   if (retVal != wxID_OK)
2904     return;
2905
2906   m_iDefaultNX = dialogReconstruction.getXSize();
2907   m_iDefaultNY = dialogReconstruction.getYSize();
2908   wxString optFilterName (dialogReconstruction.getFilterName(), wxConvUTF8);
2909   m_iDefaultFilter = SignalFilter::convertFilterNameToID (optFilterName.mb_str(wxConvUTF8));
2910   m_dDefaultFilterParam = dialogReconstruction.getFilterParam();
2911   wxString optFilterMethodName (dialogReconstruction.getFilterMethodName(), wxConvUTF8);
2912   m_iDefaultFilterMethod = ProcessSignal::convertFilterMethodNameToID(optFilterMethodName.mb_str(wxConvUTF8));
2913   m_iDefaultZeropad = dialogReconstruction.getZeropad();
2914   wxString optFilterGenerationName (dialogReconstruction.getFilterGenerationName(), wxConvUTF8);
2915   m_iDefaultFilterGeneration = ProcessSignal::convertFilterGenerationNameToID (optFilterGenerationName.mb_str(wxConvUTF8));
2916   wxString optInterpName (dialogReconstruction.getInterpName(), wxConvUTF8);
2917   m_iDefaultInterpolation = Backprojector::convertInterpNameToID (optInterpName.mb_str(wxConvUTF8));
2918   m_iDefaultInterpParam = dialogReconstruction.getInterpParam();
2919   wxString optBackprojectName (dialogReconstruction.getBackprojectName(), wxConvUTF8);
2920   m_iDefaultBackprojector = Backprojector::convertBackprojectNameToID (optBackprojectName.mb_str(wxConvUTF8));
2921   m_iDefaultTrace = dialogReconstruction.getTrace();
2922   dialogReconstruction.getROI (&defaultROI);
2923
2924   if (m_iDefaultNX <= 0 && m_iDefaultNY <= 0)
2925     return;
2926
2927   std::ostringstream os;
2928   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);
2929   if (bRebinToParallel)
2930     os << "; Interpolate to Parallel";
2931
2932   Timer timerRecon;
2933   ImageFile* pImageFile = NULL;
2934   if (m_iDefaultTrace > Trace::TRACE_CONSOLE) {
2935     pImageFile = new ImageFile (m_iDefaultNX, m_iDefaultNY);
2936     Reconstructor* pReconstructor = new Reconstructor (rProj, *pImageFile, optFilterName.mb_str(wxConvUTF8),
2937       m_dDefaultFilterParam, optFilterMethodName.mb_str(wxConvUTF8), m_iDefaultZeropad, optFilterGenerationName.mb_str(wxConvUTF8),
2938       optInterpName.mb_str(wxConvUTF8), m_iDefaultInterpParam, optBackprojectName.mb_str(wxConvUTF8), m_iDefaultTrace,
2939       &defaultROI, bRebinToParallel);
2940
2941     ReconstructDialog* pDlgReconstruct = new ReconstructDialog (*pReconstructor, rProj, *pImageFile, m_iDefaultTrace, getFrameForChild());
2942     for (int iView = 0; iView < rProj.nView(); iView++) {
2943       ::wxYield();
2944       if (pDlgReconstruct->isCancelled() || ! pDlgReconstruct->reconstructView (iView, true)) {
2945         delete pDlgReconstruct;
2946         delete pReconstructor;
2947         return;
2948       }
2949       ::wxYield();
2950       ::wxYield();
2951       while (pDlgReconstruct->isPaused()) {
2952         ::wxYield();
2953         ::wxMilliSleep(50);
2954       }
2955     }
2956     pReconstructor->postProcessing();
2957     delete pDlgReconstruct;
2958     delete pReconstructor;
2959   } else {
2960 #if HAVE_WXTHREADS
2961     if (theApp->getUseBackgroundTasks()) {
2962       ReconstructorSupervisorThread* pReconstructor = new ReconstructorSupervisorThread
2963         (this, m_iDefaultNX, m_iDefaultNY, optFilterName.mb_str(wxConvUTF8), 
2964          m_dDefaultFilterParam, optFilterMethodName.mb_str(wxConvUTF8),
2965          m_iDefaultZeropad, optFilterGenerationName.mb_str(wxConvUTF8), 
2966          optInterpName.mb_str(wxConvUTF8), m_iDefaultInterpParam,
2967          optBackprojectName.mb_str(wxConvUTF8), 
2968          wxConvUTF8.cMB2WX(os.str().c_str()), &defaultROI, bRebinToParallel);
2969       if (pReconstructor->Create() != wxTHREAD_NO_ERROR) {
2970         sys_error (ERR_SEVERE, "Error creating reconstructor thread");
2971         delete pReconstructor;
2972         return;
2973       }
2974       pReconstructor->SetPriority (60);
2975       pReconstructor->Run();
2976       return;
2977     } else
2978 #endif
2979     {
2980       pImageFile = new ImageFile (m_iDefaultNX, m_iDefaultNY);
2981       wxProgressDialog dlgProgress (_T("Reconstruction"), _T("Reconstruction Progress"), rProj.nView() + 1, getFrameForChild(), wxPD_CAN_ABORT );
2982       Reconstructor* pReconstructor = new Reconstructor (rProj, *pImageFile, optFilterName.mb_str(wxConvUTF8),
2983         m_dDefaultFilterParam, optFilterMethodName.mb_str(wxConvUTF8), m_iDefaultZeropad, optFilterGenerationName.mb_str(wxConvUTF8),
2984         optInterpName.mb_str(wxConvUTF8), m_iDefaultInterpParam, optBackprojectName.mb_str(wxConvUTF8), m_iDefaultTrace,
2985         &defaultROI, bRebinToParallel);
2986
2987       for (int iView = 0; iView < rProj.nView(); iView++) {
2988         pReconstructor->reconstructView (iView, 1);
2989         if ((iView + 1) % ITER_PER_UPDATE == 0)
2990           if (! dlgProgress.Update (iView + 1)) {
2991             delete pReconstructor;
2992             return; // don't make new window, thread will do this
2993           }
2994       }
2995       pReconstructor->postProcessing();
2996       delete pReconstructor;
2997     }
2998   }
2999   ImageFileDocument* pReconDoc = theApp->newImageDoc();
3000   if (! pReconDoc) {
3001     sys_error (ERR_SEVERE, "Unable to create image file");
3002     return;
3003   }
3004   *theApp->getLog() << wxConvUTF8.cMB2WX(os.str().c_str()) << _T("\n");
3005   pImageFile->labelAdd (rProj.getLabel());
3006   pImageFile->labelAdd (os.str().c_str(), timerRecon.timerEnd());
3007
3008   pReconDoc->setImageFile (pImageFile);
3009   if (theApp->getAskDeleteNewDocs())
3010     pReconDoc->Modify (true);
3011   pReconDoc->UpdateAllViews();
3012   pReconDoc->getView()->setInitialClientSize();
3013   pReconDoc->Activate();
3014 }
3015
3016
3017 void
3018 ProjectionFileView::OnArtifactReduction (wxCommandEvent& event)
3019 {
3020 }
3021
3022
3023 ProjectionFileCanvas*
3024 ProjectionFileView::CreateCanvas (wxFrame *parent)
3025 {
3026   ProjectionFileCanvas* pCanvas;
3027   int width, height;
3028   parent->GetClientSize(&width, &height);
3029
3030   pCanvas = new ProjectionFileCanvas (this, parent, wxPoint(-1,-1), wxSize(width, height), 0);
3031
3032   pCanvas->SetScrollbars(20, 20, 50, 50);
3033   pCanvas->SetBackgroundColour(*wxWHITE);
3034   pCanvas->ClearBackground();
3035
3036   return pCanvas;
3037 }
3038
3039 #if CTSIM_MDI
3040 wxDocMDIChildFrame*
3041 #else
3042 wxDocChildFrame*
3043 #endif
3044 ProjectionFileView::CreateChildFrame(wxDocument *doc, wxView *view)
3045 {
3046 #ifdef CTSIM_MDI
3047   wxDocMDIChildFrame *subframe = new wxDocMDIChildFrame (doc, view, theApp->getMainFrame(), -1, _T("Projection Frame"), wxPoint(-1,-1), wxSize(-1,-1), wxDEFAULT_FRAME_STYLE);
3048 #else
3049   wxDocChildFrame *subframe = new wxDocChildFrame (doc, view, theApp->getMainFrame(), -1, _T("Projection Frame"), wxPoint(-1,-1), wxSize(-1,-1), wxDEFAULT_FRAME_STYLE);
3050 #endif
3051   theApp->setIconForFrame (subframe);
3052
3053   m_pFileMenu = new wxMenu;
3054
3055   m_pFileMenu->Append(MAINMENU_FILE_CREATE_PHANTOM, _T("Cr&eate Phantom...\tCtrl-P"));
3056   m_pFileMenu->Append(MAINMENU_FILE_CREATE_FILTER, _T("Create &Filter...\tCtrl-F"));
3057   m_pFileMenu->Append(wxID_OPEN, _T("&Open...\tCtrl-O"));
3058   m_pFileMenu->Append(wxID_SAVE, _T("&Save\tCtrl-S"));
3059   m_pFileMenu->Append(wxID_SAVEAS, _T("Save &As..."));
3060   m_pFileMenu->Append(wxID_CLOSE, _T("&Close\tCtrl-W"));
3061
3062   m_pFileMenu->AppendSeparator();
3063   m_pFileMenu->Append(PJMENU_FILE_PROPERTIES, _T("P&roperties\tCtrl-I"));
3064
3065   m_pFileMenu->AppendSeparator();
3066   m_pFileMenu->Append(wxID_PRINT, _T("&Print..."));
3067   m_pFileMenu->Append(wxID_PRINT_SETUP, _T("Print &Setup..."));
3068   m_pFileMenu->Append(wxID_PREVIEW, _T("Print Pre&view"));
3069   m_pFileMenu->AppendSeparator();
3070   m_pFileMenu->Append(MAINMENU_IMPORT, _T("&Import...\tCtrl-M"));
3071   m_pFileMenu->AppendSeparator();
3072   m_pFileMenu->Append (MAINMENU_FILE_PREFERENCES, _T("Prefere&nces..."));
3073   m_pFileMenu->Append(MAINMENU_FILE_EXIT, _T("E&xit"));
3074   GetDocumentManager()->FileHistoryAddFilesToMenu(m_pFileMenu);
3075   GetDocumentManager()->FileHistoryUseMenu(m_pFileMenu);
3076
3077   m_pConvertMenu = new wxMenu;
3078   m_pConvertMenu->Append (PJMENU_CONVERT_RECTANGULAR, _T("&Rectangular Image"));
3079   m_pConvertMenu->Append (PJMENU_CONVERT_POLAR, _T("&Polar Image...\tCtrl-L"));
3080   m_pConvertMenu->Append (PJMENU_CONVERT_FFT_POLAR, _T("FF&T->Polar Image...\tCtrl-T"));
3081   m_pConvertMenu->AppendSeparator();
3082   m_pConvertMenu->Append (PJMENU_CONVERT_PARALLEL, _T("&Interpolate to Parallel"));
3083
3084   //  wxMenu* filter_menu = new wxMenu;
3085   //  filter_menu->Append (PJMENU_ARTIFACT_REDUCTION, _T("&Artifact Reduction"));
3086
3087   wxMenu* analyze_menu = new wxMenu;
3088   analyze_menu->Append (PJMENU_PLOT_HISTOGRAM, _T("&Plot Histogram"));
3089   analyze_menu->Append (PJMENU_PLOT_TTHETA_SAMPLING, _T("Plot T-T&heta Sampling...\tCtrl-H"));
3090
3091   m_pReconstructMenu = new wxMenu;
3092   m_pReconstructMenu->Append (PJMENU_RECONSTRUCT_FBP, _T("&Filtered Backprojection...\tCtrl-R"), _T("Reconstruct image using filtered backprojection"));
3093   m_pReconstructMenu->Append (PJMENU_RECONSTRUCT_FBP_REBIN, _T("Filtered &Backprojection (Rebin to Parallel)...\tCtrl-B"), _T("Reconstruct image using filtered backprojection"));
3094   m_pReconstructMenu->Append (PJMENU_RECONSTRUCT_FOURIER, _T("&Inverse Fourier...\tCtrl-E"), _T("Direct inverse Fourier"));
3095
3096   wxMenu *help_menu = new wxMenu;
3097   help_menu->Append(MAINMENU_HELP_CONTENTS, _T("&Contents\tF1"));
3098   help_menu->Append (MAINMENU_HELP_TIPS, _T("&Tips"));
3099   help_menu->Append (IDH_QUICKSTART, _T("&Quick Start"));
3100   help_menu->Append(MAINMENU_HELP_ABOUT, _T("&About"));
3101
3102   wxMenuBar *menu_bar = new wxMenuBar;
3103
3104   menu_bar->Append (m_pFileMenu, _T("&File"));
3105   menu_bar->Append (m_pConvertMenu, _T("&Convert"));
3106   //  menu_bar->Append (filter_menu, _T("Fi&lter"));
3107   menu_bar->Append (analyze_menu, _T("&Analyze"));
3108   menu_bar->Append (m_pReconstructMenu, _T("&Reconstruct"));
3109   menu_bar->Append (help_menu, _T("&Help"));
3110
3111   subframe->SetMenuBar(menu_bar);
3112   subframe->Centre(wxBOTH);
3113
3114   wxAcceleratorEntry accelEntries[7];
3115   accelEntries[0].Set (wxACCEL_CTRL, static_cast<int>('L'), PJMENU_CONVERT_POLAR);
3116   accelEntries[1].Set (wxACCEL_CTRL, static_cast<int>('T'), PJMENU_CONVERT_FFT_POLAR);
3117   accelEntries[2].Set (wxACCEL_CTRL, static_cast<int>('R'), PJMENU_RECONSTRUCT_FBP);
3118   accelEntries[3].Set (wxACCEL_CTRL, static_cast<int>('B'), PJMENU_RECONSTRUCT_FBP_REBIN);
3119   accelEntries[4].Set (wxACCEL_CTRL, static_cast<int>('E'), PJMENU_RECONSTRUCT_FOURIER);
3120   accelEntries[5].Set (wxACCEL_CTRL, static_cast<int>('I'), PJMENU_FILE_PROPERTIES);
3121   accelEntries[6].Set (wxACCEL_CTRL, static_cast<int>('H'), PJMENU_PLOT_TTHETA_SAMPLING);
3122   wxAcceleratorTable accelTable (7, accelEntries);
3123   subframe->SetAcceleratorTable (accelTable);
3124
3125   return subframe;
3126 }
3127
3128
3129 bool
3130 ProjectionFileView::OnCreate(wxDocument *doc, long WXUNUSED(flags) )
3131 {
3132   m_pFrame = CreateChildFrame(doc, this);
3133   SetFrame(m_pFrame);
3134   m_pCanvas = CreateCanvas (m_pFrame);
3135   m_pFrame->SetClientSize (m_pCanvas->GetBestSize());
3136   m_pCanvas->SetClientSize (m_pCanvas->GetBestSize());
3137   m_pFrame->SetTitle (_T("ProjectionFileView"));
3138
3139   m_pFrame->Show(true);
3140   Activate(true);
3141
3142   return true;
3143 }
3144
3145 void
3146 ProjectionFileView::OnDraw (wxDC* dc)
3147 {
3148   if (m_pBitmap && m_pBitmap->Ok())
3149     dc->DrawBitmap (*m_pBitmap, 0, 0, false);
3150 }
3151
3152
3153 void
3154 ProjectionFileView::setInitialClientSize ()
3155 {
3156   if (m_pFrame && m_pCanvas) {
3157     wxSize bestSize = m_pCanvas->GetBestSize();
3158
3159     m_pFrame->SetClientSize (bestSize);
3160     m_pFrame->Show (true);
3161     m_pFrame->SetFocus();
3162   }
3163 }
3164
3165 void
3166 ProjectionFileView::OnUpdate (wxView *WXUNUSED(sender), wxObject *WXUNUSED(hint) )
3167 {
3168   const Projections& rProj = GetDocument()->getProjections();
3169   const int nDet = rProj.nDet();
3170   const int nView = rProj.nView();
3171   if (rProj.geometry() == Scanner::GEOMETRY_PARALLEL) {
3172     m_pReconstructMenu->Enable (PJMENU_RECONSTRUCT_FBP_REBIN, false);
3173     m_pConvertMenu->Enable (PJMENU_CONVERT_PARALLEL, false);
3174   } else {
3175     m_pReconstructMenu->Enable (PJMENU_RECONSTRUCT_FBP_REBIN, true);
3176     m_pConvertMenu->Enable (PJMENU_CONVERT_PARALLEL, true);
3177   }
3178
3179   if (nDet != 0 && nView != 0) {
3180     const DetectorArray& detarray = rProj.getDetectorArray(0);
3181     const DetectorValue* detval = detarray.detValues();
3182     double min = detval[0];
3183     double max = detval[0];
3184     for (int iy = 0; iy < nView; iy++) {
3185       const DetectorArray& detarray = rProj.getDetectorArray(iy);
3186       const DetectorValue* detval = detarray.detValues();
3187       for (int ix = 0; ix < nDet; ix++) {
3188         if (min > detval[ix])
3189           min = detval[ix];
3190         else if (max < detval[ix])
3191           max = detval[ix];
3192       }
3193     }
3194
3195     unsigned char* imageData = new unsigned char [nDet * nView * 3];
3196     if (! imageData) {
3197       sys_error (ERR_SEVERE, "Unable to allocate memory for image display");
3198       return;
3199     }
3200     double scale = (max - min) / 255;
3201     for (int iy2 = 0; iy2 < nView; iy2++) {
3202       const DetectorArray& detarray = rProj.getDetectorArray (iy2);
3203       const DetectorValue* detval = detarray.detValues();
3204       for (int ix = 0; ix < nDet; ix++) {
3205         int intensity = static_cast<int>(((detval[ix] - min) / scale) + 0.5);
3206         intensity = clamp(intensity, 0, 255);
3207         int baseAddr = (iy2 * nDet + ix) * 3;
3208         imageData[baseAddr] = imageData[baseAddr+1] = imageData[baseAddr+2] = intensity;
3209       }
3210     }
3211     wxImage image (nDet, nView, imageData, true);
3212     if (m_pBitmap) {
3213       delete m_pBitmap;
3214       m_pBitmap = NULL;
3215     }
3216     m_pBitmap = new wxBitmap (image);
3217     delete imageData;
3218   }
3219
3220     m_pCanvas->SetScrollbars(20, 20, nDet/20, nView/20);
3221     m_pCanvas->SetBackgroundColour(*wxWHITE);
3222
3223     if (m_pCanvas)
3224       m_pCanvas->Refresh();
3225 }
3226
3227 bool
3228 ProjectionFileView::OnClose (bool deleteWindow)
3229 {
3230   //GetDocumentManager()->ActivateView (this, false);
3231   if (! GetDocument() || ! GetDocument()->Close())
3232     return false;
3233
3234   Activate(false);
3235   if (m_pCanvas) {
3236         m_pCanvas->setView(NULL);
3237     m_pCanvas = NULL;
3238   }
3239   wxString s(wxTheApp->GetAppName());
3240   if (m_pFrame)
3241     m_pFrame->SetTitle(s);
3242
3243   SetFrame(NULL);
3244
3245   if (deleteWindow) {
3246     delete m_pFrame;
3247     m_pFrame = NULL;
3248     if (GetDocument() && GetDocument()->getBadFileOpen())
3249       ::wxYield();  // wxWindows bug workaround
3250   }
3251
3252   return true;
3253 }
3254
3255
3256
3257 // PlotFileCanvas
3258 PlotFileCanvas::PlotFileCanvas (PlotFileView* v, wxFrame *frame, const wxPoint& pos, const wxSize& size, const long style)
3259   : wxScrolledWindow(frame, -1, pos, size, style), m_pView(v)
3260 {
3261 }
3262
3263 PlotFileCanvas::~PlotFileCanvas ()
3264 {
3265 }
3266
3267 wxSize
3268 PlotFileCanvas::GetBestSize() const
3269 {
3270   return wxSize (500, 300);
3271 }
3272
3273
3274 void
3275 PlotFileCanvas::OnDraw(wxDC& dc)
3276 {
3277   if (m_pView)
3278     m_pView->OnDraw(& dc);
3279 }
3280
3281
3282 // PlotFileView
3283
3284 IMPLEMENT_DYNAMIC_CLASS(PlotFileView, wxView)
3285
3286 BEGIN_EVENT_TABLE(PlotFileView, wxView)
3287 EVT_MENU(PLOTMENU_FILE_PROPERTIES, PlotFileView::OnProperties)
3288 EVT_MENU(PLOTMENU_VIEW_SCALE_MINMAX, PlotFileView::OnScaleMinMax)
3289 EVT_MENU(PLOTMENU_VIEW_SCALE_AUTO, PlotFileView::OnScaleAuto)
3290 EVT_MENU(PLOTMENU_VIEW_SCALE_FULL, PlotFileView::OnScaleFull)
3291 END_EVENT_TABLE()
3292
3293 PlotFileView::PlotFileView()
3294 : wxView(), m_pFrame(0), m_pCanvas(0), m_pEZPlot(0), m_pFileMenu(0),
3295   m_bMinSpecified(false), m_bMaxSpecified(false)
3296 {
3297 }
3298
3299 PlotFileView::~PlotFileView()
3300 {
3301   if (m_pEZPlot)
3302     delete m_pEZPlot;
3303
3304   GetDocumentManager()->FileHistoryRemoveMenu (m_pFileMenu);
3305   GetDocumentManager()->ActivateView(this, FALSE);
3306 }
3307
3308 void
3309 PlotFileView::OnProperties (wxCommandEvent& event)
3310 {
3311   const PlotFile& rPlot = GetDocument()->getPlotFile();
3312   std::ostringstream os;
3313   os << "Columns: " << rPlot.getNumColumns() << ", Records: " << rPlot.getNumRecords() << "\n";
3314   rPlot.printHeadersBrief (os);
3315   *theApp->getLog() << _T(">>>>\n") << wxConvUTF8.cMB2WX(os.str().c_str()) << _T("<<<<<\n");
3316   wxMessageDialog dialogMsg (getFrameForChild(), wxConvUTF8.cMB2WX(os.str().c_str()), _T("Plot File Properties"), 
3317                              wxOK | wxICON_INFORMATION);
3318   dialogMsg.ShowModal();
3319   GetDocument()->Activate();
3320 }
3321
3322
3323 void
3324 PlotFileView::OnScaleAuto (wxCommandEvent& event)
3325 {
3326   const PlotFile& rPlotFile = GetDocument()->getPlotFile();
3327   double min, max, mean, mode, median, stddev;
3328   rPlotFile.statistics (1, min, max, mean, mode, median, stddev);
3329   DialogAutoScaleParameters dialogAutoScale (getFrameForChild(), mean, mode, median, stddev, m_dAutoScaleFactor);
3330   int iRetVal = dialogAutoScale.ShowModal();
3331   if (iRetVal == wxID_OK) {
3332     m_bMinSpecified = true;
3333     m_bMaxSpecified = true;
3334     double dMin, dMax;
3335     if (dialogAutoScale.getMinMax (&dMin, &dMax)) {
3336       m_dMinPixel = dMin;
3337       m_dMaxPixel = dMax;
3338       m_dAutoScaleFactor = dialogAutoScale.getAutoScaleFactor();
3339       OnUpdate (this, NULL);
3340     }
3341   }
3342   GetDocument()->Activate();
3343 }
3344
3345 void
3346 PlotFileView::OnScaleMinMax (wxCommandEvent& event)
3347 {
3348   const PlotFile& rPlotFile = GetDocument()->getPlotFile();
3349   double min;
3350   double max;
3351
3352   if (! m_bMinSpecified || ! m_bMaxSpecified) {
3353     if (! rPlotFile.getMinMax (1, min, max)) {
3354       *theApp->getLog() << _T("Error: unable to find Min/Max\n");
3355       return;
3356     }
3357   }
3358
3359   if (m_bMinSpecified)
3360     min = m_dMinPixel;
3361   if (m_bMaxSpecified)
3362     max = m_dMaxPixel;
3363
3364   DialogGetMinMax dialogMinMax (getFrameForChild(), _T("Set Y-axis Minimum & Maximum"), min, max);
3365   int retVal = dialogMinMax.ShowModal();
3366   if (retVal == wxID_OK) {
3367     m_bMinSpecified = true;
3368     m_bMaxSpecified = true;
3369     m_dMinPixel = dialogMinMax.getMinimum();
3370     m_dMaxPixel = dialogMinMax.getMaximum();
3371     OnUpdate (this, NULL);
3372   }
3373   GetDocument()->Activate();
3374 }
3375
3376 void
3377 PlotFileView::OnScaleFull (wxCommandEvent& event)
3378 {
3379   if (m_bMinSpecified || m_bMaxSpecified) {
3380     m_bMinSpecified = false;
3381     m_bMaxSpecified = false;
3382     OnUpdate (this, NULL);
3383   }
3384   GetDocument()->Activate();
3385 }
3386
3387
3388 PlotFileCanvas*
3389 PlotFileView::CreateCanvas (wxFrame* parent)
3390 {
3391   PlotFileCanvas* pCanvas;
3392
3393   pCanvas = new PlotFileCanvas (this, parent, wxPoint(-1,-1), wxSize(-1,-1),
3394                                 wxFULL_REPAINT_ON_RESIZE);
3395   pCanvas->SetBackgroundColour(*wxWHITE);
3396   pCanvas->ClearBackground();
3397
3398   return pCanvas;
3399 }
3400
3401 #if CTSIM_MDI
3402 wxDocMDIChildFrame*
3403 #else
3404 wxDocChildFrame*
3405 #endif
3406 PlotFileView::CreateChildFrame(wxDocument *doc, wxView *view)
3407 {
3408 #ifdef CTSIM_MDI
3409   wxDocMDIChildFrame *subframe = new wxDocMDIChildFrame (doc, view, theApp->getMainFrame(), -1, _T("Plot Frame"), wxPoint(-1,-1), wxSize(-1,-1), wxDEFAULT_FRAME_STYLE);
3410 #else
3411   wxDocChildFrame *subframe = new wxDocChildFrame(doc, view, theApp->getMainFrame(), -1, _T("Plot Frame"), wxPoint(-1,-1), wxSize(-1,-1), wxDEFAULT_FRAME_STYLE);
3412 #endif
3413   theApp->setIconForFrame (subframe);
3414
3415   m_pFileMenu = new wxMenu;
3416
3417   m_pFileMenu->Append(MAINMENU_FILE_CREATE_PHANTOM, _T("Cr&eate Phantom...\tCtrl-P"));
3418   m_pFileMenu->Append(MAINMENU_FILE_CREATE_FILTER, _T("Create &Filter...\tCtrl-F"));
3419   m_pFileMenu->Append(wxID_OPEN, _T("&Open...\tCtrl-O"));
3420   m_pFileMenu->Append(wxID_SAVE, _T("&Save\tCtrl-S"));
3421   m_pFileMenu->Append(wxID_SAVEAS, _T("Save &As..."));
3422   m_pFileMenu->Append(wxID_CLOSE, _T("&Close\tCtrl-W"));
3423
3424   m_pFileMenu->AppendSeparator();
3425   m_pFileMenu->Append(PLOTMENU_FILE_PROPERTIES, _T("P&roperties\tCtrl-I"));
3426
3427   m_pFileMenu->AppendSeparator();
3428   m_pFileMenu->Append(wxID_PRINT, _T("&Print..."));
3429   m_pFileMenu->Append(wxID_PRINT_SETUP, _T("Print &Setup..."));
3430   m_pFileMenu->Append(wxID_PREVIEW, _T("Print Pre&view"));
3431   m_pFileMenu->AppendSeparator();
3432   m_pFileMenu->Append(MAINMENU_IMPORT, _T("&Import...\tCtrl-M"));
3433   m_pFileMenu->AppendSeparator();
3434   m_pFileMenu->Append (MAINMENU_FILE_PREFERENCES, _T("Prefere&nces..."));
3435   m_pFileMenu->Append(MAINMENU_FILE_EXIT, _T("E&xit"));
3436   GetDocumentManager()->FileHistoryAddFilesToMenu(m_pFileMenu);
3437   GetDocumentManager()->FileHistoryUseMenu(m_pFileMenu);
3438
3439   wxMenu *view_menu = new wxMenu;
3440   view_menu->Append(PLOTMENU_VIEW_SCALE_MINMAX, _T("Display Scale &Set...\tCtrl-E"));
3441   view_menu->Append(PLOTMENU_VIEW_SCALE_AUTO, _T("Display Scale &Auto...\tCtrl-A"));
3442   view_menu->Append(PLOTMENU_VIEW_SCALE_FULL, _T("Display &Full Scale\tCtrl-U"));
3443
3444   wxMenu *help_menu = new wxMenu;
3445   help_menu->Append(MAINMENU_HELP_CONTENTS, _T("&Contents\tF1"));
3446   help_menu->Append (MAINMENU_HELP_TIPS, _T("&Tips"));
3447   help_menu->Append (IDH_QUICKSTART, _T("&Quick Start"));
3448   help_menu->Append(MAINMENU_HELP_ABOUT, _T("&About"));
3449
3450   wxMenuBar *menu_bar = new wxMenuBar;
3451
3452   menu_bar->Append(m_pFileMenu, _T("&File"));
3453   menu_bar->Append(view_menu, _T("&View"));
3454   menu_bar->Append(help_menu, _T("&Help"));
3455
3456   subframe->SetMenuBar(menu_bar);
3457   subframe->Centre(wxBOTH);
3458
3459   wxAcceleratorEntry accelEntries[4];
3460   accelEntries[0].Set (wxACCEL_CTRL, static_cast<int>('E'), PLOTMENU_VIEW_SCALE_MINMAX);
3461   accelEntries[1].Set (wxACCEL_CTRL, static_cast<int>('A'), PLOTMENU_VIEW_SCALE_AUTO);
3462   accelEntries[2].Set (wxACCEL_CTRL, static_cast<int>('U'), PLOTMENU_VIEW_SCALE_FULL);
3463   accelEntries[3].Set (wxACCEL_CTRL, static_cast<int>('I'), PLOTMENU_FILE_PROPERTIES);
3464   wxAcceleratorTable accelTable (4, accelEntries);
3465   subframe->SetAcceleratorTable (accelTable);
3466
3467   return subframe;
3468 }
3469
3470
3471 bool
3472 PlotFileView::OnCreate (wxDocument *doc, long WXUNUSED(flags) )
3473 {
3474   m_bMinSpecified = false;
3475   m_bMaxSpecified = false;
3476   m_dAutoScaleFactor = 1.;
3477
3478   m_pFrame = CreateChildFrame(doc, this);
3479   SetFrame(m_pFrame);
3480   m_pCanvas = CreateCanvas (m_pFrame);
3481   m_pFrame->SetClientSize (m_pCanvas->GetBestSize());
3482   m_pCanvas->SetClientSize (m_pCanvas->GetBestSize());
3483   m_pFrame->SetTitle (_T("Plot File"));
3484
3485   m_pFrame->Show(true);
3486   Activate(true);
3487
3488   return true;
3489 }
3490
3491 void
3492 PlotFileView::setInitialClientSize ()
3493 {
3494   if (m_pFrame && m_pCanvas) {
3495     wxSize bestSize = m_pCanvas->GetBestSize();
3496
3497     m_pFrame->SetClientSize (bestSize);
3498     m_pFrame->Show (true);
3499     m_pFrame->SetFocus();
3500   }
3501 }
3502
3503
3504 void
3505 PlotFileView::OnDraw (wxDC* dc)
3506 {
3507   const PlotFile& rPlotFile = GetDocument()->getPlotFile();
3508   const int iNColumns = rPlotFile.getNumColumns();
3509   const int iNRecords = rPlotFile.getNumRecords();
3510
3511   if (iNColumns > 0 && iNRecords > 0) {
3512     int xsize, ysize;
3513     m_pCanvas->GetClientSize (&xsize, &ysize);
3514     SGPDriver driver (dc, xsize, ysize);
3515     SGP sgp (driver);
3516     if (m_pEZPlot)
3517       m_pEZPlot->plot (&sgp);
3518   }
3519 }
3520
3521
3522 void
3523 PlotFileView::OnUpdate (wxView *WXUNUSED(sender), wxObject *WXUNUSED(hint) )
3524 {
3525   const PlotFile& rPlotFile = GetDocument()->getPlotFile();
3526   const int iNColumns = rPlotFile.getNumColumns();
3527   const int iNRecords = rPlotFile.getNumRecords();
3528   const bool bScatterPlot = rPlotFile.getIsScatterPlot();
3529
3530   if (iNColumns > 0 && iNRecords > 0) {
3531     if (m_pEZPlot)
3532       delete m_pEZPlot;
3533     m_pEZPlot = new EZPlot;
3534
3535     for (unsigned int iEzset = 0; iEzset < rPlotFile.getNumEzsetCommands(); iEzset++)
3536       m_pEZPlot->ezset (rPlotFile.getEzsetCommand (iEzset));
3537
3538     if (m_bMinSpecified) {
3539       std::ostringstream os;
3540       os << "ymin " << m_dMinPixel;
3541       m_pEZPlot->ezset (os.str());
3542     }
3543
3544     if (m_bMaxSpecified) {
3545       std::ostringstream os;
3546       os << "ymax " << m_dMaxPixel;
3547       m_pEZPlot->ezset (os.str());
3548     }
3549
3550     m_pEZPlot->ezset("box");
3551     m_pEZPlot->ezset("grid");
3552
3553     double* pdX = new double [iNRecords];
3554     double* pdY = new double [iNRecords];
3555     if (! bScatterPlot) {
3556       rPlotFile.getColumn (0, pdX);
3557
3558       for (int iCol = 1; iCol < iNColumns; iCol++) {
3559         rPlotFile.getColumn (iCol, pdY);
3560         m_pEZPlot->addCurve (pdX, pdY, iNRecords);
3561       }
3562     } else {
3563       rPlotFile.getColumn (0, pdX);
3564       rPlotFile.getColumn (1, pdY);
3565       m_pEZPlot->addCurve (pdX, pdY, iNRecords);
3566     }
3567     delete pdX;
3568     delete pdY;
3569   }
3570
3571   if (m_pCanvas)
3572     m_pCanvas->Refresh();
3573 }
3574
3575 bool
3576 PlotFileView::OnClose (bool deleteWindow)
3577 {
3578   if (! GetDocument() || ! GetDocument()->Close())
3579     return false;
3580
3581   Activate(false);
3582   if (m_pCanvas) {
3583     m_pCanvas->setView (NULL);
3584     m_pCanvas = NULL;
3585   }
3586   wxString s(wxTheApp->GetAppName());
3587   if (m_pFrame)
3588     m_pFrame->SetTitle(s);
3589
3590   SetFrame(NULL);
3591   if (deleteWindow) {
3592     delete m_pFrame;
3593     m_pFrame = NULL;
3594     if (GetDocument() && GetDocument()->getBadFileOpen())
3595       ::wxYield();  // wxWindows bug workaround
3596   }
3597
3598   return true;
3599 }
3600
3601
3602 ////////////////////////////////////////////////////////////////
3603
3604
3605 IMPLEMENT_DYNAMIC_CLASS(TextFileView, wxView)
3606
3607 TextFileView::~TextFileView()
3608 {
3609   GetDocumentManager()->FileHistoryRemoveMenu (m_pFileMenu);
3610   GetDocumentManager()->ActivateView(this, FALSE);;
3611 }
3612
3613 bool TextFileView::OnCreate(wxDocument *doc, long WXUNUSED(flags) )
3614 {
3615   m_pFrame = CreateChildFrame(doc, this);
3616   SetFrame (m_pFrame);
3617
3618   int width, height;
3619   m_pFrame->GetClientSize(&width, &height);
3620   m_pFrame->SetTitle(_T("TextFile"));
3621   m_pCanvas = new TextFileCanvas (this, m_pFrame, wxPoint(-1,-1), wxSize(width, height), wxTE_MULTILINE | wxTE_READONLY);
3622   m_pFrame->SetTitle(_T("Log"));
3623
3624   m_pFrame->Show (true);
3625   Activate (true);
3626
3627   return true;
3628 }
3629
3630 // Handled by wxTextWindow
3631 void TextFileView::OnDraw(wxDC *WXUNUSED(dc) )
3632 {
3633 }
3634
3635 void TextFileView::OnUpdate (wxView *WXUNUSED(sender), wxObject *WXUNUSED(hint) )
3636 {
3637 }
3638
3639 bool
3640 TextFileView::OnClose (bool deleteWindow)
3641 {
3642   if (! theApp->getMainFrame()->getShuttingDown())
3643     return false;
3644
3645   Activate(false);
3646   //GetDocumentManager()->ActivateView (this, false);
3647   if (! GetDocument() || ! GetDocument()->Close())
3648     return false;
3649
3650   SetFrame(NULL);
3651   if (deleteWindow) {
3652     delete m_pFrame;
3653     m_pFrame = NULL;
3654     if (GetDocument() && GetDocument()->getBadFileOpen())
3655       ::wxYield();  // wxWindows bug workaround
3656   }
3657
3658   return TRUE;
3659 }
3660
3661 #if CTSIM_MDI
3662 wxDocMDIChildFrame*
3663 #else
3664 wxDocChildFrame*
3665 #endif
3666 TextFileView::CreateChildFrame (wxDocument *doc, wxView *view)
3667 {
3668 #if CTSIM_MDI
3669   wxDocMDIChildFrame* subframe = new wxDocMDIChildFrame (doc, view, theApp->getMainFrame(), -1, _T("TextFile Frame"), wxPoint(-1, -1), wxSize(-1,-1), wxDEFAULT_FRAME_STYLE, _T("Log"));
3670 #else
3671   wxDocChildFrame* subframe = new wxDocChildFrame (doc, view, theApp->getMainFrame(), -1, _T("TextFile Frame"), wxPoint(-1, -1), wxSize(300, 150), wxDEFAULT_FRAME_STYLE, _T("Log"));
3672 #endif
3673   theApp->setIconForFrame (subframe);
3674
3675   m_pFileMenu = new wxMenu;
3676
3677   m_pFileMenu->Append(MAINMENU_FILE_CREATE_PHANTOM, _T("Cr&eate Phantom...\tCtrl-P"));
3678   m_pFileMenu->Append(MAINMENU_FILE_CREATE_FILTER, _T("Create &Filter...\tCtrl-F"));
3679   m_pFileMenu->Append(wxID_OPEN, _T("&Open...\tCtrl-O"));
3680   m_pFileMenu->Append(wxID_SAVE, _T("&Save\tCtrl-S"));
3681   m_pFileMenu->Append(wxID_SAVEAS, _T("Save &As..."));
3682   //  m_pFileMenu->Append(wxID_CLOSE, _T("&Close\tCtrl-W"));
3683
3684   m_pFileMenu->AppendSeparator();
3685   m_pFileMenu->Append(wxID_PRINT, _T("&Print..."));
3686   m_pFileMenu->Append(wxID_PRINT_SETUP, _T("Print &Setup..."));
3687   m_pFileMenu->Append(wxID_PREVIEW, _T("Print Pre&view"));
3688   m_pFileMenu->AppendSeparator();
3689   m_pFileMenu->Append(MAINMENU_IMPORT, _T("&Import...\tCtrl-M"));
3690   m_pFileMenu->AppendSeparator();
3691   m_pFileMenu->Append (MAINMENU_FILE_PREFERENCES, _T("Prefere&nces..."));
3692   m_pFileMenu->Append(MAINMENU_FILE_EXIT, _T("E&xit"));
3693   GetDocumentManager()->FileHistoryAddFilesToMenu(m_pFileMenu);
3694   GetDocumentManager()->FileHistoryUseMenu(m_pFileMenu);
3695
3696   wxMenu *help_menu = new wxMenu;
3697   help_menu->Append(MAINMENU_HELP_CONTENTS, _T("&Contents\tF1"));
3698   help_menu->Append (MAINMENU_HELP_TIPS, _T("&Tips"));
3699   help_menu->Append (IDH_QUICKSTART, _T("&Quick Start"));
3700   help_menu->Append(MAINMENU_HELP_ABOUT, _T("&About"));
3701
3702   wxMenuBar *menu_bar = new wxMenuBar;
3703
3704   menu_bar->Append(m_pFileMenu, _T("&File"));
3705   menu_bar->Append(help_menu, _T("&Help"));
3706
3707   subframe->SetMenuBar(menu_bar);
3708   subframe->Centre(wxBOTH);
3709
3710   return subframe;
3711 }
3712
3713
3714 // Define a constructor for my text subwindow
3715 TextFileCanvas::TextFileCanvas (TextFileView* v, wxFrame* frame, const wxPoint& pos, const wxSize& size, long style)
3716   : wxTextCtrl (frame, -1, _T(""), pos, size, style), m_pView(v)
3717 {
3718 }
3719
3720 TextFileCanvas::~TextFileCanvas ()
3721 {
3722   m_pView = NULL;
3723 }
3724
3725 wxSize
3726 TextFileCanvas::GetBestSize() const
3727 {
3728   int xSize, ySize;
3729   theApp->getMainFrame()->GetClientSize (&xSize, &ySize);
3730   xSize = maxValue<int> (xSize, ySize);
3731 #ifdef CTSIM_MDI
3732   ySize = xSize = (xSize / 4);
3733 #else
3734   ySize = xSize;
3735 #endif
3736   return wxSize (xSize, ySize);
3737 }