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.3 2001/01/30 13:47:46 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;
125 Graph3dFileView::~Graph3dFileView()
127 GetDocumentManager()->FileHistoryRemoveMenu (m_pFileMenu);
128 GetDocumentManager()->ActivateView(this, FALSE, TRUE);
132 Graph3dFileView::OnCreate (wxDocument *doc, long WXUNUSED(flags) )
134 m_pFrame = CreateChildFrame(doc, this);
138 m_pFrame->GetClientSize (&width, &height);
139 m_pFrame->SetTitle("Graph3dFileView");
140 m_pCanvas = CreateCanvas (m_pFrame);
142 m_pFrame->Show(TRUE);
143 m_pCanvas->SetCurrent();
147 int x, y; // X requires a forced resize
148 m_pFrame->GetSize(&x, &y);
149 m_pFrame->SetSize(-1, -1, x, y);
150 m_pFrame->SetFocus();
151 m_pFrame->Show(true);
158 Graph3dFileView::CreateCanvas (wxFrame* parent)
160 Graph3dFileCanvas* pCanvas;
162 parent->GetClientSize (&width, &height);
165 int *gl_attrib = NULL;
167 int gl_attrib[20] = { GLX_RGBA, GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1,
168 GLX_BLUE_SIZE, 1, GLX_DEPTH_SIZE, 1, GLX_DOUBLEBUFFER, None };
171 if(! m_bDoubleBuffer) {
177 pCanvas = new Graph3dFileCanvas (this, parent, wxPoint(0, 0), wxSize(200, 200), 0, gl_attrib);
179 pCanvas->SetBackgroundColour(*wxWHITE);
188 Graph3dFileView::DrawSurface()
190 int nVertices = GetDocument()->m_nVertices;
191 glTripleFloat* pVertices = GetDocument()->m_pVertices;
192 glTripleFloat* pNormals = GetDocument()->m_pNormals;
194 #ifdef GL_EXT_vertex_array
195 if (m_bUseVertexArrays) {
196 glDrawArraysEXT( GL_TRIANGLE_STRIP, 0, nVertices );
201 unsigned int nx = GetDocument()->m_nx;
202 unsigned int ny = GetDocument()->m_ny;
203 const ImageFileArray v = GetDocument()->m_array;
204 if (nx == 0 || ny == 0 || ! v)
207 double dMin = v[0][0];
210 for (ix = 0; ix < nx; ix++)
211 for (unsigned int iy = 0; iy < ny; iy++)
212 if (v[ix][iy] < dMin)
214 else if (v[ix][iy] > dMax)
217 double actOffset = dMin;
218 double actScale = 0.3 * sqrt(nx*nx+ny*ny) / (dMax - dMin);
220 glRotatef( m_dYRotate, 0.0, 0.0, 1.0 );
221 glRotatef( m_dXRotate, 1.0, 0.0, 0.0 );
222 glTranslatef (-static_cast<double>(nx) / 2, -static_cast<double>(ny) / 2, 0);
224 // glNewList(opnListNum++,GL_COMPILE);
225 for (ix = 0; ix < nx-1; ix++) {
226 for (unsigned int iy = 0; iy < ny-1; iy++) {
228 float p1[3], p2[3], p3[3], p4[3];
229 float n1[3], n2[3], n3[3], n4[3];
230 glBegin(GL_LINE_LOOP);
232 p1[0] = ix; p1[1] = actScale * (v[ix][iy] + actOffset); p1[2] = iy;
233 p2[0] = ix+1; p2[1] = actScale * (v[ix+1][iy] + actOffset); p2[2] = iy;
234 p3[0] = ix+1; p3[1] = actScale * (v[ix+1][iy+1] + actOffset); p3[2] = iy;
235 p4[0] = ix; p4[1] = actScale * (v[ix][iy+1] + actOffset); p4[2] = iy;
237 n1[0] = -(p2[1] - p1[1])*(p3[2] - p1[2]) + (p2[2] - p1[2])*(p3[1] - p2[1]);
238 n1[1] = -(p2[2] - p1[2])*(p3[0] - p2[0]) + (p2[0] - p1[0])*(p3[2] - p2[2]);
239 n1[2] = -(p2[0] - p1[0])*(p3[1] - p2[1]) + (p2[1] - p1[1])*(p3[0] - p2[0]);
241 glVertex3fv(p1); glNormal3fv(n1);
242 glVertex3fv(p2); glNormal3fv(n1);
243 glVertex3fv(p3); glNormal3fv(n1);
244 glVertex3fv(p4); glNormal3fv(n1);
251 #ifdef GL_EXT_vertex_array
258 Graph3dFileView::OnProperties (wxCommandEvent& event)
260 std::ostringstream os;
261 *theApp->getLog() << ">>>>\n" << os.str().c_str() << "<<<<\n";
262 wxMessageDialog dialogMsg (getFrameForChild(), os.str().c_str(), "Imagefile Properties", wxOK | wxICON_INFORMATION);
263 dialogMsg.ShowModal();
269 Graph3dFileView::OnDraw (wxDC* dc)
272 if (! m_pCanvas->GetContext()) return;
276 m_pCanvas->SwapBuffers();
281 Graph3dFileView::Draw ()
283 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
293 Graph3dFileView::InitMaterials()
295 static float ambient[] = {0.1, 0.1, 0.1, 1.0};
296 static float diffuse[] = {0.5, 1.0, 1.0, 1.0};
297 static float position0[] = {0.0, 0.0, 20.0, 0.0};
298 static float position1[] = {0.0, 0.0, -20.0, 0.0};
299 static float front_mat_shininess[] = {60.0};
300 static float front_mat_specular[] = {0.2, 0.2, 0.2, 1.0};
301 static float front_mat_diffuse[] = {0.5, 0.28, 0.38, 1.0};
303 static float back_mat_shininess[] = {60.0};
304 static float back_mat_specular[] = {0.5, 0.5, 0.2, 1.0};
305 static float back_mat_diffuse[] = {1.0, 1.0, 0.2, 1.0};
307 static float lmodel_ambient[] = {1.0, 1.0, 1.0, 1.0};
308 static float lmodel_twoside[] = {GL_FALSE};
310 glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
311 glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
312 glLightfv(GL_LIGHT0, GL_POSITION, position0);
315 glLightfv(GL_LIGHT1, GL_AMBIENT, ambient);
316 glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse);
317 glLightfv(GL_LIGHT1, GL_POSITION, position1);
320 glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);
321 glLightModelfv(GL_LIGHT_MODEL_TWO_SIDE, lmodel_twoside);
322 glEnable(GL_LIGHTING);
324 glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, front_mat_shininess);
325 glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, front_mat_specular);
326 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, front_mat_diffuse);
331 Graph3dFileView::InitGL ()
333 glClearColor(0.0, 0.0, 0.0, 0.0);
335 glShadeModel(GL_SMOOTH);
336 glEnable(GL_DEPTH_TEST);
340 glMatrixMode(GL_PROJECTION);
342 glOrtho (-300, 300, -300, 300, 200, -200);
343 glMatrixMode(GL_MODELVIEW);
349 Graph3dFileView::OnUpdate (wxView *WXUNUSED(sender), wxObject *WXUNUSED(hint) )
351 int nVertices = GetDocument()->m_nVertices;
352 glTripleFloat* pVertices = GetDocument()->m_pVertices;
353 glTripleFloat* pNormals = GetDocument()->m_pNormals;
356 const ImageFile& rIF = GetDocument()->getImageFile();
357 ImageFileArrayConst v = rIF.getArray();
360 if (v != NULL && nx != 0 && ny != 0) {
361 unsigned char* imageData = new unsigned char [nx * ny * 3];
362 for (int ix = 0; ix < nx; ix++) {
363 for (int iy = 0; iy < ny; iy++) {
364 double scaleValue = ((v[ix][iy] - m_dMinPixel) / scaleWidth) * 255;
365 int intensity = static_cast<int>(scaleValue + 0.5);
366 intensity = clamp (intensity, 0, 255);
367 int baseAddr = ((ny - 1 - iy) * nx + ix) * 3;
368 imageData[baseAddr] = imageData[baseAddr+1] = imageData[baseAddr+2] = intensity;
371 wxImage image (nx, ny, imageData, true);
372 m_bitmap = image.ConvertToBitmap();
376 ySize = clamp (ySize, 0, 800);
377 m_pFrame->SetClientSize (xSize, ySize);
378 m_pCanvas->SetScrollbars(20, 20, nx/20, ny/20);
379 m_pCanvas->SetBackgroundColour(*wxWHITE);
383 #ifdef GL_EXT_vertex_array
384 if (m_bUseVertexArrays) {
385 // glVertexPointerEXT( 3, GL_FLOAT, 0, nVertices, pVertices );
386 // glNormalPointerEXT( GL_FLOAT, 0, nVertices, pNormals );
387 glEnable( GL_VERTEX_ARRAY_EXT );
388 glEnable( GL_NORMAL_ARRAY_EXT );
392 m_pCanvas->Refresh();
396 Graph3dFileView::OnClose (bool deleteWindow)
398 if (! GetDocument() || ! GetDocument()->Close())
403 m_pCanvas->setView(NULL);
406 wxString s(theApp->GetAppName());
408 m_pFrame->SetTitle(s);
415 if (GetDocument() && GetDocument()->getBadFileOpen())
416 ::wxYield(); // wxWindows bug workaround
427 Graph3dFileView::CreateChildFrame (wxDocument *doc, wxView *view)
430 wxDocMDIChildFrame* subframe = new wxDocMDIChildFrame (doc, view, theApp->getMainFrame(), -1, "Graph3dFile Frame", wxPoint(-1, -1), wxSize(0, 0), wxDEFAULT_FRAME_STYLE);
432 wxDocChildFrame* subframe = new wxDocChildFrame (doc, view, theApp->getMainFrame(), -1, "Graph3dFile Frame", wxPoint(-1, -1), wxSize(0, 0), wxDEFAULT_FRAME_STYLE);
434 theApp->setIconForFrame (subframe);
436 m_pFileMenu = new wxMenu;
438 m_pFileMenu->Append(MAINMENU_FILE_CREATE_PHANTOM, "Cr&eate Phantom...\tCtrl-P");
439 m_pFileMenu->Append(MAINMENU_FILE_CREATE_FILTER, "Create &Filter...\tCtrl-F");
440 m_pFileMenu->Append(wxID_OPEN, "&Open...\tCtrl-O");
441 m_pFileMenu->Append(wxID_SAVE, "&Save\tCtrl-S");
442 m_pFileMenu->Append(wxID_SAVEAS, "Save &As...");
443 m_pFileMenu->Append(wxID_CLOSE, "&Close\tCtrl-W");
445 m_pFileMenu->AppendSeparator();
446 m_pFileMenu->Append(IFMENU_FILE_PROPERTIES, "P&roperties");
448 m_pFileMenu->AppendSeparator();
449 m_pFileMenu->Append(wxID_PRINT, "&Print...");
450 m_pFileMenu->Append(wxID_PRINT_SETUP, "Print &Setup...");
451 m_pFileMenu->Append(wxID_PREVIEW, "Print Preview");
453 m_pFileMenu->AppendSeparator();
454 m_pFileMenu->Append(MAINMENU_FILE_EXIT, "E&xit");
456 GetDocumentManager()->FileHistoryAddFilesToMenu(m_pFileMenu);
457 GetDocumentManager()->FileHistoryUseMenu(m_pFileMenu);
459 wxMenu *help_menu = new wxMenu;
460 help_menu->Append(MAINMENU_HELP_CONTENTS, "&Contents\tF1");
461 help_menu->Append(MAINMENU_HELP_TOPICS, "&Topics\tCtrl-H");
462 help_menu->Append(MAINMENU_HELP_ABOUT, "&About");
464 wxMenuBar *menu_bar = new wxMenuBar;
466 menu_bar->Append(m_pFileMenu, "&File");
467 menu_bar->Append(help_menu, "&Help");
469 subframe->SetMenuBar(menu_bar);
471 subframe->Centre(wxBOTH);
473 wxAcceleratorEntry accelEntries[10];
474 accelEntries[0].Set (wxACCEL_CTRL, static_cast<int>('O'), wxID_OPEN);
475 accelEntries[1].Set (wxACCEL_CTRL, static_cast<int>('S'), wxID_SAVE);
476 accelEntries[2].Set (wxACCEL_CTRL, static_cast<int>('W'), wxID_CLOSE);
477 accelEntries[3].Set (wxACCEL_CTRL, static_cast<int>('H'), MAINMENU_HELP_TOPICS);
478 accelEntries[4].Set (wxACCEL_CTRL, static_cast<int>('P'), MAINMENU_FILE_CREATE_PHANTOM);
479 accelEntries[5].Set (wxACCEL_CTRL, static_cast<int>('F'), MAINMENU_FILE_CREATE_FILTER);
480 accelEntries[6].Set (wxACCEL_NORMAL, WXK_F1, MAINMENU_HELP_CONTENTS);
481 accelEntries[7].Set (wxACCEL_CTRL, static_cast<int>('A'), IFMENU_VIEW_SCALE_AUTO);
482 accelEntries[8].Set (wxACCEL_CTRL, static_cast<int>('U'), IFMENU_VIEW_SCALE_FULL);
483 accelEntries[9].Set (wxACCEL_CTRL, static_cast<int>('E'), IFMENU_VIEW_SCALE_MINMAX);
484 wxAcceleratorTable accelTable (10, accelEntries);
485 subframe->SetAcceleratorTable (accelTable);
492 BEGIN_EVENT_TABLE(Graph3dFileCanvas, wxGLCanvas)
493 EVT_SIZE(Graph3dFileCanvas::OnSize)
494 EVT_PAINT(Graph3dFileCanvas::OnPaint)
495 EVT_CHAR(Graph3dFileCanvas::OnChar)
496 EVT_MOUSE_EVENTS(Graph3dFileCanvas::OnMouseEvent)
497 EVT_ERASE_BACKGROUND(Graph3dFileCanvas::OnEraseBackground)
503 Graph3dFileCanvas::Graph3dFileCanvas (Graph3dFileView* view, wxWindow *parent, const wxPoint& pos,
504 const wxSize& size, long style, int* gl_attrib):
505 wxGLCanvas (parent, -1, pos, size, style, _T("Graph3dCanvas"), gl_attrib), m_pView(view)
509 /* Make sure server supports the vertex array extension */
510 char* extensions = (char *) glGetString( GL_EXTENSIONS );
511 if (!extensions || !strstr( extensions, "GL_EXT_vertex_array" )) {
512 m_pView->m_bUseVertexArrays = GL_FALSE;
517 Graph3dFileCanvas::~Graph3dFileCanvas(void)
523 Graph3dFileCanvas::OnDraw(wxDC& dc)
526 m_pView->OnDraw(& dc);
530 Graph3dFileCanvas::OnSize(wxSizeEvent& event)
533 if (!GetContext()) return;
538 GetClientSize (&width, &height);
539 Reshape (width, height);
543 Graph3dFileCanvas::OnChar(wxKeyEvent& event)
548 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.