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.4 2001/01/31 01:01:22 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 ******************************************************************************/
29 #pragma implementation
33 // For compilers that support precompilation, includes "wx.h".
34 #include "wx/wxprec.h"
47 #error Please set wxUSE_GLCANVAS to 1 in setup.h.
51 #include "wx/glcanvas.h"
61 #include "dlgprojections.h"
62 #include "dlgreconstruct.h"
63 #include "backprojectors.h"
64 #include "reconstruct.h"
67 #if defined(MSVC) || HAVE_SSTREAM
70 #include <sstream_subst>
74 //***********************************************************************
75 // Function: CalculateVectorNormal
77 // Purpose: Given three points of a 3D plane, this function calculates
78 // the normal vector of that plane.
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).
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 //*************************************************************************
92 GLvoid CalculateVectorNormal (GLfloat fVert1[], GLfloat fVert2[],
93 GLfloat fVert3[], GLfloat *fNormalX,
94 GLfloat *fNormalY, GLfloat *fNormalZ)
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];
103 *fNormalX = Py*Qz - Pz*Qy;
104 *fNormalY = Pz*Qx - Px*Qz;
105 *fNormalZ = Px*Qy - Py*Qx;
109 IMPLEMENT_DYNAMIC_CLASS(Graph3dFileView, wxView)
111 BEGIN_EVENT_TABLE(Graph3dFileView, wxView)
112 EVT_MENU(IFMENU_FILE_PROPERTIES, Graph3dFileView::OnProperties)
115 Graph3dFileView::Graph3dFileView ()
118 m_bUseVertexArrays = GL_FALSE;
119 m_bDoubleBuffer = GL_TRUE;
121 m_bLighting = GL_TRUE;
126 Graph3dFileView::~Graph3dFileView()
128 GetDocumentManager()->FileHistoryRemoveMenu (m_pFileMenu);
129 GetDocumentManager()->ActivateView(this, FALSE, TRUE);
133 Graph3dFileView::OnCreate (wxDocument *doc, long WXUNUSED(flags) )
135 m_pFrame = CreateChildFrame(doc, this);
139 m_pFrame->GetClientSize (&width, &height);
140 m_pFrame->SetTitle("Graph3dFileView");
141 m_pCanvas = CreateCanvas (m_pFrame);
143 m_pFrame->Show(TRUE);
144 m_pCanvas->SetCurrent();
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);
159 Graph3dFileView::CreateCanvas (wxFrame* parent)
161 Graph3dFileCanvas* pCanvas;
163 parent->GetClientSize (&width, &height);
166 int *gl_attrib = NULL;
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 };
172 if(! m_bDoubleBuffer) {
178 pCanvas = new Graph3dFileCanvas (this, parent, wxPoint(0, 0), wxSize(200, 200), 0, gl_attrib);
180 pCanvas->SetBackgroundColour(*wxWHITE);
189 Graph3dFileView::DrawSurface()
191 int nVertices = GetDocument()->m_nVertices;
192 glTripleFloat* pVertices = GetDocument()->m_pVertices;
193 glTripleFloat* pNormals = GetDocument()->m_pNormals;
195 #ifdef GL_EXT_vertex_array
196 if (m_bUseVertexArrays) {
197 glDrawArraysEXT( GL_TRIANGLE_STRIP, 0, nVertices );
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)
208 double dMin = v[0][0];
211 for (ix = 0; ix < nx; ix++)
212 for (unsigned int iy = 0; iy < ny; iy++)
213 if (v[ix][iy] < dMin)
215 else if (v[ix][iy] > dMax)
218 double actOffset = dMin;
219 double actScale = 0.3 * sqrt(nx*nx+ny*ny) / (dMax - dMin);
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);
225 // glNewList(opnListNum++,GL_COMPILE);
226 for (ix = 0; ix < nx-1; ix++) {
227 for (unsigned int iy = 0; iy < ny-1; iy++) {
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);
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;
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]);
242 glVertex3fv(p1); glNormal3fv(n1);
243 glVertex3fv(p2); glNormal3fv(n1);
244 glVertex3fv(p3); glNormal3fv(n1);
245 glVertex3fv(p4); glNormal3fv(n1);
252 #ifdef GL_EXT_vertex_array
259 Graph3dFileView::OnProperties (wxCommandEvent& event)
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();
270 Graph3dFileView::OnDraw (wxDC* dc)
273 if (! m_pCanvas->GetContext()) return;
277 m_pCanvas->SwapBuffers();
282 Graph3dFileView::Draw ()
284 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
294 Graph3dFileView::InitMaterials()
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};
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};
308 static float lmodel_ambient[] = {1.0, 1.0, 1.0, 1.0};
309 static float lmodel_twoside[] = {GL_FALSE};
311 glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
312 glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
313 glLightfv(GL_LIGHT0, GL_POSITION, position0);
316 glLightfv(GL_LIGHT1, GL_AMBIENT, ambient);
317 glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse);
318 glLightfv(GL_LIGHT1, GL_POSITION, position1);
321 glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);
322 glLightModelfv(GL_LIGHT_MODEL_TWO_SIDE, lmodel_twoside);
323 glEnable(GL_LIGHTING);
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);
332 Graph3dFileView::InitGL ()
334 glClearColor(0.0, 0.0, 0.0, 0.0);
336 glShadeModel(GL_SMOOTH);
337 glEnable(GL_DEPTH_TEST);
341 glMatrixMode(GL_PROJECTION);
343 glOrtho (-300, 300, -300, 300, 200, -200);
344 glMatrixMode(GL_MODELVIEW);
350 Graph3dFileView::OnUpdate (wxView *WXUNUSED(sender), wxObject *WXUNUSED(hint) )
352 int nVertices = GetDocument()->m_nVertices;
353 glTripleFloat* pVertices = GetDocument()->m_pVertices;
354 glTripleFloat* pNormals = GetDocument()->m_pNormals;
357 const ImageFile& rIF = GetDocument()->getImageFile();
358 ImageFileArrayConst v = rIF.getArray();
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;
372 wxImage image (nx, ny, imageData, true);
373 m_bitmap = image.ConvertToBitmap();
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);
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 );
393 m_pCanvas->Refresh();
397 Graph3dFileView::OnClose (bool deleteWindow)
399 if (! GetDocument() || ! GetDocument()->Close())
404 m_pCanvas->setView(NULL);
407 wxString s(theApp->GetAppName());
409 m_pFrame->SetTitle(s);
416 if (GetDocument() && GetDocument()->getBadFileOpen())
417 ::wxYield(); // wxWindows bug workaround
428 Graph3dFileView::CreateChildFrame (wxDocument *doc, wxView *view)
431 wxDocMDIChildFrame* subframe = new wxDocMDIChildFrame (doc, view, theApp->getMainFrame(), -1, "Graph3dFile Frame", wxPoint(-1, -1), wxSize(0, 0), wxDEFAULT_FRAME_STYLE);
433 wxDocChildFrame* subframe = new wxDocChildFrame (doc, view, theApp->getMainFrame(), -1, "Graph3dFile Frame", wxPoint(-1, -1), wxSize(0, 0), wxDEFAULT_FRAME_STYLE);
435 theApp->setIconForFrame (subframe);
437 m_pFileMenu = new wxMenu;
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");
446 m_pFileMenu->AppendSeparator();
447 m_pFileMenu->Append(IFMENU_FILE_PROPERTIES, "P&roperties");
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");
454 m_pFileMenu->AppendSeparator();
455 m_pFileMenu->Append(MAINMENU_FILE_EXIT, "E&xit");
457 GetDocumentManager()->FileHistoryAddFilesToMenu(m_pFileMenu);
458 GetDocumentManager()->FileHistoryUseMenu(m_pFileMenu);
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");
465 wxMenuBar *menu_bar = new wxMenuBar;
467 menu_bar->Append(m_pFileMenu, "&File");
468 menu_bar->Append(help_menu, "&Help");
470 subframe->SetMenuBar(menu_bar);
472 subframe->Centre(wxBOTH);
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);
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)
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)
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;
518 Graph3dFileCanvas::~Graph3dFileCanvas(void)
524 Graph3dFileCanvas::OnDraw(wxDC& dc)
527 m_pView->OnDraw(& dc);
531 Graph3dFileCanvas::OnSize(wxSizeEvent& event)
534 if (!GetContext()) return;
539 GetClientSize (&width, &height);
540 Reshape (width, height);
544 Graph3dFileCanvas::OnChar(wxKeyEvent& event)
549 switch(event.KeyCode()) {
551 m_pView->m_dYRotate -= 15.0;
554 m_pView->m_dYRotate += 15.0;
557 m_pView->m_dXRotate += 15.0;
560 m_pView->m_dXRotate -= 15.0;
563 m_pView->m_bSmooth = !m_pView->m_bSmooth;
564 if (m_pView->m_bSmooth) {
565 glShadeModel(GL_SMOOTH);
567 glShadeModel(GL_FLAT);
571 m_pView->m_bLighting = !m_pView->m_bLighting;
572 if (m_pView->m_bLighting) {
573 glEnable(GL_LIGHTING);
575 glDisable(GL_LIGHTING);
589 Graph3dFileCanvas::Reshape (int width, int height)
591 glViewport (0, 0, (GLint)width, (GLint)height);
596 Graph3dFileCanvas::OnMouseEvent(wxMouseEvent& event)
598 static int dragging = 0;
599 static float last_x, last_y;
601 if(event.LeftIsDown()) {
605 m_pView->m_dXRotate += (event.GetX() - last_x)*1.0;
606 m_pView->m_dYRotate += (event.GetY() - last_y)*1.0;
609 last_x = event.GetX();
610 last_y = event.GetY();
616 Graph3dFileCanvas::OnEraseBackground(wxEraseEvent& event)
618 // Do nothing: avoid flashing.
624 #endif // wxUSE_GLCANVAS