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