00001 /*************************************************************************** 00002 vuCellProjector.cpp - description 00003 ------------------- 00004 begin : Thu May 1 2003 00005 copyright : (C) 2003 by tmeng 00006 email : tmeng@sfu.ca 00007 ***************************************************************************/ 00008 00009 /*************************************************************************** 00010 * * 00011 * This program is free software; you can redistribute it and/or modify * 00012 * it under the terms of the GNU General Public License as published by * 00013 * the Free Software Foundation; either version 2 of the License, or * 00014 * (at your option) any later version. * 00015 * * 00016 ***************************************************************************/ 00017 00018 #include "vuCellProjector.h" 00019 #include "../../wxUIElements/vuGLCanvas.h" 00020 #include "../../wxUIElements/vuTFDialogSpec.h" 00021 #include "vuColourRGBa.h" 00022 00023 //---------------------------------------------------------------------------- 00024 00025 enum IDEnum //for the event table and wx controls initilization. 00026 { 00027 idCheckBoxIsSort, 00028 idListBoxSubdivScheme, 00029 idButtonRender 00030 }; 00031 00032 //---------------------------------------------------------------------------- 00033 //----------------- The vuCellProjector event table -------------------------- 00034 //---------------------------------------------------------------------------- 00035 00036 //Message map, very similar to MFC. 00037 BEGIN_EVENT_TABLE(vuCellProjector, vuBasicUtility) 00038 EVT_CHECKBOX(idCheckBoxIsSort, vuCellProjector::onCheckBoxIsSort) 00039 EVT_LISTBOX(idListBoxSubdivScheme, vuCellProjector::onListBoxSubdivScheme) 00040 EVT_BUTTON(idButtonRender, vuCellProjector::onButtonRender) 00041 END_EVENT_TABLE(); 00042 00043 //---------------------------------------------------------------------------- 00044 //------------------------- The constructor ---------------------------------- 00045 //---------------------------------------------------------------------------- 00046 00047 vuCellProjector::vuCellProjector() 00048 : vuBasicUtility(), 00049 m_Data(new vu111211a), //default cell projector. 00050 m_ViewScale(1.0f), //Default zooming is 100% 00051 m_TFunc(4), //4 means RGBA (4 channels) 00052 m_TFDialog(this, m_TFunc), //construct transfer function dialog. 00053 m_x(0), m_y(0) //sensible values before actual mouse clicks. 00054 { 00055 } 00056 00057 //---------------------------------------------------------------------------- 00058 //------------------------- The destructor ----------------------------------- 00059 //---------------------------------------------------------------------------- 00060 00061 /*virtual*/ vuCellProjector::~vuCellProjector() 00062 { 00063 if (m_Data != 0) delete m_Data; 00064 } 00065 00066 //---------------------------------------------------------------------------- 00067 //------------------------- public: getFileType() ---------------------------- 00068 //---------------------------------------------------------------------------- 00069 00070 /*static*/ const char* vuCellProjector::getFileType() 00071 { 00072 return "11121"; 00073 } 00074 00075 //---------------------------------------------------------------------------- 00076 //------------------------- public: addRight() ------------------------------- 00077 //---------------------------------------------------------------------------- 00078 00079 00080 /*virtual*/ void vuCellProjector::addRight(wxSizer* sizer) 00081 { 00082 //top alignment by default. 00083 addCheckBoxIsSort(sizer); 00084 addListBoxSubdivScheme(sizer); 00085 addButtonRender(sizer); 00086 } 00087 00088 //---------------------------------------------------------------------------- 00089 //------------------------- public: init() ----------------------------------- 00090 //---------------------------------------------------------------------------- 00091 00092 /*virtual*/ bool vuCellProjector::init(const char* DataFile) 00093 //Initializes the cellProjector utility. 00094 //Initializes the utility window. A cellProjector object is created and 00095 //the volume data is read. The window appears when finished. 00096 { 00097 //Set up the window for the cellProjectorter. 00098 SetTitle("vuCellProjector"); 00099 CreateStatusBar(); //shows render time. 00100 00101 //Create a volume data instance. 00102 // m_Data = new vu111211a; //allocate space for a cell projector. 00103 m_Data->setFileName(DataFile); //will project the volume data in the specified file. 00104 00105 //see readme.txt within vuVolume/TFunc for more info 00106 m_TFunc.loadTF("TFunc/default.tf"); 00107 m_TFunc.setOpacitySmoothing(0); 00108 m_TFunc.setColourSmoothing(1); 00109 m_TFunc.generateFunction(); 00110 m_Data->setTransferFunc(m_TFunc); 00111 //the CellProjector (not the utility window) now has a transfer function. 00112 00113 //Read in the data. 00114 bool success = m_Data->read(); 00115 if (success) 00116 { 00117 m_glCanvas->SetSize(512,512); //default utility size. 00118 Fit(); //likely a wxWindows command. 00119 } 00120 else 00121 { 00122 //Show the error message. 00123 wxMessageDialog dlg(this, m_Data->getErrorMessage(), "vuCellProjector",wxOK); 00124 dlg.ShowModal(); 00125 } 00126 00127 //Not sure if false should be returned anywhere, since as I recall in MFC 00128 //false is never returned in init() either. So may have to look back to this later. 00129 return success; //initialization successful. 00130 }; 00131 00132 //---------------------------------------------------------------------------- 00133 //------------------------- public: notifyDataChanged() ---------------------- 00134 //---------------------------------------------------------------------------- 00135 00136 /*virtual*/ void vuCellProjector::notifyDataChanged() 00137 //Acts kind of like Invalidate() in MFC, which does a redraw. 00138 { 00139 m_glCanvas->redraw(); 00140 } 00141 00142 //---------------------------------------------------------------------------- 00143 //------------------------- public: DrawAgain() ------------------------------ 00144 //---------------------------------------------------------------------------- 00145 00146 /*virtual*/ void vuCellProjector::DrawAgain() 00147 //Rerenders the screen from the current camera position. 00148 { 00149 m_glCanvas->redraw(); 00150 } 00151 00152 //---------------------------------------------------------------------------- 00153 //------------------------- public: DrawFromImage() -------------------------- 00154 //---------------------------------------------------------------------------- 00155 00156 /*virtual*/ void vuCellProjector::DrawFromImage() 00157 //Draws on the screen the image contained in the image buffer. 00158 //Basically, it's a flush() function. 00159 { 00160 m_Data->drawPic(); 00161 } 00162 00163 //---------------------------------------------------------------------------- 00164 //------------------------- public: getCurrentImage() ------------------------ 00165 //---------------------------------------------------------------------------- 00166 00167 /*virtual*/ vuImage* vuCellProjector::getCurrentImage() 00168 //This will return a pointer to the image buffer. 00169 { 00170 return m_Data->getBuffer (); 00171 } 00172 00173 //---------------------------------------------------------------------------- 00174 //------------------------- public: getCamera() ------------------------------ 00175 //---------------------------------------------------------------------------- 00176 00177 /*virtual*/ vuCamera* vuCellProjector::getCamera() 00178 //This will return a pointer to the camera that this class is using 00179 //(note that this may be derived from vuCamera...) 00180 { 00181 return &m_Camera; 00182 } 00183 00184 //---------------------------------------------------------------------------- 00185 //------------------------- public: IsReRendering() -------------------------- 00186 //---------------------------------------------------------------------------- 00187 00188 /*virtual*/ bool vuCellProjector::IsReRendering() 00189 //This will return whether or not the cellProjector is rerendering to the screen. 00190 { 00191 return m_Data->IsReRendering (); 00192 } 00193 00194 //---------------------------------------------------------------------------- 00195 //------------------------- public: setIsReRendering() ----------------------- 00196 //---------------------------------------------------------------------------- 00197 00198 /*virtual*/ void vuCellProjector::setIsReRendering (bool isit) 00199 //This will set the state of rerendering to be the same as "isit". 00200 { 00201 m_Data->setIsReRendering(isit); 00202 } 00203 00204 //---------------------------------------------------------------------------- 00205 //------------------------- protected: glInit() ------------------------------ 00206 //---------------------------------------------------------------------------- 00207 00208 /*virtual*/ bool vuCellProjector::glInit() 00209 //Initializes openGL for the cellProjector utility. 00210 //The method calls the CellProjector initGL() method so it can do the 00211 //proper initialization. 00212 { 00213 if (m_Data == 0) return false; 00214 00215 //Paint a black background to the utility window. 00216 const GLfloat RED = 0.0f; 00217 const GLfloat GREEN = 0.0f; 00218 const GLfloat BLUE = 0.0f; 00219 const GLfloat ALPHA = 1.0f; 00220 glClearColor(RED, GREEN, BLUE, ALPHA); 00221 00222 //Enable one light for now. 00223 glEnable(GL_LIGHTING); 00224 glEnable(GL_LIGHT0); 00225 00226 //Enable lighting calculation for backfaces as well. 00227 glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE); 00228 00229 //Call CellProjector::initGL() which does initilization particular 00230 //to the CellProjector algorithm. 00231 m_Data->initOpenGL(); 00232 return true; 00233 }; 00234 00235 //---------------------------------------------------------------------------- 00236 //------------------------- protected: glRender() ---------------------------- 00237 //---------------------------------------------------------------------------- 00238 00239 /*virtual*/ void vuCellProjector::glRender() 00240 { 00241 wxStopWatch watch; //used to time how long it takes to render each frame. 00242 watch.Start(); 00243 00244 //Clears the color buffer. 00245 glClear(GL_COLOR_BUFFER_BIT); 00246 00247 glMatrixMode(GL_MODELVIEW); 00248 glLoadIdentity(); 00249 00250 m_Camera.gluLookAt(); //applied to the model view matrix. 00251 00252 glTranslatef((float)m_Data->getDim1Size()/(-2.0f), 00253 (float)m_Data->getDim2Size()/(-2.0f), 00254 (float)m_Data->getDim3Size()/(-2.0f)); 00255 00256 m_Data->setViewVectors(m_Camera.getLookAtVector(), 00257 m_Camera.getUpVector(), 00258 m_Camera.getRightVector()); 00259 00260 //This is not necessary every frame, but done anyways, 00261 //because the non-modal tfuncDlg might have changed it. 00262 //The overhead is negligible so it's ok to keep. 00263 m_Data->setTransferFunc(m_TFunc); 00264 00265 //Render the volume. 00266 m_Data->render(); 00267 00268 //print out the time it took to render the frame onto the status bar. 00269 watch.Pause(); 00270 SetStatusText(wxString("Render Time: ") + vuString(watch.Time()).c_str() + "ms"); 00271 }; 00272 00273 //---------------------------------------------------------------------------- 00274 //------------------------- protected: glResize() ---------------------------- 00275 //---------------------------------------------------------------------------- 00276 00277 /*virtual*/ void vuCellProjector::glResize() 00278 { 00279 //Retrieves the new width and height of the window from the window itself. 00280 GLint newWidth = (GLint)m_glCanvas->getWidth(); 00281 GLint newHeight = (GLint)m_glCanvas->getHeight(); 00282 00283 //Calculate an aspect ratio. 00284 GLfloat aspect_ratio = ((float) newWidth) / ((float) newHeight); 00285 00286 //Set the viewport(left, bottom, width, height). 00287 glViewport(0, 0, newWidth, newHeight); 00288 00289 //Find the largest dimension of the data, so it fits inside the window. 00290 dword max = m_Data->getDim1Size(); 00291 if (m_Data->getDim2Size() > max) 00292 max = m_Data->getDim2Size(); 00293 if (m_Data->getDim3Size() > max) 00294 max = m_Data->getDim3Size(); 00295 00296 //Switch the openGL projection matrix, required before calling glOrtho. 00297 glMatrixMode(GL_PROJECTION); 00298 glLoadIdentity(); //reset the projection matrix. 00299 00300 //The following if/else loop gets rid of the horizontal or vertical 00301 //distortions associated with stretching the window. 00302 if(newHeight >= newWidth) 00303 //This means aspect_ratio <= 1.0 00304 //Output image stretched along y direction, so need to divide top and 00305 //bottom by aspect_ratio (i.e. need more "slots" in the y direction to 00306 //not distort image) 00307 { 00308 //Sets up orthographic projection matrix and defines a viewing volume 00309 //that is a right parallelepiped. 00310 //glOrtho(left, right, bottom, top, near, far) 00311 glOrtho((float)max/(-1.0f*m_ViewScale), 00312 (float)max/m_ViewScale, 00313 (float)max/aspect_ratio/(-1.0f*m_ViewScale), 00314 (float)max/aspect_ratio/m_ViewScale, 00315 10000.0f, -10000.0f); 00316 //10000 SHOULD be large enough, data sets that are larger 00317 //probably can't be rendered in available memory anyway. 00318 } 00319 else 00320 //This means aspect_ratio > 1.0 00321 //Output image stretched along x direction, so need to divide left and 00322 //right by aspect_ratio (i.e. need more "slots" in the x direction to 00323 //not distort image) 00324 { 00325 glOrtho((float)max*aspect_ratio/(-1.0f*m_ViewScale), 00326 (float)max*aspect_ratio/m_ViewScale, 00327 (float)max/(-1.0f*m_ViewScale), 00328 (float)max/m_ViewScale, 00329 10000.0f, -10000.0f); 00330 00331 } 00332 00333 //Return to the model view matrix (for manipulating objects). 00334 glMatrixMode(GL_MODELVIEW); 00335 glLoadIdentity(); //this line probably can be removed. 00336 00337 //Set the opengl light info. 00338 //float lpos[4] = {0.0, 0.0, 1024.0, 1.0}; 00339 //glLightfv(GL_LIGHT0, GL_POSITION, lpos); 00340 } 00341 00342 //---------------------------------------------------------------------------- 00343 //------------------------- protected: onMouse() ----------------------------- 00344 //---------------------------------------------------------------------------- 00345 00346 /*virtual*/ void vuCellProjector::glOnMouse(wxMouseEvent &ev) 00347 //Eventually, this could extend vuBasicUtility::glOnMouse(event). 00348 //For now, I have followed what everyone else has done. 00349 { 00350 //vuBasicUtility::glOnMouse(ev); 00351 //extend the parent function, which deals with rotation and zooming 00352 //already. 00353 00354 if (ev.LeftDown() || ev.RightDown()) 00355 { 00356 //Store the click position. 00357 m_x = (int) ev.GetX(); 00358 m_y = (int) ev.GetY(); 00359 } 00360 else if (ev.LeftIsDown() && ev.Moving()) 00361 //Rotate the camera (same as rotating the volume) 00362 { 00363 //get the camera location. 00364 vuVector t = m_Camera.getPosition(); 00365 00366 //get distance from camera to origin. Same as d = (t-origin).norm() 00367 float d = t.norm(); 00368 00369 m_Camera.translateXYZ(0.0f, 0.0f, d); 00370 m_Camera.rotateAboutUp(ev.GetX() - m_x); 00371 m_Camera.rotateAboutRight(ev.GetY() - m_y); 00372 m_Camera.translateXYZ(0.0f, 0.0f, -d); 00373 m_glCanvas->redraw(); 00374 00375 //Store the click position. 00376 m_x = (int) ev.GetX(); 00377 m_y = (int) ev.GetY(); 00378 } 00379 else if (ev.RightIsDown() && ev.Moving()) 00380 //Zooming. 00381 { 00382 m_ViewScale -= ((float)ev.GetY() - m_y)/500.0f; 00383 this->glResize(); 00384 m_glCanvas->redraw(); 00385 00386 //Store the click position. 00387 m_x = (int) ev.GetX(); 00388 m_y = (int) ev.GetY(); 00389 } 00390 else if (ev.LeftDClick()) 00391 //Pop up the transfer function editor. 00392 { 00393 m_TFDialog.Show(true); 00394 } 00395 } 00396 00397 //---------------------------------------------------------------------------- 00398 //------------------------- protected: getVolume() --------------------------- 00399 //---------------------------------------------------------------------------- 00400 00401 /*virtual*/ vu1* vuCellProjector::getVolume() 00402 // Returns a parent pointer to the current algorithm. 00403 { 00404 return m_Data; 00405 //m_Data is a pointer to the current algorithm, and vu1* is a parent 00406 //pointer so the upcasting is auto. 00407 } 00408 00409 //---------------------------------------------------------------------------- 00410 //------------------------- protected: OnChar() ------------------------------ 00411 //---------------------------------------------------------------------------- 00412 00413 /*virtual*/ void vuCellProjector::OnChar(wxKeyEvent& event) 00414 { 00415 //vuBasicUtility::OnChar(event); //chain to parent. 00416 //the above function does not work right now, CellProjector has both 00417 //getVolume() and getCameraPtr() but does not work, not quite sure why. 00418 //should look into it later. 00419 00420 dword dataSize = m_Data->m_DataSize; 00421 char key = event.GetKeyCode(); 00422 if (dataSize == 8) 00423 //Possibly enable manual display of tets. 00424 { 00425 switch(key) 00426 { 00427 case 'm': //manual tet enable 00428 m_Data->m_state = MANUAL_TET; 00429 break; 00430 case 'n': //normal (default) 00431 m_Data->m_state = NORMAL; 00432 break; 00433 }; 00434 if (key >= '0' && key <= '9') 00435 //if 8 data points, then only tets avail for display. 00436 //if 12 data points, 10 tets avail for display 00437 { 00438 m_Data->m_tetIndex = key - '0'; 00439 //the above line has been tested with cout statements 00440 if (m_Data->m_subdivScheme == SIX_FOLD && m_Data->m_tetIndex > 5) 00441 { 00442 m_Data->m_tetIndex = 5; //clamp, else out of bound index. 00443 cout<<"tet index: "<<m_Data->m_tetIndex<<endl; 00444 } 00445 } 00446 } 00447 //the following line seems to call glRender() from somewhere 00448 //already so no need to call glRender() again. 00449 this->DrawAgain(); //or this->notifyDataChanged(); 00450 } 00451 00452 //---------------------------------------------------------------------------- 00453 //--------------- protected: onCheckBoxIsSort() ------------------------------ 00454 //---------------------------------------------------------------------------- 00455 00456 #if wxMINOR_VERSION < 5 00457 void vuCellProjector::onCheckBoxIsSort() 00458 #else 00459 void vuCellProjector::onCheckBoxIsSort(wxCommandEvent&) 00460 #endif 00461 { 00462 m_Data->m_isSort = (bool) m_checkBoxIsSort->GetValue(); 00463 this->notifyDataChanged(); 00464 } 00465 00466 //---------------------------------------------------------------------------- 00467 //--------------- protected: onListBoxSubdivScheme() ------------------------- 00468 //---------------------------------------------------------------------------- 00469 00470 #if wxMINOR_VERSION < 5 00471 void vuCellProjector::onListBoxSubdivScheme() 00472 #else 00473 void vuCellProjector::onListBoxSubdivScheme(wxCommandEvent&) 00474 #endif 00475 { 00476 m_Data->m_subdivScheme = (SubdivEnum) m_listBoxSubdivScheme->GetSelection(); 00477 this->notifyDataChanged(); 00478 } 00479 00480 //---------------------------------------------------------------------------- 00481 //--------------- protected: onButtonRender() -------------------------------- 00482 //---------------------------------------------------------------------------- 00483 00484 #if wxMINOR_VERSION < 5 00485 void vuCellProjector::onButtonRender() 00486 #else 00487 void vuCellProjector::onButtonRender(wxCommandEvent&) 00488 #endif 00489 { 00490 m_Data->m_isRender = true; 00491 this->notifyDataChanged(); 00492 } 00493 00494 //---------------------------------------------------------------------------- 00495 //--------------- protected: addCheckBoxIsSort() ----------------------------- 00496 //---------------------------------------------------------------------------- 00497 00498 void vuCellProjector::addCheckBoxIsSort(wxSizer* sizer) 00499 { 00500 m_checkBoxIsSort = new wxCheckBox(this, //parent window pointer 00501 idCheckBoxIsSort, //unique control id 00502 "Sort and Triangulate?"); 00503 m_checkBoxIsSort->SetValue(m_Data->m_isSort); 00504 00505 //if no following line, the checkbox will show up at the top left hand 00506 //corner of the utility window. 00507 sizer->Add(m_checkBoxIsSort, //check box added to right panel (sizer) 00508 0, //not stretcheable 00509 wxALL | wxALIGN_LEFT, //wxALL->all 4 borders, 00510 1); //border width = 1 00511 } 00512 00513 //---------------------------------------------------------------------------- 00514 //--------------- protected: addListBoxSubdivScheme() ------------------------ 00515 //---------------------------------------------------------------------------- 00516 00517 void vuCellProjector::addListBoxSubdivScheme(wxSizer* sizer) 00518 { 00519 char scheme1[] = "5-Fold"; 00520 char scheme2[] = "6-Fold"; 00521 wxString listBoxEntries[] = {scheme1, scheme2}; 00522 m_listBoxSubdivScheme = 00523 new wxListBox(this, //parent window pointer 00524 idListBoxSubdivScheme, //unique id 00525 wxDefaultPosition, 00526 wxSize(150,70), //arbitrary, in pixels 00527 2, //2 entries initially 00528 listBoxEntries, 00529 wxLB_SINGLE | wxLB_ALWAYS_SB); 00530 //can only select one entry at once and always show vert scrolling bar. 00531 00532 m_listBoxSubdivScheme->SetSelection((int)(m_Data->m_subdivScheme), 00533 TRUE); //select subdiv scheme. 00534 00535 sizer->Add(m_listBoxSubdivScheme , //check box added to right panel (sizer) 00536 0, //not stretcheable 00537 wxALL | wxALIGN_LEFT, //wxALL->all 4 borders, 00538 1); //border width = 1 00539 } 00540 00541 //---------------------------------------------------------------------------- 00542 //--------------- protected: addButtonRender() ------------------------------- 00543 //---------------------------------------------------------------------------- 00544 00545 void vuCellProjector::addButtonRender(wxSizer* sizer) 00546 { 00547 sizer->Add(new wxButton(this, idButtonRender, "Render"), //ptr to button 00548 0, //unstretcheable 00549 wxALL | wxALIGN_LEFT, //all borders 00550 10); //border width = 10. 00551 }