00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #include <iostream>
00027 #include <fstream>
00028
00029
00030 #include <octovis/ViewerGui.h>
00031 #include <octovis/ColorOcTreeDrawer.h>
00032 #include <octomap/MapCollection.h>
00033
00034
00035 #define _MAXRANGE_URG 5.1
00036 #define _MAXRANGE_SICK 50.0
00037
00038 namespace octomap{
00039
00040 ViewerGui::ViewerGui(const std::string& filename, QWidget *parent)
00041 : QMainWindow(parent), m_scanGraph(NULL),
00042 m_trajectoryDrawer(NULL), m_pointcloudDrawer(NULL),
00043 m_cameraFollowMode(NULL),
00044 m_octreeResolution(0.1), m_laserMaxRange(-1.), m_occupancyThresh(0.5),
00045 m_max_tree_depth(16), m_laserType(LASERTYPE_SICK),
00046 m_cameraStored(false), m_filename("") {
00047
00048 ui.setupUi(this);
00049 m_glwidget = new ViewerWidget(this);
00050 this->setCentralWidget(m_glwidget);
00051
00052
00053 ViewerSettingsPanel* settingsPanel = new ViewerSettingsPanel(this);
00054 QDockWidget* settingsDock = new QDockWidget("Octree / Scan graph settings", this);
00055 settingsDock->setWidget(settingsPanel);
00056 this->addDockWidget(Qt::RightDockWidgetArea, settingsDock);
00057 ui.menuShow->addAction(settingsDock->toggleViewAction());
00058
00059
00060 ViewerSettingsPanelCamera* settingsCameraPanel = new ViewerSettingsPanelCamera(this);
00061 QDockWidget *settingsCameraDock = new QDockWidget("Camera settings", this);
00062 settingsCameraDock->setWidget(settingsCameraPanel);
00063 this->addDockWidget(Qt::RightDockWidgetArea, settingsCameraDock);
00064 this->tabifyDockWidget(settingsDock, settingsCameraDock);
00065 settingsDock->raise();
00066 ui.menuShow->addAction(settingsCameraDock->toggleViewAction());
00067
00068
00069 m_mapSizeStatus = new QLabel("Map size", this);
00070 m_mapMemoryStatus = new QLabel("Memory consumption", this);
00071 m_mapSizeStatus->setFrameStyle(QFrame::Panel | QFrame::Sunken);
00072 m_mapMemoryStatus->setFrameStyle(QFrame::Panel | QFrame::Sunken);
00073 statusBar()->addPermanentWidget(m_mapSizeStatus);
00074 statusBar()->addPermanentWidget(m_mapMemoryStatus);
00075
00076 m_cameraFollowMode = new CameraFollowMode();
00077
00078 connect(this, SIGNAL(updateStatusBar(QString, int)), statusBar(), SLOT(showMessage(QString, int)));
00079
00080 connect(settingsPanel, SIGNAL(treeDepthChanged(int)), this, SLOT(changeTreeDepth(int)));
00081 connect(settingsPanel, SIGNAL(addNextScans(unsigned)), this, SLOT(addNextScans(unsigned)));
00082 connect(settingsPanel, SIGNAL(gotoFirstScan()), this, SLOT(gotoFirstScan()));
00083
00084 connect(settingsCameraPanel, SIGNAL(jumpToFrame(unsigned)), m_cameraFollowMode, SLOT(jumpToFrame(unsigned)));
00085 connect(settingsCameraPanel, SIGNAL(play()), m_cameraFollowMode, SLOT(play()));
00086 connect(settingsCameraPanel, SIGNAL(pause()), m_cameraFollowMode, SLOT(pause()));
00087 connect(settingsCameraPanel, SIGNAL(clearCameraPath()), m_cameraFollowMode, SLOT(clearCameraPath()));
00088 connect(settingsCameraPanel, SIGNAL(saveToCameraPath()), m_cameraFollowMode, SLOT(saveToCameraPath()));
00089 connect(settingsCameraPanel, SIGNAL(removeFromCameraPath()), m_cameraFollowMode, SLOT(removeFromCameraPath()));
00090 connect(settingsCameraPanel, SIGNAL(addToCameraPath()), m_cameraFollowMode, SLOT(addToCameraPath()));
00091 connect(settingsCameraPanel, SIGNAL(followCameraPath()), m_cameraFollowMode, SLOT(followCameraPath()));
00092 connect(settingsCameraPanel, SIGNAL(followRobotPath()), m_cameraFollowMode, SLOT(followRobotPath()));
00093
00094 connect(m_cameraFollowMode, SIGNAL(changeNumberOfFrames(unsigned)), settingsCameraPanel, SLOT(setNumberOfFrames(unsigned)));
00095 connect(m_cameraFollowMode, SIGNAL(frameChanged(unsigned)), settingsCameraPanel, SLOT(setCurrentFrame(unsigned)));
00096 connect(m_cameraFollowMode, SIGNAL(stopped()), settingsCameraPanel, SLOT(setStopped()));
00097 connect(m_cameraFollowMode, SIGNAL(scanGraphAvailable(bool)), settingsCameraPanel, SLOT(setRobotTrajectoryAvailable(bool)));
00098
00099 connect(m_cameraFollowMode, SIGNAL(deleteCameraPath(int)), m_glwidget, SLOT(deleteCameraPath(int)));
00100 connect(m_cameraFollowMode, SIGNAL(removeFromCameraPath(int,int)), m_glwidget, SLOT(removeFromCameraPath(int,int)));
00101 connect(m_cameraFollowMode, SIGNAL(appendToCameraPath(int, const octomath::Pose6D&)), m_glwidget, SLOT(appendToCameraPath(int, const octomath::Pose6D&)));
00102 connect(m_cameraFollowMode, SIGNAL(appendCurrentToCameraPath(int)), m_glwidget, SLOT(appendCurrentToCameraPath(int)));
00103 connect(m_cameraFollowMode, SIGNAL(addCurrentToCameraPath(int,int)), m_glwidget, SLOT(addCurrentToCameraPath(int,int)));
00104 connect(m_cameraFollowMode, SIGNAL(updateCameraPath(int,int)), m_glwidget, SLOT(updateCameraPath(int,int)));
00105 connect(m_cameraFollowMode, SIGNAL(playCameraPath(int,int)), m_glwidget, SLOT(playCameraPath(int,int)));
00106 connect(m_cameraFollowMode, SIGNAL(stopCameraPath(int)), m_glwidget, SLOT(stopCameraPath(int)));
00107 connect(m_cameraFollowMode, SIGNAL(jumpToCamFrame(int, int)), m_glwidget, SLOT(jumpToCamFrame(int, int)));
00108 connect(m_glwidget, SIGNAL(cameraPathStopped(int)), m_cameraFollowMode, SLOT(cameraPathStopped(int)));
00109 connect(m_glwidget, SIGNAL(cameraPathFrameChanged(int, int)), m_cameraFollowMode, SLOT(cameraPathFrameChanged(int, int)));
00110
00111 connect(this, SIGNAL(changeNumberOfScans(unsigned)), settingsPanel, SLOT(setNumberOfScans(unsigned)));
00112 connect(this, SIGNAL(changeCurrentScan(unsigned)), settingsPanel, SLOT(setCurrentScan(unsigned)));
00113 connect(this, SIGNAL(changeResolution(double)), settingsPanel, SLOT(setResolution(double)));
00114
00115 connect(settingsCameraPanel, SIGNAL(changeCamPosition(double, double, double, double, double, double)),
00116 m_glwidget, SLOT(setCamPosition(double, double, double, double, double, double)));
00117 connect(m_cameraFollowMode, SIGNAL(changeCamPose(const octomath::Pose6D&)),
00118 m_glwidget, SLOT(setCamPose(const octomath::Pose6D&)));
00119
00120 connect(ui.actionReset_view, SIGNAL(triggered()), m_glwidget, SLOT(resetView()));
00121
00122 if (filename != ""){
00123 m_filename = filename;
00124 openFile();
00125 }
00126 }
00127
00128 ViewerGui::~ViewerGui() {
00129 if (m_trajectoryDrawer){
00130 m_glwidget->removeSceneObject(m_trajectoryDrawer);
00131 delete m_trajectoryDrawer;
00132 m_trajectoryDrawer = NULL;
00133 }
00134
00135 if (m_pointcloudDrawer){
00136 m_glwidget->removeSceneObject(m_pointcloudDrawer);
00137 delete m_pointcloudDrawer;
00138 m_pointcloudDrawer = NULL;
00139 }
00140
00141 for (std::map<int, OcTreeRecord>::iterator it = m_octrees.begin(); it != m_octrees.end(); ++it) {
00142 m_glwidget->removeSceneObject(it->second.octree_drawer);
00143 delete (it->second.octree_drawer);
00144 delete (it->second.octree);
00145 }
00146 m_octrees.clear();
00147
00148 if(m_cameraFollowMode) {
00149 delete m_cameraFollowMode;
00150 m_cameraFollowMode = NULL;
00151 }
00152 }
00153
00154 bool ViewerGui::isShown() {
00155 return m_glwidget->isVisible();
00156 }
00157
00158 void ViewerGui::showInfo(QString string, bool newline) {
00159 std::cerr << string.toLocal8Bit().data();
00160 if (newline) std::cerr << std::endl;
00161 else std::cerr << std::flush;
00162 int duration = 0;
00163 if (newline)
00164 duration = 3000;
00165 emit updateStatusBar(string, duration);
00166 }
00167
00168 bool ViewerGui::getOctreeRecord(int id, OcTreeRecord*& otr) {
00169 std::map<int, OcTreeRecord>::iterator it = m_octrees.find(id);
00170 if( it != m_octrees.end() ) {
00171 otr = &(it->second);
00172 return true;
00173 }
00174 else {
00175 return false;
00176 }
00177 }
00178
00179 void ViewerGui::addOctree(octomap::AbstractOcTree* tree, int id, octomap::pose6d origin) {
00180
00181 OcTreeRecord* r;
00182 bool foundRecord = getOctreeRecord(id, r);
00183 if (foundRecord && r->octree->getTreeType().compare(tree->getTreeType()) !=0){
00184
00185 delete r->octree_drawer;
00186 if (dynamic_cast<OcTree*>(tree)) {
00187 r->octree_drawer = new OcTreeDrawer();
00188
00189 }
00190 else if (dynamic_cast<ColorOcTree*>(tree)) {
00191 r->octree_drawer = new ColorOcTreeDrawer();
00192 } else{
00193 OCTOMAP_ERROR("Could not create drawer for tree type %s\n", tree->getTreeType().c_str());
00194 }
00195
00196 delete r->octree;
00197 r->octree = tree;
00198 r->origin = origin;
00199
00200 } else if (foundRecord && r->octree->getTreeType().compare(tree->getTreeType()) ==0) {
00201
00202
00203 delete r->octree;
00204 r->octree = tree;
00205 r->origin = origin;
00206 } else {
00207
00208 OcTreeRecord otr;
00209 otr.id = id;
00210 if (dynamic_cast<OcTree*>(tree)) {
00211 otr.octree_drawer = new OcTreeDrawer();
00212
00213 }
00214 else if (dynamic_cast<ColorOcTree*>(tree)) {
00215 otr.octree_drawer = new ColorOcTreeDrawer();
00216 } else{
00217 OCTOMAP_ERROR("Could not create drawer for tree type %s\n", tree->getTreeType().c_str());
00218 }
00219 otr.octree = tree;
00220 otr.origin = origin;
00221 m_octrees[id] = otr;
00222 m_glwidget->addSceneObject(otr.octree_drawer);
00223 }
00224 }
00225
00226 void ViewerGui::addOctree(octomap::AbstractOcTree* tree, int id) {
00227 octomap::pose6d o;
00228 addOctree(tree, id, o);
00229 }
00230
00231 void ViewerGui::showOcTree() {
00232
00233
00234 double minX, minY, minZ, maxX, maxY, maxZ;
00235 minX = minY = minZ = -10;
00236 maxX = maxY = maxZ = 10;
00237 double sizeX, sizeY, sizeZ;
00238 sizeX = sizeY = sizeZ = 0.;
00239 size_t memoryUsage = 0;
00240 size_t num_nodes = 0;
00241 size_t memorySingleNode = 0;
00242
00243
00244 for (std::map<int, OcTreeRecord>::iterator it = m_octrees.begin(); it != m_octrees.end(); ++it) {
00245
00246 double lminX, lminY, lminZ, lmaxX, lmaxY, lmaxZ;
00247 it->second.octree->getMetricMin(lminX, lminY, lminZ);
00248 it->second.octree->getMetricMax(lmaxX, lmaxY, lmaxZ);
00249
00250 octomap::point3d pmin(lminX, lminY, lminZ);
00251 octomap::point3d pmax(lmaxX, lmaxY, lmaxZ);
00252 pmin = it->second.origin.transform(pmin);
00253 pmax = it->second.origin.transform(pmax);
00254 lminX = pmin.x(); lminY = pmin.y(); lminZ = pmin.z();
00255 lmaxX = pmax.x(); lmaxY = pmax.y(); lmaxZ = pmax.z();
00256
00257 if (lminX < minX) minX = lminX;
00258 if (lminY < minY) minY = lminY;
00259 if (lminZ < minZ) minZ = lminZ;
00260 if (lmaxX > maxX) maxX = lmaxX;
00261 if (lmaxY > maxY) maxY = lmaxY;
00262 if (lmaxZ > maxZ) maxZ = lmaxZ;
00263 double lsizeX, lsizeY, lsizeZ;
00264
00265 it->second.octree->getMetricSize(lsizeX, lsizeY, lsizeZ);
00266 if (lsizeX > sizeX) sizeX = lsizeX;
00267 if (lsizeY > sizeY) sizeY = lsizeY;
00268 if (lsizeZ > sizeZ) sizeZ = lsizeZ;
00269 memoryUsage += it->second.octree->memoryUsage();
00270 num_nodes += it->second.octree->size();
00271 memorySingleNode = std::max(memorySingleNode, it->second.octree->memoryUsageNode());
00272 }
00273
00274 m_glwidget->setSceneBoundingBox(qglviewer::Vec(minX, minY, minZ), qglviewer::Vec(maxX, maxY, maxZ));
00275
00276
00277 QString size = QString("%L1 x %L2 x %L3 m^3; %L4 nodes").arg(sizeX).arg(sizeY).arg(sizeZ).arg(unsigned(num_nodes));
00278 QString memory = QString("Single node: %L1 B; ").arg(memorySingleNode)
00279 + QString ("Octree: %L1 B (%L2 MB)").arg(memoryUsage).arg((double) memoryUsage/(1024.*1024.), 0, 'f', 3);
00280 m_mapMemoryStatus->setText(memory);
00281 m_mapSizeStatus->setText(size);
00282
00283
00284 m_glwidget->updateGL();
00285
00286
00287
00288
00289
00290 for (std::map<int, OcTreeRecord>::iterator it = m_octrees.begin(); it != m_octrees.end(); ++it) {
00291 it->second.octree_drawer->setMax_tree_depth(m_max_tree_depth);
00292 it->second.octree_drawer->setOcTree(*it->second.octree, it->second.origin, it->second.id);
00293 }
00294
00295
00296
00297 m_glwidget->updateGL();
00298 }
00299
00300
00301 void ViewerGui::generateOctree() {
00302
00303 if (m_scanGraph) {
00304
00305 QApplication::setOverrideCursor(Qt::WaitCursor);
00306
00307 showInfo("Generating OcTree... ");
00308 std::cerr << std::endl;
00309
00310
00311 OcTree* tree = new octomap::OcTree(m_octreeResolution);
00312
00313 octomap::ScanGraph::iterator it;
00314 unsigned numScans = m_scanGraph->size();
00315 unsigned currentScan = 1;
00316 for (it = m_scanGraph->begin(); it != m_nextScanToAdd; it++) {
00317 tree->insertPointCloud(**it, m_laserMaxRange);
00318 fprintf(stderr, "generateOctree:: inserting scan node with %d points, origin: %.2f ,%.2f , %.2f.\n",
00319 (unsigned int) (*it)->scan->size(), (*it)->pose.x(), (*it)->pose.y(), (*it)->pose.z() );
00320
00321 std::cout << " S ("<<currentScan<<"/"<<numScans<<") " << std::flush;
00322 currentScan++;
00323 }
00324
00325 this->addOctree(tree, DEFAULT_OCTREE_ID);
00326 this->showOcTree();
00327
00328 showInfo("Done.", true);
00329 QApplication::restoreOverrideCursor();
00330 }
00331 else {
00332 std::cerr << "generateOctree called but no ScanGraph present!\n";
00333 }
00334
00335 }
00336
00337
00338
00339 void ViewerGui::gotoFirstScan(){
00340 if (m_scanGraph){
00341 showInfo("Inserting first scan node into tree... ", true);
00342 QApplication::setOverrideCursor(Qt::WaitCursor);
00343
00344 m_nextScanToAdd = m_scanGraph->begin();
00345
00346
00347
00348 OcTree* tree = new octomap::OcTree(m_octreeResolution);
00349 this->addOctree(tree, DEFAULT_OCTREE_ID);
00350
00351 addNextScan();
00352
00353 QApplication::restoreOverrideCursor();
00354 showOcTree();
00355 }
00356 }
00357
00358 void ViewerGui::addNextScan(){
00359
00360 if (m_scanGraph){
00361 showInfo("Inserting next scan node into tree... ", true);
00362
00363 QApplication::setOverrideCursor(Qt::WaitCursor);
00364 if (m_nextScanToAdd != m_scanGraph->end()){
00365 OcTreeRecord* r;
00366 if (!getOctreeRecord(DEFAULT_OCTREE_ID, r)) {
00367 fprintf(stderr, "ERROR: OctreeRecord for id %d not found!\n", DEFAULT_OCTREE_ID);
00368 return;
00369 }
00370
00371 ((OcTree*) r->octree)->insertPointCloud(**m_nextScanToAdd, m_laserMaxRange);
00372 m_nextScanToAdd++;
00373 }
00374
00375 QApplication::restoreOverrideCursor();
00376 showOcTree();
00377
00378 }
00379 }
00380
00381
00382 void ViewerGui::addNextScans(unsigned scans){
00383 for (unsigned i = 0; i < scans; ++i){
00384 addNextScan();
00385 }
00386 }
00387
00388
00389
00390
00391 void ViewerGui::openFile(){
00392 if (!m_filename.empty()){
00393 m_glwidget->clearAll();
00394
00395 QString temp = QString(m_filename.c_str());
00396 QFileInfo fileinfo(temp);
00397 if (fileinfo.suffix() == "graph"){
00398 openGraph();
00399 }else if (fileinfo.suffix() == "bt"){
00400 openTree();
00401 }
00402 else if (fileinfo.suffix() == "ot")
00403 {
00404 openOcTree();
00405 }
00406 else if (fileinfo.suffix() == "hot"){
00407 openMapCollection();
00408 }
00409 else if (fileinfo.suffix() == "dat"){
00410 openPointcloud();
00411 }
00412 else {
00413 QMessageBox::warning(this, "Unknown file", "Cannot open file, unknown extension: "+fileinfo.suffix(), QMessageBox::Ok);
00414 }
00415 }
00416 }
00417
00418
00419 void ViewerGui::openGraph(bool completeGraph){
00420
00421 QApplication::setOverrideCursor(Qt::WaitCursor);
00422 showInfo("Loading scan graph from file " + QString(m_filename.c_str()) );
00423
00424 if (m_scanGraph) delete m_scanGraph;
00425 m_scanGraph = new octomap::ScanGraph();
00426 m_scanGraph->readBinary(m_filename);
00427
00428 loadGraph(completeGraph);
00429 }
00430
00431
00432 void ViewerGui::openPointcloud(){
00433
00434 QApplication::setOverrideCursor(Qt::WaitCursor);
00435 showInfo("Loading ASCII pointcloud from file "+QString(m_filename.c_str()) + "...");
00436
00437 if (m_scanGraph) delete m_scanGraph;
00438 m_scanGraph = new octomap::ScanGraph();
00439
00440
00441
00442 std::ifstream s(m_filename.c_str());
00443 Pointcloud* pc = new Pointcloud();
00444
00445 if (!s) {
00446 std::cout <<"ERROR: could not read " << m_filename << std::endl;
00447 return;
00448 }
00449
00450 pc->read(s);
00451
00452 pose6d laser_pose(0,0,0,0,0,0);
00453 m_scanGraph->addNode(pc, laser_pose);
00454
00455 loadGraph(true);
00456 showInfo("Done.", true);
00457 }
00458
00459
00460 void ViewerGui::setOcTreeUISwitches() {
00461 ui.actionPointcloud->setChecked(false);
00462 ui.actionPointcloud->setEnabled(false);
00463 ui.actionOctree_cells->setChecked(true);
00464 ui.actionOctree_cells->setEnabled(true);
00465 ui.actionFree->setChecked(false);
00466 ui.actionFree->setEnabled(true);
00467 ui.actionOctree_structure->setEnabled(true);
00468 ui.actionOctree_structure->setChecked(false);
00469 ui.actionTrajectory->setEnabled(false);
00470 ui.actionConvert_ml_tree->setEnabled(true);
00471 ui.actionReload_Octree->setEnabled(true);
00472 ui.actionSettings->setEnabled(false);
00473 }
00474
00475 void ViewerGui::openTree(){
00476 OcTree* tree = new octomap::OcTree(m_filename);
00477 this->addOctree(tree, DEFAULT_OCTREE_ID);
00478
00479 m_octreeResolution = tree->getResolution();
00480 emit changeResolution(m_octreeResolution);
00481
00482 setOcTreeUISwitches();
00483 showOcTree();
00484 m_glwidget->resetView();
00485 }
00486
00487 void ViewerGui::openOcTree(){
00488 AbstractOcTree* tree = AbstractOcTree::read(m_filename);
00489
00490 if (tree){
00491 this->addOctree(tree, DEFAULT_OCTREE_ID);
00492
00493 m_octreeResolution = tree->getResolution();
00494 emit changeResolution(m_octreeResolution);
00495
00496 setOcTreeUISwitches();
00497 showOcTree();
00498 m_glwidget->resetView();
00499
00500 if (tree->getTreeType() == "ColorOcTree"){
00501
00502 ui.actionHeight_map->setText ("Map color");
00503 this->on_actionHeight_map_toggled(true);
00504 ui.actionHeight_map->setChecked(true);
00505 }
00506 }
00507 else {
00508 QMessageBox::warning(this, "File error", "Cannot open OcTree file", QMessageBox::Ok);
00509 }
00510 }
00511
00512
00513
00514 void ViewerGui::openMapCollection() {
00515
00516 OCTOMAP_DEBUG("Opening hierarchy from %s...\n", m_filename.c_str());
00517
00518 std::ifstream infile(m_filename.c_str(), std::ios_base::in |std::ios_base::binary);
00519 if (!infile.is_open()) {
00520 QMessageBox::warning(this, "File error", "Cannot open OcTree file", QMessageBox::Ok);
00521 return;
00522 }
00523 infile.close();
00524
00525 MapCollection<MapNode<OcTree> > collection(m_filename);
00526 int i=0;
00527 for (MapCollection<MapNode<OcTree> >::iterator it = collection.begin();
00528 it != collection.end(); ++it) {
00529 OCTOMAP_DEBUG("Adding hierarchy node %s\n", (*it)->getId().c_str());
00530 OcTree* tree = (*it)->getMap();
00531 if (!tree)
00532 OCTOMAP_ERROR("Error while reading node %s\n", (*it)->getId().c_str());
00533 else {
00534 OCTOMAP_DEBUG("Read tree with %zu tree nodes\n", tree->size());
00535 }
00536 pose6d origin = (*it)->getOrigin();
00537 this->addOctree(tree, i, origin);
00538 ++i;
00539 }
00540 setOcTreeUISwitches();
00541 showOcTree();
00542 m_glwidget->resetView();
00543 OCTOMAP_DEBUG("done\n");
00544 }
00545
00546 void ViewerGui::loadGraph(bool completeGraph) {
00547
00548 ui.actionSettings->setEnabled(true);
00549 ui.actionPointcloud->setEnabled(true);
00550 ui.actionPointcloud->setChecked(false);
00551 ui.actionTrajectory->setEnabled(true);
00552 ui.actionOctree_cells->setEnabled(true);
00553 ui.actionOctree_cells->setChecked(true);
00554 ui.actionOctree_structure->setEnabled(true);
00555 ui.actionOctree_structure->setChecked(false);
00556 ui.actionFree->setChecked(false);
00557 ui.actionFree->setEnabled(true);
00558 ui.actionReload_Octree->setEnabled(true);
00559 ui.actionConvert_ml_tree->setEnabled(true);
00560
00561 unsigned graphSize = m_scanGraph->size();
00562 unsigned currentScan;
00563
00564 if (completeGraph){
00565 fprintf(stderr, "loadGraph:: generating octree from complete graph.\n" );
00566 m_nextScanToAdd = m_scanGraph->end();
00567 generateOctree();
00568 currentScan = graphSize;
00569 }
00570 else{
00571 m_nextScanToAdd = m_scanGraph->begin();
00572
00573
00574
00575 OcTree* tree = new octomap::OcTree(m_octreeResolution);
00576 this->addOctree(tree, DEFAULT_OCTREE_ID);
00577
00578 addNextScan();
00579
00580 currentScan = 1;
00581 }
00582
00583 m_glwidget->resetView();
00584 QApplication::restoreOverrideCursor();
00585
00586 emit changeNumberOfScans(graphSize);
00587 emit changeCurrentScan(currentScan);
00588 showInfo("Done (" +QString::number(currentScan)+ " of "+ QString::number(graphSize)+" nodes)", true);
00589
00590 if (!m_trajectoryDrawer){
00591 m_trajectoryDrawer = new TrajectoryDrawer();
00592 }
00593 m_trajectoryDrawer->setScanGraph(*m_scanGraph);
00594
00595 if (!m_pointcloudDrawer){
00596 m_pointcloudDrawer = new PointcloudDrawer();
00597 }
00598 m_pointcloudDrawer->setScanGraph(*m_scanGraph);
00599
00600 m_cameraFollowMode->setScanGraph(m_scanGraph);
00601
00602 if (ui.actionTrajectory->isChecked())
00603 m_glwidget->addSceneObject(m_trajectoryDrawer);
00604
00605 if (ui.actionPointcloud->isChecked())
00606 m_glwidget->addSceneObject(m_pointcloudDrawer);
00607 }
00608
00609 void ViewerGui::changeTreeDepth(int depth){
00610
00611 if (depth < 1 || depth > 16)
00612 return;
00613
00614 m_max_tree_depth = unsigned(depth);
00615
00616 if (m_octrees.size() > 0)
00617 showOcTree();
00618 }
00619
00620
00621 void ViewerGui::on_actionExit_triggered(){
00622 this->close();
00623 }
00624
00625 void ViewerGui::on_actionHelp_triggered(){
00626 m_glwidget->help();
00627 }
00628
00629 void ViewerGui::on_actionSettings_triggered(){
00630
00631 ViewerSettings dialog(this);
00632 dialog.setResolution(m_octreeResolution);
00633 dialog.setLaserType(m_laserType);
00634 dialog.setMaxRange(m_laserMaxRange);
00635
00636
00637 if (dialog.exec()){
00638
00639 double oldResolution = m_octreeResolution;
00640 double oldLaserMaxRange = m_laserMaxRange;
00641 double oldType = m_laserType;
00642
00643 m_octreeResolution = dialog.getResolution();
00644 m_laserType = dialog.getLaserType();
00645 m_laserMaxRange = dialog.getMaxRange();
00646
00647
00648 bool resolutionChanged = (std::abs(oldResolution - m_octreeResolution) > 1e-5);
00649 bool maxRangeChanged = (std::abs(oldLaserMaxRange - m_laserMaxRange) > 1e-5);
00650
00651 if (resolutionChanged)
00652 emit changeResolution(m_octreeResolution);
00653
00654 if (oldType != m_laserType){
00655 openFile();
00656 } else if (resolutionChanged || maxRangeChanged){
00657 generateOctree();
00658 }
00659
00660 }
00661 }
00662
00663 void ViewerGui::on_actionOpen_file_triggered(){
00664 QString filename = QFileDialog::getOpenFileName(this,
00665 tr("Open data file"), "",
00666 "All supported files (*.graph *.bt *.ot *.dat);;OcTree file (*.ot);;Bonsai tree file (*.bt);;Binary scan graph (*.graph);;Pointcloud (*.dat);;All files (*)");
00667 if (!filename.isEmpty()){
00668 #ifdef _WIN32
00669 m_filename = std::string(filename.toLocal8Bit().data());
00670 #else
00671 m_filename = filename.toStdString();
00672 #endif
00673 openFile();
00674 }
00675 }
00676
00677
00678 void ViewerGui::on_actionOpen_graph_incremental_triggered(){
00679 QString filename = QFileDialog::getOpenFileName(this,
00680 tr("Open graph file incrementally (at start)"), "",
00681 "Binary scan graph (*.graph)");
00682 if (!filename.isEmpty()){
00683 m_glwidget->clearAll();
00684
00685 #ifdef _WIN32
00686 m_filename = std::string(filename.toLocal8Bit().data());
00687 #else
00688 m_filename = filename.toStdString();
00689 #endif
00690
00691 openGraph(false);
00692 }
00693 }
00694
00695 void ViewerGui::on_actionSave_file_triggered(){
00696
00697 OcTreeRecord* r;
00698 if (!getOctreeRecord(DEFAULT_OCTREE_ID, r)) {
00699 fprintf(stderr, "ERROR: OctreeRecord for id %d not found!\n", DEFAULT_OCTREE_ID);
00700 QMessageBox::warning(this, tr("3D Mapping Viewer"),
00701 "Error: No OcTree present.",
00702 QMessageBox::Ok);
00703 return;
00704 }
00705
00706 QString filename = QFileDialog::getSaveFileName(this, tr("Save octree file"),
00707 "", tr("Full OcTree (*.ot);;Bonsai Tree file (*.bt);;"));
00708
00709 if (filename != ""){
00710 QApplication::setOverrideCursor(Qt::WaitCursor);
00711 showInfo("Writing file... ", false);
00712
00713 QFileInfo fileinfo(filename);
00714 std::string std_filename;
00715 #ifdef _WIN32
00716 std_filename = filename.toLocal8Bit().data();
00717 #else
00718 std_filename = filename.toStdString();
00719 #endif
00720
00721 AbstractOcTree* t = r->octree;
00722
00723 if (fileinfo.suffix() == "bt") {
00724 AbstractOccupancyOcTree* ot = dynamic_cast<AbstractOccupancyOcTree*> (t);
00725 if (ot)
00726 ot->writeBinaryConst(std_filename);
00727 else{
00728 QMessageBox::warning(this, "Unknown tree type",
00729 "Could not convert to occupancy tree for writing .bt file",
00730 QMessageBox::Ok);
00731 }
00732 }
00733 else if (fileinfo.suffix() == "ot"){
00734 r->octree->write(std_filename);
00735 }
00736 else {
00737 QMessageBox::warning(this, "Unknown file",
00738 "Cannot write file, unknown extension: "+fileinfo.suffix(),
00739 QMessageBox::Ok);
00740 }
00741
00742 QApplication::restoreOverrideCursor();
00743 showInfo("Done.", true);
00744 }
00745 }
00746
00747 void ViewerGui::on_actionClear_nodes_in_selection_triggered(){
00748 point3d min, max;
00749 m_glwidget->selectionBox().getBBXMin(min.x(), min.y(), min.z());
00750 m_glwidget->selectionBox().getBBXMax(max.x(), max.y(), max.z());
00751
00752 updateNodesInBBX(min, max, false);
00753 }
00754
00755 void ViewerGui::on_actionClear_selection_triggered(){
00756 point3d min, max;
00757 m_glwidget->selectionBox().getBBXMin(min.x(), min.y(), min.z());
00758 m_glwidget->selectionBox().getBBXMax(max.x(), max.y(), max.z());
00759
00760 setNodesInBBX(min, max, false);
00761 }
00762
00763 void ViewerGui::on_actionClear_unknown_in_selection_triggered()
00764 {
00765 point3d min, max;
00766 m_glwidget->selectionBox().getBBXMin(min.x(), min.y(), min.z());
00767 m_glwidget->selectionBox().getBBXMax(max.x(), max.y(), max.z());
00768
00769 setNonNodesInBBX(min, max, false);
00770 }
00771
00772 void ViewerGui::on_actionFill_unknown_in_selection_triggered()
00773 {
00774 point3d min, max;
00775 m_glwidget->selectionBox().getBBXMin(min.x(), min.y(), min.z());
00776 m_glwidget->selectionBox().getBBXMax(max.x(), max.y(), max.z());
00777
00778 setNonNodesInBBX(min, max, true);
00779 }
00780
00781 void ViewerGui::on_actionFill_selection_triggered(){
00782 point3d min, max;
00783 m_glwidget->selectionBox().getBBXMin(min.x(), min.y(), min.z());
00784 m_glwidget->selectionBox().getBBXMax(max.x(), max.y(), max.z());
00785
00786 setNodesInBBX(min, max, true);
00787 }
00788
00789 void ViewerGui::on_actionFill_nodes_in_selection_triggered(){
00790 point3d min, max;
00791 m_glwidget->selectionBox().getBBXMin(min.x(), min.y(), min.z());
00792 m_glwidget->selectionBox().getBBXMax(max.x(), max.y(), max.z());
00793
00794 updateNodesInBBX(min, max, true);
00795 }
00796
00797 void ViewerGui::on_actionDelete_nodes_in_selection_triggered(){
00798 point3d min, max;
00799 m_glwidget->selectionBox().getBBXMin(min.x(), min.y(), min.z());
00800 m_glwidget->selectionBox().getBBXMax(max.x(), max.y(), max.z());
00801
00802 for (std::map<int, OcTreeRecord>::iterator t_it = m_octrees.begin(); t_it != m_octrees.end(); ++t_it) {
00803 OcTree* octree = dynamic_cast<OcTree*>(t_it->second.octree);
00804
00805 if (octree){
00806 for(OcTree::leaf_bbx_iterator it = octree->begin_leafs_bbx(min,max),
00807 end=octree->end_leafs_bbx(); it!= end; ++it){
00808 octree->deleteNode(it.getKey(), it.getDepth());
00809 }
00810 } else{
00811 QMessageBox::warning(this, "Not implemented", "Functionality not yet implemented for this octree type",
00812 QMessageBox::Ok);
00813
00814 }
00815 }
00816
00817 showOcTree();
00818 }
00819
00820 void ViewerGui::on_actionDelete_nodes_outside_of_selection_triggered(){
00821 point3d min, max;
00822 m_glwidget->selectionBox().getBBXMin(min.x(), min.y(), min.z());
00823 m_glwidget->selectionBox().getBBXMax(max.x(), max.y(), max.z());
00824
00825 for (std::map<int, OcTreeRecord>::iterator t_it = m_octrees.begin(); t_it != m_octrees.end(); ++t_it) {
00826 OcTree* octree = dynamic_cast<OcTree*>(t_it->second.octree);
00827
00828 if (octree){
00829 octomap::OcTreeKey minKey, maxKey;
00830
00831 if (!octree->coordToKeyChecked(min, minKey) || !octree->coordToKeyChecked(max, maxKey)){
00832 return;
00833 }
00834
00835 for(OcTree::leaf_iterator it = octree->begin_leafs(),
00836 end=octree->end_leafs(); it!= end; ++it){
00837
00838 OcTreeKey k = it.getKey();
00839 if (k[0] < minKey[0] || k[1] < minKey[1] || k[2] < minKey[2]
00840 || k[0] > maxKey[0] || k[1] > maxKey[1] || k[2] > maxKey[2])
00841 {
00842 octree->deleteNode(k, it.getDepth());
00843 }
00844 }
00845 } else
00846 QMessageBox::warning(this, "Not implemented", "Functionality not yet implemented for this octree type",
00847 QMessageBox::Ok);
00848 }
00849
00850 showOcTree();
00851 }
00852
00853 void ViewerGui::updateNodesInBBX(const point3d& min, const point3d& max, bool occupied){
00854 for (std::map<int, OcTreeRecord>::iterator t_it = m_octrees.begin(); t_it != m_octrees.end(); ++t_it) {
00855 OcTree* octree = dynamic_cast<OcTree*>(t_it->second.octree);
00856
00857 if (octree){
00858 float logodds;
00859 if (occupied)
00860 logodds = octree->getClampingThresMaxLog();
00861 else
00862 logodds = octree->getClampingThresMinLog();
00863
00864 for(OcTree::leaf_bbx_iterator it = octree->begin_leafs_bbx(min,max),
00865 end=octree->end_leafs_bbx(); it!= end; ++it)
00866 {
00867
00868 it->setLogOdds(logodds);
00869 }
00870
00871
00872 octree->updateInnerOccupancy();
00873
00874 } else
00875 QMessageBox::warning(this, "Not implemented", "Functionality not yet implemented for this octree type",
00876 QMessageBox::Ok);
00877
00878 }
00879
00880 showOcTree();
00881 }
00882
00883
00884 void ViewerGui::setNodesInBBX(const point3d& min, const point3d& max, bool occupied){
00885 for (std::map<int, OcTreeRecord>::iterator t_it = m_octrees.begin(); t_it != m_octrees.end(); ++t_it) {
00886 OcTree* octree = dynamic_cast<OcTree*>(t_it->second.octree);
00887
00888 if (octree){
00889 float logodds = octree->getClampingThresMaxLog() - octree->getClampingThresMinLog();
00890 if (!occupied)
00891 logodds *= -1;
00892
00893 OcTreeKey minKey(0,0,0);
00894 OcTreeKey maxKey(0,0,0);
00895 octree->coordToKeyChecked(min, minKey);
00896 octree->coordToKeyChecked(max, maxKey);
00897 OcTreeKey k;
00898 for (k[0] = minKey[0]; k[0] < maxKey[0]; ++k[0]){
00899 for (k[1] = minKey[1]; k[1] < maxKey[1]; ++k[1]){
00900 for (k[2] = minKey[2]; k[2] < maxKey[2]; ++k[2]){
00901 octree->updateNode(k, logodds);
00902 }
00903 }
00904 }
00905 }
00906
00907 }
00908
00909 showOcTree();
00910 }
00911
00912 void ViewerGui::setNonNodesInBBX(const point3d& min, const point3d& max, bool occupied) {
00913 for (std::map<int, OcTreeRecord>::iterator t_it = m_octrees.begin(); t_it != m_octrees.end(); ++t_it) {
00914 OcTree* octree = dynamic_cast<OcTree*>(t_it->second.octree);
00915
00916 if (octree){
00917 float logodds = octree->getClampingThresMaxLog() - octree->getClampingThresMinLog();
00918 if (!occupied)
00919 logodds *= -1;
00920
00921 OcTreeKey minKey(0,0,0);
00922 OcTreeKey maxKey(0,0,0);
00923 octree->coordToKeyChecked(min, minKey);
00924 octree->coordToKeyChecked(max, maxKey);
00925 OcTreeKey k;
00926 for (k[0] = minKey[0]; k[0] < maxKey[0]; ++k[0]){
00927 for (k[1] = minKey[1]; k[1] < maxKey[1]; ++k[1]){
00928 for (k[2] = minKey[2]; k[2] < maxKey[2]; ++k[2]){
00929 OcTreeNode* n = octree->search(k);
00930 if(!n)
00931 octree->updateNode(k, logodds);
00932 }
00933 }
00934 }
00935 }
00936
00937 }
00938
00939 showOcTree();
00940 }
00941
00942 void ViewerGui::on_actionExport_view_triggered(){
00943 m_glwidget->openSnapshotFormatDialog();
00944 m_glwidget->saveSnapshot(false);
00945 }
00946
00947 void ViewerGui::on_actionExport_sequence_triggered(bool checked){
00948 if(checked) {
00949 if(m_glwidget->openSnapshotFormatDialog()) {
00950 m_glwidget->saveSnapshot(false);
00951 m_glwidget->setSnapshotCounter(0);
00952 connect(m_glwidget, SIGNAL(drawFinished(bool)), m_glwidget, SLOT(saveSnapshot(bool)));
00953 } else {
00954 ui.actionExport_sequence->setChecked(false);
00955 }
00956 } else {
00957 disconnect(m_glwidget, SIGNAL(drawFinished(bool)), m_glwidget, SLOT(saveSnapshot(bool)));
00958 }
00959 }
00960
00961 void ViewerGui::on_actionPrintout_mode_toggled(bool checked){
00962 if (checked) {
00963 ui.actionHeight_map->setChecked(false);
00964 ui.actionSemanticColoring->setChecked(false);
00965 }
00966
00967 m_glwidget->enablePrintoutMode(checked);
00968 }
00969
00970 void ViewerGui::on_actionSelection_box_toggled(bool checked){
00971
00972 ui.menuDelete_nodes->setEnabled(checked);
00973 ui.menuFill_selection->setEnabled(checked);
00974 ui.menuChange_nodes_in_selection->setEnabled(checked);
00975
00976
00977 m_glwidget->enableSelectionBox(checked);
00978
00979
00980 m_glwidget->updateGL();
00981 }
00982
00983 void ViewerGui::on_actionHeight_map_toggled(bool checked){
00984 if (checked) {
00985 ui.actionPrintout_mode->setChecked(false);
00986 ui.actionSemanticColoring->setChecked(false);
00987 }
00988 m_glwidget->enableHeightColorMode(checked);
00989 }
00990
00991 void ViewerGui::on_actionSemanticColoring_toggled(bool checked) {
00992 if (checked) {
00993 ui.actionHeight_map->setChecked(false);
00994 ui.actionPrintout_mode->setChecked(false);
00995 }
00996
00997 m_glwidget->enableSemanticColoring(checked);
00998 }
00999
01000
01001 void ViewerGui::on_actionStore_camera_triggered(){
01002 m_glwidget->camera()->deletePath(0);
01003 m_glwidget->camera()->addKeyFrameToPath(0);
01004 m_cameraStored = true;
01005 ui.actionRestore_camera->setEnabled(true);
01006 }
01007
01008 void ViewerGui::on_actionRestore_camera_triggered(){
01009 if (m_cameraStored){
01010 m_glwidget->camera()->playPath(0);
01011 }
01012 }
01013
01014 void ViewerGui::on_actionPointcloud_toggled(bool checked){
01015 if (m_pointcloudDrawer){
01016 if (checked)
01017 m_glwidget->addSceneObject(m_pointcloudDrawer);
01018 else
01019 m_glwidget->removeSceneObject(m_pointcloudDrawer);
01020 }
01021 }
01022
01023 void ViewerGui::on_actionTrajectory_toggled(bool checked){
01024 if (m_trajectoryDrawer){
01025 if (checked)
01026 m_glwidget->addSceneObject(m_trajectoryDrawer);
01027 else
01028 m_glwidget->removeSceneObject(m_trajectoryDrawer);
01029 }
01030 }
01031
01032 void ViewerGui::on_actionAxes_toggled(bool checked){
01033 for (std::map<int, OcTreeRecord>::iterator it = m_octrees.begin();
01034 it != m_octrees.end(); ++it) {
01035 it->second.octree_drawer->enableAxes(checked);
01036 }
01037 m_glwidget->updateGL();
01038 }
01039
01040 void ViewerGui::on_actionHideBackground_toggled(bool checked) {
01041 OcTreeRecord* r;
01042 if (getOctreeRecord(DEFAULT_OCTREE_ID, r)) {
01043 if (checked) m_glwidget->removeSceneObject(r->octree_drawer);
01044 else m_glwidget->addSceneObject(r->octree_drawer);
01045 m_glwidget->updateGL();
01046 }
01047 }
01048
01049 void ViewerGui::on_actionClear_triggered() {
01050 for (std::map<int, OcTreeRecord>::iterator it = m_octrees.begin();
01051 it != m_octrees.end(); ++it) {
01052 m_glwidget->removeSceneObject(it->second.octree_drawer);
01053 delete (it->second.octree_drawer);
01054 delete (it->second.octree);
01055 }
01056 m_octrees.clear();
01057 showOcTree();
01058 }
01059
01060 void ViewerGui::on_actionTest_triggered(){
01061
01062 }
01063
01064 void ViewerGui::on_actionReload_Octree_triggered(){
01065 if (m_scanGraph) {
01066 generateOctree();
01067 } else {
01068 openFile();
01069 }
01070 }
01071
01072 void ViewerGui::on_actionConvert_ml_tree_triggered(){
01073 QApplication::setOverrideCursor(Qt::WaitCursor);
01074 if (m_octrees.size()) {
01075 showInfo("Converting OcTree to maximum Likelihood map... ");
01076 for (std::map<int, OcTreeRecord>::iterator it = m_octrees.begin();
01077 it != m_octrees.end(); ++it) {
01078 AbstractOcTree* t = it->second.octree;
01079 if (dynamic_cast<OcTree*>(t)) {
01080 ((OcTree*) t)->toMaxLikelihood();
01081 }
01082 else if (dynamic_cast<OcTree*>(t)) {
01083 ((ColorOcTree*) t)->toMaxLikelihood();
01084 }
01085 }
01086 showInfo("Done.", true);
01087 showOcTree();
01088 QApplication::restoreOverrideCursor();
01089 }
01090 }
01091
01092
01093 void ViewerGui::on_actionPrune_tree_triggered(){
01094 QApplication::setOverrideCursor(Qt::WaitCursor);
01095 if (m_octrees.size()) {
01096 showInfo("Pruning OcTree... ");
01097 for (std::map<int, OcTreeRecord>::iterator it = m_octrees.begin();
01098 it != m_octrees.end(); ++it) {
01099 it->second.octree->prune();
01100 }
01101 showOcTree();
01102 showInfo("Done.", true);
01103 }
01104 QApplication::restoreOverrideCursor();
01105 }
01106
01107
01108 void ViewerGui::on_actionExpand_tree_triggered(){
01109
01110 QApplication::setOverrideCursor(Qt::WaitCursor);
01111
01112
01113 if (m_octrees.size()) {
01114 showInfo("Expanding OcTree... ");
01115 for (std::map<int, OcTreeRecord>::iterator it = m_octrees.begin();
01116 it != m_octrees.end(); ++it) {
01117 it->second.octree->expand();
01118 }
01119 showOcTree();
01120
01121 showInfo("Done.", true);
01122 }
01123 QApplication::restoreOverrideCursor();
01124 }
01125
01126
01127 void ViewerGui::on_actionOctree_cells_toggled(bool enabled) {
01128 for (std::map<int, OcTreeRecord>::iterator it = m_octrees.begin();
01129 it != m_octrees.end(); ++it) {
01130 it->second.octree_drawer->enableOcTreeCells(enabled);
01131 }
01132 m_glwidget->updateGL();
01133 }
01134
01135 void ViewerGui::on_actionOctree_structure_toggled(bool enabled) {
01136 for (std::map<int, OcTreeRecord>::iterator it = m_octrees.begin();
01137 it != m_octrees.end(); ++it) {
01138 it->second.octree_drawer->enableOcTree(enabled);
01139 }
01140 m_glwidget->updateGL();
01141 }
01142
01143 void ViewerGui::on_actionFree_toggled(bool enabled) {
01144 for (std::map<int, OcTreeRecord>::iterator it = m_octrees.begin();
01145 it != m_octrees.end(); ++it) {
01146 it->second.octree_drawer->enableFreespace(enabled);
01147 }
01148 m_glwidget->updateGL();
01149
01150 }
01151
01152 void ViewerGui::on_actionSelected_toggled(bool enabled) {
01153
01154
01155
01156
01157
01158
01159
01160
01161
01162
01163
01164
01165
01166
01167
01168 }
01169
01170
01171 void ViewerGui::on_action_bg_black_triggered() {
01172 m_glwidget->setBackgroundColor( QColor(0,0,0) );
01173 m_glwidget->qglClearColor( m_glwidget->backgroundColor() );
01174 }
01175
01176 void ViewerGui::on_action_bg_white_triggered() {
01177 m_glwidget->setBackgroundColor( QColor(255,255,255) );
01178 m_glwidget->qglClearColor( m_glwidget->backgroundColor() );
01179 }
01180
01181 void ViewerGui::on_action_bg_gray_triggered() {
01182 m_glwidget->setBackgroundColor( QColor(117,117,117) );
01183 m_glwidget->qglClearColor( m_glwidget->backgroundColor() );
01184 }
01185
01186 void ViewerGui::on_savecampose_triggered() {
01187 QString filename = QFileDialog::getSaveFileName(this, "Save Viewer State", "camera.xml", "Camera/State file (*.xml)");
01188 if (!filename.isEmpty()) {
01189 saveCameraPosition(filename.toAscii().constData());
01190 }
01191 }
01192
01193 void ViewerGui::on_loadcampose_triggered() {
01194 QString filename = QFileDialog::getOpenFileName(this, "Load Viewer State", "camera.xml", "Camera/State file (*.xml)");
01195 if (!filename.isEmpty()) {
01196 loadCameraPosition(filename.toAscii().constData());
01197 }
01198 }
01199
01200
01201
01202 void ViewerGui::saveCameraPosition(const char* filename) const {
01203
01204 ViewerWidget* aux = const_cast<ViewerWidget*>( m_glwidget);
01205 aux->setStateFileName(QString(filename));
01206 aux->saveStateToFile();
01207 aux->setStateFileName(QString::null);
01208 }
01209
01210 void ViewerGui::loadCameraPosition(const char* filename) {
01211 m_glwidget->setStateFileName(QString(filename));
01212 m_glwidget->restoreStateFromFile();
01213 m_glwidget->setStateFileName(QString::null);
01214 }
01215
01216
01217 }
01218
01219