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