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