r352: no message
[ctsim.git] / src / views.cpp
1 /*****************************************************************************
2 ** FILE IDENTIFICATION
3 **
4 **   Name:          view.cpp
5 **   Purpose:       View & Canvas routines for CTSim program
6 **   Programmer:    Kevin Rosenberg
7 **   Date Started:  July 2000
8 **
9 **  This is part of the CTSim program
10 **  Copyright (C) 1983-2000 Kevin Rosenberg
11 **
12 **  $Id: views.cpp,v 1.50 2001/01/03 22:00:46 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 // For compilers that support precompilation, includes "wx/wx.h".
29 #include "wx/wxprec.h"
30
31 #ifdef __BORLANDC__
32 #pragma hdrstop
33 #endif
34
35 #ifndef WX_PRECOMP
36 #include "wx/wx.h"
37 #endif
38
39 #if !wxUSE_DOC_VIEW_ARCHITECTURE
40 #error You must set wxUSE_DOC_VIEW_ARCHITECTURE to 1 in setup.h!
41 #endif
42
43 #include "wx/image.h"
44 #include "wx/progdlg.h"
45
46 #include "ct.h"
47 #include "ctsim.h"
48 #include "docs.h"
49 #include "views.h"
50 #include "dialogs.h"
51 #include "dlgprojections.h"
52 #include "dlgreconstruct.h"
53 #include "backprojectors.h"
54 #include "reconstruct.h"
55 #include "timer.h"
56
57 #if defined(MSVC) || HAVE_SSTREAM
58 #include <sstream>
59 #else
60 #include <sstream_subst>
61 #endif
62
63
64 // ImageFileCanvas
65
66 BEGIN_EVENT_TABLE(ImageFileCanvas, wxScrolledWindow)
67 EVT_MOUSE_EVENTS(ImageFileCanvas::OnMouseEvent)
68 END_EVENT_TABLE()
69
70
71 ImageFileCanvas::ImageFileCanvas (ImageFileView* v, wxFrame *frame, const wxPoint& pos, const wxSize& size, const long style)
72 : wxScrolledWindow(frame, -1, pos, size, style)
73 {
74   m_pView = v;
75   m_xCursor = -1;
76   m_yCursor = -1;
77 }
78
79 void 
80 ImageFileCanvas::OnDraw(wxDC& dc)
81 {
82   if (m_pView)
83     m_pView->OnDraw(& dc);
84 }
85
86 void 
87 ImageFileCanvas::DrawRubberBandCursor (wxDC& dc, int x, int y)
88 {
89   const ImageFile& rIF = m_pView->GetDocument()->getImageFile();
90   int nx = rIF.nx();
91   int ny = rIF.ny();
92   
93   int yPt = ny - y - 1;
94   dc.SetLogicalFunction (wxINVERT);
95   dc.SetPen (*wxGREEN_PEN);
96   dc.DrawLine (0, yPt, nx, yPt);
97   dc.DrawLine (x, 0, x, ny);
98   dc.SetLogicalFunction (wxCOPY);
99 }
100
101 bool
102 ImageFileCanvas::GetCurrentCursor (int& x, int& y)
103 {
104   x = m_xCursor;
105   y = m_yCursor;
106   
107   if (m_xCursor >= 0 && m_yCursor >= 0)
108     return true;
109   else
110     return false;
111 }
112
113 void 
114 ImageFileCanvas::OnMouseEvent(wxMouseEvent& event)
115 {
116   if (! m_pView)
117     return;
118   
119   wxClientDC dc(this);
120   PrepareDC(dc);
121   
122   wxPoint pt(event.GetLogicalPosition(dc));
123   
124   const ImageFile& rIF = m_pView->GetDocument()->getImageFile();
125   ImageFileArrayConst v = rIF.getArray();
126   int nx = rIF.nx();
127   int ny = rIF.ny();
128   const int yPt = ny - 1 - pt.y;
129   if (event.RightIsDown()) {
130     if (pt.x >= 0 && pt.x < nx && pt.y >= 0 && pt.y < ny) {
131       std::ostringstream os;
132       os << "Image value (" << pt.x << "," << yPt << ") = " << v[pt.x][yPt];
133       if (rIF.isComplex()) {
134         double dImag = rIF.getImaginaryArray()[pt.x][yPt];
135         if (dImag < 0)
136           os << " - " << -dImag;
137         else
138           os << " + " << dImag;
139         os << "i\n";
140       } else
141         os << "\n";
142       *theApp->getLog() << os.str().c_str();
143     } else
144       *theApp->getLog() << "Mouse out of image range (" << pt.x << "," << yPt << ")\n";
145   }
146   else if (event.LeftIsDown() || event.LeftUp() || event.RightUp()) {
147     if (pt.x >= 0 && pt.x < nx && pt.y >= 0 && pt.y < ny) {
148       if (m_xCursor >= 0 && m_yCursor >= 0) {
149         DrawRubberBandCursor (dc, m_xCursor, m_yCursor);
150       }
151       DrawRubberBandCursor (dc, pt.x, yPt);
152       m_xCursor = pt.x;
153       m_yCursor = yPt;
154     } else
155       *theApp->getLog() << "Mouse out of image range (" << pt.x << "," << yPt << ")\n";
156   }
157   if (event.LeftUp()) {
158     std::ostringstream os;
159     os << "Selected column " << pt.x << " , row " << yPt << "\n";
160     *theApp->getLog() << os.str().c_str();
161   }
162 }
163
164 // ImageFileView
165
166 IMPLEMENT_DYNAMIC_CLASS(ImageFileView, wxView)
167
168 BEGIN_EVENT_TABLE(ImageFileView, wxView)
169 EVT_MENU(IFMENU_FILE_EXPORT, ImageFileView::OnExport)
170 EVT_MENU(IFMENU_FILE_PROPERTIES, ImageFileView::OnProperties)
171 EVT_MENU(IFMENU_VIEW_SCALE_MINMAX, ImageFileView::OnScaleMinMax)
172 EVT_MENU(IFMENU_VIEW_SCALE_AUTO, ImageFileView::OnScaleAuto)
173 EVT_MENU(IFMENU_COMPARE_IMAGES, ImageFileView::OnCompare)
174 EVT_MENU(IFMENU_COMPARE_ROW, ImageFileView::OnCompareRow)
175 EVT_MENU(IFMENU_COMPARE_COL, ImageFileView::OnCompareCol)
176 EVT_MENU(IFMENU_FILTER_INVERTVALUES, ImageFileView::OnInvertValues)
177 EVT_MENU(IFMENU_FILTER_SQUARE, ImageFileView::OnSquare)
178 EVT_MENU(IFMENU_FILTER_SQRT, ImageFileView::OnSquareRoot)
179 EVT_MENU(IFMENU_FILTER_LOG, ImageFileView::OnLog)
180 EVT_MENU(IFMENU_FILTER_EXP, ImageFileView::OnExp)
181 EVT_MENU(IFMENU_FILTER_FOURIER, ImageFileView::OnFourier)
182 EVT_MENU(IFMENU_FILTER_INVERSE_FOURIER, ImageFileView::OnInverseFourier)
183 EVT_MENU(IFMENU_FILTER_SHUFFLEFOURIERTONATURALORDER, ImageFileView::OnShuffleFourierToNaturalOrder)
184 EVT_MENU(IFMENU_FILTER_SHUFFLENATURALTOFOURIERORDER, ImageFileView::OnShuffleNaturalToFourierOrder)
185 EVT_MENU(IFMENU_IMAGE_ADD, ImageFileView::OnAdd)
186 EVT_MENU(IFMENU_IMAGE_SUBTRACT, ImageFileView::OnSubtract)
187 EVT_MENU(IFMENU_IMAGE_MULTIPLY, ImageFileView::OnMultiply)
188 EVT_MENU(IFMENU_IMAGE_DIVIDE, ImageFileView::OnDivide)
189 EVT_MENU(IFMENU_IMAGE_SCALESIZE, ImageFileView::OnScaleSize)
190 #ifdef HAVE_FFTW
191 EVT_MENU(IFMENU_FILTER_FFT, ImageFileView::OnFFT)
192 EVT_MENU(IFMENU_FILTER_IFFT, ImageFileView::OnIFFT)
193 #endif
194 EVT_MENU(IFMENU_FILTER_MAGNITUDE, ImageFileView::OnMagnitude)
195 EVT_MENU(IFMENU_FILTER_PHASE, ImageFileView::OnPhase)
196 EVT_MENU(IFMENU_PLOT_ROW, ImageFileView::OnPlotRow)
197 EVT_MENU(IFMENU_PLOT_COL, ImageFileView::OnPlotCol)
198 EVT_MENU(IFMENU_PLOT_HISTOGRAM, ImageFileView::OnPlotHistogram)
199 END_EVENT_TABLE()
200
201 ImageFileView::ImageFileView(void) 
202 : wxView(), m_canvas(NULL), m_frame(NULL), m_bMinSpecified(false), m_bMaxSpecified(false)
203 {
204   m_iDefaultExportFormatID = ImageFile::FORMAT_PNG;
205 }
206
207 ImageFileView::~ImageFileView(void)
208 {
209 }
210
211 void
212 ImageFileView::OnProperties (wxCommandEvent& event)
213 {
214   const ImageFile& rIF = GetDocument()->getImageFile();
215   if (rIF.nx() == 0 || rIF.ny() == 0)
216     *theApp->getLog() << "Properties: empty imagefile\n";
217   else {
218     const std::string& rFilename = rIF.getFilename();
219     std::ostringstream os;
220     double min, max, mean, mode, median, stddev;
221     rIF.statistics (rIF.getArray(), min, max, mean, mode, median, stddev);
222     os << "Filename: " << rFilename << "\n";
223     os << "Size: (" << rIF.nx() << "," << rIF.ny() << ")\n";
224     os << "Data type: ";
225     if (rIF.isComplex())
226       os << "Complex\n";
227     else
228       os << "Real\n";
229     os << "\nMinimum: "<<min<<"\nMaximum: "<<max<<"\nMean: "<<mean<<"\nMedian: "<<median<<"\nMode: "<<mode<<"\nStandard Deviation: "<<stddev << "\n";
230     if (rIF.isComplex()) {
231       rIF.statistics (rIF.getImaginaryArray(), min, max, mean, mode, median, stddev);
232       os << "\nImaginary: min: "<<min<<"\nmax: "<<max<<"\nmean: "<<mean<<"\nmedian: "<<median<<"\nmode: "<<mode<<"\nstddev: "<<stddev << "\n";
233     }
234     if (rIF.nLabels() > 0) {
235       os << "\n";
236       rIF.printLabelsBrief (os);
237     }
238     *theApp->getLog() << os.str().c_str();
239     wxMessageDialog dialogMsg (m_frame, os.str().c_str(), "Imagefile Properties", wxOK | wxICON_INFORMATION);
240     dialogMsg.ShowModal();
241   }
242 }
243
244 void 
245 ImageFileView::OnScaleAuto (wxCommandEvent& event)
246 {
247   const ImageFile& rIF = GetDocument()->getImageFile();
248   double min, max, mean, mode, median, stddev;
249   rIF.statistics(min, max, mean, mode, median, stddev);
250   DialogAutoScaleParameters dialogAutoScale (m_frame, mean, mode, median, stddev, m_dAutoScaleFactor);
251   int iRetVal = dialogAutoScale.ShowModal();
252   if (iRetVal == wxID_OK) {
253     m_bMinSpecified = true;
254     m_bMaxSpecified = true;
255     double dMin, dMax;
256     if (dialogAutoScale.getMinMax (&dMin, &dMax)) {
257       m_dMinPixel = dMin;
258       m_dMaxPixel = dMax;
259       m_dAutoScaleFactor = dialogAutoScale.getAutoScaleFactor();
260       OnUpdate (this, NULL);
261     }
262   }
263 }
264
265 void 
266 ImageFileView::OnScaleMinMax (wxCommandEvent& event)
267 {
268   const ImageFile& rIF = GetDocument()->getImageFile();
269   double min, max;
270   if (! m_bMinSpecified && ! m_bMaxSpecified)
271     rIF.getMinMax (min, max);
272   
273   if (m_bMinSpecified)
274     min = m_dMinPixel;
275   if (m_bMaxSpecified)
276     max = m_dMaxPixel;
277   
278   DialogGetMinMax dialogMinMax (m_frame, "Set Image Minimum & Maximum", min, max);
279   int retVal = dialogMinMax.ShowModal();
280   if (retVal == wxID_OK) {
281     m_bMinSpecified = true;
282     m_bMaxSpecified = true;
283     m_dMinPixel = dialogMinMax.getMinimum();
284     m_dMaxPixel = dialogMinMax.getMaximum();
285     OnUpdate (this, NULL);
286   }
287 }
288
289 void
290 ImageFileView::OnCompare (wxCommandEvent& event)
291 {
292   std::vector<ImageFileDocument*> vecIF;
293   theApp->getCompatibleImages (GetDocument(), vecIF);
294   
295   if (vecIF.size() == 0) {
296     wxMessageBox("There are no compatible image files open for comparision", "No comparison images");
297   } else {
298     DialogGetComparisonImage dialogGetCompare(m_frame, "Get Comparison Image", vecIF, true);
299     
300     if (dialogGetCompare.ShowModal() == wxID_OK) {
301       const ImageFile& rIF = GetDocument()->getImageFile();
302       ImageFileDocument* pCompareDoc = dialogGetCompare.getImageFileDocument();
303       const ImageFile& rCompareIF = pCompareDoc->getImageFile();
304       std::ostringstream os;
305       double min, max, mean, mode, median, stddev;
306       rIF.statistics (min, max, mean, mode, median, stddev);
307       os << rIF.getFilename() << ": minimum=" << min << ", maximum=" << max << ", mean=" << mean << ", mode=" << mode << ", median=" << median << ", stddev=" << stddev << "\n";
308       rCompareIF.statistics (min, max, mean, mode, median, stddev);
309       os << pCompareDoc->GetFirstView()->GetFrame()->GetTitle().c_str() << ": minimum=" << min << ", maximum=" << max << ", mean=" << mean << ", mode=" << mode << ", median=" << median << ", stddev=" << stddev << "\n";
310       os << "\n";
311       double d, r, e;
312       rIF.comparativeStatistics (rCompareIF, d, r, e);
313       os << "Comparative Statistics: d=" << d << ", r=" << r << ", e=" << e << "\n";
314       *theApp->getLog() << os.str().c_str();
315       if (dialogGetCompare.getMakeDifferenceImage()) {
316         ImageFileDocument* pDifferenceDoc = dynamic_cast<ImageFileDocument*>(theApp->getDocManager()->CreateDocument("untitled.if", wxDOC_SILENT));
317         if (! pDifferenceDoc) {
318           sys_error (ERR_SEVERE, "Unable to create image file");
319           return;
320         }
321         ImageFile& differenceImage = pDifferenceDoc->getImageFile();
322         
323         differenceImage.setArraySize (rIF.nx(), rIF.ny());
324         if (! rIF.subtractImages (rCompareIF, differenceImage)) {
325           pDifferenceDoc->DeleteAllViews();
326           return;
327         }
328         
329         wxString s = GetDocument()->GetFirstView()->GetFrame()->GetTitle() + ": ";
330         differenceImage.labelsCopy (rIF, s.c_str());
331         s = pCompareDoc->GetFirstView()->GetFrame()->GetTitle() + ": ";
332         differenceImage.labelsCopy (rCompareIF, s.c_str());
333         std::ostringstream osLabel;
334         osLabel << "Compare image " << GetDocument()->GetFirstView()->GetFrame()->GetTitle().c_str() 
335           << " and " << pCompareDoc->GetFirstView()->GetFrame()->GetTitle().c_str() << ": "
336           << os.str().c_str();
337         differenceImage.labelAdd (os.str().c_str());
338         if (theApp->getSetModifyNewDocs())
339           pDifferenceDoc->Modify(true);
340         pDifferenceDoc->UpdateAllViews(this);
341         pDifferenceDoc->GetFirstView()->OnUpdate (this, NULL);
342       }
343       wxMessageBox(os.str().c_str(), "Image Comparison");
344     }
345   }
346 }
347
348 void
349 ImageFileView::OnInvertValues (wxCommandEvent& event)
350 {
351   ImageFile& rIF = GetDocument()->getImageFile();
352   rIF.invertPixelValues (rIF);
353   rIF.labelAdd ("Invert Pixel Values");
354   if (theApp->getSetModifyNewDocs())
355     GetDocument()->Modify(TRUE);
356   GetDocument()->UpdateAllViews(this);
357 }
358
359 void
360 ImageFileView::OnSquare (wxCommandEvent& event)
361 {
362   ImageFile& rIF = GetDocument()->getImageFile();
363   rIF.square (rIF);
364   rIF.labelAdd ("Square Pixel Values");
365   if (theApp->getSetModifyNewDocs())
366     GetDocument()->Modify(TRUE);
367   GetDocument()->UpdateAllViews(this);
368 }
369
370 void
371 ImageFileView::OnSquareRoot (wxCommandEvent& event)
372 {
373   ImageFile& rIF = GetDocument()->getImageFile();
374   rIF.sqrt (rIF);
375   rIF.labelAdd ("Square-root Pixel Values");
376   if (theApp->getSetModifyNewDocs())
377     GetDocument()->Modify(TRUE);
378   GetDocument()->UpdateAllViews(this);
379 }
380
381 void
382 ImageFileView::OnLog (wxCommandEvent& event)
383 {
384   ImageFile& rIF = GetDocument()->getImageFile();
385   rIF.log (rIF);
386   rIF.labelAdd ("Logrithm base-e Pixel Values");
387   if (theApp->getSetModifyNewDocs())
388     GetDocument()->Modify(TRUE);
389   GetDocument()->UpdateAllViews(this);
390 }
391
392 void
393 ImageFileView::OnExp (wxCommandEvent& event)
394 {
395   ImageFile& rIF = GetDocument()->getImageFile();
396   rIF.exp (rIF);
397   rIF.labelAdd ("Exponent base-e Pixel Values");
398   if (theApp->getSetModifyNewDocs())
399     GetDocument()->Modify(TRUE);
400   GetDocument()->UpdateAllViews(this);
401 }
402
403 void
404 ImageFileView::OnAdd (wxCommandEvent& event)
405 {
406   std::vector<ImageFileDocument*> vecIF;
407   theApp->getCompatibleImages (GetDocument(), vecIF);
408   
409   if (vecIF.size() == 0) {
410     wxMessageBox ("There are no compatible image files open for comparision", "No comparison images");
411   } else {
412     DialogGetComparisonImage dialogGetCompare (m_frame, "Get Image to Add", vecIF, false);
413     
414     if (dialogGetCompare.ShowModal() == wxID_OK) {
415       ImageFile& rIF = GetDocument()->getImageFile();
416       ImageFileDocument* pRHSDoc = dialogGetCompare.getImageFileDocument();
417       const ImageFile& rRHSIF = pRHSDoc->getImageFile();
418       ImageFileDocument* pNewDoc = dynamic_cast<ImageFileDocument*>(theApp->getDocManager()->CreateDocument("untitled.if", wxDOC_SILENT));
419         if (! pNewDoc) {
420           sys_error (ERR_SEVERE, "Unable to create image file");
421           return;
422         }
423       ImageFile& newImage = pNewDoc->getImageFile();  
424       newImage.setArraySize (rIF.nx(), rIF.ny());
425       rIF.addImages (rRHSIF, newImage);
426       std::ostringstream os;
427       os << "Add image " << GetDocument()->GetFirstView()->GetFrame()->GetTitle().c_str() << " and " 
428         << pRHSDoc->GetFirstView()->GetFrame()->GetTitle().c_str();
429       wxString s = GetDocument()->GetFirstView()->GetFrame()->GetTitle() + ": ";
430       newImage.labelsCopy (rIF, s.c_str());
431       s = pRHSDoc->GetFirstView()->GetFrame()->GetTitle() + ": ";
432       newImage.labelsCopy (rRHSIF, s.c_str());
433       newImage.labelAdd (os.str().c_str());
434       *theApp->getLog() << os.str().c_str() << "\n";
435       if (theApp->getSetModifyNewDocs())
436         pNewDoc->Modify(TRUE);
437       pNewDoc->UpdateAllViews(this);
438       pNewDoc->GetFirstView()->OnUpdate (this, NULL);
439     }
440   }
441 }
442
443 void
444 ImageFileView::OnSubtract (wxCommandEvent& event)
445 {
446   std::vector<ImageFileDocument*> vecIF;
447   theApp->getCompatibleImages (GetDocument(), vecIF);
448   
449   if (vecIF.size() == 0) {
450     wxMessageBox ("There are no compatible image files open for comparision", "No comparison images");
451   } else {
452     DialogGetComparisonImage dialogGetCompare (m_frame, "Get Image to Subtract", vecIF, false);
453     
454     if (dialogGetCompare.ShowModal() == wxID_OK) {
455       ImageFile& rIF = GetDocument()->getImageFile();
456       ImageFileDocument* pRHSDoc = dialogGetCompare.getImageFileDocument();
457       const ImageFile& rRHSIF = pRHSDoc->getImageFile();
458       ImageFileDocument* pNewDoc = dynamic_cast<ImageFileDocument*>(theApp->getDocManager()->CreateDocument("untitled.if", wxDOC_SILENT));
459         if (! pNewDoc) {
460           sys_error (ERR_SEVERE, "Unable to create image file");
461           return;
462         }
463       ImageFile& newImage = pNewDoc->getImageFile();  
464       newImage.setArraySize (rIF.nx(), rIF.ny());
465       rIF.subtractImages (rRHSIF, newImage);
466       std::ostringstream os;
467       os << "Subtract image " << GetDocument()->GetFirstView()->GetFrame()->GetTitle().c_str() << " and " 
468         << pRHSDoc->GetFirstView()->GetFrame()->GetTitle().c_str();
469       wxString s = GetDocument()->GetFirstView()->GetFrame()->GetTitle() + ": ";
470       newImage.labelsCopy (rIF, s.c_str());
471       s = pRHSDoc->GetFirstView()->GetFrame()->GetTitle() + ": ";
472       newImage.labelsCopy (rRHSIF, s.c_str());
473       newImage.labelAdd (os.str().c_str());
474       *theApp->getLog() << os.str().c_str() << "\n";
475       if (theApp->getSetModifyNewDocs())
476         pNewDoc->Modify(TRUE);
477       pNewDoc->UpdateAllViews(this);
478       pNewDoc->GetFirstView()->OnUpdate (this, NULL);
479     }
480   }
481 }
482
483 void
484 ImageFileView::OnMultiply (wxCommandEvent& event)
485 {
486   std::vector<ImageFileDocument*> vecIF;
487   theApp->getCompatibleImages (GetDocument(), vecIF);
488   
489   if (vecIF.size() == 0) {
490     wxMessageBox ("There are no compatible image files open for comparision", "No comparison images");
491   } else {
492     DialogGetComparisonImage dialogGetCompare (m_frame, "Get Image to Multiply", vecIF, false);
493     
494     if (dialogGetCompare.ShowModal() == wxID_OK) {
495       ImageFile& rIF = GetDocument()->getImageFile();
496       ImageFileDocument* pRHSDoc = dialogGetCompare.getImageFileDocument();
497       const ImageFile& rRHSIF = pRHSDoc->getImageFile();
498       ImageFileDocument* pNewDoc = dynamic_cast<ImageFileDocument*>(theApp->getDocManager()->CreateDocument("untitled.if", wxDOC_SILENT));
499         if (! pNewDoc) {
500           sys_error (ERR_SEVERE, "Unable to create image file");
501           return;
502         }
503       ImageFile& newImage = pNewDoc->getImageFile();  
504       newImage.setArraySize (rIF.nx(), rIF.ny());
505       rIF.multiplyImages (rRHSIF, newImage);
506       std::ostringstream os;
507       os << "Multiply image " << GetDocument()->GetFirstView()->GetFrame()->GetTitle().c_str() << " and " 
508         << pRHSDoc->GetFirstView()->GetFrame()->GetTitle().c_str();
509       wxString s = GetDocument()->GetFirstView()->GetFrame()->GetTitle() + ": ";
510       newImage.labelsCopy (rIF, s.c_str());
511       s = pRHSDoc->GetFirstView()->GetFrame()->GetTitle() + ": ";
512       newImage.labelsCopy (rRHSIF, s.c_str());
513       newImage.labelAdd (os.str().c_str());
514       *theApp->getLog() << os.str().c_str() << "\n";
515       if (theApp->getSetModifyNewDocs())
516         pNewDoc->Modify(TRUE);
517       pNewDoc->UpdateAllViews(this);
518       pNewDoc->GetFirstView()->OnUpdate (this, NULL);
519     }
520   }
521 }
522
523 void
524 ImageFileView::OnDivide (wxCommandEvent& event)
525 {
526   std::vector<ImageFileDocument*> vecIF;
527   theApp->getCompatibleImages (GetDocument(), vecIF);
528   
529   if (vecIF.size() == 0) {
530     wxMessageBox ("There are no compatible image files open for comparision", "No comparison images");
531   } else {
532     DialogGetComparisonImage dialogGetCompare (m_frame, "Get Image to Divide", vecIF, false);
533     
534     if (dialogGetCompare.ShowModal() == wxID_OK) {
535       ImageFile& rIF = GetDocument()->getImageFile();
536       ImageFileDocument* pRHSDoc = dialogGetCompare.getImageFileDocument();
537       const ImageFile& rRHSIF = pRHSDoc->getImageFile();
538       ImageFileDocument* pNewDoc = dynamic_cast<ImageFileDocument*>(theApp->getDocManager()->CreateDocument("untitled.if", wxDOC_SILENT));
539         if (! pNewDoc) {
540           sys_error (ERR_SEVERE, "Unable to create image file");
541           return;
542         }
543       ImageFile& newImage = pNewDoc->getImageFile();  
544       newImage.setArraySize (rIF.nx(), rIF.ny());
545       rIF.divideImages (rRHSIF, newImage);
546       std::ostringstream os;
547       os << "Divide image " << GetDocument()->GetFirstView()->GetFrame()->GetTitle().c_str() << " by " 
548         << pRHSDoc->GetFirstView()->GetFrame()->GetTitle().c_str();
549       wxString s = GetDocument()->GetFirstView()->GetFrame()->GetTitle() + ": ";
550       newImage.labelsCopy (rIF, s.c_str());
551       s = pRHSDoc->GetFirstView()->GetFrame()->GetTitle() + ": ";
552       newImage.labelsCopy (rRHSIF, s.c_str());
553       newImage.labelAdd (os.str().c_str());
554       *theApp->getLog() << os.str().c_str() << "\n";
555       if (theApp->getSetModifyNewDocs())
556         pNewDoc->Modify(TRUE);
557       pNewDoc->UpdateAllViews(this);
558       pNewDoc->GetFirstView()->OnUpdate (this, NULL);
559     }
560   }
561 }
562
563
564 #ifdef HAVE_FFTW
565 void
566 ImageFileView::OnFFT (wxCommandEvent& event)
567 {
568   ImageFile& rIF = GetDocument()->getImageFile();
569   wxProgressDialog dlgProgress (wxString("FFT"), wxString("FFT Progress"), 1, m_frame, wxPD_APP_MODAL);
570   rIF.fft (rIF);
571   rIF.labelAdd ("FFT Image");
572   m_bMinSpecified = false;
573   m_bMaxSpecified = false;
574   if (theApp->getSetModifyNewDocs())
575     GetDocument()->Modify(TRUE);
576   GetDocument()->UpdateAllViews(this);
577 }
578
579 void
580 ImageFileView::OnIFFT (wxCommandEvent& event)
581 {
582   ImageFile& rIF = GetDocument()->getImageFile();
583   wxProgressDialog dlgProgress (wxString("IFFT"), wxString("IFFT Progress"), 1, m_frame, wxPD_APP_MODAL);
584   rIF.ifft (rIF);
585   rIF.labelAdd ("IFFT Image");
586   m_bMinSpecified = false;
587   m_bMaxSpecified = false;
588   if (theApp->getSetModifyNewDocs())
589     GetDocument()->Modify(TRUE);
590   GetDocument()->UpdateAllViews(this);
591 }
592 #endif
593
594 void
595 ImageFileView::OnFourier (wxCommandEvent& event)
596 {
597   ImageFile& rIF = GetDocument()->getImageFile();
598   wxProgressDialog dlgProgress (wxString("Fourier"), wxString("Fourier Progress"), 1, m_frame, wxPD_APP_MODAL);
599   rIF.fourier (rIF);
600   rIF.labelAdd ("Fourier Image");
601   m_bMinSpecified = false;
602   m_bMaxSpecified = false;
603   if (theApp->getSetModifyNewDocs())
604     GetDocument()->Modify(TRUE);
605   GetDocument()->UpdateAllViews(this);
606 }
607 void
608 ImageFileView::OnInverseFourier (wxCommandEvent& event)
609 {
610   ImageFile& rIF = GetDocument()->getImageFile();
611   wxProgressDialog dlgProgress (wxString("Inverse Fourier"), wxString("Inverse Fourier Progress"), 1, m_frame, wxPD_APP_MODAL);
612   rIF.inverseFourier (rIF);
613   rIF.labelAdd ("Inverse Fourier Image");
614   m_bMinSpecified = false;
615   m_bMaxSpecified = false;
616   if (theApp->getSetModifyNewDocs())
617     GetDocument()->Modify(TRUE);
618   GetDocument()->UpdateAllViews(this);
619 }
620
621 void
622 ImageFileView::OnShuffleNaturalToFourierOrder (wxCommandEvent& event)
623 {
624   ImageFile& rIF = GetDocument()->getImageFile();
625   Fourier::shuffleNaturalToFourierOrder (rIF);
626   rIF.labelAdd ("Shuffle Natural To Fourier Order");
627   m_bMinSpecified = false;
628   m_bMaxSpecified = false;
629   if (theApp->getSetModifyNewDocs())
630     GetDocument()->Modify(TRUE);
631   GetDocument()->UpdateAllViews(this);
632 }
633
634 void
635 ImageFileView::OnShuffleFourierToNaturalOrder (wxCommandEvent& event)
636 {
637   ImageFile& rIF = GetDocument()->getImageFile();
638   Fourier::shuffleFourierToNaturalOrder (rIF);
639   rIF.labelAdd ("Shuffle Fourier To Natural Order");
640   m_bMinSpecified = false;
641   m_bMaxSpecified = false;
642   if (theApp->getSetModifyNewDocs())
643     GetDocument()->Modify(TRUE);
644   GetDocument()->UpdateAllViews(this);
645 }
646
647 void
648 ImageFileView::OnMagnitude (wxCommandEvent& event)
649 {
650   ImageFile& rIF = GetDocument()->getImageFile();
651   if (rIF.isComplex()) {
652     rIF.magnitude (rIF);
653     rIF.labelAdd ("Magnitude of complex-image");
654   m_bMinSpecified = false;
655   m_bMaxSpecified = false;
656   if (theApp->getSetModifyNewDocs())
657     GetDocument()->Modify(TRUE);
658   GetDocument()->UpdateAllViews(this);
659   }
660 }
661
662 void
663 ImageFileView::OnPhase (wxCommandEvent& event)
664 {
665   ImageFile& rIF = GetDocument()->getImageFile();
666   if (rIF.isComplex()) {
667     rIF.phase (rIF);
668     rIF.labelAdd ("Phase of complex-image");
669   m_bMinSpecified = false;
670   m_bMaxSpecified = false;
671   if (theApp->getSetModifyNewDocs())
672     GetDocument()->Modify(TRUE);
673   GetDocument()->UpdateAllViews(this);
674   }
675 }
676
677
678 ImageFileCanvas* 
679 ImageFileView::CreateCanvas (wxView *view, wxFrame *parent)
680 {
681   ImageFileCanvas* pCanvas;
682   int width, height;
683   parent->GetClientSize(&width, &height);
684   
685   pCanvas = new ImageFileCanvas (dynamic_cast<ImageFileView*>(view), parent, wxPoint(0, 0), wxSize(width, height), 0);
686   
687   pCanvas->SetScrollbars(20, 20, 50, 50);
688   pCanvas->SetBackgroundColour(*wxWHITE);
689   pCanvas->Clear();
690   
691   return pCanvas;
692 }
693
694 wxFrame*
695 ImageFileView::CreateChildFrame(wxDocument *doc, wxView *view)
696 {
697   wxDocChildFrame *subframe = new wxDocChildFrame(doc, view, theApp->getMainFrame(), -1, "ImageFile Frame", wxPoint(-1, -1), wxSize(0, 0), wxDEFAULT_FRAME_STYLE);
698   
699   wxMenu *file_menu = new wxMenu;
700   
701   file_menu->Append(MAINMENU_FILE_CREATE_PHANTOM, "Cr&eate Phantom...");
702   file_menu->Append(MAINMENU_FILE_CREATE_FILTER, "Create &Filter...");
703   file_menu->Append(wxID_OPEN, "&Open...");
704   file_menu->Append(wxID_SAVE, "&Save");
705   file_menu->Append(wxID_SAVEAS, "Save &As...");
706   file_menu->Append(wxID_CLOSE, "&Close");
707   
708   file_menu->AppendSeparator();
709   file_menu->Append(IFMENU_FILE_PROPERTIES, "P&roperties");
710   file_menu->Append(IFMENU_FILE_EXPORT, "&Export...");
711   
712   file_menu->AppendSeparator();
713   file_menu->Append(wxID_PRINT, "&Print...");
714   file_menu->Append(wxID_PRINT_SETUP, "Print &Setup...");
715   file_menu->Append(wxID_PREVIEW, "Print Pre&view");
716   
717   wxMenu *view_menu = new wxMenu;
718   view_menu->Append(IFMENU_VIEW_SCALE_MINMAX, "Display Scale &Set...");
719   view_menu->Append(IFMENU_VIEW_SCALE_AUTO, "Display Scale &Auto...");
720   
721   wxMenu* filter_menu = new wxMenu;
722   filter_menu->Append (IFMENU_FILTER_INVERTVALUES, "&Invert Values");
723   filter_menu->Append (IFMENU_FILTER_SQUARE, "&Square");
724   filter_menu->Append (IFMENU_FILTER_SQRT, "Square &Root");
725   filter_menu->Append (IFMENU_FILTER_LOG, "&Log");
726   filter_menu->Append (IFMENU_FILTER_EXP, "&Exp");
727   filter_menu->AppendSeparator();
728 #ifdef HAVE_FFTW
729   filter_menu->Append (IFMENU_FILTER_FFT, "&FFT");
730   filter_menu->Append (IFMENU_FILTER_IFFT, "&IFFT");
731   filter_menu->Append (IFMENU_FILTER_FOURIER, "F&ourier");
732   filter_menu->Append (IFMENU_FILTER_INVERSE_FOURIER, "Inverse Fo&urier");
733 #else
734   filter_menu->Append (IFMENU_FILTER_FOURIER, "&Fourier");
735   filter_menu->Append (IFMENU_FILTER_INVERSE_FOURIER, "&Inverse Fourier");
736 #endif
737   filter_menu->Append (IFMENU_FILTER_SHUFFLEFOURIERTONATURALORDER, "S&huffle Fourier to Natural Order");
738   filter_menu->Append (IFMENU_FILTER_SHUFFLENATURALTOFOURIERORDER, "Shu&ffle Natural to Fourier Order");
739   filter_menu->Append (IFMENU_FILTER_MAGNITUDE, "&Magnitude");
740   filter_menu->Append (IFMENU_FILTER_PHASE, "&Phase");
741   
742   wxMenu* image_menu = new wxMenu;
743   image_menu->Append (IFMENU_IMAGE_ADD, "&Add...");
744   image_menu->Append (IFMENU_IMAGE_SUBTRACT, "&Subtract...");
745   image_menu->Append (IFMENU_IMAGE_MULTIPLY, "&Multiply...");
746   image_menu->Append (IFMENU_IMAGE_DIVIDE, "&Divide...");
747   image_menu->AppendSeparator();
748   image_menu->Append (IFMENU_IMAGE_SCALESIZE, "S&cale Size...");
749
750   wxMenu *analyze_menu = new wxMenu;
751   analyze_menu->Append (IFMENU_PLOT_ROW, "Plot &Row");
752   analyze_menu->Append (IFMENU_PLOT_COL, "Plot &Column");
753   analyze_menu->Append (IFMENU_PLOT_HISTOGRAM, "Plot &Histogram");
754   analyze_menu->AppendSeparator();
755   analyze_menu->Append (IFMENU_COMPARE_IMAGES, "Compare &Images...");
756   analyze_menu->Append (IFMENU_COMPARE_ROW, "Compare &Row");
757   analyze_menu->Append (IFMENU_COMPARE_COL, "Compare &Column");
758   
759   wxMenu *help_menu = new wxMenu;
760   help_menu->Append(MAINMENU_HELP_ABOUT, "&About");
761   
762   wxMenuBar *menu_bar = new wxMenuBar;
763   
764   menu_bar->Append(file_menu, "&File");
765   menu_bar->Append(view_menu, "&View");
766   menu_bar->Append(image_menu, "&Image");
767   menu_bar->Append(filter_menu, "Fi&lter");
768   menu_bar->Append(analyze_menu, "&Analyze");
769   menu_bar->Append(help_menu, "&Help");
770   
771   subframe->SetMenuBar(menu_bar);
772   
773   subframe->Centre(wxBOTH);
774   
775   return subframe;
776 }
777
778
779 bool 
780 ImageFileView::OnCreate (wxDocument *doc, long WXUNUSED(flags) )
781 {
782   m_frame = CreateChildFrame(doc, this);
783   SetFrame (m_frame);
784   
785   m_bMinSpecified = false;
786   m_bMaxSpecified = false;
787   m_dAutoScaleFactor = 1.;
788   
789   int width, height;
790   m_frame->GetClientSize (&width, &height);
791   m_frame->SetTitle("ImageFileView");
792   m_canvas = CreateCanvas (this, m_frame);
793   
794   int x, y;  // X requires a forced resize
795   m_frame->GetSize(&x, &y);
796   m_frame->SetSize(-1, -1, x, y);
797   m_frame->SetFocus();
798   m_frame->Show(true);
799   Activate(true);
800   
801   return true;
802 }
803
804 void 
805 ImageFileView::OnDraw (wxDC* dc)
806 {
807   if (m_bitmap.Ok())
808     dc->DrawBitmap(m_bitmap, 0, 0, false);
809   
810   int xCursor, yCursor;
811   if (m_canvas->GetCurrentCursor (xCursor, yCursor))
812     m_canvas->DrawRubberBandCursor (*dc, xCursor, yCursor);
813 }
814
815
816 void 
817 ImageFileView::OnUpdate (wxView *WXUNUSED(sender), wxObject *WXUNUSED(hint) )
818 {
819   const ImageFile& rIF = dynamic_cast<ImageFileDocument*>(GetDocument())->getImageFile();
820   ImageFileArrayConst v = rIF.getArray();
821   int nx = rIF.nx();
822   int ny = rIF.ny();
823   if (v != NULL && nx != 0 && ny != 0) {
824     if (! m_bMinSpecified || ! m_bMaxSpecified) {
825       double min, max;
826       rIF.getMinMax (min, max);
827       if (! m_bMinSpecified)
828         m_dMinPixel = min;
829       if (! m_bMaxSpecified)
830         m_dMaxPixel = max;
831     }
832     double scaleWidth = m_dMaxPixel - m_dMinPixel;
833     
834     unsigned char* imageData = new unsigned char [nx * ny * 3];
835     for (int ix = 0; ix < nx; ix++) {
836       for (int iy = 0; iy < ny; iy++) {
837         double scaleValue = ((v[ix][iy] - m_dMinPixel) / scaleWidth) * 255;
838         int intensity = static_cast<int>(scaleValue + 0.5);
839         intensity = clamp (intensity, 0, 255);
840         int baseAddr = ((ny - 1 - iy) * nx + ix) * 3;
841         imageData[baseAddr] = imageData[baseAddr+1] = imageData[baseAddr+2] = intensity;
842       }
843     }
844     wxImage image (nx, ny, imageData, true);
845     m_bitmap = image.ConvertToBitmap();
846     delete imageData;
847     int xSize = nx;
848     int ySize = ny;
849     xSize = clamp (xSize, 0, 800);
850     ySize = clamp (ySize, 0, 800);
851     m_frame->SetClientSize (xSize, ySize);
852     m_canvas->SetScrollbars(20, 20, nx/20, ny/20);
853     m_canvas->SetBackgroundColour(*wxWHITE);
854   } 
855   
856   if (m_canvas)
857     m_canvas->Refresh();
858 }
859
860 bool 
861 ImageFileView::OnClose (bool deleteWindow)
862 {
863   if (!GetDocument()->Close())
864     return false;
865   
866   m_canvas->Clear();
867   m_canvas->m_pView = NULL;
868   m_canvas = NULL;
869   wxString s(theApp->GetAppName());
870   if (m_frame)
871     m_frame->SetTitle(s);
872   SetFrame(NULL);
873   
874   Activate(false);
875   
876   if (deleteWindow) {
877     delete m_frame;
878     return true;
879   }
880   return true;
881 }
882
883 void
884 ImageFileView::OnExport (wxCommandEvent& event)
885 {
886   ImageFile& rIF = dynamic_cast<ImageFileDocument*>(GetDocument())->getImageFile();
887   ImageFileArrayConst v = rIF.getArray();
888   int nx = rIF.nx();
889   int ny = rIF.ny();
890   if (v != NULL && nx != 0 && ny != 0) {
891     if (! m_bMinSpecified || ! m_bMaxSpecified) {
892       double min, max;
893       rIF.getMinMax (min, max);
894       if (! m_bMinSpecified)
895         m_dMinPixel = min;
896       if (! m_bMaxSpecified)
897         m_dMaxPixel = max;
898     }
899
900     DialogExportParameters dialogExport (m_frame, m_iDefaultExportFormatID);
901     if (dialogExport.ShowModal() == wxID_OK) {
902       wxString strFormatName (dialogExport.getFormatName ());
903       m_iDefaultExportFormatID = ImageFile::convertFormatNameToID (strFormatName.c_str());
904
905       wxString strExt;
906       wxString strWildcard;
907       if (m_iDefaultExportFormatID == ImageFile::FORMAT_PGM || m_iDefaultExportFormatID == ImageFile::FORMAT_PGMASCII) {
908         strExt = ".pgm";
909         strWildcard = "PGM Files (*.pgm)|*.pgm";
910       }
911 #ifdef HAVE_PNG
912       else if (m_iDefaultExportFormatID == ImageFile::FORMAT_PNG || m_iDefaultExportFormatID == ImageFile::FORMAT_PNG16) {
913         strExt = ".png";
914         strWildcard = "PNG Files (*.png)|*.png";
915       }
916 #endif
917
918       const wxString& strFilename = wxFileSelector (wxString("Export Filename"), wxString(""), 
919         wxString(""), strExt, strWildcard, wxOVERWRITE_PROMPT | wxHIDE_READONLY | wxSAVE);
920       if (strFilename) {
921         rIF.exportImage (strFormatName.c_str(), strFilename.c_str(), 1, 1, m_dMinPixel, m_dMaxPixel);
922         *theApp->getLog() << "Exported file " << strFilename << "\n";
923       }
924     }
925   }
926 }
927
928 void
929 ImageFileView::OnScaleSize (wxCommandEvent& event)
930 {
931   ImageFile& rIF = GetDocument()->getImageFile();
932   unsigned int iOldNX = rIF.nx();
933   unsigned int iOldNY = rIF.ny();
934
935   DialogGetXYSize dialogGetXYSize (m_frame, "Set New X & Y Dimensions", iOldNX, iOldNY);
936   if (dialogGetXYSize.ShowModal() == wxID_OK) {
937     unsigned int iNewNX = dialogGetXYSize.getXSize();
938     unsigned int iNewNY = dialogGetXYSize.getYSize();
939     std::ostringstream os;
940     os << "Scale Size from (" << iOldNX << "," << iOldNY << ") to (" << iNewNX << "," << iNewNY << ")";
941     ImageFileDocument* pScaledDoc = dynamic_cast<ImageFileDocument*>(theApp->getDocManager()->CreateDocument("untitled.if", wxDOC_SILENT));
942     if (! pScaledDoc) {
943       sys_error (ERR_SEVERE, "Unable to create image file");
944       return;
945     }
946     ImageFile& rScaledIF = pScaledDoc->getImageFile();
947     rScaledIF.setArraySize (iNewNX, iNewNY);
948     rScaledIF.labelAdd (os.str().c_str());
949     rIF.scaleImage (rScaledIF);
950     *theApp->getLog() << os.str().c_str() << "\n";
951     if (theApp->getSetModifyNewDocs())
952       pScaledDoc->Modify(TRUE);
953     pScaledDoc->UpdateAllViews (this);
954     pScaledDoc->GetFirstView()->OnUpdate (this, NULL);
955   }
956 }
957
958 void
959 ImageFileView::OnPlotRow (wxCommandEvent& event)
960 {
961   int xCursor, yCursor;
962   if (! m_canvas->GetCurrentCursor (xCursor, yCursor)) {
963     wxMessageBox ("No row selected. Please use left mouse button on image to select column","Error");
964     return;
965   }
966   
967   const ImageFile& rIF = dynamic_cast<ImageFileDocument*>(GetDocument())->getImageFile();
968   ImageFileArrayConst v = rIF.getArray();
969   int nx = rIF.nx();
970   int ny = rIF.ny();
971   
972   if (v != NULL && yCursor < ny) {
973     double* pX = new double [nx];
974     double* pY = new double [nx];
975     for (int i = 0; i < nx; i++) {
976       pX[i] = i;
977       pY[i] = v[i][yCursor];
978     }
979     PlotFileDocument* pPlotDoc = dynamic_cast<PlotFileDocument*>(theApp->getDocManager()->CreateDocument("untitled.plt", wxDOC_SILENT));
980     if (! pPlotDoc) {
981       sys_error (ERR_SEVERE, "Internal error: unable to create Plot file");
982     } else {
983       PlotFile& rPlotFile = pPlotDoc->getPlotFile();
984       std::ostringstream os;
985       os << "Row " << yCursor;
986       std::string title("title ");
987       title += os.str();
988       rPlotFile.addEzsetCommand (title.c_str());
989       rPlotFile.addEzsetCommand ("xlabel Column");
990       rPlotFile.addEzsetCommand ("ylabel Pixel Value");
991       rPlotFile.addEzsetCommand ("lxfrac 0");
992       rPlotFile.addEzsetCommand ("box");
993       rPlotFile.addEzsetCommand ("grid");
994       rPlotFile.setCurveSize (2, nx);
995       rPlotFile.addColumn (0, pX);
996       rPlotFile.addColumn (1, pY);
997     }
998     delete pX;
999     delete pY;
1000     if (theApp->getSetModifyNewDocs())
1001       pPlotDoc->Modify(true);
1002     pPlotDoc->UpdateAllViews();
1003   }
1004 }
1005
1006 void
1007 ImageFileView::OnPlotCol (wxCommandEvent& event)
1008 {
1009   int xCursor, yCursor;
1010   if (! m_canvas->GetCurrentCursor (xCursor, yCursor)) {
1011     wxMessageBox ("No column selected. Please use left mouse button on image to select column","Error");
1012     return;
1013   }
1014   
1015   const ImageFile& rIF = dynamic_cast<ImageFileDocument*>(GetDocument())->getImageFile();
1016   ImageFileArrayConst v = rIF.getArray();
1017   int nx = rIF.nx();
1018   int ny = rIF.ny();
1019   
1020   if (v != NULL && xCursor < nx) {
1021     double* pX = new double [ny];
1022     double* pY = new double [ny];
1023     for (int i = 0; i < ny; i++) {
1024       pX[i] = i;
1025       pY[i] = v[xCursor][i];
1026     }
1027     PlotFileDocument* pPlotDoc = dynamic_cast<PlotFileDocument*>(theApp->getDocManager()->CreateDocument("untitled.plt", wxDOC_SILENT));
1028     if (! pPlotDoc) {
1029       sys_error (ERR_SEVERE, "Internal error: unable to create Plot file");
1030     } else {
1031       PlotFile& rPlotFile = pPlotDoc->getPlotFile();
1032       std::ostringstream os;
1033       os << "Column " << xCursor;
1034       std::string title("title ");
1035       title += os.str();
1036       rPlotFile.addEzsetCommand (title.c_str());
1037       rPlotFile.addEzsetCommand ("xlabel Row");
1038       rPlotFile.addEzsetCommand ("ylabel Pixel Value");
1039       rPlotFile.addEzsetCommand ("lxfrac 0");
1040       rPlotFile.addEzsetCommand ("box");
1041       rPlotFile.addEzsetCommand ("grid");
1042       rPlotFile.setCurveSize (2, nx);
1043       rPlotFile.addColumn (0, pX);
1044       rPlotFile.addColumn (1, pY);
1045     }
1046     delete pX;
1047     delete pY;
1048     if (theApp->getSetModifyNewDocs())
1049       pPlotDoc->Modify(true);
1050     pPlotDoc->UpdateAllViews();
1051   }
1052 }
1053
1054 void
1055 ImageFileView::OnCompareCol (wxCommandEvent& event)
1056 {
1057   int xCursor, yCursor;
1058   if (! m_canvas->GetCurrentCursor (xCursor, yCursor)) {
1059     wxMessageBox ("No column selected. Please use left mouse button on image to select column","Error");
1060     return;
1061   }
1062   
1063   std::vector<ImageFileDocument*> vecIFDoc;
1064   theApp->getCompatibleImages (GetDocument(), vecIFDoc);
1065   if (vecIFDoc.size() == 0) {
1066     wxMessageBox ("No compatible images for Column Comparison", "Error");
1067     return;
1068   }
1069   DialogGetComparisonImage dialogGetCompare (m_frame, "Get Comparison Image", vecIFDoc, false);
1070   
1071   if (dialogGetCompare.ShowModal() == wxID_OK) {
1072     ImageFileDocument* pCompareDoc = dialogGetCompare.getImageFileDocument();
1073     const ImageFile& rIF = GetDocument()->getImageFile();
1074     const ImageFile& rCompareIF = pCompareDoc->getImageFile();
1075     
1076     ImageFileArrayConst v1 = rIF.getArray();
1077     ImageFileArrayConst v2 = rCompareIF.getArray();
1078     int nx = rIF.nx();
1079     int ny = rIF.ny();
1080     
1081     if (v1 != NULL && xCursor < nx) {
1082       double* pX = new double [ny];
1083       double* pY1 = new double [ny];
1084       double* pY2 = new double [ny];
1085       for (int i = 0; i < ny; i++) {
1086         pX[i] = i;
1087         pY1[i] = v1[xCursor][i];
1088         pY2[i] = v2[xCursor][i];
1089       }
1090       PlotFileDocument* pPlotDoc = dynamic_cast<PlotFileDocument*>(theApp->getDocManager()->CreateDocument("untitled.plt", wxDOC_SILENT));
1091       if (! pPlotDoc) {
1092         sys_error (ERR_SEVERE, "Internal error: unable to create Plot file");
1093       } else {
1094         PlotFile& rPlotFile = pPlotDoc->getPlotFile();
1095         std::ostringstream os;
1096         os << "Column " << xCursor << " Comparison";
1097         std::string title("title ");
1098         title += os.str();
1099         rPlotFile.addEzsetCommand (title.c_str());
1100         rPlotFile.addEzsetCommand ("xlabel Row");
1101         rPlotFile.addEzsetCommand ("ylabel Pixel Value");
1102         rPlotFile.addEzsetCommand ("lxfrac 0");
1103         rPlotFile.addEzsetCommand ("curve 1");
1104         rPlotFile.addEzsetCommand ("color 2");
1105         rPlotFile.addEzsetCommand ("curve 2");
1106         rPlotFile.addEzsetCommand ("color 4");
1107         rPlotFile.addEzsetCommand ("dash 5");
1108         rPlotFile.addEzsetCommand ("box");
1109         rPlotFile.addEzsetCommand ("grid");
1110         rPlotFile.setCurveSize (3, nx);
1111         rPlotFile.addColumn (0, pX);
1112         rPlotFile.addColumn (1, pY1);
1113         rPlotFile.addColumn (2, pY2);
1114       }
1115       delete pX;
1116       delete pY1;
1117       delete pY2;
1118       if (theApp->getSetModifyNewDocs())
1119         pPlotDoc->Modify(true);
1120       pPlotDoc->UpdateAllViews();
1121     }
1122   }
1123 }
1124
1125 void
1126 ImageFileView::OnCompareRow (wxCommandEvent& event)
1127 {
1128   int xCursor, yCursor;
1129   if (! m_canvas->GetCurrentCursor (xCursor, yCursor)) {
1130     wxMessageBox ("No column selected. Please use left mouse button on image to select column","Error");
1131     return;
1132   }
1133   
1134   std::vector<ImageFileDocument*> vecIFDoc;
1135   theApp->getCompatibleImages (GetDocument(), vecIFDoc);
1136   
1137   if (vecIFDoc.size() == 0) {
1138     wxMessageBox ("No compatible images for Row Comparison", "Error");
1139     return;
1140   }
1141   
1142   DialogGetComparisonImage dialogGetCompare (m_frame, "Get Comparison Image", vecIFDoc, false);
1143   
1144   if (dialogGetCompare.ShowModal() == wxID_OK) {
1145     ImageFileDocument* pCompareDoc = dialogGetCompare.getImageFileDocument();
1146     const ImageFile& rIF = GetDocument()->getImageFile();
1147     const ImageFile& rCompareIF = pCompareDoc->getImageFile();
1148     
1149     ImageFileArrayConst v1 = rIF.getArray();
1150     ImageFileArrayConst v2 = rCompareIF.getArray();
1151     int nx = rIF.nx();
1152     int ny = rIF.ny();
1153     
1154     if (v1 != NULL && yCursor < ny) {
1155       double* pX = new double [nx];
1156       double* pY1 = new double [nx];
1157       double* pY2 = new double [nx];
1158       for (int i = 0; i < nx; i++) {
1159         pX[i] = i;
1160         pY1[i] = v1[i][yCursor];
1161         pY2[i] = v2[i][yCursor];
1162       }
1163       PlotFileDocument* pPlotDoc = dynamic_cast<PlotFileDocument*>(theApp->getDocManager()->CreateDocument("untitled.plt", wxDOC_SILENT));
1164       if (! pPlotDoc) {
1165         sys_error (ERR_SEVERE, "Internal error: unable to create Plot file");
1166       } else {
1167         PlotFile& rPlotFile = pPlotDoc->getPlotFile();
1168         std::ostringstream os;
1169         os << "Row " << yCursor << " Comparison";
1170         std::string title("title ");
1171         title += os.str();
1172         rPlotFile.addEzsetCommand (title.c_str());
1173         rPlotFile.addEzsetCommand ("xlabel Column");
1174         rPlotFile.addEzsetCommand ("ylabel Pixel Value");
1175         rPlotFile.addEzsetCommand ("lxfrac 0");
1176         rPlotFile.addEzsetCommand ("curve 1");
1177         rPlotFile.addEzsetCommand ("color 2");
1178         rPlotFile.addEzsetCommand ("curve 2");
1179         rPlotFile.addEzsetCommand ("color 4");
1180         rPlotFile.addEzsetCommand ("dash 5");
1181         rPlotFile.addEzsetCommand ("box");
1182         rPlotFile.addEzsetCommand ("grid");
1183         rPlotFile.setCurveSize (3, ny);
1184         rPlotFile.addColumn (0, pX);
1185         rPlotFile.addColumn (1, pY1);
1186         rPlotFile.addColumn (2, pY2);
1187       }
1188       delete pX;
1189       delete pY1;
1190       delete pY2;
1191       if (theApp->getSetModifyNewDocs())
1192         pPlotDoc->Modify(true);
1193       pPlotDoc->UpdateAllViews();
1194     }
1195   }
1196 }
1197
1198 static int NUMBER_HISTOGRAM_BINS = 256;
1199
1200 void
1201 ImageFileView::OnPlotHistogram (wxCommandEvent& event)
1202
1203   const ImageFile& rIF = dynamic_cast<ImageFileDocument*>(GetDocument())->getImageFile();
1204   ImageFileArrayConst v = rIF.getArray();
1205   int nx = rIF.nx();
1206   int ny = rIF.ny();
1207   
1208   if (v != NULL && nx > 0 && ny > 0) {
1209     PlotFileDocument* pPlotDoc = dynamic_cast<PlotFileDocument*>(theApp->getDocManager()->CreateDocument("untitled.plt", wxDOC_SILENT));
1210     if (! pPlotDoc) {
1211       sys_error (ERR_SEVERE, "Internal error: unable to create Plot file");
1212       return;
1213     }
1214
1215     double* pX = new double [NUMBER_HISTOGRAM_BINS];
1216     double* pY = new double [NUMBER_HISTOGRAM_BINS];
1217     double dMin, dMax;
1218     rIF.getMinMax (dMin, dMax);
1219     double dBinWidth = (dMax - dMin) / NUMBER_HISTOGRAM_BINS;
1220
1221     for (int i = 0; i < NUMBER_HISTOGRAM_BINS; i++) {
1222       pX[i] = dMin + (i + 0.5) * dBinWidth;
1223       pY[i] = 0;
1224     }
1225     for (int ix = 0; ix < nx; ix++)
1226       for (int iy = 0; iy < ny; iy++) {
1227         int iBin = nearest<int> ((v[ix][iy] - dMin) / dBinWidth);
1228         if (iBin >= 0 && iBin < NUMBER_HISTOGRAM_BINS)
1229           pY[iBin] += 1;
1230       }
1231
1232     PlotFile& rPlotFile = pPlotDoc->getPlotFile();
1233     std::ostringstream os;
1234     os << "Histogram";
1235     std::string title("title ");
1236     title += os.str();
1237     rPlotFile.addEzsetCommand (title.c_str());
1238     rPlotFile.addEzsetCommand ("xlabel Pixel Value");
1239     rPlotFile.addEzsetCommand ("ylabel Count");
1240     rPlotFile.addEzsetCommand ("box");
1241     rPlotFile.addEzsetCommand ("grid");
1242     rPlotFile.setCurveSize (2, nx);
1243     rPlotFile.addColumn (0, pX);
1244     rPlotFile.addColumn (1, pY);
1245     delete pX;
1246     delete pY;
1247     if (theApp->getSetModifyNewDocs())
1248       pPlotDoc->Modify(true);
1249     pPlotDoc->UpdateAllViews();
1250   }
1251 }
1252
1253
1254 // PhantomCanvas
1255
1256 PhantomCanvas::PhantomCanvas (PhantomView* v, wxFrame *frame, const wxPoint& pos, const wxSize& size, const long style)
1257 : wxScrolledWindow(frame, -1, pos, size, style)
1258 {
1259   m_pView = v;
1260 }
1261
1262 void 
1263 PhantomCanvas::OnDraw (wxDC& dc)
1264 {
1265   if (m_pView)
1266     m_pView->OnDraw(& dc);
1267 }
1268
1269
1270 // PhantomView
1271
1272 IMPLEMENT_DYNAMIC_CLASS(PhantomView, wxView)
1273
1274 BEGIN_EVENT_TABLE(PhantomView, wxView)
1275 EVT_MENU(PHMMENU_FILE_PROPERTIES, PhantomView::OnProperties)
1276 EVT_MENU(PHMMENU_PROCESS_RASTERIZE, PhantomView::OnRasterize)
1277 EVT_MENU(PHMMENU_PROCESS_PROJECTIONS, PhantomView::OnProjections)
1278 END_EVENT_TABLE()
1279
1280 PhantomView::PhantomView(void) 
1281 : wxView(), m_canvas(NULL), m_frame(NULL)
1282 {
1283   m_iDefaultNDet = 367;
1284   m_iDefaultNView = 320;
1285   m_iDefaultNSample = 2;
1286   m_dDefaultRotation = 2;
1287   m_dDefaultFocalLength = 2;
1288   m_dDefaultFieldOfView = 1;
1289   m_iDefaultGeometry = Scanner::GEOMETRY_PARALLEL;
1290   m_iDefaultTrace = Trace::TRACE_NONE;
1291 }
1292
1293 PhantomView::~PhantomView(void)
1294 {
1295 }
1296
1297 void
1298 PhantomView::OnProperties (wxCommandEvent& event)
1299 {
1300   const int idPhantom = GetDocument()->getPhantomID();
1301   const wxString& namePhantom = GetDocument()->getPhantomName();
1302   std::ostringstream os;
1303   os << "Phantom " << namePhantom.c_str() << " (" << idPhantom << ")" << "\n";
1304   const Phantom& rPhantom = GetDocument()->getPhantom();
1305   rPhantom.printDefinitions (os);
1306 #if DEBUG
1307   rPhantom.print (os);
1308 #endif
1309   *theApp->getLog() << os.str().c_str() << "\n";
1310   wxMessageBox (os.str().c_str(), "Phantom Properties");
1311 }
1312
1313
1314 void
1315 PhantomView::OnProjections (wxCommandEvent& event)
1316 {
1317   DialogGetProjectionParameters dialogProjection (m_frame, m_iDefaultNDet, m_iDefaultNView, m_iDefaultNSample, m_dDefaultRotation, m_dDefaultFocalLength, m_dDefaultFieldOfView, m_iDefaultGeometry, m_iDefaultTrace);
1318   int retVal = dialogProjection.ShowModal();
1319   if (retVal == wxID_OK) {
1320     m_iDefaultNDet = dialogProjection.getNDet();
1321     m_iDefaultNView = dialogProjection.getNView();
1322     m_iDefaultNSample = dialogProjection.getNSamples();
1323     m_iDefaultTrace = dialogProjection.getTrace();
1324     m_dDefaultRotation = dialogProjection.getRotAngle();
1325     m_dDefaultFocalLength = dialogProjection.getFocalLengthRatio();
1326     m_dDefaultFieldOfView = dialogProjection.getFieldOfViewRatio();
1327     wxString sGeometry = dialogProjection.getGeometry();
1328     m_iDefaultGeometry = Scanner::convertGeometryNameToID (sGeometry.c_str());
1329     
1330     if (m_iDefaultNDet > 0 && m_iDefaultNView > 0 && sGeometry != "") {
1331       const Phantom& rPhantom = GetDocument()->getPhantom();
1332       ProjectionFileDocument* pProjectionDoc = dynamic_cast<ProjectionFileDocument*>(theApp->getDocManager()->CreateDocument("untitled.pj", wxDOC_SILENT));
1333       if (! pProjectionDoc) {
1334         sys_error (ERR_SEVERE, "Unable to create projection document");
1335         return;
1336       }
1337       Projections& rProj = pProjectionDoc->getProjections();
1338       Scanner theScanner (rPhantom, sGeometry.c_str(), m_iDefaultNDet, m_iDefaultNView, m_iDefaultNSample, m_dDefaultRotation, m_dDefaultFocalLength, m_dDefaultFieldOfView);
1339       if (theScanner.fail()) {
1340         *theApp->getLog() << "Failed making scanner: " << theScanner.failMessage().c_str() << "\n";
1341         return;
1342       }
1343       rProj.initFromScanner (theScanner);
1344       m_dDefaultRotation /= PI;  // convert back to PI units
1345       
1346       Timer timer;
1347       if (m_iDefaultTrace > Trace::TRACE_CONSOLE) {
1348         ProjectionsDialog dialogProjections (theScanner, rProj, rPhantom, m_iDefaultTrace, dynamic_cast<wxWindow*>(m_frame));
1349         for (int iView = 0; iView < rProj.nView(); iView++) {
1350           ::wxYield();
1351           ::wxYield();
1352           if (dialogProjections.isCancelled() || ! dialogProjections.projectView (iView)) {
1353             pProjectionDoc->DeleteAllViews();
1354             return;
1355           }
1356           ::wxYield();
1357           ::wxYield();
1358           while (dialogProjections.isPaused()) {
1359             ::wxYield();
1360             ::wxUsleep(50);
1361           }
1362         }
1363       } else {
1364         wxProgressDialog dlgProgress (wxString("Projection"), wxString("Projection Progress"), rProj.nView() + 1, m_frame, wxPD_CAN_ABORT);
1365         for (int i = 0; i < rProj.nView(); i++) {
1366           theScanner.collectProjections (rProj, rPhantom, i, 1, true, m_iDefaultTrace);
1367           if (! dlgProgress.Update (i+1)) {
1368             pProjectionDoc->DeleteAllViews();
1369             return;
1370           }
1371         }
1372       }
1373       
1374       std::ostringstream os;
1375       os << "Projections for " << rPhantom.name() << ": nDet=" << m_iDefaultNDet << ", nView=" << m_iDefaultNView << ", nSamples=" << m_iDefaultNSample << ", RotAngle=" << m_dDefaultRotation << ", FocalLengthRatio=" << m_dDefaultFocalLength << ", FieldOfViewRatio=" << m_dDefaultFieldOfView << ", Geometry=" << sGeometry.c_str();
1376       rProj.setCalcTime (timer.timerEnd());
1377       rProj.setRemark (os.str());
1378       *theApp->getLog() << os.str().c_str() << "\n";
1379       
1380       m_frame->Lower();
1381       ::wxYield();
1382       ProjectionFileView* projView = dynamic_cast<ProjectionFileView*>(pProjectionDoc->GetFirstView());
1383       if (projView) {
1384         projView->getFrame()->SetFocus();
1385         projView->OnUpdate (projView, NULL);
1386       }
1387       if (wxView* pView = pProjectionDoc->GetFirstView()) {
1388         if (wxFrame* pFrame = pView->GetFrame()) {
1389           pFrame->SetFocus();
1390           pFrame->Raise();
1391         }
1392         theApp->getDocManager()->ActivateView (pView, true, false);
1393       }
1394       ::wxYield();
1395       if (theApp->getSetModifyNewDocs())
1396         pProjectionDoc->Modify(true);
1397       pProjectionDoc->UpdateAllViews(this);
1398     }
1399   }
1400 }
1401
1402
1403 void
1404 PhantomView::OnRasterize (wxCommandEvent& event)
1405 {
1406   DialogGetRasterParameters dialogRaster (m_frame, 256, 256, 1);
1407   int retVal = dialogRaster.ShowModal();
1408   if (retVal == wxID_OK) {
1409     int xSize = dialogRaster.getXSize();
1410     int ySize = dialogRaster.getYSize();
1411     int nSamples = dialogRaster.getNSamples();
1412     if (nSamples < 1)
1413       nSamples = 1;
1414     if (xSize > 0 && ySize > 0) {
1415       const Phantom& rPhantom = GetDocument()->getPhantom();
1416       ImageFileDocument* pRasterDoc = dynamic_cast<ImageFileDocument*>(theApp->getDocManager()->CreateDocument("untitled.if", wxDOC_SILENT));
1417       if (! pRasterDoc) {
1418         sys_error (ERR_SEVERE, "Unable to create image file");
1419         return;
1420       }
1421       ImageFile& imageFile = pRasterDoc->getImageFile();
1422       
1423       imageFile.setArraySize (xSize, ySize);
1424       wxProgressDialog dlgProgress (wxString("Rasterize"), wxString("Rasterization Progress"), imageFile.nx() + 1, m_frame, wxPD_CAN_ABORT);
1425       Timer timer;
1426       for (unsigned int i = 0; i < imageFile.nx(); i++) {
1427         rPhantom.convertToImagefile (imageFile, nSamples, Trace::TRACE_NONE, i, 1, true);
1428         if (! dlgProgress.Update(i+1)) {
1429           pRasterDoc->DeleteAllViews();
1430           return;
1431         }
1432       }
1433       if (theApp->getSetModifyNewDocs())
1434         pRasterDoc->Modify(true);
1435       pRasterDoc->UpdateAllViews(this);
1436       std::ostringstream os;
1437       os << "Rasterize Phantom " << rPhantom.name() << ": XSize=" << xSize << ", YSize=" << ySize << ", nSamples=" << nSamples;
1438       *theApp->getLog() << os.str().c_str() << "\n";
1439       imageFile.labelAdd (os.str().c_str(), timer.timerEnd());
1440       ImageFileView* rasterView = dynamic_cast<ImageFileView*>(pRasterDoc->GetFirstView());
1441       if (rasterView) {
1442         rasterView->getFrame()->SetFocus();
1443         rasterView->OnUpdate (rasterView, NULL);
1444       }
1445       
1446     }
1447   }
1448 }
1449
1450
1451 PhantomCanvas* 
1452 PhantomView::CreateCanvas (wxView *view, wxFrame *parent)
1453 {
1454   PhantomCanvas* pCanvas;
1455   int width, height;
1456   parent->GetClientSize(&width, &height);
1457   
1458   pCanvas = new PhantomCanvas (dynamic_cast<PhantomView*>(view), parent, wxPoint(0, 0), wxSize(width, height), 0);
1459   
1460   pCanvas->SetBackgroundColour(*wxWHITE);
1461   pCanvas->Clear();
1462   
1463   return pCanvas;
1464 }
1465
1466 wxFrame*
1467 PhantomView::CreateChildFrame(wxDocument *doc, wxView *view)
1468 {
1469   wxDocChildFrame *subframe = new wxDocChildFrame(doc, view, theApp->getMainFrame(), -1, "Phantom Frame", wxPoint(10, 10), wxSize(256, 256), wxDEFAULT_FRAME_STYLE);
1470   
1471   wxMenu *file_menu = new wxMenu;
1472   
1473   file_menu->Append(MAINMENU_FILE_CREATE_PHANTOM, "Cr&eate Phantom...");
1474   file_menu->Append(MAINMENU_FILE_CREATE_FILTER, "Create &Filter...");
1475   file_menu->Append(wxID_OPEN, "&Open...");
1476   file_menu->Append(wxID_SAVEAS, "Save &As...");
1477   file_menu->Append(wxID_CLOSE, "&Close");
1478   
1479   file_menu->AppendSeparator();
1480   file_menu->Append(PHMMENU_FILE_PROPERTIES, "P&roperties");
1481   
1482   file_menu->AppendSeparator();
1483   file_menu->Append(wxID_PRINT, "&Print...");
1484   file_menu->Append(wxID_PRINT_SETUP, "Print &Setup...");
1485   file_menu->Append(wxID_PREVIEW, "Print Pre&view");
1486   
1487   wxMenu *process_menu = new wxMenu;
1488   process_menu->Append(PHMMENU_PROCESS_RASTERIZE, "&Rasterize...");
1489   process_menu->Append(PHMMENU_PROCESS_PROJECTIONS, "&Projections...");
1490   
1491   wxMenu *help_menu = new wxMenu;
1492   help_menu->Append(MAINMENU_HELP_CONTENTS, "&Contents");
1493   help_menu->Append(MAINMENU_HELP_ABOUT, "&About");
1494   
1495   wxMenuBar *menu_bar = new wxMenuBar;
1496   
1497   menu_bar->Append(file_menu, "&File");
1498   menu_bar->Append(process_menu, "&Process");
1499   menu_bar->Append(help_menu, "&Help");
1500   
1501   subframe->SetMenuBar(menu_bar);
1502   
1503   subframe->Centre(wxBOTH);
1504   
1505   return subframe;
1506 }
1507
1508
1509 bool 
1510 PhantomView::OnCreate(wxDocument *doc, long WXUNUSED(flags) )
1511 {
1512   m_frame = CreateChildFrame(doc, this);
1513   SetFrame(m_frame);
1514   
1515   int width, height;
1516   m_frame->GetClientSize(&width, &height);
1517   m_frame->SetTitle("PhantomView");
1518   m_canvas = CreateCanvas(this, m_frame);
1519   
1520 #ifdef __X__
1521   int x, y;  // X requires a forced resize
1522   m_frame->GetSize(&x, &y);
1523   m_frame->SetSize(-1, -1, x, y);
1524 #endif
1525   
1526   m_frame->Show(true);
1527   Activate(true);
1528   
1529   return true;
1530 }
1531
1532
1533 void 
1534 PhantomView::OnUpdate(wxView *WXUNUSED(sender), wxObject *WXUNUSED(hint) )
1535 {
1536   if (m_canvas)
1537     m_canvas->Refresh();
1538 }
1539
1540 bool 
1541 PhantomView::OnClose (bool deleteWindow)
1542 {
1543   if (!GetDocument()->Close())
1544     return false;
1545   
1546   m_canvas->Clear();
1547   m_canvas->m_pView = NULL;
1548   m_canvas = NULL;
1549   wxString s(wxTheApp->GetAppName());
1550   if (m_frame)
1551     m_frame->SetTitle(s);
1552   SetFrame(NULL);
1553   
1554   Activate(false);
1555   
1556   if (deleteWindow) {
1557     delete m_frame;
1558     return true;
1559   }
1560   return true;
1561 }
1562
1563 void
1564 PhantomView::OnDraw (wxDC* dc)
1565 {
1566   int xsize, ysize;
1567   m_canvas->GetClientSize (&xsize, &ysize);
1568   SGPDriver driver (dc, xsize, ysize);
1569   SGP sgp (driver);
1570   const Phantom& rPhantom = GetDocument()->getPhantom();
1571   sgp.setColor (C_RED);
1572   rPhantom.show (sgp);
1573 }
1574
1575 // ProjectionCanvas
1576
1577 ProjectionFileCanvas::ProjectionFileCanvas (ProjectionFileView* v, wxFrame *frame, const wxPoint& pos, const wxSize& size, const long style)
1578 : wxScrolledWindow(frame, -1, pos, size, style)
1579 {
1580   m_pView = v;
1581 }
1582
1583 void 
1584 ProjectionFileCanvas::OnDraw(wxDC& dc)
1585 {
1586   if (m_pView)
1587     m_pView->OnDraw(& dc);
1588 }
1589
1590 // ProjectionFileView
1591
1592 IMPLEMENT_DYNAMIC_CLASS(ProjectionFileView, wxView)
1593
1594 BEGIN_EVENT_TABLE(ProjectionFileView, wxView)
1595 EVT_MENU(PJMENU_FILE_PROPERTIES, ProjectionFileView::OnProperties)
1596 EVT_MENU(PJMENU_RECONSTRUCT_FBP, ProjectionFileView::OnReconstructFBP)
1597 EVT_MENU(PJMENU_CONVERT_POLAR, ProjectionFileView::OnConvertPolar)
1598 EVT_MENU(PJMENU_CONVERT_FFT_POLAR, ProjectionFileView::OnConvertFFTPolar)
1599 END_EVENT_TABLE()
1600
1601 ProjectionFileView::ProjectionFileView(void) 
1602 : wxView(), m_canvas(NULL), m_frame(NULL)
1603 {
1604   m_iDefaultNX = 256;
1605   m_iDefaultNY = 256;
1606   m_iDefaultFilter = SignalFilter::FILTER_ABS_BANDLIMIT;
1607   m_dDefaultFilterParam = 1.;
1608 #if HAVE_FFTW
1609   m_iDefaultFilterMethod = ProcessSignal::FILTER_METHOD_RFFTW;
1610   m_iDefaultFilterGeneration = ProcessSignal::FILTER_GENERATION_INVERSE_FOURIER;
1611 #else
1612   m_iDefaultFilterMethod = ProcessSignal::FILTER_METHOD_CONVOLUTION;
1613   m_iDefaultFilterGeneration = ProcessSignal::FILTER_GENERATION_DIRECT;
1614 #endif
1615   m_iDefaultZeropad = 1;
1616   m_iDefaultBackprojector = Backprojector::BPROJ_IDIFF3;
1617   m_iDefaultInterpolation = Backprojector::INTERP_LINEAR;
1618   m_iDefaultInterpParam = 1;
1619   m_iDefaultTrace = Trace::TRACE_NONE;
1620 }
1621
1622 ProjectionFileView::~ProjectionFileView(void)
1623 {
1624 }
1625
1626 void
1627 ProjectionFileView::OnProperties (wxCommandEvent& event)
1628 {
1629   const Projections& rProj = GetDocument()->getProjections();
1630   std::ostringstream os;
1631   rProj.printScanInfo(os);
1632   *theApp->getLog() << os.str().c_str();
1633   wxMessageDialog dialogMsg (m_frame, os.str().c_str(), "Projection File Properties", wxOK | wxICON_INFORMATION);
1634   dialogMsg.ShowModal();
1635 }
1636
1637
1638 void
1639 ProjectionFileView::OnConvertPolar (wxCommandEvent& event)
1640 {
1641   Projections& rProj = GetDocument()->getProjections();
1642 }
1643
1644 void
1645 ProjectionFileView::OnConvertFFTPolar (wxCommandEvent& event)
1646 {
1647   Projections& rProj = GetDocument()->getProjections();
1648   wxMessageBox ("Polar conversion not yet implemented", "Unimplemented function");
1649 #if 0
1650   rProj.convertPolar ();
1651   if (theApp->getSetModifyNewDocs())
1652     GetDocument()->Modify(true);
1653   GetDocument()->UpdateAllViews();
1654 #ifndef HAVE_FFT
1655   wxMessageBox ("FFT support has not been compiled into this version of CTSim", "Error");
1656 #endif
1657 #endif
1658 }
1659
1660 void
1661 ProjectionFileView::OnReconstructFourier (wxCommandEvent& event)
1662 {
1663   wxMessageBox ("Fourier Reconstruction is not yet supported", "Unimplemented function");
1664 }
1665
1666 void
1667 ProjectionFileView::OnReconstructFBP (wxCommandEvent& event)
1668 {
1669   DialogGetReconstructionParameters dialogReconstruction (m_frame, m_iDefaultNX, m_iDefaultNY, m_iDefaultFilter, m_dDefaultFilterParam, m_iDefaultFilterMethod, m_iDefaultFilterGeneration, m_iDefaultZeropad, m_iDefaultInterpolation, m_iDefaultInterpParam, m_iDefaultBackprojector, m_iDefaultTrace);
1670   
1671   int retVal = dialogReconstruction.ShowModal();
1672   if (retVal == wxID_OK) {
1673     m_iDefaultNX = dialogReconstruction.getXSize();
1674     m_iDefaultNY = dialogReconstruction.getYSize();
1675     wxString optFilterName = dialogReconstruction.getFilterName();
1676     m_iDefaultFilter = SignalFilter::convertFilterNameToID (optFilterName.c_str());
1677     m_dDefaultFilterParam = dialogReconstruction.getFilterParam();
1678     wxString optFilterMethodName = dialogReconstruction.getFilterMethodName();
1679     m_iDefaultFilterMethod = ProcessSignal::convertFilterMethodNameToID(optFilterMethodName.c_str());
1680     m_iDefaultZeropad = dialogReconstruction.getZeropad();
1681     wxString optFilterGenerationName = dialogReconstruction.getFilterGenerationName();
1682     m_iDefaultFilterGeneration = ProcessSignal::convertFilterGenerationNameToID (optFilterGenerationName.c_str());
1683     wxString optInterpName = dialogReconstruction.getInterpName();
1684     m_iDefaultInterpolation = Backprojector::convertInterpNameToID (optInterpName.c_str());
1685     m_iDefaultInterpParam = dialogReconstruction.getInterpParam();
1686     wxString optBackprojectName = dialogReconstruction.getBackprojectName();
1687     m_iDefaultBackprojector = Backprojector::convertBackprojectNameToID (optBackprojectName.c_str());
1688     m_iDefaultTrace = dialogReconstruction.getTrace();
1689     if (m_iDefaultNX > 0 && m_iDefaultNY > 0) {
1690       ImageFileDocument* pReconDoc = dynamic_cast<ImageFileDocument*>(theApp->getDocManager()->CreateDocument("untitled.if", wxDOC_SILENT));
1691       if (! pReconDoc) {
1692         sys_error (ERR_SEVERE, "Unable to create image file");
1693         return;
1694       }
1695       ImageFile& imageFile = pReconDoc->getImageFile();
1696       const Projections& rProj = GetDocument()->getProjections();
1697       imageFile.setArraySize (m_iDefaultNX, m_iDefaultNY);
1698       
1699       if (m_iDefaultFilterMethod != ProcessSignal::FILTER_METHOD_CONVOLUTION && m_iDefaultFilterGeneration == ProcessSignal::FILTER_GENERATION_DIRECT && rProj.geometry() != Scanner::GEOMETRY_PARALLEL) {
1700         wxMessageBox ("Sorry!\nCurrently, frequency-based filtering with direct filter generation is not support for geometries other than parallel.\nAborting command.", "Not Supported", wxOK | wxICON_WARNING, m_frame);
1701         return;
1702       }
1703
1704       Reconstructor* pReconstruct = new Reconstructor (rProj, imageFile, optFilterName.c_str(), m_dDefaultFilterParam, optFilterMethodName.c_str(), m_iDefaultZeropad, optFilterGenerationName.c_str(), optInterpName.c_str(), m_iDefaultInterpParam, optBackprojectName.c_str(), m_iDefaultTrace);
1705
1706       Timer timerRecon;
1707       if (m_iDefaultTrace > Trace::TRACE_CONSOLE) {
1708         ReconstructDialog* pDlgReconstruct = new ReconstructDialog (*pReconstruct, rProj, imageFile, m_iDefaultTrace, m_frame);
1709         for (int iView = 0; iView < rProj.nView(); iView++) {
1710           ::wxYield();
1711           ::wxYield();
1712           if (pDlgReconstruct->isCancelled() || ! pDlgReconstruct->reconstructView (iView)) {
1713             delete pDlgReconstruct;
1714             delete pReconstruct;
1715             pReconDoc->DeleteAllViews();
1716             return;
1717           }
1718           ::wxYield();
1719           ::wxYield();
1720           while (pDlgReconstruct->isPaused()) {
1721             ::wxYield();
1722             ::wxUsleep(50);
1723           }
1724         }
1725         delete pDlgReconstruct;
1726       } else {
1727         wxProgressDialog dlgProgress (wxString("Reconstruction"), wxString("Reconstruction Progress"), rProj.nView() + 1, m_frame, wxPD_CAN_ABORT);
1728         for (int i = 0; i < rProj.nView(); i++) {
1729           pReconstruct->reconstructView (i, 1);
1730           if (! dlgProgress.Update(i + 1)) {
1731             delete pReconstruct;
1732             pReconDoc->DeleteAllViews();
1733             return;
1734           }
1735         }
1736       }
1737       delete pReconstruct;
1738       if (theApp->getSetModifyNewDocs())
1739         pReconDoc->Modify(true);
1740       pReconDoc->UpdateAllViews(this);
1741       ImageFileView* rasterView = dynamic_cast<ImageFileView*>(pReconDoc->GetFirstView());
1742       if (rasterView) {
1743         rasterView->getFrame()->SetFocus();
1744         rasterView->OnUpdate (rasterView, NULL);
1745       }
1746       std::ostringstream os;
1747       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();
1748       *theApp->getLog() << os.str().c_str() << "\n";
1749       imageFile.labelAdd (rProj.getLabel());
1750       imageFile.labelAdd (os.str().c_str(), timerRecon.timerEnd());
1751     }
1752   }
1753 }
1754
1755
1756 ProjectionFileCanvas* 
1757 ProjectionFileView::CreateCanvas (wxView *view, wxFrame *parent)
1758 {
1759   ProjectionFileCanvas* pCanvas;
1760   int width, height;
1761   parent->GetClientSize(&width, &height);
1762   
1763   pCanvas = new ProjectionFileCanvas (dynamic_cast<ProjectionFileView*>(view), parent, wxPoint(0, 0), wxSize(width, height), 0);
1764   
1765   pCanvas->SetScrollbars(20, 20, 50, 50);
1766   pCanvas->SetBackgroundColour(*wxWHITE);
1767   pCanvas->Clear();
1768   
1769   return pCanvas;
1770 }
1771
1772 wxFrame*
1773 ProjectionFileView::CreateChildFrame(wxDocument *doc, wxView *view)
1774 {
1775   wxDocChildFrame *subframe = new wxDocChildFrame(doc, view, theApp->getMainFrame(), -1, "Projection Frame", wxPoint(10, 10), wxSize(0, 0), wxDEFAULT_FRAME_STYLE);
1776   
1777   wxMenu *file_menu = new wxMenu;
1778   
1779   file_menu->Append(MAINMENU_FILE_CREATE_PHANTOM, "Cr&eate Phantom...");
1780   file_menu->Append(MAINMENU_FILE_CREATE_FILTER, "Create &Filter...");
1781   file_menu->Append(wxID_OPEN, "&Open...");
1782   file_menu->Append(wxID_SAVE, "&Save");
1783   file_menu->Append(wxID_SAVEAS, "Save &As...");
1784   file_menu->Append(wxID_CLOSE, "&Close");
1785   
1786   file_menu->AppendSeparator();
1787   file_menu->Append(PJMENU_FILE_PROPERTIES, "P&roperties");
1788   
1789   file_menu->AppendSeparator();
1790   file_menu->Append(wxID_PRINT, "&Print...");
1791   file_menu->Append(wxID_PRINT_SETUP, "Print &Setup...");
1792   file_menu->Append(wxID_PREVIEW, "Print Pre&view");
1793   
1794   wxMenu *convert_menu = new wxMenu;
1795   convert_menu->Append (PJMENU_CONVERT_POLAR, "&Polar Image...");
1796   convert_menu->Append (PJMENU_CONVERT_FFT_POLAR, "&FFT->Polar Image...");
1797   
1798   wxMenu *reconstruct_menu = new wxMenu;
1799   reconstruct_menu->Append (PJMENU_RECONSTRUCT_FBP, "&Filtered Backprojection...");
1800   reconstruct_menu->Append (PJMENU_RECONSTRUCT_FOURIER, "&Fourier...");
1801
1802   wxMenu *help_menu = new wxMenu;
1803   help_menu->Append(MAINMENU_HELP_CONTENTS, "&Contents");
1804   help_menu->AppendSeparator();
1805   help_menu->Append(MAINMENU_HELP_ABOUT, "&About");
1806   
1807   wxMenuBar *menu_bar = new wxMenuBar;
1808   
1809   menu_bar->Append (file_menu, "&File");
1810   menu_bar->Append (convert_menu, "&Convert");
1811   menu_bar->Append (reconstruct_menu, "&Reconstruct");
1812   menu_bar->Append (help_menu, "&Help");
1813   
1814   subframe->SetMenuBar(menu_bar);
1815   
1816   subframe->Centre(wxBOTH);
1817   
1818   return subframe;
1819 }
1820
1821
1822 bool 
1823 ProjectionFileView::OnCreate(wxDocument *doc, long WXUNUSED(flags) )
1824 {
1825   m_frame = CreateChildFrame(doc, this);
1826   SetFrame(m_frame);
1827   
1828   int width, height;
1829   m_frame->GetClientSize(&width, &height);
1830   m_frame->SetTitle("ProjectionFileView");
1831   m_canvas = CreateCanvas(this, m_frame);
1832   
1833 #ifdef __X__
1834   int x, y;  // X requires a forced resize
1835   m_frame->GetSize(&x, &y);
1836   m_frame->SetSize(-1, -1, x, y);
1837 #endif
1838   
1839   m_frame->Show(true);
1840   Activate(true);
1841   
1842   return true;
1843 }
1844
1845 void 
1846 ProjectionFileView::OnDraw (wxDC* dc)
1847 {
1848   if (m_bitmap.Ok())
1849     dc->DrawBitmap (m_bitmap, 0, 0, false);
1850 }
1851
1852
1853 void 
1854 ProjectionFileView::OnUpdate(wxView *WXUNUSED(sender), wxObject *WXUNUSED(hint) )
1855 {
1856   const Projections& rProj = GetDocument()->getProjections();
1857   const int nDet = rProj.nDet();
1858   const int nView = rProj.nView();
1859   if (nDet != 0 && nView != 0) {
1860     const DetectorArray& detarray = rProj.getDetectorArray(0);
1861     const DetectorValue* detval = detarray.detValues();
1862     double min = detval[0];
1863     double max = detval[0];
1864     for (int iy = 0; iy < nView; iy++) {
1865       const DetectorArray& detarray = rProj.getDetectorArray(iy);
1866       const DetectorValue* detval = detarray.detValues();
1867       for (int ix = 0; ix < nDet; ix++) {
1868         if (min > detval[ix])
1869           min = detval[ix];
1870         else if (max < detval[ix])
1871           max = detval[ix];
1872       }
1873     }
1874     
1875     unsigned char* imageData = new unsigned char [nDet * nView * 3];
1876     double scale = (max - min) / 255;
1877     for (int iy2 = 0; iy2 < nView; iy2++) {
1878       const DetectorArray& detarray = rProj.getDetectorArray (iy2);
1879       const DetectorValue* detval = detarray.detValues();
1880       for (int ix = 0; ix < nDet; ix++) {
1881         int intensity = static_cast<int>(((detval[ix] - min) / scale) + 0.5);
1882         intensity = clamp(intensity, 0, 255);
1883         int baseAddr = (iy2 * nDet + ix) * 3;
1884         imageData[baseAddr] = imageData[baseAddr+1] = imageData[baseAddr+2] = intensity;
1885       }
1886     }
1887     wxImage image (nDet, nView, imageData, true);
1888     m_bitmap = image.ConvertToBitmap();
1889     delete imageData;
1890     int xSize = nDet;
1891     int ySize = nView;
1892     xSize = clamp (xSize, 0, 800);
1893     ySize = clamp (ySize, 0, 800);
1894     m_frame->SetClientSize (xSize, ySize);
1895     m_canvas->SetScrollbars (20, 20, nDet/20, nView/20);
1896   }
1897   
1898   if (m_canvas)
1899     m_canvas->Refresh();
1900 }
1901
1902 bool 
1903 ProjectionFileView::OnClose (bool deleteWindow)
1904 {
1905   if (!GetDocument()->Close())
1906     return false;
1907   
1908   m_canvas->Clear();
1909   m_canvas->m_pView = NULL;
1910   m_canvas = NULL;
1911   wxString s(wxTheApp->GetAppName());
1912   if (m_frame)
1913     m_frame->SetTitle(s);
1914   SetFrame(NULL);
1915   
1916   Activate(false);
1917   
1918   if (deleteWindow) {
1919     delete m_frame;
1920     return true;
1921   }
1922   return true;
1923 }
1924
1925
1926
1927 // PlotFileCanvas
1928 PlotFileCanvas::PlotFileCanvas (PlotFileView* v, wxFrame *frame, const wxPoint& pos, const wxSize& size, const long style)
1929 : wxScrolledWindow(frame, -1, pos, size, style)
1930 {
1931   m_pView = v;
1932 }
1933
1934 void 
1935 PlotFileCanvas::OnDraw(wxDC& dc)
1936 {
1937   if (m_pView)
1938     m_pView->OnDraw(& dc);
1939 }
1940
1941
1942 // PlotFileView
1943
1944 IMPLEMENT_DYNAMIC_CLASS(PlotFileView, wxView)
1945
1946 BEGIN_EVENT_TABLE(PlotFileView, wxView)
1947 EVT_MENU(PJMENU_FILE_PROPERTIES, PlotFileView::OnProperties)
1948 EVT_MENU(PLOTMENU_VIEW_SCALE_MINMAX, PlotFileView::OnScaleMinMax)
1949 EVT_MENU(PLOTMENU_VIEW_SCALE_AUTO, PlotFileView::OnScaleAuto)
1950 END_EVENT_TABLE()
1951
1952 PlotFileView::PlotFileView(void) 
1953 : wxView(), m_canvas(NULL), m_frame(NULL), m_pEZPlot(NULL)
1954 {
1955   m_bMinSpecified = false;
1956   m_bMaxSpecified = false;
1957 }
1958
1959 PlotFileView::~PlotFileView(void)
1960 {
1961   if (m_pEZPlot)
1962     delete m_pEZPlot;
1963 }
1964
1965 void
1966 PlotFileView::OnProperties (wxCommandEvent& event)
1967 {
1968   const PlotFile& rPlot = GetDocument()->getPlotFile();
1969   std::ostringstream os;
1970   os << "Columns: " << rPlot.getNumColumns() << ", Records: " << rPlot.getNumRecords() << "\n";
1971   rPlot.printHeaders (os);
1972   *theApp->getLog() << os.str().c_str();
1973   wxMessageDialog dialogMsg (m_frame, os.str().c_str(), "Plot File Properties", wxOK | wxICON_INFORMATION);
1974   dialogMsg.ShowModal();
1975 }
1976
1977
1978 void 
1979 PlotFileView::OnScaleAuto (wxCommandEvent& event)
1980 {
1981   const PlotFile& rPlotFile = GetDocument()->getPlotFile();
1982   double min, max, mean, mode, median, stddev;
1983   rPlotFile.statistics (1, min, max, mean, mode, median, stddev);
1984   DialogAutoScaleParameters dialogAutoScale (m_frame, mean, mode, median, stddev, m_dAutoScaleFactor);
1985   int iRetVal = dialogAutoScale.ShowModal();
1986   if (iRetVal == wxID_OK) {
1987     m_bMinSpecified = true;
1988     m_bMaxSpecified = true;
1989     double dMin, dMax;
1990     if (dialogAutoScale.getMinMax (&dMin, &dMax)) {
1991       m_dMinPixel = dMin;
1992       m_dMaxPixel = dMax;
1993       m_dAutoScaleFactor = dialogAutoScale.getAutoScaleFactor();
1994       OnUpdate (this, NULL);
1995     }
1996   }
1997 }
1998
1999 void 
2000 PlotFileView::OnScaleMinMax (wxCommandEvent& event)
2001 {
2002   const PlotFile& rPlotFile = GetDocument()->getPlotFile();
2003   double min;
2004   double max;
2005
2006   if (! m_bMinSpecified || ! m_bMaxSpecified) {
2007     if (! rPlotFile.getMinMax (1, min, max)) {
2008       *theApp->getLog() << "Error: unable to find Min/Max\n";
2009       return;
2010     }
2011   }
2012   
2013   if (m_bMinSpecified)
2014     min = m_dMinPixel;
2015   if (m_bMaxSpecified)
2016     max = m_dMaxPixel;
2017   
2018   DialogGetMinMax dialogMinMax (m_frame, "Set Y-axis Minimum & Maximum", min, max);
2019   int retVal = dialogMinMax.ShowModal();
2020   if (retVal == wxID_OK) {
2021     m_bMinSpecified = true;
2022     m_bMaxSpecified = true;
2023     m_dMinPixel = dialogMinMax.getMinimum();
2024     m_dMaxPixel = dialogMinMax.getMaximum();
2025     OnUpdate (this, NULL);
2026   }
2027 }
2028
2029
2030 PlotFileCanvas* 
2031 PlotFileView::CreateCanvas (wxView *view, wxFrame *parent)
2032 {
2033   PlotFileCanvas* pCanvas;
2034   int width, height;
2035   parent->GetClientSize(&width, &height);
2036   
2037   pCanvas = new PlotFileCanvas (dynamic_cast<PlotFileView*>(view), parent, wxPoint(0, 0), wxSize(width, height), 0);
2038   
2039   pCanvas->SetBackgroundColour(*wxWHITE);
2040   pCanvas->Clear();
2041   
2042   return pCanvas;
2043 }
2044
2045 wxFrame*
2046 PlotFileView::CreateChildFrame(wxDocument *doc, wxView *view)
2047 {
2048   wxDocChildFrame *subframe = new wxDocChildFrame(doc, view, theApp->getMainFrame(), -1, "Plot Frame", wxPoint(10, 10), wxSize(500, 300), wxDEFAULT_FRAME_STYLE);
2049   
2050   wxMenu *file_menu = new wxMenu;
2051   
2052   file_menu->Append(MAINMENU_FILE_CREATE_PHANTOM, "Cr&eate Phantom...");
2053   file_menu->Append(MAINMENU_FILE_CREATE_FILTER, "Create &Filter...");
2054   file_menu->Append(wxID_OPEN, "&Open...");
2055   file_menu->Append(wxID_SAVE, "&Save");
2056   file_menu->Append(wxID_SAVEAS, "Save &As...");
2057   file_menu->Append(wxID_CLOSE, "&Close");
2058   
2059   file_menu->AppendSeparator();
2060   file_menu->Append(PJMENU_FILE_PROPERTIES, "P&roperties");
2061   
2062   file_menu->AppendSeparator();
2063   file_menu->Append(wxID_PRINT, "&Print...");
2064   file_menu->Append(wxID_PRINT_SETUP, "Print &Setup...");
2065   file_menu->Append(wxID_PREVIEW, "Print Pre&view");
2066   
2067   wxMenu *view_menu = new wxMenu;
2068   view_menu->Append(PLOTMENU_VIEW_SCALE_MINMAX, "Display Scale &Set...");
2069   view_menu->Append(PLOTMENU_VIEW_SCALE_AUTO, "Display Scale &Auto...");
2070   
2071   wxMenu *help_menu = new wxMenu;
2072   help_menu->Append(MAINMENU_HELP_CONTENTS, "&Contents");
2073   help_menu->AppendSeparator();
2074   help_menu->Append(MAINMENU_HELP_ABOUT, "&About");
2075   
2076   wxMenuBar *menu_bar = new wxMenuBar;
2077   
2078   menu_bar->Append(file_menu, "&File");
2079   menu_bar->Append(view_menu, "&View");
2080   menu_bar->Append(help_menu, "&Help");
2081   
2082   subframe->SetMenuBar(menu_bar);
2083   
2084   subframe->Centre(wxBOTH);
2085   
2086   return subframe;
2087 }
2088
2089
2090 bool 
2091 PlotFileView::OnCreate (wxDocument *doc, long WXUNUSED(flags) )
2092 {
2093   m_frame = CreateChildFrame(doc, this);
2094   SetFrame(m_frame);
2095   
2096   m_bMinSpecified = false;
2097   m_bMaxSpecified = false;
2098   m_dAutoScaleFactor = 1.;
2099   
2100   int width, height;
2101   m_frame->GetClientSize(&width, &height);
2102   m_frame->SetTitle ("Plot File");
2103   m_canvas = CreateCanvas (this, m_frame);
2104   
2105 #ifdef __X__
2106   int x, y;  // X requires a forced resize
2107   m_frame->GetSize(&x, &y);
2108   m_frame->SetSize(-1, -1, x, y);
2109 #endif
2110   
2111   m_frame->Show(true);
2112   Activate(true);
2113    
2114   return true;
2115 }
2116
2117 void 
2118 PlotFileView::OnDraw (wxDC* dc)
2119 {
2120   const PlotFile& rPlotFile = GetDocument()->getPlotFile();
2121   const int iNColumns = rPlotFile.getNumColumns();
2122   const int iNRecords = rPlotFile.getNumRecords();
2123   
2124   if (iNColumns > 0 && iNRecords > 0) {
2125     int xsize, ysize;
2126     m_canvas->GetClientSize (&xsize, &ysize);
2127     SGPDriver driver (dc, xsize, ysize);
2128     SGP sgp (driver);
2129     if (m_pEZPlot)
2130       m_pEZPlot->plot (&sgp);
2131   }
2132 }
2133
2134
2135 void 
2136 PlotFileView::OnUpdate (wxView *WXUNUSED(sender), wxObject *WXUNUSED(hint) )
2137 {
2138     const PlotFile& rPlotFile = GetDocument()->getPlotFile();
2139     const int iNColumns = rPlotFile.getNumColumns();
2140     const int iNRecords = rPlotFile.getNumRecords();
2141     
2142     if (iNColumns > 0 && iNRecords > 0) {
2143       if (m_pEZPlot)
2144         delete m_pEZPlot;
2145       m_pEZPlot = new EZPlot;
2146       
2147       for (unsigned int iEzset = 0; iEzset < rPlotFile.getNumEzsetCommands(); iEzset++)
2148         m_pEZPlot->ezset (rPlotFile.getEzsetCommand (iEzset));
2149       
2150       if (m_bMinSpecified) {
2151         std::ostringstream os;
2152         os << "ymin " << m_dMinPixel;
2153         m_pEZPlot->ezset (os.str());
2154       }
2155       
2156       if (m_bMaxSpecified) {
2157         std::ostringstream os;
2158         os << "ymax " << m_dMaxPixel;
2159         m_pEZPlot->ezset (os.str());
2160       }
2161       
2162       m_pEZPlot->ezset("box");
2163       m_pEZPlot->ezset("grid");
2164       
2165       double* pdXaxis = new double [iNRecords];
2166       rPlotFile.getColumn (0, pdXaxis);
2167       
2168       double* pdY = new double [iNRecords];
2169       for (int iCol = 1; iCol < iNColumns; iCol++) {
2170         rPlotFile.getColumn (iCol, pdY);
2171         m_pEZPlot->addCurve (pdXaxis, pdY, iNRecords);
2172       }
2173       
2174       delete pdXaxis;
2175       delete pdY;
2176     }
2177
2178     if (m_canvas)
2179       m_canvas->Refresh();
2180 }
2181
2182 bool 
2183 PlotFileView::OnClose (bool deleteWindow)
2184 {
2185   if (!GetDocument()->Close())
2186     return false;
2187   
2188   m_canvas->Clear();
2189   m_canvas->m_pView = NULL;
2190   m_canvas = NULL;
2191   wxString s(wxTheApp->GetAppName());
2192   if (m_frame)
2193     m_frame->SetTitle(s);
2194   SetFrame(NULL);
2195   
2196   Activate(false);
2197   
2198   if (deleteWindow) {
2199     delete m_frame;
2200     return true;
2201   }
2202   return true;
2203 }
2204