3df51fa5e3c94227185b8acb602024602de88d15
[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.18 2000/08/31 08:38:58 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 #ifdef __GNUG__
29 // #pragma implementation
30 #endif
31
32 // For compilers that support precompilation, includes "wx/wx.h".
33 #include "wx/wxprec.h"
34
35 #ifdef __BORLANDC__
36 #pragma hdrstop
37 #endif
38
39 #ifndef WX_PRECOMP
40 #include "wx/wx.h"
41 #endif
42
43 #if !wxUSE_DOC_VIEW_ARCHITECTURE
44 #error You must set wxUSE_DOC_VIEW_ARCHITECTURE to 1 in setup.h!
45 #endif
46
47 #include "wx/image.h"
48
49 #include "ct.h"
50 #include "ctsim.h"
51 #include "docs.h"
52 #include "views.h"
53 #include "dialogs.h"
54 #include "dlgprojections.h"
55 #include <sstream>
56 #include "backprojectors.h"
57
58 // ImageFileCanvas
59
60 BEGIN_EVENT_TABLE(ImageFileCanvas, wxScrolledWindow)
61     EVT_MOUSE_EVENTS(ImageFileCanvas::OnMouseEvent)
62 END_EVENT_TABLE()
63
64
65 ImageFileCanvas::ImageFileCanvas (ImageFileView* v, wxFrame *frame, const wxPoint& pos, const wxSize& size, const long style)
66   : wxScrolledWindow(frame, -1, pos, size, style)
67 {
68     m_pView = v;
69 }
70
71 void 
72 ImageFileCanvas::OnDraw(wxDC& dc)
73 {
74     if (m_pView)
75         m_pView->OnDraw(& dc);
76 }
77
78 void 
79 ImageFileCanvas::OnMouseEvent(wxMouseEvent& event)
80 {
81     if (! m_pView)
82         return;
83     
84     wxClientDC dc(this);
85     PrepareDC(dc);
86     
87     wxPoint pt(event.GetLogicalPosition(dc));
88
89     if (event.LeftIsDown()) {
90         const ImageFile& rIF = m_pView->GetDocument()->getImageFile();
91         ImageFileArrayConst v = rIF.getArray();
92         int nx = rIF.nx();
93         int ny = rIF.ny();
94
95         if (pt.x >= 0 && pt.x < nx && pt.y >= 0 & pt.y < ny) {
96           ostringstream os;
97           os << "Image value (" << pt.x << "," << pt.y << ") = " << v[pt.x][ny - 1 - pt.y] << "\n";
98             *theApp->getLog() << os.str().c_str();
99         } else
100             *theApp->getLog() << "Mouse out of image range (" << pt.x << "," << pt.y << ")\n";
101             
102     }
103 }
104
105
106 // ImageFileView
107
108 IMPLEMENT_DYNAMIC_CLASS(ImageFileView, wxView)
109
110 BEGIN_EVENT_TABLE(ImageFileView, wxView)
111   EVT_MENU(IFMENU_FILE_PROPERTIES, ImageFileView::OnProperties)
112   EVT_MENU(IFMENU_VIEW_SCALE_MINMAX, ImageFileView::OnScaleMinMax)
113   EVT_MENU(IFMENU_VIEW_SCALE_AUTO, ImageFileView::OnScaleAuto)
114 END_EVENT_TABLE()
115
116 ImageFileView::ImageFileView(void) 
117   : wxView(), m_canvas(NULL), m_frame(NULL), m_bMinSpecified(false), m_bMaxSpecified(false)
118 {
119 }
120
121 ImageFileView::~ImageFileView(void)
122 {
123 }
124
125 void
126 ImageFileView::OnProperties (wxCommandEvent& event)
127 {
128   double min, max, mean, mode, median, stddev;
129   const ImageFile& rIF = GetDocument()->getImageFile();
130   if (rIF.nx() == 0 || rIF.ny() == 0)
131     *theApp->getLog() << "Properties: empty imagefile\n";
132   else {
133     const string& rFilename = rIF.getFilename();
134     rIF.statistics (min, max, mean, mode, median, stddev);
135     ostringstream os;
136     os << "file: " << rFilename << "\nmin: "<<min<<"\nmax: "<<max<<"\nmean: "<<mean<<"\nmode: "<<mode<<"\nstddev: "<<stddev << "\n";
137     *theApp->getLog() << os.str().c_str();
138
139     int xSize, ySize;
140     ostringstream osSize;
141     m_frame->GetSize (&xSize, &ySize);
142     osSize << "Frame size: (" << xSize << "," << ySize << ")\n";
143     m_frame->GetClientSize (&xSize, &ySize);
144     osSize << "Frame Client size: (" << xSize << "," << ySize << ")\n";
145     m_canvas->GetSize (&xSize, &ySize);
146     osSize << "Canvas size: (" << xSize << "," << ySize << ")\n";
147     *theApp->getLog() << osSize.str().c_str();
148   }
149 }
150
151 void 
152 ImageFileView::OnScaleAuto (wxCommandEvent& event)
153 {
154     const ImageFile& rIF = GetDocument()->getImageFile();
155     DialogAutoScaleParameters dialogAutoScale (m_frame, rIF, m_dAutoScaleFactor);
156     int iRetVal = dialogAutoScale.ShowModal();
157     if (iRetVal == wxID_OK) {
158       m_bMinSpecified = true;
159       m_bMaxSpecified = true;
160       double dMin, dMax;
161       dialogAutoScale.getMinMax (&dMin, &dMax);
162       m_dMinPixel = dMin;
163       m_dMaxPixel = dMax;
164       m_dAutoScaleFactor = dialogAutoScale.getAutoScaleFactor();
165       OnUpdate (this, NULL);
166     }
167 }
168
169 void 
170 ImageFileView::OnScaleMinMax (wxCommandEvent& event)
171 {
172     const ImageFile& rIF = GetDocument()->getImageFile();
173     double min, max;
174     if (! m_bMinSpecified && ! m_bMaxSpecified)
175       rIF.getMinMax (min, max);
176
177     if (m_bMinSpecified)
178       min = m_dMinPixel;
179     if (m_bMaxSpecified)
180       max = m_dMaxPixel;
181
182     DialogGetImageMinMax dialogMinMax (m_frame, rIF, min, max);
183     int retVal = dialogMinMax.ShowModal();
184     if (retVal == wxID_OK) {
185       m_bMinSpecified = true;
186       m_bMaxSpecified = true;
187       m_dMinPixel = dialogMinMax.getMinimum();
188       m_dMaxPixel = dialogMinMax.getMaximum();
189       OnUpdate (this, NULL);
190     }
191 }
192
193
194 ImageFileCanvas* 
195 ImageFileView::CreateCanvas (wxView *view, wxFrame *parent)
196 {
197     ImageFileCanvas* pCanvas;
198     int width, height;
199     parent->GetClientSize(&width, &height);
200     
201     pCanvas = new ImageFileCanvas (dynamic_cast<ImageFileView*>(view), parent, wxPoint(0, 0), wxSize(width, height), 0);
202     
203     pCanvas->SetScrollbars(20, 20, 50, 50);
204     pCanvas->SetBackgroundColour(*wxWHITE);
205     pCanvas->Clear();
206     
207     return pCanvas;
208 }
209
210 wxFrame*
211 ImageFileView::CreateChildFrame(wxDocument *doc, wxView *view)
212 {
213     wxDocChildFrame *subframe = new wxDocChildFrame(doc, view, theApp->getMainFrame(), -1, "ImageFile Frame", wxPoint(-1, -1), wxSize(0, 0), wxDEFAULT_FRAME_STYLE);
214     
215     wxMenu *file_menu = new wxMenu;
216     
217     file_menu->Append(MAINMENU_FILE_CREATE_PHANTOM, "Cr&eate Phantom...");
218     file_menu->Append(wxID_OPEN, "&Open...");
219     file_menu->Append(wxID_SAVE, "&Save");
220     file_menu->Append(wxID_SAVEAS, "Save &As...");
221     file_menu->Append(wxID_CLOSE, "&Close");
222     
223     file_menu->AppendSeparator();
224     file_menu->Append(IFMENU_FILE_PROPERTIES, "P&roperties");
225
226     file_menu->AppendSeparator();
227     file_menu->Append(wxID_PRINT, "&Print...");
228     file_menu->Append(wxID_PRINT_SETUP, "Print &Setup...");
229     file_menu->Append(wxID_PREVIEW, "Print Pre&view");
230     
231     wxMenu *view_menu = new wxMenu;
232     view_menu->Append(IFMENU_VIEW_SCALE_MINMAX, "Display Scale &Set...");
233     view_menu->Append(IFMENU_VIEW_SCALE_AUTO, "Display Scale &Auto...");
234     
235     wxMenu *help_menu = new wxMenu;
236     help_menu->Append(MAINMENU_HELP_ABOUT, "&About");
237     
238     wxMenuBar *menu_bar = new wxMenuBar;
239     
240     menu_bar->Append(file_menu, "&File");
241     menu_bar->Append(view_menu, "&View");
242     menu_bar->Append(help_menu, "&Help");
243     
244     subframe->SetMenuBar(menu_bar);
245     
246     subframe->Centre(wxBOTH);
247     
248     return subframe;
249 }
250
251
252 bool 
253 ImageFileView::OnCreate(wxDocument *doc, long WXUNUSED(flags) )
254 {
255     m_frame = CreateChildFrame(doc, this);
256     SetFrame(m_frame);
257     
258     m_bMinSpecified = false;
259     m_bMaxSpecified = false;
260     m_dAutoScaleFactor = 1.;
261
262     int width, height;
263     m_frame->GetClientSize(&width, &height);
264     m_frame->SetTitle("ImageFileView");
265     m_canvas = CreateCanvas(this, m_frame);
266
267 #ifdef __X__
268     int x, y;  // X requires a forced resize
269     m_frame->GetSize(&x, &y);
270     m_frame->SetSize(-1, -1, x, y);
271 #endif
272
273     m_frame->Show(true);
274     Activate(true);
275     
276     return true;
277 }
278
279 void 
280 ImageFileView::OnDraw (wxDC* dc)
281 {
282   if (m_bitmap.Ok())
283     dc->DrawBitmap(m_bitmap, 0, 0, false);
284 }
285
286
287 void 
288 ImageFileView::OnUpdate(wxView *WXUNUSED(sender), wxObject *WXUNUSED(hint) )
289 {
290     const ImageFile& rIF = dynamic_cast<ImageFileDocument*>(GetDocument())->getImageFile();
291     ImageFileArrayConst v = rIF.getArray();
292     int nx = rIF.nx();
293     int ny = rIF.ny();
294     if (v != NULL && nx != 0 && ny != 0) {
295         if (! m_bMinSpecified || ! m_bMaxSpecified) {
296             double min, max;
297             rIF.getMinMax (min, max);
298             if (! m_bMinSpecified)
299                 m_dMinPixel = min;
300             if (! m_bMaxSpecified)
301                 m_dMaxPixel = max;
302         }
303         double scaleWidth = m_dMaxPixel - m_dMinPixel;
304     
305         unsigned char* imageData = new unsigned char [nx * ny * 3];
306         for (int ix = 0; ix < nx; ix++) {
307             for (int iy = 0; iy < ny; iy++) {
308                 double scaleValue = ((v[ix][iy] - m_dMinPixel) / scaleWidth) * 255;
309                 int intensity = static_cast<int>(scaleValue + 0.5);
310                 intensity = clamp (intensity, 0, 255);
311                 int baseAddr = ((ny - 1 - iy) * nx + ix) * 3;
312                 imageData[baseAddr] = imageData[baseAddr+1] = imageData[baseAddr+2] = intensity;
313             }
314         }
315         wxImage image (nx, ny, imageData, true);
316         m_bitmap = image.ConvertToBitmap();
317         delete imageData;
318         int xSize = nx;
319         int ySize = ny;
320         xSize = clamp (xSize, 0, 800);
321         ySize = clamp (ySize, 0, 800);
322         m_frame->SetClientSize (xSize, ySize);
323         m_canvas->SetScrollbars(20, 20, nx/20, ny/20);
324         m_canvas->SetBackgroundColour(*wxWHITE);
325     } 
326
327     if (m_canvas)
328         m_canvas->Refresh();
329 }
330
331 bool 
332 ImageFileView::OnClose (bool deleteWindow)
333 {
334     if (!GetDocument()->Close())
335         return false;
336
337     m_canvas->Clear();
338     m_canvas->m_pView = NULL;
339     m_canvas = NULL;
340     wxString s(theApp->GetAppName());
341     if (m_frame)
342       m_frame->SetTitle(s);
343     SetFrame(NULL);
344
345     Activate(false);
346     
347     if (deleteWindow) {
348         delete m_frame;
349         return true;
350     }
351     return true;
352 }
353
354
355
356 // PhantomCanvas
357
358 PhantomCanvas::PhantomCanvas (PhantomView* v, wxFrame *frame, const wxPoint& pos, const wxSize& size, const long style)
359   : wxScrolledWindow(frame, -1, pos, size, style)
360 {
361     m_pView = v;
362 }
363
364 void 
365 PhantomCanvas::OnDraw(wxDC& dc)
366 {
367     if (m_pView)
368         m_pView->OnDraw(& dc);
369 }
370
371
372 // PhantomView
373
374 IMPLEMENT_DYNAMIC_CLASS(PhantomView, wxView)
375
376 BEGIN_EVENT_TABLE(PhantomView, wxView)
377     EVT_MENU(PHMMENU_FILE_PROPERTIES, PhantomView::OnProperties)
378     EVT_MENU(PHMMENU_PROCESS_RASTERIZE, PhantomView::OnRasterize)
379     EVT_MENU(PHMMENU_PROCESS_PROJECTIONS, PhantomView::OnProjections)
380 END_EVENT_TABLE()
381
382 PhantomView::PhantomView(void) 
383   : wxView(), m_canvas(NULL), m_frame(NULL)
384 {
385 }
386
387 PhantomView::~PhantomView(void)
388 {
389 }
390
391 void
392 PhantomView::OnProperties (wxCommandEvent& event)
393 {
394   const int idPhantom = GetDocument()->getPhantomID();
395   const string& namePhantom = GetDocument()->getPhantomName();
396   ostringstream os;
397   os << "Phantom " << namePhantom << " (" << idPhantom << ")\n";
398   *theApp->getLog() << os.str().c_str();
399 #if DEBUG
400   const Phantom& rPhantom = GetDocument()->getPhantom();
401   rPhantom.print();
402 #endif
403 }
404
405
406 void
407 PhantomView::OnProjections (wxCommandEvent& event)
408 {
409   DialogGetProjectionParameters dialogProjection (m_frame, 367, 320, 1, 1., 1., 1., Scanner::GEOMETRY_PARALLEL, Trace::TRACE_NONE);
410   int retVal = dialogProjection.ShowModal();
411   if (retVal == wxID_OK) {
412     int nDet = dialogProjection.getNDet();
413     int nView = dialogProjection.getNView();
414     int nSamples = dialogProjection.getNSamples();
415     int iTrace = dialogProjection.getTrace();
416     double dRotAngle = dialogProjection.getRotAngle();
417     double dFocalLengthRatio = dialogProjection.getFocalLengthRatio();
418     double dFieldOfViewRatio = dialogProjection.getFieldOfViewRatio();
419
420     wxString sGeometry = dialogProjection.getGeometry();
421     if (nDet > 0 && nView > 0 && sGeometry != "") {
422       const Phantom& rPhantom = GetDocument()->getPhantom();
423       ProjectionFileDocument* pProjectionDoc = dynamic_cast<ProjectionFileDocument*>(theApp->getDocManager()->CreateDocument("untitled.pj", wxDOC_SILENT));
424       Projections& rProj = pProjectionDoc->getProjections();
425       Scanner theScanner (rPhantom, sGeometry.c_str(), nDet, nView, nSamples, dRotAngle, dFocalLengthRatio, dFieldOfViewRatio);
426       if (theScanner.fail()) {
427         *theApp->getLog() << "Failed making scanner: " << theScanner.failMessage().c_str() << "\n";
428         return;
429       }
430       rProj.initFromScanner (theScanner);
431
432       if (iTrace > Trace::TRACE_CONSOLE) {
433         ProjectionsDialog dialogProjections (theScanner, rProj, rPhantom, iTrace, dynamic_cast<wxWindow*>(m_frame));
434         for (int iView = 0; iView < rProj.nView(); iView++) {
435           ::wxYield();
436           ::wxYield();
437           if (dialogProjections.isCancelled() || ! dialogProjections.projectView (iView)) {
438             pProjectionDoc->DeleteAllViews();
439             return;
440           }
441           ::wxYield();
442           ::wxYield();
443           while (dialogProjections.isPaused()) {
444               ::wxYield();
445               ::wxUsleep(50);
446           }
447         }
448       } else
449         theScanner.collectProjections (rProj, rPhantom, iTrace);
450
451       pProjectionDoc->Modify(true);
452       pProjectionDoc->UpdateAllViews(this);
453       if (wxView* pView = pProjectionDoc->GetFirstView())
454         if (wxFrame* pFrame = pView->GetFrame()) {
455           pFrame->SetFocus();
456         }
457       ostringstream os;
458       os << "Projections for " << rPhantom.name() << ": nDet=" << nDet << ", nView=" << nView << ", nSamples=" << nSamples << ", RotAngle=" << dRotAngle << ", FocalLengthRatio=" << dFocalLengthRatio << ", FieldOfViewRatio=" << dFieldOfViewRatio << ", Geometry=" << sGeometry.c_str() << "\n";
459       rProj.setRemark (os.str());
460       *theApp->getLog() << os.str().c_str();
461     }
462   }
463 }
464
465
466 void
467 PhantomView::OnRasterize (wxCommandEvent& event)
468 {
469   DialogGetRasterParameters dialogRaster (m_frame, 256, 256, 1);
470   int retVal = dialogRaster.ShowModal();
471   if (retVal == wxID_OK) {
472     int xSize = dialogRaster.getXSize();
473     int ySize = dialogRaster.getYSize();
474     int nSamples = dialogRaster.getNSamples();
475     if (nSamples < 1)
476       nSamples = 1;
477     if (xSize > 0 && ySize > 0) {
478       const Phantom& rPhantom = GetDocument()->getPhantom();
479       ImageFileDocument* pRasterDoc = dynamic_cast<ImageFileDocument*>(theApp->getDocManager()->CreateDocument("untitled.if", wxDOC_SILENT));
480       ImageFile& imageFile = pRasterDoc->getImageFile();
481
482       imageFile.setArraySize (xSize, ySize);
483       rPhantom.convertToImagefile (imageFile, nSamples, Trace::TRACE_NONE);
484       pRasterDoc->Modify(true);
485       pRasterDoc->UpdateAllViews(this);
486
487       ostringstream os;
488       os << "Rasterize Phantom " << rPhantom.name() << ": XSize=" << xSize << ", YSize=" << ySize << ", nSamples=" << nSamples << "\n";
489       *theApp->getLog() << os.str().c_str();
490     }
491   }
492 }
493
494
495 PhantomCanvas* 
496 PhantomView::CreateCanvas (wxView *view, wxFrame *parent)
497 {
498     PhantomCanvas* pCanvas;
499     int width, height;
500     parent->GetClientSize(&width, &height);
501     
502     pCanvas = new PhantomCanvas (dynamic_cast<PhantomView*>(view), parent, wxPoint(0, 0), wxSize(width, height), 0);
503     
504     pCanvas->SetBackgroundColour(*wxWHITE);
505     pCanvas->Clear();
506     
507     return pCanvas;
508 }
509
510 wxFrame*
511 PhantomView::CreateChildFrame(wxDocument *doc, wxView *view)
512 {
513     wxDocChildFrame *subframe = new wxDocChildFrame(doc, view, theApp->getMainFrame(), -1, "Phantom Frame", wxPoint(10, 10), wxSize(256, 256), wxDEFAULT_FRAME_STYLE);
514     
515     wxMenu *file_menu = new wxMenu;
516     
517     file_menu->Append(MAINMENU_FILE_CREATE_PHANTOM, "Cr&eate Phantom...");
518     file_menu->Append(wxID_OPEN, "&Open...");
519     file_menu->Append(wxID_CLOSE, "&Close");
520     
521     file_menu->AppendSeparator();
522     file_menu->Append(PHMMENU_FILE_PROPERTIES, "P&roperties");
523
524     file_menu->AppendSeparator();
525     file_menu->Append(wxID_PRINT, "&Print...");
526     file_menu->Append(wxID_PRINT_SETUP, "Print &Setup...");
527     file_menu->Append(wxID_PREVIEW, "Print Pre&view");
528     
529     wxMenu *process_menu = new wxMenu;
530     process_menu->Append(PHMMENU_PROCESS_RASTERIZE, "&Rasterize...");
531     process_menu->Append(PHMMENU_PROCESS_PROJECTIONS, "&Projections...");
532
533     wxMenu *help_menu = new wxMenu;
534     help_menu->Append(MAINMENU_HELP_CONTENTS, "&Contents");
535     help_menu->Append(MAINMENU_HELP_ABOUT, "&About");
536     
537     wxMenuBar *menu_bar = new wxMenuBar;
538     
539     menu_bar->Append(file_menu, "&File");
540     menu_bar->Append(process_menu, "&Process");
541     menu_bar->Append(help_menu, "&Help");
542     
543     subframe->SetMenuBar(menu_bar);
544     
545     subframe->Centre(wxBOTH);
546     
547     return subframe;
548 }
549
550
551 bool 
552 PhantomView::OnCreate(wxDocument *doc, long WXUNUSED(flags) )
553 {
554     m_frame = CreateChildFrame(doc, this);
555     SetFrame(m_frame);
556
557     int width, height;
558     m_frame->GetClientSize(&width, &height);
559     m_frame->SetTitle("PhantomView");
560     m_canvas = CreateCanvas(this, m_frame);
561
562 #ifdef __X__
563     int x, y;  // X requires a forced resize
564     m_frame->GetSize(&x, &y);
565     m_frame->SetSize(-1, -1, x, y);
566 #endif
567
568     m_frame->Show(true);
569     Activate(true);
570
571     return true;
572 }
573
574
575 void 
576 PhantomView::OnUpdate(wxView *WXUNUSED(sender), wxObject *WXUNUSED(hint) )
577 {
578     if (m_canvas)
579         m_canvas->Refresh();
580 }
581
582 bool 
583 PhantomView::OnClose (bool deleteWindow)
584 {
585     if (!GetDocument()->Close())
586         return false;
587
588     m_canvas->Clear();
589     m_canvas->m_pView = NULL;
590     m_canvas = NULL;
591     wxString s(wxTheApp->GetAppName());
592     if (m_frame)
593       m_frame->SetTitle(s);
594     SetFrame(NULL);
595
596     Activate(false);
597     
598     if (deleteWindow) {
599         delete m_frame;
600         return true;
601     }
602     return true;
603 }
604
605 void
606 PhantomView::OnDraw (wxDC* dc)
607 {
608   int xsize, ysize;
609   m_canvas->GetClientSize (&xsize, &ysize);
610   SGPDriver driver (dc, xsize, ysize);
611   SGP sgp (driver);
612   const Phantom& rPhantom = GetDocument()->getPhantom();
613   sgp.setColor (C_RED);
614   rPhantom.show (sgp);
615 }
616
617 // ProjectionCanvas
618
619 ProjectionFileCanvas::ProjectionFileCanvas (ProjectionFileView* v, wxFrame *frame, const wxPoint& pos, const wxSize& size, const long style)
620   : wxScrolledWindow(frame, -1, pos, size, style)
621 {
622     m_pView = v;
623 }
624
625 void 
626 ProjectionFileCanvas::OnDraw(wxDC& dc)
627 {
628     if (m_pView)
629         m_pView->OnDraw(& dc);
630 }
631
632 // ProjectionFileView
633
634 IMPLEMENT_DYNAMIC_CLASS(ProjectionFileView, wxView)
635
636 BEGIN_EVENT_TABLE(ProjectionFileView, wxView)
637     EVT_MENU(PJMENU_FILE_PROPERTIES, ProjectionFileView::OnProperties)
638     EVT_MENU(PJMENU_PROCESS_RECONSTRUCT, ProjectionFileView::OnReconstruct)
639 END_EVENT_TABLE()
640
641 ProjectionFileView::ProjectionFileView(void) 
642   : wxView(), m_canvas(NULL), m_frame(NULL)
643 {
644 }
645
646 ProjectionFileView::~ProjectionFileView(void)
647 {
648 }
649
650 void
651 ProjectionFileView::OnProperties (wxCommandEvent& event)
652 {
653   const Projections& rProj = GetDocument()->getProjections();
654   ostringstream os;
655   rProj.printScanInfo(os);
656   *theApp->getLog() << os.str().c_str();
657 }
658
659
660 void
661 ProjectionFileView::OnReconstruct (wxCommandEvent& event)
662 {
663 #if HAVE_FFTW
664   DialogGetReconstructionParameters dialogReconstruction (m_frame, 256, 256, SignalFilter::FILTER_ABS_BANDLIMIT, 1., ProcessSignal::FILTER_METHOD_RFFTW, ProcessSignal::FILTER_GENERATION_INVERSE_FOURIER, 1, Backprojector::INTERP_LINEAR, 1, Backprojector::BPROJ_IDIFF3);
665 #else
666   DialogGetReconstructionParameters dialogReconstruction (m_frame, 256, 256, SignalFilter::FILTER_ABS_BANDLIMIT, 1., ProcessSignal::FILTER_METHOD_CONVOLUTION, ProcessSignal::FILTER_GENERATION_DIRECT, 1, Backprojector::INTERP_LINEAR, 1, Backprojector::BPROJ_IDIFF3);
667 #endif
668   int retVal = dialogReconstruction.ShowModal();
669   if (retVal == wxID_OK) {
670     int xSize = dialogReconstruction.getXSize();
671     int ySize = dialogReconstruction.getYSize();
672     wxString optFilterName = dialogReconstruction.getFilterName();
673     double optFilterParam = dialogReconstruction.getFilterParam();
674     wxString optFilterMethodName = dialogReconstruction.getFilterMethodName();
675     int optZeropad = dialogReconstruction.getZeropad();
676     wxString optFilterGenerationName = dialogReconstruction.getFilterGenerationName();
677     wxString optInterpName = dialogReconstruction.getInterpName();
678     int optInterpParam = dialogReconstruction.getInterpParam();
679     wxString optBackprojectName = dialogReconstruction.getBackprojectName();
680     if (xSize > 0 && ySize > 0) {
681       ImageFileDocument* pReconDoc = dynamic_cast<ImageFileDocument*>(theApp->getDocManager()->CreateDocument("untitled.if", wxDOC_SILENT));
682       ImageFile& imageFile = pReconDoc->getImageFile();
683       const Projections& rProj = GetDocument()->getProjections();
684       imageFile.setArraySize (xSize, ySize);
685       rProj.reconstruct (imageFile, optFilterName.c_str(), optFilterParam, optFilterMethodName.c_str(), optZeropad, optFilterGenerationName.c_str(), optInterpName.c_str(), optInterpParam, optBackprojectName.c_str(), Trace::TRACE_NONE);
686       pReconDoc->Modify(true);
687       pReconDoc->UpdateAllViews(this);
688       ostringstream os;
689       os << "Reconstruct " << rProj.getFilename() << ": xSize=" << xSize << ", ySize=" << ySize << ", Filter=" << optFilterName.c_str() << ", FilterParam=" << optFilterParam << ", FilterMethod=" << optFilterMethodName.c_str() << ", FilterGeneration=" << optFilterGenerationName.c_str() << ", Zeropad=" << optZeropad << ", Interpolation=" << optInterpName.c_str() << ", InterpolationParam=" << optInterpParam << ", Backprojection=" << optBackprojectName.c_str() << "\n";
690       *theApp->getLog() << os.str().c_str();
691     }
692   }
693 }
694
695
696 ProjectionFileCanvas* 
697 ProjectionFileView::CreateCanvas (wxView *view, wxFrame *parent)
698 {
699     ProjectionFileCanvas* pCanvas;
700     int width, height;
701     parent->GetClientSize(&width, &height);
702     
703     pCanvas = new ProjectionFileCanvas (dynamic_cast<ProjectionFileView*>(view), parent, wxPoint(0, 0), wxSize(width, height), 0);
704     
705     pCanvas->SetScrollbars(20, 20, 50, 50);
706     pCanvas->SetBackgroundColour(*wxWHITE);
707     pCanvas->Clear();
708     
709     return pCanvas;
710 }
711
712 wxFrame*
713 ProjectionFileView::CreateChildFrame(wxDocument *doc, wxView *view)
714 {
715     wxDocChildFrame *subframe = new wxDocChildFrame(doc, view, theApp->getMainFrame(), -1, "Projection Frame", wxPoint(10, 10), wxSize(0, 0), wxDEFAULT_FRAME_STYLE);
716     
717     wxMenu *file_menu = new wxMenu;
718     
719     file_menu->Append(MAINMENU_FILE_CREATE_PHANTOM, "Cr&eate Phantom...");
720     file_menu->Append(wxID_OPEN, "&Open...");
721     file_menu->Append(wxID_SAVE, "&Save");
722     file_menu->Append(wxID_SAVEAS, "Save &As...");
723     file_menu->Append(wxID_CLOSE, "&Close");
724     
725     file_menu->AppendSeparator();
726     file_menu->Append(PJMENU_FILE_PROPERTIES, "P&roperties");
727
728     file_menu->AppendSeparator();
729     file_menu->Append(wxID_PRINT, "&Print...");
730     file_menu->Append(wxID_PRINT_SETUP, "Print &Setup...");
731     file_menu->Append(wxID_PREVIEW, "Print Pre&view");
732     
733     wxMenu *process_menu = new wxMenu;
734     process_menu->Append(PJMENU_PROCESS_RECONSTRUCT, "R&econstruct...");
735
736     wxMenu *help_menu = new wxMenu;
737     help_menu->Append(MAINMENU_HELP_CONTENTS, "&Contents");
738     help_menu->AppendSeparator();
739     help_menu->Append(MAINMENU_HELP_ABOUT, "&About");
740     
741     wxMenuBar *menu_bar = new wxMenuBar;
742     
743     menu_bar->Append(file_menu, "&File");
744     menu_bar->Append(process_menu, "&Process");
745     menu_bar->Append(help_menu, "&Help");
746     
747     subframe->SetMenuBar(menu_bar);
748     
749     subframe->Centre(wxBOTH);
750     
751     return subframe;
752 }
753
754
755 bool 
756 ProjectionFileView::OnCreate(wxDocument *doc, long WXUNUSED(flags) )
757 {
758     m_frame = CreateChildFrame(doc, this);
759     SetFrame(m_frame);
760     
761     int width, height;
762     m_frame->GetClientSize(&width, &height);
763     m_frame->SetTitle("ProjectionFileView");
764     m_canvas = CreateCanvas(this, m_frame);
765
766 #ifdef __X__
767     int x, y;  // X requires a forced resize
768     m_frame->GetSize(&x, &y);
769     m_frame->SetSize(-1, -1, x, y);
770 #endif
771
772     m_frame->Show(true);
773     Activate(true);
774     
775     return true;
776 }
777
778 void 
779 ProjectionFileView::OnDraw (wxDC* dc)
780 {
781     if (m_bitmap.Ok())
782         dc->DrawBitmap (m_bitmap, 0, 0, false);
783 }
784
785
786 void 
787 ProjectionFileView::OnUpdate(wxView *WXUNUSED(sender), wxObject *WXUNUSED(hint) )
788 {
789     const Projections& rProj = GetDocument()->getProjections();
790     const int nDet = rProj.nDet();
791     const int nView = rProj.nView();
792     if (nDet != 0 && nView != 0) {
793         const DetectorArray& detarray = rProj.getDetectorArray(0);
794         const DetectorValue* detval = detarray.detValues();
795         double min = detval[0];
796         double max = detval[0];
797         for (int iy = 0; iy < nView; iy++) {
798             const DetectorArray& detarray = rProj.getDetectorArray(iy);
799             const DetectorValue* detval = detarray.detValues();
800             for (int ix = 0; ix < nDet; ix++) {
801                 if (min > detval[ix])
802                     min = detval[ix];
803                 else if (max < detval[ix])
804                     max = detval[ix];
805             }
806         }
807
808         unsigned char* imageData = new unsigned char [nDet * nView * 3];
809         double scale = (max - min) / 255;
810         for (int iy = 0; iy < nView; iy++) {
811             const DetectorArray& detarray = rProj.getDetectorArray(iy);
812             const DetectorValue* detval = detarray.detValues();
813             for (int ix = 0; ix < nDet; ix++) {
814                 int intensity = static_cast<int>(((detval[ix] - min) / scale) + 0.5);
815                 intensity = clamp(intensity, 0, 255);
816                 int baseAddr = (iy * nDet + ix) * 3;
817                 imageData[baseAddr] = imageData[baseAddr+1] = imageData[baseAddr+2] = intensity;
818             }
819         }
820         wxImage image (nDet, nView, imageData, true);
821         m_bitmap = image.ConvertToBitmap();
822         delete imageData;
823         int xSize = nDet;
824         int ySize = nView;
825         xSize = clamp (xSize, 0, 800);
826         ySize = clamp (ySize, 0, 800);
827         m_frame->SetClientSize (xSize, ySize);
828         m_canvas->SetScrollbars (20, 20, nDet/20, nView/20);
829     }
830
831     if (m_canvas)
832         m_canvas->Refresh();
833 }
834
835 bool 
836 ProjectionFileView::OnClose (bool deleteWindow)
837 {
838     if (!GetDocument()->Close())
839         return false;
840
841     m_canvas->Clear();
842     m_canvas->m_pView = NULL;
843     m_canvas = NULL;
844     wxString s(wxTheApp->GetAppName());
845     if (m_frame)
846       m_frame->SetTitle(s);
847     SetFrame(NULL);
848
849     Activate(false);
850     
851     if (deleteWindow) {
852         delete m_frame;
853         return true;
854     }
855     return true;
856 }
857