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 
void add_point(const ACG::Vec3d &_p)
add point
Definition: PointNode.hh:125
Update type class.
Definition: UpdateType.hh:60
ACG::SceneGraph::MaterialNode MaterialNode
Materialnode.
void slotObjectUpdated(int _identifier, const UpdateType &_type)
Called when an object gets updated.
void clear()
clear points and normals and colors
Definition: PointNode.hh:141
#define DATA_POLY_MESH
Definition: PolyMesh.hh:59
void compare(int _sourceId, int _targetId, bool _computeDist=true, bool _computeNormal=true, bool _computeGauss=true, bool _computeMean=true)
Kernel::Point Point
Coordinate type.
Definition: PolyMeshT.hh:112
NearestNeighbor nearest(const Point &_p) const
Return handle of the nearest neighbor face.
DrawMode POINTS_COLORED
draw colored, but not lighted points (requires point colors)
Definition: DrawModes.cc:74
const QStringList SOURCE_OBJECTS("source")
Iterable object range.
int id() const
Definition: BaseObject.cc:190
OMTriangleBSP * requestTriangleBsp()
DrawModes::DrawMode drawMode() const
Return the own draw modes of this node.
bool getAdditionalNode(NodeT *&_node, QString _pluginName, QString _nodeName, int _id=0)
get an addition node from the object
Kernel::Scalar Scalar
Scalar type.
Definition: PolyMeshT.hh:110
void visualizeData(const std::vector< double > &_data, double _maxValue, ACG::SceneGraph::PointNode *_pnode)
Visualize data.
const QStringList TARGET_OBJECTS("target")
Iterable object range.
TriMeshObject * triMeshObject(BaseObjectData *_object)
Cast an BaseObject to a TriMeshObject if possible.
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::VertexHandle VertexHandle
Handle for referencing the corresponding item.
Definition: PolyMeshT.hh:136
TriMesh * triMesh(BaseObjectData *_object)
Get a triangle mesh from an object.
const QStringList ALL_OBJECTS
Iterable object range.
bool addAdditionalNode(NodeT *_node, QString _pluginName, QString _nodeName, int _id=0)
add an additional node to the object
void add_color(const ACG::Vec4f &_c)
add color
Definition: PointNode.hh:129
bool getObject(const int _identifier, BaseObject *&_object)
Get the object which has the given identifier.
Type for a MeshObject containing a triangle mesh.
Definition: TriangleMesh.hh:67
bool removeAdditionalNode(NodeT *&_node, QString _pluginName, QString _nodeName, int _id=0)
remove an additional node from the object
void slotClampBox(bool _checked)
If the checkbox is changed to be checked, the values in the labels will be written into the spin boxe...
Kernel::Normal Normal
Normal type.
Definition: PolyMeshT.hh:114
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
void slotClear()
Clears the visualization.
void compareButton()
Triggers comparison of the selected meshes.
unsigned int applyProperties() const
get properties that will be applied (OR&#39;ed ApplyProperties)
Class for generating nice colors for doubles.
Definition: ColorCoder.hh:68
void set_point_size(float _sz)
set point size (default: 1.0)
DLLEXPORT ObjectIterator objectsEnd()
Return Iterator to Object End.
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
void reserve(unsigned int _np, unsigned int _nn, unsigned int _nc)
reserve mem for _np points and _nn normals
Definition: PointNode.hh:120
#define DATA_TRIANGLE_MESH
Definition: TriangleMesh.hh:60
QtTranslationManipulatorNode * manipulatorNode()