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