meshlab/src/meshlabplugins/edit_pickpoints/pickpointsDialog.cpp

862 lines
22 KiB
C++

/* A class to represent the ui for the pickpoints plugin
*
* @author Oscar Barney
*/
#include <QtGui>
#include <math.h>
#include <meshlab/stdpardialog.h>
#include <meshlab/meshmodel.h>
#include <meshlab/glarea.h>
#include <vcg/complex/trimesh/allocate.h>
#include "editpickpoints.h"
#include "pickpointsDialog.h"
#include <vcg/space/index/grid_static_ptr.h>
#include <vcg/complex/trimesh/closest.h>
using namespace vcg;
class GetClosestFace
{
typedef GridStaticPtr<CMeshO::FaceType, CMeshO::ScalarType > MetroMeshGrid;
typedef trimesh::FaceTmark<CMeshO> 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.Zero();
//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<QCheckBox *>(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()));
connect(ui.renamePointButton, SIGNAL(clicked()), 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.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<PickedPointTreeWidgetItem *>(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)
{
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);
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<QString> 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());
}
std::vector<PickedPointTreeWidgetItem*>& 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<PickedPoints*> ppHandle =
vcg::tri::Allocator<CMeshO>::GetPerMeshAttribute<PickedPoints*>(newMeshModel->cm, PickedPoints::Key);
PickedPoints *pickedPoints = ppHandle();
if(NULL != pickedPoints){
const QString &name = pickedPoints->getTemplateName();
setTemplateName(name);
std::vector<PickedPoint*> * 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<PickedPointTreeWidgetItem *>(item);
//remove the point completely
std::vector<PickedPointTreeWidgetItem*>::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<PickedPointTreeWidgetItem *>(item);
QString name = pickedItem->getName();
//qDebug("Rename \n");
//qDebug() << name;
const QString newName = "newName";
FilterParameterSet parameterSet;
parameterSet.addString(newName, name, "New Name", "Enter the new name");
GenericParamDialog getNameDialog(this,&parameterSet);
//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<PickedPointTreeWidgetItem *>(item);
pickedItem->clearPoint();
//redraw without deleted point
redrawPoints();
} else
{
qDebug("no item picked");
}
}
void PickPointsDialog::togglePickMode(bool checked){
if(checked){
//qDebug() << "pick mode";
currentMode = ADD_POINT;
//make sure radio button reflects this change
ui.pickPointModeRadioButton->setChecked(true);
}
}
void PickPointsDialog::toggleMoveMode(bool checked)
{
if(checked)
{
//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)
{
//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<PickedPoint*> *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);
pickedPoints->save(filename, QString(meshModel->fileName.c_str()));
}
savePointsToMetaData();
}
void PickPointsDialog::savePointsToMetaData()
{
//save the points to the metadata
if(NULL != meshModel){
CMeshO::PerMeshAttributeHandle<PickedPoints*> ppHandle =
(vcg::tri::HasPerMeshAttribute(meshModel->cm, PickedPoints::Key) ?
vcg::tri::Allocator<CMeshO>::GetPerMeshAttribute<PickedPoints*> (meshModel->cm, PickedPoints::Key) :
vcg::tri::Allocator<CMeshO>::AddPerMeshAttribute<PickedPoints*> (meshModel->cm, PickedPoints::Key) );
ppHandle() = getPickedPoints();
//qDebug() << "saved points";
}
}
void PickPointsDialog::askUserForFileAndLoadPoints()
{
QString filename = QFileDialog::getOpenFileName(this,tr("Load File"),".", "*"+PickedPoints::fileExtension);
loadPoints(filename);
}
void PickPointsDialog::savePointTemplate(){
std::vector<QString> 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"), templateName, "*"+PickPointsTemplate::fileExtension);
//if the user pushes cancel dont do anything
if("" == filename) return;
}
//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"),".", "*"+PickPointsTemplate::fileExtension);
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();
}
}