r473: no message
[ctsim.git] / src / graph3dview.cpp
1 /*****************************************************************************
2 ** FILE IDENTIFICATION
3 **
4 **   Name:          graph3dview.cpp
5 **   Purpose:       3d graph view classes
6 **   Programmer:    Kevin Rosenberg
7 **   Date Started:  Jan 2001
8 **
9 **  This is part of the CTSim program
10 **  Copyright (c) 1983-2001 Kevin Rosenberg
11 **
12 **  $Id: graph3dview.cpp,v 1.2 2001/01/30 10:58:13 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
29 #ifdef __GNUG__
30 #pragma implementation
31 #pragma interface
32 #endif
33
34 // For compilers that support precompilation, includes "wx.h".
35 #include "wx/wxprec.h"
36
37 #ifdef __BORLANDC__
38 #pragma hdrstop
39 #endif
40
41 #ifndef WX_PRECOMP
42 #include "wx/wx.h"
43 #endif
44
45 #if !wxUSE_GLCANVAS
46 #error Please set wxUSE_GLCANVAS to 1 in setup.h.
47 #endif
48
49 #include "wx/timer.h"
50 #include "wx/glcanvas.h"
51
52 #include <GL/gl.h>
53 #include <GL/glu.h>
54
55 #include "ct.h"
56 #include "ctsim.h"
57 #include "docs.h"
58 #include "views.h"
59 #include "dialogs.h"
60 #include "dlgprojections.h"
61 #include "dlgreconstruct.h"
62 #include "backprojectors.h"
63 #include "reconstruct.h"
64 #include "timer.h"
65
66 #if defined(MSVC) || HAVE_SSTREAM
67 #include <sstream>
68 #else
69 #include <sstream_subst>
70 #endif
71
72
73 //***********************************************************************
74 // Function: CalculateVectorNormal
75 //
76 // Purpose: Given three points of a 3D plane, this function calculates
77 //          the normal vector of that plane.
78 //
79 // Parameters:
80 //     fVert1[]   == array for 1st point (3 elements are x, y, and z).
81 //     fVert2[]   == array for 2nd point (3 elements are x, y, and z).
82 //     fVert3[]   == array for 3rd point (3 elements are x, y, and z).
83 //
84 // Returns:
85 //     fNormalX   == X vector for the normal vector
86 //     fNormalY   == Y vector for the normal vector
87 //     fNormalZ   == Z vector for the normal vector
88 //*************************************************************************
89
90
91 GLvoid CalculateVectorNormal (GLfloat fVert1[], GLfloat fVert2[], 
92                               GLfloat fVert3[], GLfloat *fNormalX,
93                               GLfloat *fNormalY, GLfloat *fNormalZ)
94 {
95   GLfloat Qx = fVert2[0] - fVert1[0];
96   GLfloat Qy = fVert2[1] - fVert1[1];
97   GLfloat Qz = fVert2[2] - fVert1[2];
98   GLfloat Px = fVert3[0] - fVert1[0];
99   GLfloat Py = fVert3[1] - fVert1[1];
100   GLfloat Pz = fVert3[2] - fVert1[2];
101   
102   *fNormalX = Py*Qz - Pz*Qy;
103   *fNormalY = Pz*Qx - Px*Qz;
104   *fNormalZ = Px*Qy - Py*Qx;
105   
106
107
108 IMPLEMENT_DYNAMIC_CLASS(Graph3dFileView, wxView)
109
110 BEGIN_EVENT_TABLE(Graph3dFileView, wxView)
111 EVT_MENU(IFMENU_FILE_PROPERTIES, Graph3dFileView::OnProperties)
112 END_EVENT_TABLE()
113
114 Graph3dFileView::Graph3dFileView ()
115 : m_pFileMenu(NULL)
116 {
117   m_bUseVertexArrays = GL_FALSE;
118   m_bDoubleBuffer = GL_TRUE;
119   m_bSmooth = GL_TRUE;
120   m_bLighting = GL_TRUE;
121   
122 }
123
124 Graph3dFileView::~Graph3dFileView()
125 {
126   GetDocumentManager()->FileHistoryRemoveMenu (m_pFileMenu);
127   GetDocumentManager()->ActivateView(this, FALSE, TRUE);
128 }
129
130 bool 
131 Graph3dFileView::OnCreate (wxDocument *doc, long WXUNUSED(flags) )
132 {
133   m_pFrame = CreateChildFrame(doc, this);
134   (m_pFrame);
135   
136   int width, height;
137   m_pFrame->GetClientSize (&width, &height);
138   m_pFrame->SetTitle("Graph3dFileView");
139   m_pCanvas = CreateCanvas (m_pFrame);
140   
141   m_pFrame->Show(TRUE);
142   m_pCanvas->SetCurrent();
143   
144   InitGL();
145   
146   int x, y;  // X requires a forced resize
147   m_pFrame->GetSize(&x, &y);
148   m_pFrame->SetSize(-1, -1, x, y);
149   m_pFrame->SetFocus();
150   m_pFrame->Show(true);
151   Activate(true);
152   
153   return true;
154
155
156 Graph3dFileCanvas* 
157 Graph3dFileView::CreateCanvas (wxFrame* parent)
158 {
159   Graph3dFileCanvas* pCanvas;
160   int width, height;
161   parent->GetClientSize (&width, &height);
162   
163 #ifdef __WXMSW__
164   int *gl_attrib = NULL;
165 #else
166   int gl_attrib[20] = { GLX_RGBA, GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1,
167     GLX_BLUE_SIZE, 1, GLX_DEPTH_SIZE, 1, GLX_DOUBLEBUFFER, None };
168 #endif
169   
170   if(! m_bDoubleBuffer) {
171 #ifdef __WXGTK__
172     gl_attrib[9] = None;
173 #endif
174   }
175   
176   pCanvas = new Graph3dFileCanvas (this, parent, wxPoint(0, 0), wxSize(200, 200), 0, gl_attrib);
177   
178   pCanvas->SetBackgroundColour(*wxWHITE);
179   pCanvas->Clear();
180   
181   return pCanvas;
182 }
183
184
185
186 void
187 Graph3dFileView::DrawSurface()
188 {
189   int nVertices = GetDocument()->m_nVertices;
190   glTripleFloat* pVertices = GetDocument()->m_pVertices;
191   glTripleFloat* pNormals = GetDocument()->m_pNormals;
192
193 #ifdef GL_EXT_vertex_array
194   if (m_bUseVertexArrays) {
195     glDrawArraysEXT( GL_TRIANGLE_STRIP, 0, nVertices );
196   }
197   else {
198 #endif
199 #if 0
200 //    glBegin( GL_TRIANGLE_STRIP );
201     //glBegin (GL_QUADS);
202     //glNormal3f (0.0, 0.0, 1.0);     glVertex3f (-1.0, -1.0, 0.0);     glVertex3f (0.0, -1.0, 0.0);     glVertex3f (0.0, 0.0, 0.0);     glVertex3f (-1.0, 0.0, 0.0);      glNormal3f (0.0, 0.0, 1.0);     glVertex3f (0.0, -1.0, 0.0);     glVertex3f (1.0, -1.0, 0.0);     glVertex3f (1.0, 0.0, 0.0);     glVertex3f (0.0, 0.0, 0.0);      glNormal3f (0.0, 0.0, 1.0);     glVertex3f (0.0, 0.0, 0.0);     glVertex3f (1.0, 0.0, 0.0);     glVertex3f (1.0, 1.0, 0.0);     glVertex3f (0.0, 1.0, 0.0);      glNormal3f (0.0, 0.0, 1.0);     glVertex3f (0.0, 0.0, 0.0);     glVertex3f (0.0, 1.0, 0.0);     glVertex3f (-1.0, 1.0, 0.0);     glVertex3f (-1.0, 0.0, 0.0); 
203     glColor3f(1.0,1.0,1.0);
204     glBegin (GL_POINTS);
205     for (GLint i = 0;i < nVertices; i++) {
206       //  glNormal3fv( &pNormals[i] );
207      glVertex3fv( pVertices[i] );
208     }
209     glEnd();
210 #else   
211         double edge = 1.;       
212         unsigned int nx = GetDocument()->m_nx;
213   unsigned int ny = GetDocument()->m_ny;
214   const ImageFileArray v = GetDocument()->m_array;
215   double actOffset = 0;
216
217 //      glNewList(opnListNum++,GL_COMPILE);             
218                 for(int ix = 0; ix < nx-1; ix++){                       
219                         for(int iy = 0; iy < ny-1; iy++){                       
220                                 
221           float p1[3], p2[3], p3[3], p4[3];
222               float n1[3], n2[3], n3[3], n4[3];
223                                 glBegin(GL_LINE_LOOP);
224                                         
225                                 p1[0] = edge*(iy);     p1[1] = v[ix][iy]     + actOffset; p1[2] = edge*(ix);
226                                 p2[0] = edge*(iy+1); p2[1] = v[ix+1][iy]   + actOffset; p2[2] = edge*(ix ); 
227                                 p3[0] = edge*(iy+1); p3[1] = v[ix+1][iy] + actOffset; p3[2] = edge*(ix +1);
228                                 p4[0] = edge*(iy);     p4[1] = v[ix][iy]   + actOffset; p4[2] = edge*(ix +1);
229                                                                                         
230                                 n1[0] = -(p2[1] - p1[1])*(p3[2] - p1[2]) + (p2[2] - p1[2])*(p3[1] - p2[1]);
231                                 n1[1] = -(p2[2] - p1[2])*(p3[0] - p2[0]) + (p2[0] - p1[0])*(p3[2] - p2[2]);
232                                 n1[2] = -(p2[0] - p1[0])*(p3[1] - p2[1]) + (p2[1] - p1[1])*(p3[0] - p2[0]);
233                                         
234                                 glVertex3fv(p1); glNormal3fv(n1);                                       
235                                 glVertex3fv(p2); glNormal3fv(n1);                                       
236                                 glVertex3fv(p3); glNormal3fv(n1);                                       
237                                 glVertex3fv(p4); glNormal3fv(n1);                                                                                                                                               
238         glEnd();                                
239                         }                       
240                         
241                 }
242         glEndList();
243                         
244 #endif
245 #ifdef GL_EXT_vertex_array
246   }
247 #endif
248 }
249
250
251 void
252 Graph3dFileView::OnProperties (wxCommandEvent& event)
253 {
254     std::ostringstream os;
255     *theApp->getLog() << ">>>>\n" << os.str().c_str() << "<<<<\n";
256     wxMessageDialog dialogMsg (getFrameForChild(), os.str().c_str(), "Imagefile Properties", wxOK | wxICON_INFORMATION);
257     dialogMsg.ShowModal();
258 }
259
260
261
262 void 
263 Graph3dFileView::OnDraw (wxDC* dc)
264 {
265 #ifndef __WXMOTIF__
266   if (! m_pCanvas->GetContext()) return;
267 #endif
268   
269   Draw();
270   m_pCanvas->SwapBuffers();
271 }
272
273
274 void 
275 Graph3dFileView::Draw ()
276 {
277   glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
278   glPushMatrix();
279   glRotatef( m_dYRotate, 0.0, 1.0, 0.0 );
280   glRotatef( m_dXRotate, 1.0, 0.0, 0.0 );
281   
282   DrawSurface();
283   
284   glPopMatrix();
285   glFlush();
286 }
287
288
289 void
290 Graph3dFileView::InitMaterials()
291 {
292   static float ambient[] = {0.1, 0.1, 0.1, 1.0};
293   static float diffuse[] = {0.5, 1.0, 1.0, 1.0};
294   static float position0[] = {0.0, 0.0, 20.0, 0.0};
295   static float position1[] = {0.0, 0.0, -20.0, 0.0};
296   static float front_mat_shininess[] = {60.0};
297   static float front_mat_specular[] = {0.2, 0.2, 0.2, 1.0};
298   static float front_mat_diffuse[] = {0.5, 0.28, 0.38, 1.0};
299   /*
300   static float back_mat_shininess[] = {60.0};
301   static float back_mat_specular[] = {0.5, 0.5, 0.2, 1.0};
302   static float back_mat_diffuse[] = {1.0, 1.0, 0.2, 1.0};
303   */
304   static float lmodel_ambient[] = {1.0, 1.0, 1.0, 1.0};
305   static float lmodel_twoside[] = {GL_FALSE};
306   
307   glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
308   glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
309   glLightfv(GL_LIGHT0, GL_POSITION, position0);
310   glEnable(GL_LIGHT0);
311   
312   glLightfv(GL_LIGHT1, GL_AMBIENT, ambient);
313   glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse);
314   glLightfv(GL_LIGHT1, GL_POSITION, position1);
315   glEnable(GL_LIGHT1);
316   
317   glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);
318   glLightModelfv(GL_LIGHT_MODEL_TWO_SIDE, lmodel_twoside);
319   glEnable(GL_LIGHTING);
320   
321   glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, front_mat_shininess);
322   glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, front_mat_specular);
323   glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, front_mat_diffuse);
324 }
325
326
327 void 
328 Graph3dFileView::InitGL ()
329 {
330   glClearColor(0.0, 0.0, 0.0, 0.0);
331   
332   glShadeModel(GL_SMOOTH);
333   glEnable(GL_DEPTH_TEST);
334   
335   InitMaterials();
336   
337   glMatrixMode(GL_PROJECTION);
338   glLoadIdentity();
339   glOrtho (-100, 100, -100, 100, -500, 500);
340   glMatrixMode(GL_MODELVIEW);
341   glLoadIdentity();
342   glTranslatef( 100, -6, 100 );
343   
344 }
345
346 void 
347 Graph3dFileView::OnUpdate (wxView *WXUNUSED(sender), wxObject *WXUNUSED(hint) )
348 {
349   int nVertices = GetDocument()->m_nVertices;
350   glTripleFloat* pVertices = GetDocument()->m_pVertices;
351   glTripleFloat* pNormals = GetDocument()->m_pNormals;
352
353 #if 0    
354   const ImageFile& rIF = GetDocument()->getImageFile();
355   ImageFileArrayConst v = rIF.getArray();
356   int nx = rIF.nx();
357   int ny = rIF.ny();
358   if (v != NULL && nx != 0 && ny != 0) {
359     unsigned char* imageData = new unsigned char [nx * ny * 3];
360     for (int ix = 0; ix < nx; ix++) {
361       for (int iy = 0; iy < ny; iy++) {
362         double scaleValue = ((v[ix][iy] - m_dMinPixel) / scaleWidth) * 255;
363         int intensity = static_cast<int>(scaleValue + 0.5);
364         intensity = clamp (intensity, 0, 255);
365         int baseAddr = ((ny - 1 - iy) * nx + ix) * 3;
366         imageData[baseAddr] = imageData[baseAddr+1] = imageData[baseAddr+2] = intensity;
367       }
368     }
369     wxImage image (nx, ny, imageData, true);
370     m_bitmap = image.ConvertToBitmap();
371     delete imageData;
372     int xSize = nx;
373     int ySize = ny;
374     ySize = clamp (ySize, 0, 800);
375     m_pFrame->SetClientSize (xSize, ySize);
376     m_pCanvas->SetScrollbars(20, 20, nx/20, ny/20);
377     m_pCanvas->SetBackgroundColour(*wxWHITE);
378   } 
379 #endif
380   
381 #ifdef GL_EXT_vertex_array
382   if (m_bUseVertexArrays) {
383     //  glVertexPointerEXT( 3, GL_FLOAT, 0, nVertices, pVertices );
384     //  glNormalPointerEXT( GL_FLOAT, 0, nVertices, pNormals );
385     glEnable( GL_VERTEX_ARRAY_EXT );
386     glEnable( GL_NORMAL_ARRAY_EXT );
387   }
388 #endif
389   if (m_pCanvas)
390     m_pCanvas->Refresh();
391 }
392
393 bool 
394 Graph3dFileView::OnClose (bool deleteWindow)
395 {
396   if (! GetDocument() || ! GetDocument()->Close())
397     return false;
398   
399   Activate (false);
400   if (m_pCanvas) {
401     m_pCanvas->setView(NULL);
402     m_pCanvas = NULL;
403   }
404   wxString s(theApp->GetAppName());
405   if (m_pFrame)
406     m_pFrame->SetTitle(s);
407   
408   SetFrame(NULL);
409   
410   if (deleteWindow) {
411     m_pFrame->Destroy();
412     m_pFrame = NULL;
413     if (GetDocument() && GetDocument()->getBadFileOpen())
414       ::wxYield();  // wxWindows bug workaround
415   }
416   
417   return true;
418 }
419
420 #if CTSIM_MDI
421 wxDocMDIChildFrame*
422 #else
423 wxDocChildFrame*
424 #endif
425 Graph3dFileView::CreateChildFrame (wxDocument *doc, wxView *view)
426 {
427 #if CTSIM_MDI
428   wxDocMDIChildFrame* subframe = new wxDocMDIChildFrame (doc, view, theApp->getMainFrame(), -1, "Graph3dFile Frame", wxPoint(-1, -1), wxSize(0, 0), wxDEFAULT_FRAME_STYLE);
429 #else
430   wxDocChildFrame* subframe = new wxDocChildFrame (doc, view, theApp->getMainFrame(), -1, "Graph3dFile Frame", wxPoint(-1, -1), wxSize(0, 0), wxDEFAULT_FRAME_STYLE);
431 #endif
432   theApp->setIconForFrame (subframe);
433   
434   m_pFileMenu = new wxMenu;
435   
436   m_pFileMenu->Append(MAINMENU_FILE_CREATE_PHANTOM, "Cr&eate Phantom...\tCtrl-P");
437   m_pFileMenu->Append(MAINMENU_FILE_CREATE_FILTER, "Create &Filter...\tCtrl-F");
438   m_pFileMenu->Append(wxID_OPEN, "&Open...\tCtrl-O");
439   m_pFileMenu->Append(wxID_SAVE, "&Save\tCtrl-S");
440   m_pFileMenu->Append(wxID_SAVEAS, "Save &As...");
441   m_pFileMenu->Append(wxID_CLOSE, "&Close\tCtrl-W");
442   
443   m_pFileMenu->AppendSeparator();
444   m_pFileMenu->Append(IFMENU_FILE_PROPERTIES, "P&roperties");
445   
446   m_pFileMenu->AppendSeparator();
447   m_pFileMenu->Append(wxID_PRINT, "&Print...");
448   m_pFileMenu->Append(wxID_PRINT_SETUP, "Print &Setup...");
449   m_pFileMenu->Append(wxID_PREVIEW, "Print Preview");
450 #ifdef CTSIM_MDI
451   m_pFileMenu->AppendSeparator();
452   m_pFileMenu->Append(MAINMENU_FILE_EXIT, "E&xit");
453 #endif
454   GetDocumentManager()->FileHistoryAddFilesToMenu(m_pFileMenu);
455   GetDocumentManager()->FileHistoryUseMenu(m_pFileMenu);
456   
457   wxMenu *help_menu = new wxMenu;
458   help_menu->Append(MAINMENU_HELP_CONTENTS, "&Contents\tF1");
459   help_menu->Append(MAINMENU_HELP_TOPICS, "&Topics\tCtrl-H");
460   help_menu->Append(MAINMENU_HELP_ABOUT, "&About");
461   
462   wxMenuBar *menu_bar = new wxMenuBar;
463   
464   menu_bar->Append(m_pFileMenu, "&File");
465   menu_bar->Append(help_menu, "&Help");
466   
467   subframe->SetMenuBar(menu_bar);
468   
469   subframe->Centre(wxBOTH);
470   
471   wxAcceleratorEntry accelEntries[10];
472   accelEntries[0].Set (wxACCEL_CTRL, static_cast<int>('O'), wxID_OPEN);
473   accelEntries[1].Set (wxACCEL_CTRL, static_cast<int>('S'), wxID_SAVE);
474   accelEntries[2].Set (wxACCEL_CTRL, static_cast<int>('W'), wxID_CLOSE);
475   accelEntries[3].Set (wxACCEL_CTRL, static_cast<int>('H'), MAINMENU_HELP_TOPICS);
476   accelEntries[4].Set (wxACCEL_CTRL, static_cast<int>('P'), MAINMENU_FILE_CREATE_PHANTOM);
477   accelEntries[5].Set (wxACCEL_CTRL, static_cast<int>('F'), MAINMENU_FILE_CREATE_FILTER);
478   accelEntries[6].Set (wxACCEL_NORMAL, WXK_F1, MAINMENU_HELP_CONTENTS);
479   accelEntries[7].Set (wxACCEL_CTRL, static_cast<int>('A'), IFMENU_VIEW_SCALE_AUTO);
480   accelEntries[8].Set (wxACCEL_CTRL, static_cast<int>('U'), IFMENU_VIEW_SCALE_FULL);
481   accelEntries[9].Set (wxACCEL_CTRL, static_cast<int>('E'), IFMENU_VIEW_SCALE_MINMAX);
482   wxAcceleratorTable accelTable (10, accelEntries);
483   subframe->SetAcceleratorTable (accelTable);
484   
485   return subframe;
486 }
487
488
489
490 BEGIN_EVENT_TABLE(Graph3dFileCanvas, wxGLCanvas)
491 EVT_SIZE(Graph3dFileCanvas::OnSize)
492 EVT_PAINT(Graph3dFileCanvas::OnPaint)
493 EVT_CHAR(Graph3dFileCanvas::OnChar)
494 EVT_MOUSE_EVENTS(Graph3dFileCanvas::OnMouseEvent)
495 EVT_ERASE_BACKGROUND(Graph3dFileCanvas::OnEraseBackground)
496 END_EVENT_TABLE()
497
498
499
500
501 Graph3dFileCanvas::Graph3dFileCanvas (Graph3dFileView* view, wxWindow *parent, const wxPoint& pos, 
502                                       const wxSize& size, long style, int* gl_attrib):
503 wxGLCanvas (parent, -1, pos, size, style, _T("Graph3dCanvas"), gl_attrib), m_pView(view)
504 {
505   parent->Show(TRUE);
506   SetCurrent();
507   /* Make sure server supports the vertex array extension */
508   char* extensions = (char *) glGetString( GL_EXTENSIONS );
509   if (!extensions || !strstr( extensions, "GL_EXT_vertex_array" )) {
510     m_pView->m_bUseVertexArrays = GL_FALSE;
511   }
512 }
513
514
515 Graph3dFileCanvas::~Graph3dFileCanvas(void)
516 {
517   m_pView = NULL;
518 }
519
520 void 
521 Graph3dFileCanvas::OnDraw(wxDC& dc)
522 {
523   if (m_pView)
524     m_pView->OnDraw(& dc);
525 }
526
527 void 
528 Graph3dFileCanvas::OnSize(wxSizeEvent& event)
529 {
530 #ifndef __WXMOTIF__
531   if (!GetContext()) return;
532 #endif
533   
534   SetCurrent();
535   int width, height;
536   GetClientSize (&width, &height);
537   Reshape (width, height);
538 }
539
540 void 
541 Graph3dFileCanvas::OnChar(wxKeyEvent& event)
542 {
543   if (! m_pView)
544     return;
545   
546   switch(event.KeyCode()) {
547   case WXK_ESCAPE:
548     exit(0);
549   case WXK_LEFT:
550         m_pView->m_dYRotate -= 15.0;
551     break;
552   case WXK_RIGHT:
553     m_pView->m_dYRotate += 15.0;
554     break;
555   case WXK_UP:
556     m_pView->m_dXRotate += 15.0;
557     break;
558   case WXK_DOWN:
559     m_pView->m_dXRotate -= 15.0;
560     break;
561   case 's': case 'S':
562     m_pView->m_bSmooth = !m_pView->m_bSmooth;
563     if (m_pView->m_bSmooth) {
564       glShadeModel(GL_SMOOTH);
565     } else {
566       glShadeModel(GL_FLAT);
567     }
568     break;
569   case 'l': case 'L':
570     m_pView->m_bLighting = !m_pView->m_bLighting;
571     if (m_pView->m_bLighting) {
572       glEnable(GL_LIGHTING);
573     } else {
574       glDisable(GL_LIGHTING);
575     }
576     break;
577   default:
578     {
579       event.Skip();
580       return;
581     }
582   }
583   
584   Refresh(FALSE);
585 }
586
587 void
588 Graph3dFileCanvas::Reshape (int width, int height)
589 {
590   glViewport(0, 0, (GLint)width, (GLint)height);
591 }
592
593
594 void 
595 Graph3dFileCanvas::OnMouseEvent(wxMouseEvent& event)
596 {
597   static int dragging = 0;
598   static float last_x, last_y;
599   
600   if(event.LeftIsDown()) {
601     if(!dragging) {
602       dragging = 1;
603     } else {
604       m_pView->m_dXRotate += (event.GetX() - last_x)*1.0;
605       m_pView->m_dYRotate += (event.GetY() - last_y)*1.0;
606       Refresh(FALSE);
607     }
608     last_x = event.GetX();
609     last_y = event.GetY();
610   } else
611     dragging = 0;
612 }
613
614 void 
615 Graph3dFileCanvas::OnEraseBackground(wxEraseEvent& event)
616 {
617   // Do nothing: avoid flashing.
618 }
619
620
621
622