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 * $Revision$ *
45 * $LastChangedBy$ *
46 * $Date$ *
47 * *
48 \*===========================================================================*/
49 
50 
51 
52 #include "MeshComparePlugin.hh"
53 
54 
57 
58 #include <ACG/Utils/ColorCoder.hh>
59 
60 
61 MeshComparePlugin::MeshComparePlugin() :
62  tool_(0),
63  maximalDistance_(-1),
64  maxNormalDeviation_(-1),
65  maxMeanCurvatureDev_(-1),
66  maxGaussCurvatureDev_(-1)
67 #ifdef WITH_QWT
68  ,plot_(0)
69 #endif
70 {
71 
72 }
73 
74 MeshComparePlugin::~MeshComparePlugin()
75 {
76 
77 }
78 
79 void MeshComparePlugin::initializePlugin()
80 {
81  if ( OpenFlipper::Options::gui()) {
82  tool_ = new MeshCompareToolbarWidget();
83 
84  connect( tool_->compare, SIGNAL(clicked()), this, SLOT(compareButton()) );
85  connect( tool_->clear, SIGNAL(clicked()), this, SLOT(slotClear()) );
86 
87  QIcon* toolIcon = new QIcon(OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator()+"MeshCompare.png");
88  emit addToolbox( tr("Mesh Compare") , tool_ , toolIcon);
89 
90  connect(tool_->doClamp,SIGNAL(toggled( bool)),this,SLOT(slotClampBox(bool)));
91 
92 #ifdef WITH_QWT
93  QVBoxLayout* layout = new QVBoxLayout(tool_->frame);
94 
95  plot_ = new QwtFunctionPlot(0);
96  plot_->setMinimumHeight(150);
97 
98  layout->addWidget(plot_);
99 #else
100  // Hide the extra frame as QWT is not available, we will not have an histogramm
101  tool_->frame->hide();
102 #endif
103  }
104 }
105 
106 void MeshComparePlugin::pluginsInitialized() {
107 
108  //===========================================================
109  // Describe scripting slots
110  //===========================================================
111  emit setSlotDescription(tr("compare(int,int)"), tr("Compare two meshes. Use lastMaximalDistance() and lastMaximalNormalDeviation() to get the results."),
112  QStringList(tr("ObjectId,ObjectId")), QStringList(tr("Id of the reference mesh, Id of the comparison mesh")));
113  emit setSlotDescription(tr("lastMaximalDistance()"), tr("Get the maximal distance between the meshes of the last comparison."),
114  QStringList(tr("")), QStringList(tr("")));
115  emit setSlotDescription(tr("lastMaximalNormalDeviation()"), tr("Get the maximal normal deviation in degree between the meshes of the last comparison."),
116  QStringList(tr("")), QStringList(tr("")));
117  emit setSlotDescription(tr("lastMaximalMeanCurvatureDeviation()"), tr("Get the maximal mean curvature deviation between the meshes of the last comparison."),
118  QStringList(tr("")), QStringList(tr("")));
119  emit setSlotDescription(tr("lastMaximalGaussCurvatureDeviation()"), tr("Get the maximal gauss curvature deviation between the meshes of the last comparison."),
120  QStringList(tr("")), QStringList(tr("")));
121 
122  //===========================================================
123  // Check mean curvature plugin and disable the box in gui mode
124  //===========================================================
125  bool meanCurvature = false;
126  emit pluginExists( "meancurvature" , meanCurvature );
127 
128  if ( OpenFlipper::Options::gui() && !meanCurvature )
129  tool_->meanCurvature->setEnabled(false);
130 
131  //===========================================================
132  // Check gauss curvature plugin and disable the box in gui mode
133  //===========================================================
134  bool gaussCurvature = false;
135  emit pluginExists( "gausscurvature" , gaussCurvature );
136 
137  if ( OpenFlipper::Options::gui() && !gaussCurvature )
138  tool_->gaussCurvature->setEnabled(false);
139 }
140 
142 
143  // Get source and target objects
144  BaseObjectData* targetObject = 0;
146 
147  if ( o_it->dataType(DATA_TRIANGLE_MESH) || o_it->dataType(DATA_POLY_MESH) ) {
148 
149  // If we found a second target, something is wrong!
150  if ( targetObject != 0 ) {
151  emit log(LOGERR,tr("Please select one source and one target mesh to compare! Source will be the reference mesh."));
152  return;
153  }
154 
155  targetObject = (*o_it);
156  }
157  }
158 
159  BaseObjectData* sourceObject = 0;
161 
162  if ( o_it->dataType(DATA_TRIANGLE_MESH) || o_it->dataType(DATA_POLY_MESH) ) {
163 
164  // If we found a second target, something is wrong!
165  if ( sourceObject != 0 ) {
166  emit log(LOGERR,tr("Please select one source and one target mesh to compare! Source will be the reference mesh."));
167  return;
168  }
169 
170  sourceObject = (*o_it);
171  }
172  }
173 
174 
175  if ( (targetObject != 0) && (sourceObject != 0) ) {
176  compare(sourceObject->id(),targetObject->id(), tool_->distance->isChecked() ,
177  tool_->normalAngle->isChecked(),
178  tool_->gaussCurvature->isChecked(),
179  tool_->meanCurvature->isChecked() );
180  } else {
181  emit log(LOGERR,tr("Please select one source and one target mesh to compare! Source will be the reference mesh."));
182  }
183 
184 
185 }
186 
187 void MeshComparePlugin::slotObjectUpdated( int _identifier, const UpdateType& _type ) {
188  // Get source and target objects
189  BaseObjectData* object = 0;
190 
191  PluginFunctions::getObject(_identifier,object);
192 
193  if ( object ) {
194  ACG::SceneGraph::MaterialNode *pMatNode = 0;
195  if ( object->getAdditionalNode(pMatNode,name(), "MeshCompareDistanceMaterial" ) )
196  object->removeAdditionalNode(pMatNode,name(),"MeshCompareDistanceMaterial");
197  }
198 
199 }
200 
202 
204 
205  ACG::SceneGraph::MaterialNode *pMatNode = 0;
206  if ( o_it->getAdditionalNode(pMatNode,name(), "MeshCompareDistanceMaterial" ) )
207  o_it->removeAdditionalNode(pMatNode,name(),"MeshCompareDistanceMaterial");
208 
209  }
210 
211  emit updateView();
212 
213 }
214 
215 void MeshComparePlugin::slotClampBox(bool _checked) {
216  if ( _checked ) {
217  tool_->minVal->setValue(tool_->minValue->text().toDouble());
218  tool_->maxVal->setValue(tool_->maxValue->text().toDouble());
219  }
220 }
221 
222 void MeshComparePlugin::compare(int _sourceId,int _targetId,bool _computeDist, bool _computeNormal, bool _computeGauss , bool _computeMean) {
223 
224 
225  TriMeshObject* source = PluginFunctions::triMeshObject(_sourceId);
226  TriMeshObject* target = PluginFunctions::triMeshObject(_targetId);
227 
228  if ( (target == 0 ) || (source == 0) ) {
229  emit log(LOGERR,tr("Please select one source and one target mesh to compare! Source will be the reference mesh."));
230  emit log(LOGERR,tr("Only triangle meshes are currently supported!"));
231  return;
232  }
233 
234  TriMesh* refMesh = PluginFunctions::triMesh(_sourceId);
235  TriMesh* compMesh = PluginFunctions::triMesh(_targetId);
236 
237  ACG::SceneGraph::PointNode *pNode = 0;
238 
239  // Check if we already attached a PointNode
240  if ( OpenFlipper::Options::gui() ) {
241 
242  if ( !target->getAdditionalNode(pNode,name(), "MeshCompareDistance" ) ) {
243 
244  ACG::SceneGraph::MaterialNode *pMatNode = 0;
245  pMatNode = new ACG::SceneGraph::MaterialNode(target->manipulatorNode(), "MeshCompare distance visualization material");
246  target->addAdditionalNode(pMatNode, name(), "MeshCompareDistanceMaterial");
247  pMatNode->set_point_size(5);
249 
250  pNode = new ACG::SceneGraph::PointNode(pMatNode, "MeshCompare distance visualization");
252  target->addAdditionalNode(pNode, name(), "MeshCompareDistance");
253  }
254 
255  // Clear but reserve memory for the required vertices
256  pNode->clear();
257  pNode->reserve(refMesh->n_vertices(),refMesh->n_vertices(),refMesh->n_vertices() );
258  }
259 
260  // Get a bsp for the target, as we will project the reference mesh onto the target mesh.
261  // It will be build automatically at this point.
263 
264  // ================================================================
265  // Compute mean curvature on both meshes ( if plugin is available )
266  // ================================================================
267  bool meanCurvature = false;
270 
271  if ( _computeMean ) {
272  emit pluginExists( "meancurvature" , meanCurvature );
273 
274  if ( meanCurvature ) {
275  RPC::callFunction("meancurvature","computeMeanCurvature",_sourceId);
276  RPC::callFunction("meancurvature","computeMeanCurvature",_targetId);
277  }
278 
279  if( meanCurvature &&
280  ((!refMesh->get_property_handle( meanRef , "Mean Curvature") ) ||
281  (!compMesh->get_property_handle( meanComp, "Mean Curvature") ))) {
282  meanCurvature = false;
283  }
284  }
285 
286  // ================================================================
287  // Compute mean curvature on both meshes ( if plugin is available )
288  // ================================================================
289  bool gaussCurvature = false;
292 
293  if ( _computeGauss ) {
294  emit pluginExists( "gausscurvature" , gaussCurvature );
295 
296  if ( gaussCurvature ) {
297  RPC::callFunction("gausscurvature","computeGaussCurvature",_sourceId);
298  RPC::callFunction("gausscurvature","computeGaussCurvature",_targetId);
299  }
300 
301  if( gaussCurvature &&
302  ((!refMesh->get_property_handle( gaussRef , "Gaussian Curvature") ) ||
303  (!compMesh->get_property_handle( gaussComp, "Gaussian Curvature") ))) {
304  gaussCurvature = false;
305  }
306  }
307 
308 
309 
310  // ================================================================
311  // Remember the maximal values as output and for specifying color coding range
312  // ================================================================
313  maximalDistance_ = -1.0;
314  maxNormalDeviation_ = -1.0;
315  maxMeanCurvatureDev_ = -1.0;
316  maxGaussCurvatureDev_ = -1.0;
317 
318 
319  // Remember distances for colorCoding after we know the maximal distance
320  std::vector<double> distances;
321  std::vector<double> normalAngles;
322  std::vector<double> meanCurvatures;
323  std::vector<double> gaussCurvatures;
324 
325  for ( TriMesh::VertexIter v_it = refMesh->vertices_begin() ; v_it != refMesh->vertices_end(); ++ v_it) {
326 
327  TriMeshObject::OMTriangleBSP::NearestNeighbor nearest = compBSP->nearest(refMesh->point(*v_it));
328  TriMesh::FaceHandle closestFace = nearest.handle;
329 
330  // Remember the maximal distance between the meshes
331  if (nearest.dist > maximalDistance_)
332  maximalDistance_ = nearest.dist;
333 
334  // Remember distance for color coding
335  distances.push_back(nearest.dist);
336 
337  // Get the vertices around that face and their properties
338  TriMesh::CFVIter fv_it = compMesh->cfv_iter(closestFace);
339 
340  const TriMesh::Point& p0 = compMesh->point(*fv_it);
341  const TriMesh::Normal n0 = compMesh->normal(*fv_it);
342  const TriMesh::VertexHandle& v0 = *fv_it;
343 
344  const TriMesh::Point& p1 = compMesh->point(*(++fv_it));
345  const TriMesh::Normal n1 = compMesh->normal(*fv_it);
346  const TriMesh::VertexHandle& v1 = *fv_it;
347 
348  const TriMesh::Point& p2 = compMesh->point(*(++fv_it));
349  const TriMesh::Normal n2 = compMesh->normal(*fv_it);
350  const TriMesh::VertexHandle& v2 = *fv_it;
351 
352  // project original point to current mesh
353  TriMesh::Point projectedPoint;
354  ACG::Geometry::distPointTriangle(refMesh->point(*v_it), p0, p1, p2, projectedPoint);
355 
356  // Add the position to the point node
357  if (pNode)
358  pNode->add_point(projectedPoint);
359 
360  // compute Barycentric coordinates
361  ACG::Geometry::baryCoord(projectedPoint, p0, p1, p2, projectedPoint);
362 
363  if ( _computeNormal) {
364  // interpolate normal on the compare mesh at the projected point via barycentric coordinates.
365  TriMesh::Normal normal;
366  normal = n0 * projectedPoint[0];
367  normal += n1 * projectedPoint[1];
368  normal += n2 * projectedPoint[2];
369  normal.normalize();
370 
371  // Compute normal deviation in degrees
372  double normalDeviation = (refMesh->normal(*v_it) | normal);
373 
374  if (normalDeviation < -1.0)
375  normalDeviation = -1.0;
376  else if (normalDeviation > 1.0)
377  normalDeviation = 1.0;
378 
379  normalDeviation = 180.0 / M_PI * acos(normalDeviation);
380 
381  // Remember normal deviation for color coding
382  normalAngles.push_back(normalDeviation);
383 
384  if (normalDeviation > maxNormalDeviation_)
385  maxNormalDeviation_ = normalDeviation;
386  }
387 
388  if (meanCurvature) {
389 
390  TriMesh::Scalar curvature = compMesh->property(meanComp, v0) * projectedPoint[0] +
391  compMesh->property(meanComp, v1) * projectedPoint[1] +
392  compMesh->property(meanComp, v2) * projectedPoint[2];
393 
394  const double curvatureDev = fabs(refMesh->property(meanRef, *v_it) - curvature);
395 
396  meanCurvatures.push_back(curvatureDev);
397 
398  if (curvatureDev > maxMeanCurvatureDev_)
399  maxMeanCurvatureDev_ = curvatureDev;
400  }
401 
402  if (gaussCurvature) {
403 
404  TriMesh::Scalar curvature = compMesh->property(gaussComp, v0) * projectedPoint[0] +
405  compMesh->property(gaussComp, v1) * projectedPoint[1] +
406  compMesh->property(gaussComp, v2) * projectedPoint[2];
407 
408  const double curvatureDev = fabs(refMesh->property(gaussRef, *v_it) - curvature);
409 
410  gaussCurvatures.push_back(curvatureDev);
411 
412  if (curvatureDev > maxGaussCurvatureDev_)
413  maxGaussCurvatureDev_ = curvatureDev;
414  }
415 
416  }
417 
418  // Generate the colors
419  if ( pNode ) {
420 
421  tool_->minValue->setText( QString::number(0.0) );
422 
423  if ( tool_->distance->isChecked() ) {
424  visualizeData(distances,maximalDistance_,pNode);
425  } else if ( tool_->normalAngle->isChecked() ) {
426  visualizeData(normalAngles,maxNormalDeviation_,pNode);
427  } else if ( tool_->meanCurvature->isChecked() ) {
428  visualizeData(meanCurvatures,maxMeanCurvatureDev_,pNode);
429  } else if ( tool_->gaussCurvature->isChecked() ) {
430  visualizeData(gaussCurvatures,maxGaussCurvatureDev_,pNode);
431  }
432 
433  emit updateView();
434  }
435 
436 }
437 
438 void MeshComparePlugin::visualizeData( const std::vector<double>& _data, double _maxValue, ACG::SceneGraph::PointNode* _pnode ) {
439 
440  // Set the current real maximal value in the label to show it to the user
441  tool_->maxValue->setText( QString::number(_maxValue) );
442 
443  // If the clamping check box is set, we take the values from the spin boxes
444  double min = 0.0;
445  double max = 1.0;
446  if ( tool_->doClamp->isChecked() ) {
447  min = tool_->minVal->value();
448  max = std::min(tool_->maxVal->value(),_maxValue);
449  } else
450  max = _maxValue;
451 
452  ACG::ColorCoder cCoder(min,max);
453 
454  for ( unsigned int i = 0 ; i < _data.size() ; ++i) {
455  _pnode->add_color(cCoder.color_float4(_data[i]));
456  }
457 
458 #ifdef WITH_QWT
459  plot_->setMinMax(min,max);
460  plot_->setFunction( _data );
461  plot_->replot();
462 #endif
463 
464 }
465 
466 #if QT_VERSION < 0x050000
467  Q_EXPORT_PLUGIN2( meshcompareplugin , MeshComparePlugin );
468 #endif
QString name()
Return a name for the plugin.
Definition: MemInfo.hh:80
DrawMode POINTS_COLORED
draw colored, but not lighted points (requires point colors)
Definition: DrawModes.cc:80
virtual void setSlotDescription(QString _slotName, QString _slotDescription, QStringList _parameters, QStringList _descriptions)
Set a description for a public slot.
void slotClampBox(bool _checked)
If the checkbox is changed to be checked, the values in the labels will be written into the spin boxe...
DrawModes::DrawMode drawMode() const
Return the own draw modes of this node.
Definition: MeshNode2T.cc:461
void slotClear()
Clears the visualization.
void clear()
clear points and normals and colors
Definition: PointNode.hh:147
Type for a MeshObject containing a triangle mesh.
Definition: TriangleMesh.hh:73
bool getObject(int _identifier, BSplineCurveObject *&_object)
const QStringList SOURCE_OBJECTS("source")
Iterable object range.
void compareButton()
Triggers comparison of the selected meshes.
const QStringList TARGET_OBJECTS("target")
Iterable object range.
void visualizeData(const std::vector< double > &_data, double _maxValue, ACG::SceneGraph::PointNode *_pnode)
Visualize data.
ACG::SceneGraph::MaterialNode MaterialNode
Materialnode.
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:93
int id() const
Definition: BaseObject.cc:201
const QStringList ALL_OBJECTS
Iterable object range.
QtTranslationManipulatorNode * manipulatorNode()
void add_point(const ACG::Vec3d &_p)
add point
Definition: PointNode.hh:131
Update type class.
Definition: UpdateType.hh:70
ACG::Vec4f color_float4(float _v) const
color coding
Definition: ColorCoder.cc:119
void reserve(unsigned int _np, unsigned int _nn, unsigned int _nc)
reserve mem for _np points and _nn normals
Definition: PointNode.hh:126
Kernel::Normal Normal
Normal type.
Definition: PolyMeshT.hh:117
bool getAdditionalNode(NodeT *&_node, QString _pluginName, QString _nodeName, int _id=0)
get an addition node from the object
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:333
OMTriangleBSP * requestTriangleBsp()
Definition: MeshObjectT.cc:756
TriMeshObject * triMeshObject(BaseObjectData *_object)
Cast an BaseObject to a TriMeshObject if possible.
Kernel::VertexHandle VertexHandle
Handle for referencing the corresponding item.
Definition: PolyMeshT.hh:139
unsigned int applyProperties() const
get properties that will be applied (OR&#39;ed ApplyProperties)
void slotObjectUpdated(int _identifier, const UpdateType &_type)
Called when an object gets updated.
virtual void updateView()
Update current view in Main Application.
bool removeAdditionalNode(NodeT *&_node, QString _pluginName, QString _nodeName, int _id=0)
remove an additional node from the object
NearestNeighbor nearest(const Point &_p) const
Return handle of the nearest neighbor face.
Definition: BSPImplT.cc:76
Kernel::Point Point
Coordinate type.
Definition: PolyMeshT.hh:115
bool addAdditionalNode(NodeT *_node, QString _pluginName, QString _nodeName, int _id=0)
add an additional node to the object
#define DATA_POLY_MESH
Definition: PolyMesh.hh:65
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:75
Kernel::Scalar Scalar
Scalar type.
Definition: PolyMeshT.hh:113
QScriptValue callFunction(QString _plugin, QString _functionName, std::vector< QScriptValue > _parameters)
Call a function provided by a plugin getting multiple parameters.
Definition: RPCWrappers.cc:63
#define DATA_TRIANGLE_MESH
Definition: TriangleMesh.hh:66
DLLEXPORT ObjectIterator objectsEnd()
Return Iterator to Object End.
TriMesh * triMesh(BaseObjectData *_object)
Get a triangle mesh from an object.
void add_color(const ACG::Vec4f &_c)
add color
Definition: PointNode.hh:135
void set_point_size(float _sz)
set point size (default: 1.0)