Developer Documentation
MeshComparePlugin.cc
1 /*===========================================================================*\
2 * *
3 * OpenFlipper *
4  * Copyright (c) 2001-2015, RWTH-Aachen University *
5  * Department of Computer Graphics and Multimedia *
6  * All rights reserved. *
7  * www.openflipper.org *
8  * *
9  *---------------------------------------------------------------------------*
10  * This file is part of OpenFlipper. *
11  *---------------------------------------------------------------------------*
12  * *
13  * Redistribution and use in source and binary forms, with or without *
14  * modification, are permitted provided that the following conditions *
15  * are met: *
16  * *
17  * 1. Redistributions of source code must retain the above copyright notice, *
18  * this list of conditions and the following disclaimer. *
19  * *
20  * 2. Redistributions in binary form must reproduce the above copyright *
21  * notice, this list of conditions and the following disclaimer in the *
22  * documentation and/or other materials provided with the distribution. *
23  * *
24  * 3. Neither the name of the copyright holder nor the names of its *
25  * contributors may be used to endorse or promote products derived from *
26  * this software without specific prior written permission. *
27  * *
28  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS *
29  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *
30  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
31  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER *
32  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, *
33  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, *
34  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR *
35  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
36  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING *
37  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS *
38  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
39 * *
40 \*===========================================================================*/
41 
42 
43 
44 
45 
46 #include "MeshComparePlugin.hh"
47 
48 
51 
52 #include <ACG/Utils/ColorCoder.hh>
53 
54 #include <ACG/Scenegraph/MaterialNode.hh>
55 #include <ACG/QtScenegraph/QtTranslationManipulatorNode.hh>
56 
57 
58 MeshComparePlugin::MeshComparePlugin() :
59  tool_(0),
60  maximalDistance_(-1),
61  maxNormalDeviation_(-1),
62  maxMeanCurvatureDev_(-1),
63  maxGaussCurvatureDev_(-1)
64 #ifdef WITH_QWT
65  ,plot_(0)
66 #endif
67 {
68 
69 }
70 
71 MeshComparePlugin::~MeshComparePlugin()
72 {
73 
74 }
75 
76 void MeshComparePlugin::initializePlugin()
77 {
78  if ( OpenFlipper::Options::gui()) {
79  tool_ = new MeshCompareToolbarWidget();
80 
81  connect( tool_->compare, SIGNAL(clicked()), this, SLOT(compareButton()) );
82  connect( tool_->clear, SIGNAL(clicked()), this, SLOT(slotClear()) );
83 
84  QIcon* toolIcon = new QIcon(OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator()+"MeshCompare.png");
85  emit addToolbox( tr("Mesh Compare") , tool_ , toolIcon);
86 
87  connect(tool_->doClamp,SIGNAL(toggled( bool)),this,SLOT(slotClampBox(bool)));
88 
89 #ifdef WITH_QWT
90  QVBoxLayout* layout = new QVBoxLayout(tool_->frame);
91 
92  plot_ = new QwtFunctionPlot(0);
93  plot_->setMinimumHeight(150);
94 
95  layout->addWidget(plot_);
96 #else
97  // Hide the extra frame as QWT is not available, we will not have an histogramm
98  tool_->frame->hide();
99 #endif
100  }
101 }
102 
103 void MeshComparePlugin::pluginsInitialized() {
104 
105  //===========================================================
106  // Describe scripting slots
107  //===========================================================
108  emit setSlotDescription(tr("compare(int,int)"), tr("Compare two meshes. Use lastMaximalDistance() and lastMaximalNormalDeviation() to get the results."),
109  QStringList(tr("ObjectId,ObjectId")), QStringList(tr("Id of the reference mesh, Id of the comparison mesh")));
110  emit setSlotDescription(tr("lastMaximalDistance()"), tr("Get the maximal distance between the meshes of the last comparison."),
111  QStringList(tr("")), QStringList(tr("")));
112  emit setSlotDescription(tr("lastMaximalNormalDeviation()"), tr("Get the maximal normal deviation in degree between the meshes of the last comparison."),
113  QStringList(tr("")), QStringList(tr("")));
114  emit setSlotDescription(tr("lastMaximalMeanCurvatureDeviation()"), tr("Get the maximal mean curvature deviation between the meshes of the last comparison."),
115  QStringList(tr("")), QStringList(tr("")));
116  emit setSlotDescription(tr("lastMaximalGaussCurvatureDeviation()"), tr("Get the maximal gauss curvature deviation between the meshes of the last comparison."),
117  QStringList(tr("")), QStringList(tr("")));
118 
119  //===========================================================
120  // Check mean curvature plugin and disable the box in gui mode
121  //===========================================================
122  bool meanCurvature = false;
123  emit pluginExists( "meancurvature" , meanCurvature );
124 
125  if ( OpenFlipper::Options::gui() && !meanCurvature )
126  tool_->meanCurvature->setEnabled(false);
127 
128  //===========================================================
129  // Check gauss curvature plugin and disable the box in gui mode
130  //===========================================================
131  bool gaussCurvature = false;
132  emit pluginExists( "gausscurvature" , gaussCurvature );
133 
134  if ( OpenFlipper::Options::gui() && !gaussCurvature )
135  tool_->gaussCurvature->setEnabled(false);
136 }
137 
139 
140  // Get source and target objects
141  BaseObjectData* targetObject = 0;
143 
144  if ( o_it->dataType(DATA_TRIANGLE_MESH) || o_it->dataType(DATA_POLY_MESH) ) {
145 
146  // If we found a second target, something is wrong!
147  if ( targetObject != 0 ) {
148  emit log(LOGERR,tr("Please select one source and one target mesh to compare! Source will be the reference mesh."));
149  return;
150  }
151 
152  targetObject = (*o_it);
153  }
154  }
155 
156  BaseObjectData* sourceObject = 0;
158 
159  if ( o_it->dataType(DATA_TRIANGLE_MESH) || o_it->dataType(DATA_POLY_MESH) ) {
160 
161  // If we found a second target, something is wrong!
162  if ( sourceObject != 0 ) {
163  emit log(LOGERR,tr("Please select one source and one target mesh to compare! Source will be the reference mesh."));
164  return;
165  }
166 
167  sourceObject = (*o_it);
168  }
169  }
170 
171 
172  if ( (targetObject != 0) && (sourceObject != 0) ) {
173  compare(sourceObject->id(),targetObject->id(), tool_->distance->isChecked() ,
174  tool_->normalAngle->isChecked(),
175  tool_->gaussCurvature->isChecked(),
176  tool_->meanCurvature->isChecked(),
177  tool_->selection->isChecked());
178  } else {
179  emit log(LOGERR,tr("Please select one source and one target mesh to compare! Source will be the reference mesh."));
180  }
181 
182 
183 }
184 
185 void MeshComparePlugin::slotObjectUpdated( int _identifier, const UpdateType& _type ) {
186  // Get source and target objects
187  BaseObjectData* object = 0;
188 
189  PluginFunctions::getObject(_identifier,object);
190 
191  if ( object ) {
192  ACG::SceneGraph::MaterialNode *pMatNode = 0;
193  if ( object->getAdditionalNode(pMatNode,name(), "MeshCompareDistanceMaterial" ) )
194  object->removeAdditionalNode(pMatNode,name(),"MeshCompareDistanceMaterial");
195  }
196 
197 }
198 
200 
202 
203  ACG::SceneGraph::MaterialNode *pMatNode = 0;
204  if ( o_it->getAdditionalNode(pMatNode,name(), "MeshCompareDistanceMaterial" ) )
205  o_it->removeAdditionalNode(pMatNode,name(),"MeshCompareDistanceMaterial");
206 
207  }
208 
209  emit updateView();
210 
211 }
212 
213 void MeshComparePlugin::slotClampBox(bool _checked) {
214  if ( _checked ) {
215  tool_->minVal->setValue(tool_->minValue->text().toDouble());
216  tool_->maxVal->setValue(tool_->maxValue->text().toDouble());
217  }
218 }
219 
220 void MeshComparePlugin::compare(int _sourceId,int _targetId,bool _computeDist, bool _computeNormal, bool _computeGauss , bool _computeMean, bool _selection) {
221 
222 
223  TriMeshObject* source = PluginFunctions::triMeshObject(_sourceId);
224  TriMeshObject* target = PluginFunctions::triMeshObject(_targetId);
225 
226  if ( (target == 0 ) || (source == 0) ) {
227  emit log(LOGERR,tr("Please select one source and one target mesh to compare! Source will be the reference mesh."));
228  emit log(LOGERR,tr("Only triangle meshes are currently supported!"));
229  return;
230  }
231 
232  TriMesh* refMesh = PluginFunctions::triMesh(_sourceId);
233  TriMesh* compMesh = PluginFunctions::triMesh(_targetId);
234 
235  ACG::SceneGraph::PointNode *pNode = 0;
236 
237  // Check if we already attached a PointNode
238  if ( OpenFlipper::Options::gui() ) {
239 
240  if ( !target->getAdditionalNode(pNode,name(), "MeshCompareDistance" ) ) {
241 
242  ACG::SceneGraph::MaterialNode *pMatNode = 0;
243  pMatNode = new ACG::SceneGraph::MaterialNode(target->manipulatorNode(), "MeshCompare distance visualization material");
244  target->addAdditionalNode(pMatNode, name(), "MeshCompareDistanceMaterial");
245  pMatNode->set_point_size(5);
247 
248  pNode = new ACG::SceneGraph::PointNode(pMatNode, "MeshCompare distance visualization");
250  target->addAdditionalNode(pNode, name(), "MeshCompareDistance");
251  }
252 
253  // Clear but reserve memory for the required vertices
254  pNode->clear();
255  pNode->reserve(refMesh->n_vertices(),refMesh->n_vertices(),refMesh->n_vertices() );
256  }
257 
258  // Get a bsp for the target, as we will project the reference mesh onto the target mesh.
259  // It will be build automatically at this point.
261 
262  // ================================================================
263  // Compute mean curvature on both meshes ( if plugin is available )
264  // ================================================================
265  bool meanCurvature = false;
268 
269  if ( _computeMean ) {
270  emit pluginExists( "meancurvature" , meanCurvature );
271 
272  if ( meanCurvature ) {
273  RPC::callFunction("meancurvature","computeMeanCurvature",_sourceId);
274  RPC::callFunction("meancurvature","computeMeanCurvature",_targetId);
275  }
276 
277  if( meanCurvature &&
278  ((!refMesh->get_property_handle( meanRef , "Mean Curvature") ) ||
279  (!compMesh->get_property_handle( meanComp, "Mean Curvature") ))) {
280  meanCurvature = false;
281  }
282  }
283 
284  // ================================================================
285  // Compute mean curvature on both meshes ( if plugin is available )
286  // ================================================================
287  bool gaussCurvature = false;
290 
291  if ( _computeGauss ) {
292  emit pluginExists( "gausscurvature" , gaussCurvature );
293 
294  if ( gaussCurvature ) {
295  RPC::callFunction("gausscurvature","computeGaussCurvature",_sourceId);
296  RPC::callFunction("gausscurvature","computeGaussCurvature",_targetId);
297  }
298 
299  if( gaussCurvature &&
300  ((!refMesh->get_property_handle( gaussRef , "Gaussian Curvature") ) ||
301  (!compMesh->get_property_handle( gaussComp, "Gaussian Curvature") ))) {
302  gaussCurvature = false;
303  }
304  }
305 
306 
307 
308  // ================================================================
309  // Remember the maximal values as output and for specifying color coding range
310  // ================================================================
311  maximalDistance_ = -1.0;
312  maxNormalDeviation_ = -1.0;
313  maxMeanCurvatureDev_ = -1.0;
314  maxGaussCurvatureDev_ = -1.0;
315 
316 
317  // Remember distances for colorCoding after we know the maximal distance
318  std::vector<double> distances;
319  std::vector<double> normalAngles;
320  std::vector<double> meanCurvatures;
321  std::vector<double> gaussCurvatures;
322 
323 
324  for (auto v_it : refMesh->vertices()) {
325  if ( _selection && refMesh->status(v_it).selected() == false) {
326  continue;
327  }
328 
329  TriMeshObject::OMTriangleBSP::NearestNeighbor nearest = compBSP->nearest(refMesh->point(v_it));
330  TriMesh::FaceHandle closestFace = nearest.handle;
331 
332  // Remember the maximal distance between the meshes
333  if (nearest.dist > maximalDistance_)
334  maximalDistance_ = nearest.dist;
335 
336  // Remember distance for color coding
337  distances.push_back(nearest.dist);
338 
339  // Get the vertices around that face and their properties
340  TriMesh::CFVIter fv_it = compMesh->cfv_iter(closestFace);
341 
342  const TriMesh::Point& p0 = compMesh->point(*fv_it);
343  const TriMesh::Normal n0 = compMesh->normal(*fv_it);
344  const TriMesh::VertexHandle& v0 = *fv_it;
345 
346  const TriMesh::Point& p1 = compMesh->point(*(++fv_it));
347  const TriMesh::Normal n1 = compMesh->normal(*fv_it);
348  const TriMesh::VertexHandle& v1 = *fv_it;
349 
350  const TriMesh::Point& p2 = compMesh->point(*(++fv_it));
351  const TriMesh::Normal n2 = compMesh->normal(*fv_it);
352  const TriMesh::VertexHandle& v2 = *fv_it;
353 
354  // project original point to current mesh
355  TriMesh::Point projectedPoint;
356  ACG::Geometry::distPointTriangle(refMesh->point(v_it), p0, p1, p2, projectedPoint);
357 
358  // Add the position to the point node
359  if (pNode)
360  pNode->add_point(projectedPoint);
361 
362  // compute Barycentric coordinates
363  ACG::Geometry::baryCoord(projectedPoint, p0, p1, p2, projectedPoint);
364 
365  if ( _computeNormal) {
366  // interpolate normal on the compare mesh at the projected point via barycentric coordinates.
367  TriMesh::Normal normal;
368  normal = n0 * projectedPoint[0];
369  normal += n1 * projectedPoint[1];
370  normal += n2 * projectedPoint[2];
371  normal.normalize();
372 
373  // Compute normal deviation in degrees
374  double normalDeviation = (refMesh->normal(v_it) | normal);
375 
376  if (normalDeviation < -1.0)
377  normalDeviation = -1.0;
378  else if (normalDeviation > 1.0)
379  normalDeviation = 1.0;
380 
381  normalDeviation = 180.0 / M_PI * acos(normalDeviation);
382 
383  // Remember normal deviation for color coding
384  normalAngles.push_back(normalDeviation);
385 
386  if (normalDeviation > maxNormalDeviation_)
387  maxNormalDeviation_ = normalDeviation;
388  }
389 
390  if (meanCurvature) {
391 
392  TriMesh::Scalar curvature = compMesh->property(meanComp, v0) * projectedPoint[0] +
393  compMesh->property(meanComp, v1) * projectedPoint[1] +
394  compMesh->property(meanComp, v2) * projectedPoint[2];
395 
396  const double curvatureDev = fabs(refMesh->property(meanRef, v_it) - curvature);
397 
398  meanCurvatures.push_back(curvatureDev);
399 
400  if (curvatureDev > maxMeanCurvatureDev_)
401  maxMeanCurvatureDev_ = curvatureDev;
402  }
403 
404  if (gaussCurvature) {
405 
406  TriMesh::Scalar curvature = compMesh->property(gaussComp, v0) * projectedPoint[0] +
407  compMesh->property(gaussComp, v1) * projectedPoint[1] +
408  compMesh->property(gaussComp, v2) * projectedPoint[2];
409 
410  const double curvatureDev = fabs(refMesh->property(gaussRef, v_it) - curvature);
411 
412  gaussCurvatures.push_back(curvatureDev);
413 
414  if (curvatureDev > maxGaussCurvatureDev_)
415  maxGaussCurvatureDev_ = curvatureDev;
416  }
417 
418  }
419 
420  // Generate the colors
421  if ( pNode ) {
422 
423  tool_->minValue->setText( QString::number(0.0) );
424 
425  if ( tool_->distance->isChecked() ) {
426  visualizeData(distances,maximalDistance_,pNode);
427  } else if ( tool_->normalAngle->isChecked() ) {
428  visualizeData(normalAngles,maxNormalDeviation_,pNode);
429  } else if ( tool_->meanCurvature->isChecked() ) {
430  visualizeData(meanCurvatures,maxMeanCurvatureDev_,pNode);
431  } else if ( tool_->gaussCurvature->isChecked() ) {
432  visualizeData(gaussCurvatures,maxGaussCurvatureDev_,pNode);
433  }
434 
435  emit updateView();
436  }
437 
438 }
439 
440 void MeshComparePlugin::visualizeData( const std::vector<double>& _data, double _maxValue, ACG::SceneGraph::PointNode* _pnode ) {
441 
442  // Set the current real maximal value in the label to show it to the user
443  tool_->maxValue->setText( QString::number(_maxValue) );
444 
445  // If the clamping check box is set, we take the values from the spin boxes
446  double min = 0.0;
447  double max = 1.0;
448  if ( tool_->doClamp->isChecked() ) {
449  min = tool_->minVal->value();
450  max = std::min(tool_->maxVal->value(),_maxValue);
451  } else
452  max = _maxValue;
453 
454  ACG::ColorCoder cCoder(min,max);
455 
456  for ( unsigned int i = 0 ; i < _data.size() ; ++i) {
457  _pnode->add_color(cCoder.color_float4(_data[i]));
458  }
459 
460 #ifdef WITH_QWT
461  plot_->setMinMax(min,max);
462  plot_->setFunction( _data );
463  plot_->replot();
464 #endif
465 
466 }
467 
#define DATA_TRIANGLE_MESH
Definition: TriangleMesh.hh:60
OMTriangleBSP * requestTriangleBsp()
bool getAdditionalNode(NodeT *&_node, QString _pluginName, QString _nodeName, int _id=0)
get an addition node from the object
void add_color(const ACG::Vec4f &_c)
add color
Definition: PointNode.hh:129
#define DATA_POLY_MESH
Definition: PolyMesh.hh:59
Kernel::Normal Normal
Normal type.
Definition: PolyMeshT.hh:114
void reserve(unsigned int _np, unsigned int _nn, unsigned int _nc)
reserve mem for _np points and _nn normals
Definition: PointNode.hh:120
DrawModes::DrawMode drawMode() const
Return the own draw modes of this node.
Definition: BaseNode.hh:430
QScriptValue callFunction(QString _plugin, QString _functionName, std::vector< QScriptValue > _parameters)
Call a function provided by a plugin getting multiple parameters.
Definition: RPCWrappers.cc:55
Kernel::Point Point
Coordinate type.
Definition: PolyMeshT.hh:112
bool getObject(const int _identifier, BaseObject *&_object)
Get the object which has the given identifier.
void slotObjectUpdated(int _identifier, const UpdateType &_type)
Called when an object gets updated.
TriMesh * triMesh(BaseObjectData *_object)
Get a triangle mesh from an object.
const QStringList SOURCE_OBJECTS("source")
Iterable object range.
const QStringList ALL_OBJECTS
Iterable object range.
void visualizeData(const std::vector< double > &_data, double _maxValue, ACG::SceneGraph::PointNode *_pnode)
Visualize data.
bool removeAdditionalNode(NodeT *&_node, QString _pluginName, QString _nodeName, int _id=0)
remove an additional node from the object
int id() const
Definition: BaseObject.cc:190
void slotClampBox(bool _checked)
If the checkbox is changed to be checked, the values in the labels will be written into the spin boxe...
DLLEXPORT ObjectIterator objectsEnd()
Return Iterator to Object End.
void clear()
clear points and normals and colors
Definition: PointNode.hh:141
QtTranslationManipulatorNode * manipulatorNode()
void add_point(const ACG::Vec3d &_p)
add point
Definition: PointNode.hh:125
void compareButton()
Triggers comparison of the selected meshes.
void slotClear()
Clears the visualization.
bool baryCoord(const VectorT< Scalar, 3 > &_p, const VectorT< Scalar, 3 > &_u, const VectorT< Scalar, 3 > &_v, const VectorT< Scalar, 3 > &_w, VectorT< Scalar, 3 > &_result)
Definition: Algorithms.cc:83
TriMeshObject * triMeshObject(BaseObjectData *_object)
Cast an BaseObject to a TriMeshObject if possible.
Type for a MeshObject containing a triangle mesh.
Definition: TriangleMesh.hh:67
Update type class.
Definition: UpdateType.hh:59
Vec::value_type distPointTriangle(const Vec &_p, const Vec &_v0, const Vec &_v1, const Vec &_v2, Vec &_nearestPoint)
distance from point _p to triangle (_v0, _v1, _v2)
Definition: Algorithms.hh:326
bool addAdditionalNode(NodeT *_node, QString _pluginName, QString _nodeName, int _id=0)
add an additional node to the object
unsigned int applyProperties() const
get properties that will be applied (OR&#39;ed ApplyProperties)
const QStringList TARGET_OBJECTS("target")
Iterable object range.
NearestNeighbor nearest(const Point &_p) const
Return handle of the nearest neighbor face.
Kernel::Scalar Scalar
Scalar type.
Definition: PolyMeshT.hh:110
DrawMode POINTS_COLORED
draw colored, but not lighted points (requires point colors)
Definition: DrawModes.cc:74
ACG::SceneGraph::MaterialNode MaterialNode
Materialnode.
Class for generating nice colors for doubles.
Definition: ColorCoder.hh:68
void compare(int _sourceId, int _targetId, bool _computeDist=true, bool _computeNormal=true, bool _computeGauss=true, bool _computeMean=true, bool _selection_=false)
Kernel::VertexHandle VertexHandle
Handle for referencing the corresponding item.
Definition: PolyMeshT.hh:136
void set_point_size(float _sz)
set point size (default: 1.0)