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