/**************************************************************************** * MeshLab o o * * An extendible mesh processor o o * * _ O _ * * Copyright(C) 2005, 2009 \/)\/ * * Visual Computing Lab /\/| * * ISTI - Italian National Research Council | * * \ * * All rights reserved. * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License (http://www.gnu.org/licenses/gpl.txt) * * for more details. * * * ****************************************************************************/ /* A class to represent the ui for the pickpoints plugin * * @author Oscar Barney */ #include #include #include #include #include #include "editpickpoints.h" #include "pickpointsDialog.h" #include #include using namespace vcg; class GetClosestFace { typedef GridStaticPtr MetroMeshGrid; typedef tri::FaceTmark MarkerFace; public: GetClosestFace(){} void init(CMeshO *_m) { m=_m; if(m) { unifGrid.Set(m->face.begin(),m->face.end()); markerFunctor.SetMesh(m); dist_upper_bound = m->bbox.Diag()/10.0f; } } CMeshO *m; MetroMeshGrid unifGrid; MarkerFace markerFunctor; float dist_upper_bound; CMeshO::FaceType * getFace(vcg::Point3f &p) { assert(m); // the results vcg::Point3f closestPt; float dist = dist_upper_bound; const CMeshO::CoordType &startPt = p; // compute distance between startPt and the mesh S2 CMeshO::FaceType *nearestF=0; vcg::face::PointDistanceBaseFunctor PDistFunct; dist=dist_upper_bound; nearestF = unifGrid.GetClosest(PDistFunct,markerFunctor,startPt,dist_upper_bound,dist,closestPt); if(dist == dist_upper_bound) qDebug() << "Dist is = upper bound"; return nearestF; } }; PickedPointTreeWidgetItem::PickedPointTreeWidgetItem( vcg::Point3f &intputPoint, CMeshO::FaceType::NormalType &faceNormal, QString name, bool _active) : QTreeWidgetItem(1001) { //name setName(name); active = _active; //would set the checkbox but qt doesnt allow a way to do this in the constructor //set point and normal setPointAndNormal(intputPoint, faceNormal); } void PickedPointTreeWidgetItem::setName(QString name){ setText(0, name); } QString PickedPointTreeWidgetItem::getName(){ return text(0); } void PickedPointTreeWidgetItem::setPointAndNormal(vcg::Point3f &intputPoint, CMeshO::FaceType::NormalType &faceNormal) { point[0] = intputPoint[0]; point[1] = intputPoint[1]; point[2] = intputPoint[2]; normal[0] = faceNormal[0]; normal[1] = faceNormal[1]; normal[2] = faceNormal[2]; QString tempString; //x tempString.setNum(point[0]); setText(1, tempString); //y tempString.setNum(point[1]); setText(2, tempString); //z tempString.setNum(point[2]); setText(3, tempString); } vcg::Point3f PickedPointTreeWidgetItem::getPoint(){ return point; } vcg::Point3f PickedPointTreeWidgetItem::getNormal(){ return normal; } void PickedPointTreeWidgetItem::clearPoint(){ point.SetZero(); //x setText(1, ""); //y setText(2, ""); //z setText(3, ""); setActive(false); } bool PickedPointTreeWidgetItem::isActive() { return active; } void PickedPointTreeWidgetItem::setActive(bool value) { active = value; //stupid way QT makes you get a widget associated with this item QTreeWidget * treeWidget = this->treeWidget(); assert(treeWidget); QWidget *widget = treeWidget->itemWidget(this, 4); assert(widget); QCheckBox *checkBox = qobject_cast(widget); assert(checkBox); checkBox->setChecked(value); } void PickedPointTreeWidgetItem::toggleActive(bool value) { active = value; } PickPointsDialog::PickPointsDialog(EditPickPointsPlugin *plugin, QWidget *parent) : QDockWidget(parent) { parentPlugin = plugin; //qt standard setup step PickPointsDialog::ui.setupUi(this); //setup borrowed from alighnDialog.cpp this->setWidget(ui.frame); this->setFeatures(QDockWidget::AllDockWidgetFeatures); this->setAllowedAreas(Qt::LeftDockWidgetArea); QPoint p=parent->mapToGlobal(QPoint(0,0)); this->setFloating(true); this->setGeometry(p.x()+(parent->width()-width()),p.y()+40,width(),height() ); //now stuff specific to pick points QStringList headerNames; headerNames << "Point Name" << "X" << "Y" << "Z" << "active"; ui.pickedPointsTreeWidget->setHeaderLabels(headerNames); //init some variables //set to nothing for now lastPointToMove = 0; itemToMove = 0; meshModel = 0; _glArea = 0; //start at 0 pointCounter = 0; //start with no template setTemplateName(""); currentMode = ADD_POINT; recordPointForUndo = false; getClosestFace = new GetClosestFace(); //signals and slots connect(ui.removePointButton, SIGNAL(clicked()), this, SLOT(removeHighlightedPoint())); //rename when rename button clicked connect(ui.renamePointButton, SIGNAL(clicked()), this, SLOT(renameHighlightedPoint())); //rename on double click of point connect(ui.pickedPointsTreeWidget, SIGNAL(itemDoubleClicked(QTreeWidgetItem *, int)), this, SLOT(renameHighlightedPoint() ) ); connect(ui.clearPointButton, SIGNAL(clicked()), this, SLOT(clearHighlightedPoint())); connect(ui.pickPointModeRadioButton, SIGNAL(toggled(bool)), this, SLOT(togglePickMode(bool)) ); connect(ui.movePointRadioButton, SIGNAL(toggled(bool)), this, SLOT(toggleMoveMode(bool)) ); connect(ui.selectPointRadioButton, SIGNAL(toggled(bool)), this, SLOT(toggleSelectMode(bool)) ); connect(ui.saveButton, SIGNAL(clicked()), this, SLOT(savePointsToFile())); connect(ui.loadPointsButton, SIGNAL(clicked()), this, SLOT(askUserForFileAndLoadPoints())); connect(ui.removeAllPointsButton, SIGNAL(clicked()), this, SLOT(clearPointsButtonClicked())); connect(ui.saveTemplateButton, SIGNAL(clicked()), this, SLOT(savePointTemplate())); connect(ui.loadTemplateButton, SIGNAL(clicked()), this, SLOT(askUserForFileAndloadTemplate())); connect(ui.clearTemplateButton, SIGNAL(clicked()), this, SLOT(clearTemplateButtonClicked()) ); connect(ui.addPointToTemplateButton, SIGNAL(clicked()), this, SLOT(addPointToTemplate()) ); connect(ui.undoButton, SIGNAL(clicked()), this, SLOT(undo())); connect(ui.pickedPointsTreeWidget, SIGNAL(itemClicked(QTreeWidgetItem *, int)), this, SLOT(redrawPoints()) ); connect(ui.showNormalCheckBox, SIGNAL(clicked()), this, SLOT(redrawPoints())); connect(ui.pinRadioButton, SIGNAL(clicked()), this, SLOT(redrawPoints())); connect(ui.lineRadioButton, SIGNAL(clicked()), this, SLOT(redrawPoints())); } PickPointsDialog::~PickPointsDialog() { delete getClosestFace; } void PickPointsDialog::addMoveSelectPoint(Point3f point, CMeshO::FaceType::NormalType faceNormal) { if(currentMode == ADD_POINT) { QTreeWidgetItem *item = 0; item = ui.pickedPointsTreeWidget->currentItem(); PickedPointTreeWidgetItem *treeItem = 0; if(NULL != item) { treeItem = dynamic_cast(item); } //if we are in template mode or if the highlighted point is not set if( (templateLoaded && NULL != treeItem ) || (NULL != treeItem && !treeItem->isActive()) ) { treeItem->setPointAndNormal(point, faceNormal); treeItem->setActive(true); item = ui.pickedPointsTreeWidget->itemBelow(treeItem); if(NULL != item){ //set the next item to be selected ui.pickedPointsTreeWidget->setCurrentItem(item); } else { //if we just picked the last point go into move mode toggleMoveMode(true); } } else { //use a number as the default name QString name; name.setNum(pointCounter); pointCounter++; addTreeWidgetItemForPoint(point, name, faceNormal, true); } } else if(currentMode == MOVE_POINT) { //test to see if there is actually a highlighted item if(NULL != itemToMove){ //for undo if(recordPointForUndo) { lastPointToMove = itemToMove; lastPointPosition = lastPointToMove->getPoint(); lastPointNormal = lastPointToMove->getNormal(); recordPointForUndo = false; } //now change the point itemToMove->setPointAndNormal(point, faceNormal); itemToMove->setActive(true); ui.pickedPointsTreeWidget->setCurrentItem(itemToMove); } } else if(currentMode == SELECT_POINT) { ui.pickedPointsTreeWidget->setCurrentItem(itemToMove); } } void PickPointsDialog::recordNextPointForUndo() { recordPointForUndo = true; } void PickPointsDialog::selectOrMoveThisPoint(Point3f point){ qDebug() << "point is: " << point[0] << " " << point[1] << " " << point[2]; //the item closest to the given point PickedPointTreeWidgetItem *closestItem = 0; //the smallest distance from the given point to one in the list //so far.... float minDistanceSoFar = -1.0; for(int i = 0; i < pickedPointTreeWidgetItemVector.size(); i++){ PickedPointTreeWidgetItem *item = pickedPointTreeWidgetItemVector.at(i); Point3f tempPoint = item->getPoint(); //qDebug() << "tempPoint is: " << tempPoint[0] << " " << tempPoint[1] << " " << tempPoint[2]; float temp = sqrt(pow(point[0]-tempPoint[0],2) + pow(point[1]-tempPoint[1],2) + pow(point[2]-tempPoint[2],2)); //qDebug() << "distance is: " << temp; if(minDistanceSoFar < 0 || minDistanceSoFar > temp){ minDistanceSoFar = temp; closestItem = item; } } //if we found an itme if(NULL != closestItem){ itemToMove = closestItem; //qDebug() << "Try to move: " << closestItem->getName(); } } void PickPointsDialog::redrawPoints() { //parentPlugin->drawPickedPoints(pickedPointTreeWidgetItemVector, meshModel->cm.bbox); assert(_glArea); _glArea->update(); } bool PickPointsDialog::showNormal() { return ui.showNormalCheckBox->isChecked(); } bool PickPointsDialog::drawNormalAsPin() { return ui.pinRadioButton->isChecked(); } void PickPointsDialog::addPoint(vcg::Point3f &point, QString &name, bool present) { //bool result = GLPickTri::PickNearestFace(currentMousePosition.x(),gla->height()-currentMousePosition.y(), // mm.cm, face); CMeshO::FaceType *face = 0; //qDebug() << "present: " << present; //now look for the normal if(NULL != meshModel && present) { //need to update the mask meshModel->updateDataMask(MeshModel::MM_FACEMARK); face = getClosestFace->getFace(point); if(NULL == face) qDebug() << "no face found for point: " << name; } //if we find a face add its normal. else add a default one if(NULL != face) addTreeWidgetItemForPoint(point, name, face->N(), present); else { vcg::Point3f faceNormal; addTreeWidgetItemForPoint(point, name, faceNormal, present); } } PickedPointTreeWidgetItem * PickPointsDialog::addTreeWidgetItemForPoint(vcg::Point3f &point, QString &name, CMeshO::FaceType::NormalType &faceNormal, bool present) { PickedPointTreeWidgetItem *widgetItem = new PickedPointTreeWidgetItem(point, faceNormal, name, present); pickedPointTreeWidgetItemVector.push_back(widgetItem); ui.pickedPointsTreeWidget->addTopLevelItem(widgetItem); //select the newest item ui.pickedPointsTreeWidget->setCurrentItem(widgetItem); //add a checkbox to the widget item's 5th column (QT makes us add it in this strange way) TreeCheckBox *checkBox = new TreeCheckBox(ui.pickedPointsTreeWidget, widgetItem, this); ui.pickedPointsTreeWidget->setItemWidget(widgetItem, 4, checkBox); //set the box to show the proper check checkBox->setChecked(present); //now connect the box to its slot that chanches the checked value of the //PickedPointTreeWidgetItem and draws all the points. dont do this before //set checked or you will have all points that should be drawn, not drawn connect(checkBox, SIGNAL(toggled(bool)), checkBox, SLOT(toggleAndDraw(bool)) ); return widgetItem; } void PickPointsDialog::clearPoints(bool clearOnlyXYZ){ if(clearOnlyXYZ){ //when using templates just clear the points that were picked but not the names for(int i = 0; i < pickedPointTreeWidgetItemVector.size(); i++){ pickedPointTreeWidgetItemVector.at(i)->clearPoint(); } //if the size is greater than 0 set the first point to be selected if(pickedPointTreeWidgetItemVector.size() > 0){ ui.pickedPointsTreeWidget->setCurrentItem( pickedPointTreeWidgetItemVector.at(0)); } } else { pickedPointTreeWidgetItemVector.clear(); ui.pickedPointsTreeWidget->clear(); pointCounter = 0; } //draw without any points that may have been cleared //parentPlugin->drawPickedPoints(pickedPointTreeWidgetItemVector, meshModel->cm.bbox,painter); assert(_glArea); _glArea->update(); //set to pick mode togglePickMode(true); } void PickPointsDialog::clearTemplate() { //always clear the points clearPoints(false); setTemplateName(""); } void PickPointsDialog::setTemplateName(QString name) { templateName = name; if("" == templateName) { ui.templateNameLabel->setText("No Template Loaded"); templateLoaded = false; } else { ui.templateNameLabel->setText(templateName); templateLoaded = true; } } void PickPointsDialog::loadPickPointsTemplate(QString filename) { //clear the points tree clearPoints(false); std::vector pointNameVector; PickPointsTemplate::load(filename, &pointNameVector); for(int i = 0; i < pointNameVector.size(); i++){ vcg::Point3f point; vcg::Point3f faceNormal; PickedPointTreeWidgetItem *widgetItem = addTreeWidgetItemForPoint(point, pointNameVector.at(i), faceNormal, false); widgetItem->clearPoint(); } //select the first item in the list if it exists if(pickedPointTreeWidgetItemVector.size() > 0){ ui.pickedPointsTreeWidget->setCurrentItem(pickedPointTreeWidgetItemVector.at(0)); } setTemplateName(QFileInfo(filename).fileName()); templateWorkingDirectory = filename; } std::vector& PickPointsDialog::getPickedPointTreeWidgetItemVector(){ return pickedPointTreeWidgetItemVector; } PickPointsDialog::Mode PickPointsDialog::getMode(){ return currentMode; } void PickPointsDialog::setCurrentMeshModel(MeshModel *newMeshModel, GLArea *gla){ meshModel = newMeshModel; assert(meshModel); _glArea = gla; assert(_glArea); //make sure undo is cleared lastPointToMove = 0; //clear any points that are still here clearPoints(false); //also clear the template clearTemplate(); //make sure we start in pick mode togglePickMode(true); //set up the getClosestFace->init(&(meshModel->cm)); //Load the points from meta data if they are there if(vcg::tri::HasPerMeshAttribute(newMeshModel->cm, PickedPoints::Key)) { CMeshO::PerMeshAttributeHandle ppHandle = vcg::tri::Allocator::GetPerMeshAttribute(newMeshModel->cm, PickedPoints::Key); PickedPoints *pickedPoints = ppHandle(); if(NULL != pickedPoints){ const QString &name = pickedPoints->getTemplateName(); setTemplateName(name); std::vector * pickedPointVector = pickedPoints->getPickedPointVector(); PickedPoint *point; for(int i = 0; i < pickedPointVector->size(); i++){ point = pickedPointVector->at(i); addPoint(point->point, point->name, point->present); } redrawPoints(); } else { qDebug() << "problem with cast!!"; } } else { QString filename = PickedPoints::getSuggestedPickedPointsFileName(*meshModel); qDebug() << "suggested filename: " << filename; QFile file(filename); if(file.exists()){ loadPoints(filename); } else { //try loading the default template if there are not saved points already tryLoadingDefaultTemplate(); } } } //loads the default template if there is one void PickPointsDialog::tryLoadingDefaultTemplate() { QString filename = PickPointsTemplate::getDefaultTemplateFileName(); QFile file(filename); if(file.exists()){ loadPickPointsTemplate(filename); } //clear all the garbage out of the names clearPoints(true); } void PickPointsDialog::removeHighlightedPoint(){ //get highlighted point QTreeWidgetItem *item = ui.pickedPointsTreeWidget->currentItem(); //test to see if there is actually a highlighted item if(NULL != item){ PickedPointTreeWidgetItem* pickedItem = dynamic_cast(item); //remove the point completely std::vector::iterator iterator; iterator = std::find(pickedPointTreeWidgetItemVector.begin(), pickedPointTreeWidgetItemVector.end(), pickedItem); //remove item from vector pickedPointTreeWidgetItemVector.erase(iterator); //free memory used by widget delete pickedItem; //redraw without deleted point redrawPoints(); } else { qDebug("no item picked"); } } void PickPointsDialog::renameHighlightedPoint(){ //get highlighted point QTreeWidgetItem *item = ui.pickedPointsTreeWidget->currentItem(); //test to see if there is actually a highlighted item if(NULL != item){ PickedPointTreeWidgetItem* pickedItem = dynamic_cast(item); QString name = pickedItem->getName(); //qDebug("Rename \n"); //qDebug() << name; const QString newName = "newName"; RichParameterSet parameterSet; parameterSet.addParam(new RichString(newName, name, "New Name", "Enter the new name")); GenericParamDialog getNameDialog(this,¶meterSet); getNameDialog.setWindowModality(Qt::WindowModal); getNameDialog.hide(); //display dialog int result = getNameDialog.exec(); if(result == QDialog::Accepted){ name = parameterSet.getString(newName); //qDebug("New name os \n"); //qDebug() << name; pickedItem->setName(name); //redraw with new point name redrawPoints(); } } } void PickPointsDialog::clearHighlightedPoint() { //get highlighted point QTreeWidgetItem *item = ui.pickedPointsTreeWidget->currentItem(); //test to see if there is actually a highlighted item if(NULL != item){ PickedPointTreeWidgetItem* pickedItem = dynamic_cast(item); pickedItem->clearPoint(); //redraw without deleted point redrawPoints(); } else { qDebug("no item picked"); } } void PickPointsDialog::togglePickMode(bool checked){ if(checked){ QApplication::setOverrideCursor( QCursor(Qt::ArrowCursor) ); //qDebug() << "pick mode"; currentMode = ADD_POINT; //make sure radio button reflects this change ui.pickPointModeRadioButton->setChecked(true); } } void PickPointsDialog::toggleMoveMode(bool checked) { if(checked) { QApplication::setOverrideCursor( QCursor(Qt::ClosedHandCursor) ); //qDebug() << "move mode"; currentMode = MOVE_POINT; //make sure the radio button reflects this change ui.movePointRadioButton->setChecked(true); } } void PickPointsDialog::toggleSelectMode(bool checked) { if(checked) { QApplication::setOverrideCursor( QCursor(Qt::PointingHandCursor) ); //qDebug() << "select mode"; currentMode = SELECT_POINT; //make radio button reflect the change ui.selectPointRadioButton->setChecked(true); } } PickedPoints * PickPointsDialog::getPickedPoints() { PickedPoints *pickedPoints = new PickedPoints(); //add all the points for(int i = 0; i < pickedPointTreeWidgetItemVector.size(); i++){ PickedPointTreeWidgetItem *item = pickedPointTreeWidgetItemVector.at(i); pickedPoints->addPoint(item->getName(), item->getPoint(), item->isActive()); } pickedPoints->setTemplateName(templateName); return pickedPoints; } void PickPointsDialog::loadPoints(QString filename){ //clear the points tree and template in case it was loaded clearTemplate(); //get the points from file PickedPoints pickedPoints; pickedPoints.open(filename); const QString &name = pickedPoints.getTemplateName(); setTemplateName(name); std::vector *points = pickedPoints.getPickedPointVector(); for(int i = 0; i < points->size(); i++){ PickedPoint *pickedPoint = points->at(i); addPoint(pickedPoint->point, pickedPoint->name, pickedPoint->present); } //redraw with new point name redrawPoints(); } void PickPointsDialog::savePointsToFile() { PickedPoints *pickedPoints = getPickedPoints(); //save to a file if so desired and there are some points to save if(pickedPointTreeWidgetItemVector.size() > 0){ QString suggestion("."); if(NULL != meshModel){ suggestion = PickedPoints::getSuggestedPickedPointsFileName(*meshModel); } QString filename = QFileDialog::getSaveFileName(this,tr("Save File"),suggestion, "*"+PickedPoints::fileExtension); if("" != filename) { pickedPoints->save(filename, QString(meshModel->shortName())); savePointsToMetaData(); } } } void PickPointsDialog::savePointsToMetaData() { //save the points to the metadata if(NULL != meshModel){ CMeshO::PerMeshAttributeHandle ppHandle =vcg::tri::Allocator::GetPerMeshAttribute (meshModel->cm, PickedPoints::Key); ppHandle() = getPickedPoints(); //qDebug() << "saved points"; } } void PickPointsDialog::askUserForFileAndLoadPoints() { QString suggestion("."); if(NULL != meshModel) suggestion = PickedPoints::getSuggestedPickedPointsFileName(*meshModel); QString filename = QFileDialog::getOpenFileName(this, tr("Load File"),suggestion, "*"+PickedPoints::fileExtension); if("" != filename) loadPoints(filename); } void PickPointsDialog::savePointTemplate(){ std::vector pointNameVector; //add all the points for(int i = 0; i < pickedPointTreeWidgetItemVector.size(); i++){ PickedPointTreeWidgetItem *item = pickedPointTreeWidgetItemVector.at(i); pointNameVector.push_back(item->getName()); } //default if for the filename to be that of the default template QString filename = PickPointsTemplate::getDefaultTemplateFileName(); if(!ui.defaultTemplateCheckBox->isChecked()) { filename = QFileDialog::getSaveFileName(this, tr("Save File"), templateWorkingDirectory, "*"+PickPointsTemplate::fileExtension); //if the user pushes cancel dont do anything if("" == filename) return; else templateWorkingDirectory = filename; } //add the extension if the user forgot it if(!filename.endsWith(PickPointsTemplate::fileExtension)) filename = filename + PickPointsTemplate::fileExtension; PickPointsTemplate::save(filename, &pointNameVector); setTemplateName(QFileInfo(filename).fileName()); if(ui.defaultTemplateCheckBox->isChecked()) { QMessageBox::information(this, "MeshLab", "Default Template Saved!", QMessageBox::Ok); } } void PickPointsDialog::askUserForFileAndloadTemplate() { QString filename = QFileDialog::getOpenFileName(this,tr("Load File"),templateWorkingDirectory, "*"+PickPointsTemplate::fileExtension); if("" != filename) loadPickPointsTemplate(filename); } void PickPointsDialog::clearPointsButtonClicked() { QMessageBox messageBox(QMessageBox::Question, "Pick Points", "Are you sure you want to clear all points?", QMessageBox::Yes|QMessageBox::No, this); int returnValue = messageBox.exec(); if(returnValue == QMessageBox::Yes) { //if the template is loaded clear only xyz values clearPoints(templateLoaded); } } void PickPointsDialog::clearTemplateButtonClicked(){ QMessageBox messageBox(QMessageBox::Question, "Pick Points", "Are you sure you want to clear the template and any picked points?", QMessageBox::Yes|QMessageBox::No, this); int returnValue = messageBox.exec(); if(returnValue == QMessageBox::Yes) { clearTemplate(); } } void PickPointsDialog::addPointToTemplate() { // if(!templateLoaded) setTemplateName("new Template"); vcg::Point3f point; vcg::Point3f faceNormal; QString name("new point"); PickedPointTreeWidgetItem *widgetItem = addTreeWidgetItemForPoint(point, name, faceNormal, false); widgetItem->clearPoint(); } void PickPointsDialog::undo() { if(NULL != lastPointToMove) { vcg::Point3f tempPoint = lastPointToMove->getPoint(); vcg::Point3f tempNormal = lastPointToMove->getNormal(); lastPointToMove->setPointAndNormal(lastPointPosition, lastPointNormal); //set things so you can undo back if need be lastPointPosition = tempPoint; lastPointNormal = tempNormal; redrawPoints(); } }