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  } else {
178  emit log(LOGERR,tr("Please select one source and one target mesh to compare! Source will be the reference mesh."));
179  }
180 
181 
182 }
183 
184 void MeshComparePlugin::slotObjectUpdated( int _identifier, const UpdateType& _type ) {
185  // Get source and target objects
186  BaseObjectData* object = 0;
187 
188  PluginFunctions::getObject(_identifier,object);
189 
190  if ( object ) {
191  ACG::SceneGraph::MaterialNode *pMatNode = 0;
192  if ( object->getAdditionalNode(pMatNode,name(), "MeshCompareDistanceMaterial" ) )
193  object->removeAdditionalNode(pMatNode,name(),"MeshCompareDistanceMaterial");
194  }
195 
196 }
197 
199 
201 
202  ACG::SceneGraph::MaterialNode *pMatNode = 0;
203  if ( o_it->getAdditionalNode(pMatNode,name(), "MeshCompareDistanceMaterial" ) )
204  o_it->removeAdditionalNode(pMatNode,name(),"MeshCompareDistanceMaterial");
205 
206  }
207 
208  emit updateView();
209 
210 }
211 
212 void MeshComparePlugin::slotClampBox(bool _checked) {
213  if ( _checked ) {
214  tool_->minVal->setValue(tool_->minValue->text().toDouble());
215  tool_->maxVal->setValue(tool_->maxValue->text().toDouble());
216  }
217 }
218 
219 void MeshComparePlugin::compare(int _sourceId,int _targetId,bool _computeDist, bool _computeNormal, bool _computeGauss , bool _computeMean) {
220 
221 
222  TriMeshObject* source = PluginFunctions::triMeshObject(_sourceId);
223  TriMeshObject* target = PluginFunctions::triMeshObject(_targetId);
224 
225  if ( (target == 0 ) || (source == 0) ) {
226  emit log(LOGERR,tr("Please select one source and one target mesh to compare! Source will be the reference mesh."));
227  emit log(LOGERR,tr("Only triangle meshes are currently supported!"));
228  return;
229  }
230 
231  TriMesh* refMesh = PluginFunctions::triMesh(_sourceId);
232  TriMesh* compMesh = PluginFunctions::triMesh(_targetId);
233 
234  ACG::SceneGraph::PointNode *pNode = 0;
235 
236  // Check if we already attached a PointNode
237  if ( OpenFlipper::Options::gui() ) {
238 
239  if ( !target->getAdditionalNode(pNode,name(), "MeshCompareDistance" ) ) {
240 
241  ACG::SceneGraph::MaterialNode *pMatNode = 0;
242  pMatNode = new ACG::SceneGraph::MaterialNode(target->manipulatorNode(), "MeshCompare distance visualization material");
243  target->addAdditionalNode(pMatNode, name(), "MeshCompareDistanceMaterial");
244  pMatNode->set_point_size(5);
246 
247  pNode = new ACG::SceneGraph::PointNode(pMatNode, "MeshCompare distance visualization");
249  target->addAdditionalNode(pNode, name(), "MeshCompareDistance");
250  }
251 
252  // Clear but reserve memory for the required vertices
253  pNode->clear();
254  pNode->reserve(refMesh->n_vertices(),refMesh->n_vertices(),refMesh->n_vertices() );
255  }
256 
257  // Get a bsp for the target, as we will project the reference mesh onto the target mesh.
258  // It will be build automatically at this point.
260 
261  // ================================================================
262  // Compute mean curvature on both meshes ( if plugin is available )
263  // ================================================================
264  bool meanCurvature = false;
267 
268  if ( _computeMean ) {
269  emit pluginExists( "meancurvature" , meanCurvature );
270 
271  if ( meanCurvature ) {
272  RPC::callFunction("meancurvature","computeMeanCurvature",_sourceId);
273  RPC::callFunction("meancurvature","computeMeanCurvature",_targetId);
274  }
275 
276  if( meanCurvature &&
277  ((!refMesh->get_property_handle( meanRef , "Mean Curvature") ) ||
278  (!compMesh->get_property_handle( meanComp, "Mean Curvature") ))) {
279  meanCurvature = false;
280  }
281  }
282 
283  // ================================================================
284  // Compute mean curvature on both meshes ( if plugin is available )
285  // ================================================================
286  bool gaussCurvature = false;
289 
290  if ( _computeGauss ) {
291  emit pluginExists( "gausscurvature" , gaussCurvature );
292 
293  if ( gaussCurvature ) {
294  RPC::callFunction("gausscurvature","computeGaussCurvature",_sourceId);
295  RPC::callFunction("gausscurvature","computeGaussCurvature",_targetId);
296  }
297 
298  if( gaussCurvature &&
299  ((!refMesh->get_property_handle( gaussRef , "Gaussian Curvature") ) ||
300  (!compMesh->get_property_handle( gaussComp, "Gaussian Curvature") ))) {
301  gaussCurvature = false;
302  }
303  }
304 
305 
306 
307  // ================================================================
308  // Remember the maximal values as output and for specifying color coding range
309  // ================================================================
310  maximalDistance_ = -1.0;
311  maxNormalDeviation_ = -1.0;
312  maxMeanCurvatureDev_ = -1.0;
313  maxGaussCurvatureDev_ = -1.0;
314 
315 
316  // Remember distances for colorCoding after we know the maximal distance
317  std::vector<double> distances;
318  std::vector<double> normalAngles;
319  std::vector<double> meanCurvatures;
320  std::vector<double> gaussCurvatures;
321 
322  for ( TriMesh::VertexIter v_it = refMesh->vertices_begin() ; v_it != refMesh->vertices_end(); ++ v_it) {
323 
324  TriMeshObject::OMTriangleBSP::NearestNeighbor nearest = compBSP->nearest(refMesh->point(*v_it));
325  TriMesh::FaceHandle closestFace = nearest.handle;
326 
327  // Remember the maximal distance between the meshes
328  if (nearest.dist > maximalDistance_)
329  maximalDistance_ = nearest.dist;
330 
331  // Remember distance for color coding
332  distances.push_back(nearest.dist);
333 
334  // Get the vertices around that face and their properties
335  TriMesh::CFVIter fv_it = compMesh->cfv_iter(closestFace);
336 
337  const TriMesh::Point& p0 = compMesh->point(*fv_it);
338  const TriMesh::Normal n0 = compMesh->normal(*fv_it);
339  const TriMesh::VertexHandle& v0 = *fv_it;
340 
341  const TriMesh::Point& p1 = compMesh->point(*(++fv_it));
342  const TriMesh::Normal n1 = compMesh->normal(*fv_it);
343  const TriMesh::VertexHandle& v1 = *fv_it;
344 
345  const TriMesh::Point& p2 = compMesh->point(*(++fv_it));
346  const TriMesh::Normal n2 = compMesh->normal(*fv_it);
347  const TriMesh::VertexHandle& v2 = *fv_it;
348 
349  // project original point to current mesh
350  TriMesh::Point projectedPoint;
351  ACG::Geometry::distPointTriangle(refMesh->point(*v_it), p0, p1, p2, projectedPoint);
352 
353  // Add the position to the point node
354  if (pNode)
355  pNode->add_point(projectedPoint);
356 
357  // compute Barycentric coordinates
358  ACG::Geometry::baryCoord(projectedPoint, p0, p1, p2, projectedPoint);
359 
360  if ( _computeNormal) {
361  // interpolate normal on the compare mesh at the projected point via barycentric coordinates.
362  TriMesh::Normal normal;
363  normal = n0 * projectedPoint[0];
364  normal += n1 * projectedPoint[1];
365  normal += n2 * projectedPoint[2];
366  normal.normalize();
367 
368  // Compute normal deviation in degrees
369  double normalDeviation = (refMesh->normal(*v_it) | normal);
370 
371  if (normalDeviation < -1.0)
372  normalDeviation = -1.0;
373  else if (normalDeviation > 1.0)
374  normalDeviation = 1.0;
375 
376  normalDeviation = 180.0 / M_PI * acos(normalDeviation);
377 
378  // Remember normal deviation for color coding
379  normalAngles.push_back(normalDeviation);
380 
381  if (normalDeviation > maxNormalDeviation_)
382  maxNormalDeviation_ = normalDeviation;
383  }
384 
385  if (meanCurvature) {
386 
387  TriMesh::Scalar curvature = compMesh->property(meanComp, v0) * projectedPoint[0] +
388  compMesh->property(meanComp, v1) * projectedPoint[1] +
389  compMesh->property(meanComp, v2) * projectedPoint[2];
390 
391  const double curvatureDev = fabs(refMesh->property(meanRef, *v_it) - curvature);
392 
393  meanCurvatures.push_back(curvatureDev);
394 
395  if (curvatureDev > maxMeanCurvatureDev_)
396  maxMeanCurvatureDev_ = curvatureDev;
397  }
398 
399  if (gaussCurvature) {
400 
401  TriMesh::Scalar curvature = compMesh->property(gaussComp, v0) * projectedPoint[0] +
402  compMesh->property(gaussComp, v1) * projectedPoint[1] +
403  compMesh->property(gaussComp, v2) * projectedPoint[2];
404 
405  const double curvatureDev = fabs(refMesh->property(gaussRef, *v_it) - curvature);
406 
407  gaussCurvatures.push_back(curvatureDev);
408 
409  if (curvatureDev > maxGaussCurvatureDev_)
410  maxGaussCurvatureDev_ = curvatureDev;
411  }
412 
413  }
414 
415  // Generate the colors
416  if ( pNode ) {
417 
418  tool_->minValue->setText( QString::number(0.0) );
419 
420  if ( tool_->distance->isChecked() ) {
421  visualizeData(distances,maximalDistance_,pNode);
422  } else if ( tool_->normalAngle->isChecked() ) {
423  visualizeData(normalAngles,maxNormalDeviation_,pNode);
424  } else if ( tool_->meanCurvature->isChecked() ) {
425  visualizeData(meanCurvatures,maxMeanCurvatureDev_,pNode);
426  } else if ( tool_->gaussCurvature->isChecked() ) {
427  visualizeData(gaussCurvatures,maxGaussCurvatureDev_,pNode);
428  }
429 
430  emit updateView();
431  }
432 
433 }
434 
435 void MeshComparePlugin::visualizeData( const std::vector<double>& _data, double _maxValue, ACG::SceneGraph::PointNode* _pnode ) {
436 
437  // Set the current real maximal value in the label to show it to the user
438  tool_->maxValue->setText( QString::number(_maxValue) );
439 
440  // If the clamping check box is set, we take the values from the spin boxes
441  double min = 0.0;
442  double max = 1.0;
443  if ( tool_->doClamp->isChecked() ) {
444  min = tool_->minVal->value();
445  max = std::min(tool_->maxVal->value(),_maxValue);
446  } else
447  max = _maxValue;
448 
449  ACG::ColorCoder cCoder(min,max);
450 
451  for ( unsigned int i = 0 ; i < _data.size() ; ++i) {
452  _pnode->add_color(cCoder.color_float4(_data[i]));
453  }
454 
455 #ifdef WITH_QWT
456  plot_->setMinMax(min,max);
457  plot_->setFunction( _data );
458  plot_->replot();
459 #endif
460 
461 }
462 
#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.
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:60
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.
void compare(int _sourceId, int _targetId, bool _computeDist=true, bool _computeNormal=true, bool _computeGauss=true, bool _computeMean=true)
Class for generating nice colors for doubles.
Definition: ColorCoder.hh:68
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)