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