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