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