Developer Documentation
Loading...
Searching...
No Matches
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
59
60MeshComparePlugin::MeshComparePlugin() :
61 tool_(nullptr),
62 toolIcon_(nullptr),
63 maximalDistance_(-1),
64 maxNormalDeviation_(-1),
65 maxMeanCurvatureDev_(-1),
66 maxGaussCurvatureDev_(-1),
67 plot_(0)
68{
69
70}
71
72MeshComparePlugin::~MeshComparePlugin()
73{
74 delete toolIcon_;
75}
76
77void MeshComparePlugin::initializePlugin()
78{
79 if ( OpenFlipper::Options::gui()) {
81
82 connect( tool_->compare, SIGNAL(clicked()), this, SLOT(compareButton()) );
83 connect( tool_->clear, SIGNAL(clicked()), this, SLOT(slotClear()) );
84
85 toolIcon_ = new QIcon(OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator()+"MeshCompare.png");
86 emit addToolbox( tr("Mesh Compare") , tool_ , toolIcon_);
87
88 connect(tool_->doClamp,SIGNAL(toggled( bool)),this,SLOT(slotClampBox(bool)));
89
90 QVBoxLayout* layout = new QVBoxLayout(tool_->frame);
91
92 plot_ = new QChartsPlot(tool_);
93 plot_->setMinimumHeight(300);
94
95 layout->addWidget(plot_);
96 }
97}
98
99bool MeshComparePlugin::eventFilter(QObject *obj, QEvent *event) {
100 if (event->type() != QEvent::Move) {
101 std::cout << "Event: " << event->type() << std::endl;
102 }
103 return false;
104}
105
106void 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 tool_->selection->isChecked());
181 } else {
182 emit log(LOGERR,tr("Please select one source and one target mesh to compare! Source will be the reference mesh."));
183 }
184
185
186}
187
188void MeshComparePlugin::slotObjectUpdated( int _identifier, const UpdateType& _type ) {
189 // Get source and target objects
190 BaseObjectData* object = 0;
191
192 PluginFunctions::getObject(_identifier,object);
193
194 if ( object ) {
195 ACG::SceneGraph::MaterialNode *pMatNode = 0;
196 if ( object->getAdditionalNode(pMatNode,name(), "MeshCompareDistanceMaterial" ) )
197 object->removeAdditionalNode(pMatNode,name(),"MeshCompareDistanceMaterial");
198 }
199
200}
201
203
205
206 ACG::SceneGraph::MaterialNode *pMatNode = 0;
207 if ( o_it->getAdditionalNode(pMatNode,name(), "MeshCompareDistanceMaterial" ) )
208 o_it->removeAdditionalNode(pMatNode,name(),"MeshCompareDistanceMaterial");
209
210 }
211
212 emit updateView();
213
214}
215
217 if ( _checked ) {
218 tool_->minVal->setValue(tool_->minValue->text().toDouble());
219 tool_->maxVal->setValue(tool_->maxValue->text().toDouble());
220 }
221}
222
223void MeshComparePlugin::compare(int _sourceId,int _targetId,bool _computeDist, bool _computeNormal, bool _computeGauss , bool _computeMean, bool _selection) {
224
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
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;
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
326 for (auto v_it : refMesh->vertices()) {
327 if ( _selection && refMesh->status(v_it).selected() == false) {
328 continue;
329 }
330
331 TriMeshObject::OMTriangleBSP::NearestNeighbor nearest = compBSP->nearest(refMesh->point(v_it));
332 TriMesh::FaceHandle closestFace = nearest.handle;
333
334 // Remember the maximal distance between the meshes
335 if (nearest.dist > maximalDistance_)
336 maximalDistance_ = nearest.dist;
337
338 // Remember distance for color coding
339 distances.push_back(nearest.dist);
340
341 // Get the vertices around that face and their properties
342 TriMesh::CFVIter fv_it = compMesh->cfv_iter(closestFace);
343
344 const TriMesh::Point& p0 = compMesh->point(*fv_it);
345 const TriMesh::Normal n0 = compMesh->normal(*fv_it);
346 const TriMesh::VertexHandle& v0 = *fv_it;
347
348 const TriMesh::Point& p1 = compMesh->point(*(++fv_it));
349 const TriMesh::Normal n1 = compMesh->normal(*fv_it);
350 const TriMesh::VertexHandle& v1 = *fv_it;
351
352 const TriMesh::Point& p2 = compMesh->point(*(++fv_it));
353 const TriMesh::Normal n2 = compMesh->normal(*fv_it);
354 const TriMesh::VertexHandle& v2 = *fv_it;
355
356 // project original point to current mesh
357 TriMesh::Point projectedPoint;
358 ACG::Geometry::distPointTriangle(refMesh->point(v_it), p0, p1, p2, projectedPoint);
359
360 // Add the position to the point node
361 if (pNode)
362 pNode->add_point(projectedPoint);
363
364 // compute Barycentric coordinates
365 ACG::Geometry::baryCoord(projectedPoint, p0, p1, p2, projectedPoint);
366
367 if ( _computeNormal) {
368 // interpolate normal on the compare mesh at the projected point via barycentric coordinates.
369 TriMesh::Normal normal;
370 normal = n0 * projectedPoint[0];
371 normal += n1 * projectedPoint[1];
372 normal += n2 * projectedPoint[2];
373 normal.normalize();
374
375 // Compute normal deviation in degrees
376 double normalDeviation = (refMesh->normal(v_it) | normal);
377
378 if (normalDeviation < -1.0)
379 normalDeviation = -1.0;
380 else if (normalDeviation > 1.0)
381 normalDeviation = 1.0;
382
383 normalDeviation = 180.0 / M_PI * acos(normalDeviation);
384
385 // Remember normal deviation for color coding
386 normalAngles.push_back(normalDeviation);
387
388 if (normalDeviation > maxNormalDeviation_)
389 maxNormalDeviation_ = normalDeviation;
390 }
391
392 if (meanCurvature) {
393
394 TriMesh::Scalar curvature = compMesh->property(meanComp, v0) * projectedPoint[0] +
395 compMesh->property(meanComp, v1) * projectedPoint[1] +
396 compMesh->property(meanComp, v2) * projectedPoint[2];
397
398 const double curvatureDev = fabs(refMesh->property(meanRef, v_it) - curvature);
399
400 meanCurvatures.push_back(curvatureDev);
401
402 if (curvatureDev > maxMeanCurvatureDev_)
403 maxMeanCurvatureDev_ = curvatureDev;
404 }
405
406 if (gaussCurvature) {
407
408 TriMesh::Scalar curvature = compMesh->property(gaussComp, v0) * projectedPoint[0] +
409 compMesh->property(gaussComp, v1) * projectedPoint[1] +
410 compMesh->property(gaussComp, v2) * projectedPoint[2];
411
412 const double curvatureDev = fabs(refMesh->property(gaussRef, v_it) - curvature);
413
414 gaussCurvatures.push_back(curvatureDev);
415
416 if (curvatureDev > maxGaussCurvatureDev_)
417 maxGaussCurvatureDev_ = curvatureDev;
418 }
419
420 }
421
422 // Generate the colors
423 if ( pNode ) {
424
425 tool_->minValue->setText( QString::number(0.0) );
426
427 if ( tool_->distance->isChecked() ) {
428 visualizeData(distances,maximalDistance_,pNode);
429 } else if ( tool_->normalAngle->isChecked() ) {
430 visualizeData(normalAngles,maxNormalDeviation_,pNode);
431 } else if ( tool_->meanCurvature->isChecked() ) {
432 visualizeData(meanCurvatures,maxMeanCurvatureDev_,pNode);
433 } else if ( tool_->gaussCurvature->isChecked() ) {
434 visualizeData(gaussCurvatures,maxGaussCurvatureDev_,pNode);
435 }
436
437 emit updateView();
438 }
439
440}
441
442void MeshComparePlugin::visualizeData( const std::vector<double>& _data, double _maxValue, ACG::SceneGraph::PointNode* _pnode ) {
443
444 // Set the current real maximal value in the label to show it to the user
445 tool_->maxValue->setText( QString::number(_maxValue) );
446
447 // If the clamping check box is set, we take the values from the spin boxes
448 double min = 0.0;
449 double max = 1.0;
450 if ( tool_->doClamp->isChecked() ) {
451 min = tool_->minVal->value();
452 max = std::min(tool_->maxVal->value(),_maxValue);
453 } else
454 max = _maxValue;
455
456 ACG::ColorCoder cCoder(min,max);
457
458 for ( unsigned int i = 0 ; i < _data.size() ; ++i) {
459 _pnode->add_color(cCoder.color_float4(_data[i]));
460 }
461
462 plot_->setMinMax(min,max);
463 plot_->setFunction( _data );
464 plot_->replot();
465
466}
467
@ LOGERR
#define DATA_POLY_MESH
Definition PolyMesh.hh:59
#define DATA_TRIANGLE_MESH
Class for generating nice colors for doubles.
Definition ColorCoder.hh:68
DrawModes::DrawMode drawMode() const
Return the own draw modes of this node.
Definition BaseNode.hh:430
unsigned int applyProperties() const
get properties that will be applied (OR'ed ApplyProperties)
void set_point_size(float _sz)
set point size (default: 1.0)
void add_color(const ACG::Vec4f &_c)
add color
Definition PointNode.hh:129
void add_point(const ACG::Vec3d &_p)
add point
Definition PointNode.hh:125
void clear()
clear points and normals and colors
Definition PointNode.hh:141
void reserve(unsigned int _np, unsigned int _nn, unsigned int _nc)
reserve mem for _np points and _nn normals
Definition PointNode.hh:120
NearestNeighbor nearest(const Point &_p) const
Return handle of the nearest neighbor face.
bool getAdditionalNode(NodeT *&_node, QString _pluginName, QString _nodeName, int _id=0)
get an addition node from the object
QtTranslationManipulatorNode * manipulatorNode()
bool addAdditionalNode(NodeT *_node, QString _pluginName, QString _nodeName, int _id=0)
add an additional node to the object
int id() const
QString name()
Return a name for the plugin.
void slotObjectUpdated(int _identifier, const UpdateType &_type)
Called when an object gets updated.
MeshCompareToolbarWidget * tool_
The toolbar widget of this plugin.
void compare(int _sourceId, int _targetId, bool _computeDist=true, bool _computeNormal=true, bool _computeGauss=true, bool _computeMean=true, bool _selection_=false)
double maxGaussCurvatureDev_
Last maximal gauss curvature deviation.
void slotClear()
Clears the visualization.
void compareButton()
Triggers comparison of the selected meshes.
double maxMeanCurvatureDev_
Last maximal mean curvature deviation.
void visualizeData(const std::vector< double > &_data, double _maxValue, ACG::SceneGraph::PointNode *_pnode)
Visualize data.
QIcon * toolIcon_
Icon for the toolbar.
double maximalDistance_
Last maximal computed distance.
double maxNormalDeviation_
Last maximal computed normal deviation in degree.
void slotClampBox(bool _checked)
If the checkbox is changed to be checked, the values in the labels will be written into the spin boxe...
OMTriangleBSP * requestTriangleBsp()
Kernel::VertexHandle VertexHandle
Handle for referencing the corresponding item.
Definition PolyMeshT.hh:136
Kernel::Scalar Scalar
Scalar type.
Definition PolyMeshT.hh:110
Kernel::Normal Normal
Normal type.
Definition PolyMeshT.hh:114
Kernel::FaceHandle FaceHandle
Scalar type.
Definition PolyMeshT.hh:139
Kernel::Point Point
Coordinate type.
Definition PolyMeshT.hh:112
void setFunction(const std::vector< double > &_values)
set the function to plot
Type for a MeshObject containing a triangle mesh.
Update type class.
Definition UpdateType.hh:59
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
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)
DrawMode POINTS_COLORED
draw colored, but not lighted points (requires point colors)
Definition DrawModes.cc:74
DLLEXPORT ObjectIterator objectsEnd()
Return Iterator to Object End.
TriMeshObject * triMeshObject(BaseObjectData *_object)
Cast an BaseObject to a TriMeshObject if possible.
bool getObject(const int _identifier, BaseObject *&_object)
Get the object which has the given identifier.
TriMesh * triMesh(BaseObjectData *_object)
Get a triangle mesh from an object.
const QStringList SOURCE_OBJECTS("source")
Iterable object range.
const QStringList TARGET_OBJECTS("target")
Iterable object range.
const QStringList ALL_OBJECTS
Iterable object range.
void callFunction(QString _plugin, QString _functionName)
call a function in another plugin
Store nearest neighbor information.
Definition BSPImplT.hh:87