1 /*****************************************************************************
4 ** Name: graph3dview.cpp
5 ** Purpose: 3d graph view classes
6 ** Programmer: Kevin Rosenberg
7 ** Date Started: Jan 2001
9 ** This is part of the CTSim program
10 ** Copyright (c) 1983-2001 Kevin Rosenberg
12 ** $Id: graph3dview.cpp,v 1.2 2001/01/30 10:58:13 kevin Exp $
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.
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.
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 ******************************************************************************/
30 #pragma implementation
34 // For compilers that support precompilation, includes "wx.h".
35 #include "wx/wxprec.h"
46 #error Please set wxUSE_GLCANVAS to 1 in setup.h.
50 #include "wx/glcanvas.h"
60 #include "dlgprojections.h"
61 #include "dlgreconstruct.h"
62 #include "backprojectors.h"
63 #include "reconstruct.h"
66 #if defined(MSVC) || HAVE_SSTREAM
69 #include <sstream_subst>
73 //***********************************************************************
74 // Function: CalculateVectorNormal
76 // Purpose: Given three points of a 3D plane, this function calculates
77 // the normal vector of that plane.
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).
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 //*************************************************************************
91 GLvoid CalculateVectorNormal (GLfloat fVert1[], GLfloat fVert2[],
92 GLfloat fVert3[], GLfloat *fNormalX,
93 GLfloat *fNormalY, GLfloat *fNormalZ)
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];
102 *fNormalX = Py*Qz - Pz*Qy;
103 *fNormalY = Pz*Qx - Px*Qz;
104 *fNormalZ = Px*Qy - Py*Qx;
108 IMPLEMENT_DYNAMIC_CLASS(Graph3dFileView, wxView)
110 BEGIN_EVENT_TABLE(Graph3dFileView, wxView)
111 EVT_MENU(IFMENU_FILE_PROPERTIES, Graph3dFileView::OnProperties)
114 Graph3dFileView::Graph3dFileView ()
117 m_bUseVertexArrays = GL_FALSE;
118 m_bDoubleBuffer = GL_TRUE;
120 m_bLighting = GL_TRUE;
124 Graph3dFileView::~Graph3dFileView()
126 GetDocumentManager()->FileHistoryRemoveMenu (m_pFileMenu);
127 GetDocumentManager()->ActivateView(this, FALSE, TRUE);
131 Graph3dFileView::OnCreate (wxDocument *doc, long WXUNUSED(flags) )
133 m_pFrame = CreateChildFrame(doc, this);
137 m_pFrame->GetClientSize (&width, &height);
138 m_pFrame->SetTitle("Graph3dFileView");
139 m_pCanvas = CreateCanvas (m_pFrame);
141 m_pFrame->Show(TRUE);
142 m_pCanvas->SetCurrent();
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);
157 Graph3dFileView::CreateCanvas (wxFrame* parent)
159 Graph3dFileCanvas* pCanvas;
161 parent->GetClientSize (&width, &height);
164 int *gl_attrib = NULL;
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 };
170 if(! m_bDoubleBuffer) {
176 pCanvas = new Graph3dFileCanvas (this, parent, wxPoint(0, 0), wxSize(200, 200), 0, gl_attrib);
178 pCanvas->SetBackgroundColour(*wxWHITE);
187 Graph3dFileView::DrawSurface()
189 int nVertices = GetDocument()->m_nVertices;
190 glTripleFloat* pVertices = GetDocument()->m_pVertices;
191 glTripleFloat* pNormals = GetDocument()->m_pNormals;
193 #ifdef GL_EXT_vertex_array
194 if (m_bUseVertexArrays) {
195 glDrawArraysEXT( GL_TRIANGLE_STRIP, 0, nVertices );
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);
205 for (GLint i = 0;i < nVertices; i++) {
206 // glNormal3fv( &pNormals[i] );
207 glVertex3fv( pVertices[i] );
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;
217 // glNewList(opnListNum++,GL_COMPILE);
218 for(int ix = 0; ix < nx-1; ix++){
219 for(int iy = 0; iy < ny-1; iy++){
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);
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);
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]);
234 glVertex3fv(p1); glNormal3fv(n1);
235 glVertex3fv(p2); glNormal3fv(n1);
236 glVertex3fv(p3); glNormal3fv(n1);
237 glVertex3fv(p4); glNormal3fv(n1);
245 #ifdef GL_EXT_vertex_array
252 Graph3dFileView::OnProperties (wxCommandEvent& event)
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();
263 Graph3dFileView::OnDraw (wxDC* dc)
266 if (! m_pCanvas->GetContext()) return;
270 m_pCanvas->SwapBuffers();
275 Graph3dFileView::Draw ()
277 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
279 glRotatef( m_dYRotate, 0.0, 1.0, 0.0 );
280 glRotatef( m_dXRotate, 1.0, 0.0, 0.0 );
290 Graph3dFileView::InitMaterials()
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};
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};
304 static float lmodel_ambient[] = {1.0, 1.0, 1.0, 1.0};
305 static float lmodel_twoside[] = {GL_FALSE};
307 glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
308 glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
309 glLightfv(GL_LIGHT0, GL_POSITION, position0);
312 glLightfv(GL_LIGHT1, GL_AMBIENT, ambient);
313 glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse);
314 glLightfv(GL_LIGHT1, GL_POSITION, position1);
317 glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);
318 glLightModelfv(GL_LIGHT_MODEL_TWO_SIDE, lmodel_twoside);
319 glEnable(GL_LIGHTING);
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);
328 Graph3dFileView::InitGL ()
330 glClearColor(0.0, 0.0, 0.0, 0.0);
332 glShadeModel(GL_SMOOTH);
333 glEnable(GL_DEPTH_TEST);
337 glMatrixMode(GL_PROJECTION);
339 glOrtho (-100, 100, -100, 100, -500, 500);
340 glMatrixMode(GL_MODELVIEW);
342 glTranslatef( 100, -6, 100 );
347 Graph3dFileView::OnUpdate (wxView *WXUNUSED(sender), wxObject *WXUNUSED(hint) )
349 int nVertices = GetDocument()->m_nVertices;
350 glTripleFloat* pVertices = GetDocument()->m_pVertices;
351 glTripleFloat* pNormals = GetDocument()->m_pNormals;
354 const ImageFile& rIF = GetDocument()->getImageFile();
355 ImageFileArrayConst v = rIF.getArray();
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;
369 wxImage image (nx, ny, imageData, true);
370 m_bitmap = image.ConvertToBitmap();
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);
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 );
390 m_pCanvas->Refresh();
394 Graph3dFileView::OnClose (bool deleteWindow)
396 if (! GetDocument() || ! GetDocument()->Close())
401 m_pCanvas->setView(NULL);
404 wxString s(theApp->GetAppName());
406 m_pFrame->SetTitle(s);
413 if (GetDocument() && GetDocument()->getBadFileOpen())
414 ::wxYield(); // wxWindows bug workaround
425 Graph3dFileView::CreateChildFrame (wxDocument *doc, wxView *view)
428 wxDocMDIChildFrame* subframe = new wxDocMDIChildFrame (doc, view, theApp->getMainFrame(), -1, "Graph3dFile Frame", wxPoint(-1, -1), wxSize(0, 0), wxDEFAULT_FRAME_STYLE);
430 wxDocChildFrame* subframe = new wxDocChildFrame (doc, view, theApp->getMainFrame(), -1, "Graph3dFile Frame", wxPoint(-1, -1), wxSize(0, 0), wxDEFAULT_FRAME_STYLE);
432 theApp->setIconForFrame (subframe);
434 m_pFileMenu = new wxMenu;
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");
443 m_pFileMenu->AppendSeparator();
444 m_pFileMenu->Append(IFMENU_FILE_PROPERTIES, "P&roperties");
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");
451 m_pFileMenu->AppendSeparator();
452 m_pFileMenu->Append(MAINMENU_FILE_EXIT, "E&xit");
454 GetDocumentManager()->FileHistoryAddFilesToMenu(m_pFileMenu);
455 GetDocumentManager()->FileHistoryUseMenu(m_pFileMenu);
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");
462 wxMenuBar *menu_bar = new wxMenuBar;
464 menu_bar->Append(m_pFileMenu, "&File");
465 menu_bar->Append(help_menu, "&Help");
467 subframe->SetMenuBar(menu_bar);
469 subframe->Centre(wxBOTH);
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);
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)
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)
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;
515 Graph3dFileCanvas::~Graph3dFileCanvas(void)
521 Graph3dFileCanvas::OnDraw(wxDC& dc)
524 m_pView->OnDraw(& dc);
528 Graph3dFileCanvas::OnSize(wxSizeEvent& event)
531 if (!GetContext()) return;
536 GetClientSize (&width, &height);
537 Reshape (width, height);
541 Graph3dFileCanvas::OnChar(wxKeyEvent& event)
546 switch(event.KeyCode()) {
550 m_pView->m_dYRotate -= 15.0;
553 m_pView->m_dYRotate += 15.0;
556 m_pView->m_dXRotate += 15.0;
559 m_pView->m_dXRotate -= 15.0;
562 m_pView->m_bSmooth = !m_pView->m_bSmooth;
563 if (m_pView->m_bSmooth) {
564 glShadeModel(GL_SMOOTH);
566 glShadeModel(GL_FLAT);
570 m_pView->m_bLighting = !m_pView->m_bLighting;
571 if (m_pView->m_bLighting) {
572 glEnable(GL_LIGHTING);
574 glDisable(GL_LIGHTING);
588 Graph3dFileCanvas::Reshape (int width, int height)
590 glViewport(0, 0, (GLint)width, (GLint)height);
595 Graph3dFileCanvas::OnMouseEvent(wxMouseEvent& event)
597 static int dragging = 0;
598 static float last_x, last_y;
600 if(event.LeftIsDown()) {
604 m_pView->m_dXRotate += (event.GetX() - last_x)*1.0;
605 m_pView->m_dYRotate += (event.GetY() - last_y)*1.0;
608 last_x = event.GetX();
609 last_y = event.GetY();
615 Graph3dFileCanvas::OnEraseBackground(wxEraseEvent& event)
617 // Do nothing: avoid flashing.