MeshComparePlugin.cc 17.8 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/*===========================================================================*\
*                                                                            *
*                              OpenFlipper                                   *
*      Copyright (C) 2001-2011 by Computer Graphics Group, RWTH Aachen       *
*                           www.openflipper.org                              *
*                                                                            *
*--------------------------------------------------------------------------- *
*  This file is part of OpenFlipper.                                         *
*                                                                            *
*  OpenFlipper is free software: you can redistribute it and/or modify       *
*  it under the terms of the GNU Lesser General Public License as            *
*  published by the Free Software Foundation, either version 3 of            *
*  the License, or (at your option) any later version with the               *
*  following exceptions:                                                     *
*                                                                            *
*  If other files instantiate templates or use macros                        *
*  or inline functions from this file, or you compile this file and          *
*  link it with other files to produce an executable, this file does         *
*  not by itself cause the resulting executable to be covered by the         *
*  GNU Lesser General Public License. This exception does not however        *
*  invalidate any other reasons why the executable file might be             *
*  covered by the GNU Lesser General Public License.                         *
*                                                                            *
*  OpenFlipper is distributed in the hope that it will be useful,            *
*  but WITHOUT ANY WARRANTY; without even the implied warranty of            *
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
*  GNU Lesser General Public License for more details.                       *
*                                                                            *
*  You should have received a copy of the GNU LesserGeneral Public           *
*  License along with OpenFlipper. If not,                                   *
*  see <http://www.gnu.org/licenses/>.                                       *
*                                                                            *
\*===========================================================================*/

/*===========================================================================*\
*                                                                            *
Jan Möbius's avatar
Jan Möbius committed
37
38
39
*   $Revision$                                                       *
*   $LastChangedBy$                                                *
*   $Date$                     *
40
41
42
43
44
45
46
47
48
49
50
51
52
*                                                                            *
\*===========================================================================*/



#include "MeshComparePlugin.hh"


#include <ObjectTypes/PolyMesh/PolyMesh.hh>
#include <ObjectTypes/TriangleMesh/TriangleMesh.hh>

#include "OpenFlipper/BasePlugin/PluginFunctions.hh"

53
#include <ACG/Geometry/bsp/TriangleBSPT.hh>
54
#include <ACG/Geometry/Algorithms.hh>
55
#include <ACG/Utils/ColorCoder.hh>
56

57

58
59
60
MeshComparePlugin::MeshComparePlugin() :
  tool_(0),
  maximalDistance_(-1),
61
  maxNormalDeviation_(-1),
62
  maxMeanCurvatureDev_(-1),
63
  maxGaussCurvatureDev_(-1)
64
65
66
#ifdef WITH_QWT
  ,plot_(0)
#endif
67
68
69
70
71
72
73
74
75
76
77
{

}

MeshComparePlugin::~MeshComparePlugin()
{

}

void MeshComparePlugin::initializePlugin()
{
78
79
  if ( OpenFlipper::Options::gui()) {
    tool_ = new MeshCompareToolbarWidget();
80

81
    connect( tool_->compare, SIGNAL(clicked()), this, SLOT(compareButton()) );
82
    connect( tool_->clear, SIGNAL(clicked()), this, SLOT(slotClear()) );
83

84
85
    QIcon* toolIcon = new QIcon(OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator()+"MeshCompare.png");
    emit addToolbox( tr("Mesh Compare") , tool_ , toolIcon);
86
87
88
89
90
91
92
93
94
95
96

    connect(tool_->doClamp,SIGNAL(toggled( bool)),this,SLOT(slotClampBox(bool)));

#ifdef WITH_QWT
    QVBoxLayout* layout = new QVBoxLayout(tool_->frame);

    plot_ = new QwtFunctionPlot(0);
    plot_->setMinimumHeight(150);

    layout->addWidget(plot_);
#else
Jan Möbius's avatar
Jan Möbius committed
97
    // Hide the extra frame as QWT is not available, we will not have an histogramm
98
99
    tool_->frame->hide();
#endif
100
  }
101
102
103
104
}

void MeshComparePlugin::pluginsInitialized() {
    
105
106
107
  //===========================================================
  // Describe scrpting slots
  //===========================================================
108
109
110
111
112
113
  emit setSlotDescription(tr("compare(int,int)"), tr("Compare two meshes. Use lastMaximalDistance() and lastMaximalNormalDeviation() to get the results."),
      QStringList(tr("ObjectId,ObjectId")), QStringList(tr("Id of the reference mesh, Id of the comparison mesh")));
  emit setSlotDescription(tr("lastMaximalDistance()"), tr("Get the maximal distance between the meshes of the last comparison."),
      QStringList(tr("")), QStringList(tr("")));
  emit setSlotDescription(tr("lastMaximalNormalDeviation()"), tr("Get the maximal normal deviation in degree between the meshes of the last comparison."),
      QStringList(tr("")), QStringList(tr("")));
114
  emit setSlotDescription(tr("lastMaximalMeanCurvatureDeviation()"), tr("Get the maximal mean curvature deviation between the meshes of the last comparison."),
115
116
117
      QStringList(tr("")), QStringList(tr("")));
  emit setSlotDescription(tr("lastMaximalGaussCurvatureDeviation()"), tr("Get the maximal gauss curvature deviation between the meshes of the last comparison."),
      QStringList(tr("")), QStringList(tr("")));
118

119
  //===========================================================
120
  // Check mean curvature plugin and disable the box in gui mode
121
  //===========================================================
122
123
124
125
126
  bool meanCurvature = false;
  emit pluginExists( "meancurvature" , meanCurvature  );

  if ( OpenFlipper::Options::gui() && !meanCurvature )
    tool_->meanCurvature->setEnabled(false);
127
128
129
130
131
132
133
134
135

  //===========================================================
  // Check gauss curvature plugin and disable the box in gui mode
  //===========================================================
  bool gaussCurvature = false;
  emit pluginExists( "gausscurvature" , gaussCurvature  );

  if ( OpenFlipper::Options::gui() && !gaussCurvature )
    tool_->gaussCurvature->setEnabled(false);
136
137
}

138
void MeshComparePlugin::compareButton() {
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172

  // Get source and target objects
  BaseObjectData* targetObject = 0;
  for ( PluginFunctions::ObjectIterator o_it(PluginFunctions::TARGET_OBJECTS) ; o_it != PluginFunctions::objectsEnd(); ++o_it) {

    if ( o_it->dataType(DATA_TRIANGLE_MESH)  || o_it->dataType(DATA_POLY_MESH) ) {

      // If we found a second target, something is wrong!
      if ( targetObject != 0 ) {
        emit log(LOGERR,tr("Please select one source and one target mesh to compare! Source will be the reference mesh."));
        return;
      }

      targetObject = (*o_it);
    }
  }

  BaseObjectData* sourceObject = 0;
  for ( PluginFunctions::ObjectIterator o_it(PluginFunctions::SOURCE_OBJECTS) ; o_it != PluginFunctions::objectsEnd(); ++o_it) {

    if ( o_it->dataType(DATA_TRIANGLE_MESH)  || o_it->dataType(DATA_POLY_MESH) ) {

      // If we found a second target, something is wrong!
      if ( sourceObject != 0 ) {
        emit log(LOGERR,tr("Please select one source and one target mesh to compare! Source will be the reference mesh."));
        return;
      }

      sourceObject = (*o_it);
    }
  }


  if ( (targetObject != 0) && (sourceObject != 0)  ) {
173
174
175
176
    compare(sourceObject->id(),targetObject->id(), tool_->distance->isChecked() ,
                                                   tool_->normalAngle->isChecked(),
                                                   tool_->gaussCurvature->isChecked(),
                                                   tool_->meanCurvature->isChecked() );
177
178
179
180
181
182
183
  } else {
    emit log(LOGERR,tr("Please select one source and one target mesh to compare! Source will be the reference mesh."));
  }


}

184
185
186
187
188
189
190
191
192
193
194
195
196
197
void MeshComparePlugin::slotObjectUpdated( int _identifier, const UpdateType& _type ) {
  // Get source and target objects
  BaseObjectData* object = 0;

  PluginFunctions::getObject(_identifier,object);

  if ( object ) {
    ACG::SceneGraph::MaterialNode *pMatNode = 0;
    if ( object->getAdditionalNode(pMatNode,name(), "MeshCompareDistanceMaterial" ) )
      object->removeAdditionalNode(pMatNode,name(),"MeshCompareDistanceMaterial");
  }

}

198
199
200
201
202
203
204
205
206
207
void MeshComparePlugin::slotClear() {

  for ( PluginFunctions::ObjectIterator o_it(PluginFunctions::ALL_OBJECTS) ; o_it != PluginFunctions::objectsEnd(); ++o_it) {

    ACG::SceneGraph::MaterialNode *pMatNode = 0;
    if ( o_it->getAdditionalNode(pMatNode,name(), "MeshCompareDistanceMaterial" ) )
      o_it->removeAdditionalNode(pMatNode,name(),"MeshCompareDistanceMaterial");

  }

208
209
  emit updateView();

210
211
}

212
213
214
215
216
217
218
void MeshComparePlugin::slotClampBox(bool _checked) {
  if ( _checked ) {
    tool_->minVal->setValue(tool_->minValue->text().toDouble());
    tool_->maxVal->setValue(tool_->maxValue->text().toDouble());
  }
}

219
void MeshComparePlugin::compare(int _sourceId,int _targetId,bool _computeDist, bool _computeNormal, bool _computeGauss , bool _computeMean) {
220
221
222
223
224
225
226
227
228
229
230
231
232
233


  TriMeshObject* source = PluginFunctions::triMeshObject(_sourceId);
  TriMeshObject* target = PluginFunctions::triMeshObject(_targetId);

  if ( (target == 0 ) || (source == 0) ) {
    emit log(LOGERR,tr("Please select one source and one target mesh to compare! Source will be the reference mesh."));
    emit log(LOGERR,tr("Only triangle meshes are currently supported!"));
    return;
  }

  TriMesh* refMesh  = PluginFunctions::triMesh(_sourceId);
  TriMesh* compMesh = PluginFunctions::triMesh(_targetId);

234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
  ACG::SceneGraph::PointNode *pNode = 0;

  // Check if we already attached a PointNode
  if ( OpenFlipper::Options::gui() ) {

    if ( !target->getAdditionalNode(pNode,name(), "MeshCompareDistance" ) ) {

      ACG::SceneGraph::MaterialNode *pMatNode = 0;
      pMatNode = new ACG::SceneGraph::MaterialNode(target->manipulatorNode(), "MeshCompare distance visualization material");
      target->addAdditionalNode(pMatNode, name(), "MeshCompareDistanceMaterial");
      pMatNode->set_point_size(5);
      pMatNode->applyProperties(ACG::SceneGraph::MaterialNode::PointSize);

      pNode = new ACG::SceneGraph::PointNode(pMatNode, "MeshCompare distance visualization");
      pNode->drawMode(ACG::SceneGraph::DrawModes::POINTS_COLORED);
      target->addAdditionalNode(pNode, name(), "MeshCompareDistance");
    }
251

252
253
254
    // Clear but reserve memory for the required vertices
    pNode->clear();
    pNode->reserve(refMesh->n_vertices(),refMesh->n_vertices(),refMesh->n_vertices() );
255
256
  }

257
258
259
260
  // Get a bsp for the target, as we will project the reference mesh onto the target mesh.
  // It will be build automatically at this point.
  TriMeshObject::OMTriangleBSP* compBSP = target->requestTriangleBsp();

261
262
263
264
265
266
267
  // ================================================================
  // Compute mean curvature on both meshes ( if plugin is available )
  // ================================================================
  bool meanCurvature = false;
  OpenMesh::VPropHandleT< double > meanRef;
  OpenMesh::VPropHandleT< double > meanComp;

268
269
270
271
272
273
274
275
276
277
278
279
280
  if ( _computeMean ) {
    emit pluginExists( "meancurvature" , meanCurvature  );

    if ( meanCurvature ) {
      RPC::callFunction("meancurvature","computeMeanCurvature",_sourceId);
      RPC::callFunction("meancurvature","computeMeanCurvature",_targetId);
    }

    if( meanCurvature &&
        ((!refMesh->get_property_handle(  meanRef , "Mean Curvature") ) ||
            (!compMesh->get_property_handle( meanComp, "Mean Curvature") ))) {
      meanCurvature = false;
    }
281
282
  }

283
284
285
286
287
288
289
  // ================================================================
  // Compute mean curvature on both meshes ( if plugin is available )
  // ================================================================
  bool gaussCurvature = false;
  OpenMesh::VPropHandleT< double > gaussRef;
  OpenMesh::VPropHandleT< double > gaussComp;

290
291
292
293
294
295
296
297
298
299
300
301
302
  if ( _computeGauss ) {
    emit pluginExists( "gausscurvature" , gaussCurvature  );

    if ( gaussCurvature ) {
      RPC::callFunction("gausscurvature","computeGaussCurvature",_sourceId);
      RPC::callFunction("gausscurvature","computeGaussCurvature",_targetId);
    }

    if( gaussCurvature &&
        ((!refMesh->get_property_handle(  gaussRef , "Gaussian Curvature") ) ||
            (!compMesh->get_property_handle( gaussComp, "Gaussian Curvature") ))) {
      gaussCurvature = false;
    }
303
304
305
  }


306
307

  // ================================================================
308
  // Remember the maximal values as output and for specifying color coding range
309
  // ================================================================
310
311
312
313
  maximalDistance_      = -1.0;
  maxNormalDeviation_   = -1.0;
  maxMeanCurvatureDev_  = -1.0;
  maxGaussCurvatureDev_ = -1.0;
314
315
316
317
318


  // Remember distances for colorCoding after we know the maximal distance
  std::vector<double> distances;
  std::vector<double> normalAngles;
319
  std::vector<double> meanCurvatures;
320
  std::vector<double> gaussCurvatures;
321
322
323

  for ( TriMesh::VertexIter v_it = refMesh->vertices_begin() ; v_it != refMesh->vertices_end(); ++ v_it) {

324
    TriMeshObject::OMTriangleBSP::NearestNeighbor nearest = compBSP->nearest(refMesh->point(v_it));
325
326
327
    TriMesh::FaceHandle closestFace = nearest.handle;

    // Remember the maximal distance between the meshes
328
    if (nearest.dist > maximalDistance_)
329
      maximalDistance_ = nearest.dist;
330

331
332
    // Remember distance for color coding
    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
338
339
    const TriMesh::Point& p0 = compMesh->point(fv_it);
    const TriMesh::Normal n0 = compMesh->normal(fv_it);
    const TriMesh::VertexHandle& v0 = fv_it.handle();
340

341
342
343
    const TriMesh::Point& p1 = compMesh->point(++fv_it);
    const TriMesh::Normal n1 = compMesh->normal(fv_it);
    const TriMesh::VertexHandle& v1 = fv_it.handle();
344

345
346
347
    const TriMesh::Point& p2 = compMesh->point(++fv_it);
    const TriMesh::Normal n2 = compMesh->normal(fv_it);
    const TriMesh::VertexHandle& v2 = fv_it.handle();
348
349
350
351
352

    // project original point to current mesh
    TriMesh::Point projectedPoint;
    ACG::Geometry::distPointTriangle(refMesh->point(v_it), p0, p1, p2, projectedPoint);

353
354
355
356
    // Add the position to the point node
    if (pNode)
      pNode->add_point(projectedPoint);

357
358
359
    // compute Barycentric coordinates
    ACG::Geometry::baryCoord(projectedPoint, p0, p1, p2, projectedPoint);

360
361
362
363
364
365
366
    if ( _computeNormal) {
      // interpolate normal on the compare mesh at the projected point via barycentric coordinates.
      TriMesh::Normal normal;
      normal = n0 * projectedPoint[0];
      normal += n1 * projectedPoint[1];
      normal += n2 * projectedPoint[2];
      normal.normalize();
367

368
369
      // Compute normal deviation in degrees
      double normalDeviation = (refMesh->normal(v_it) | normal);
370

371
372
373
374
      if (normalDeviation < -1.0)
        normalDeviation = -1.0;
      else if (normalDeviation > 1.0)
        normalDeviation = 1.0;
375

376
      normalDeviation = 180.0 / M_PI * acos(normalDeviation);
377

378
379
      // Remember normal deviation for color coding
      normalAngles.push_back(normalDeviation);
380

381
382
383
      if (normalDeviation > maxNormalDeviation_)
        maxNormalDeviation_ = normalDeviation;
    }
384
385
386
387

    if (meanCurvature) {

      TriMesh::Scalar curvature = 0.0;
Jan Möbius's avatar
Jan Möbius committed
388
      curvature =  compMesh->property(meanComp, v0) * projectedPoint[0];
389
390
      curvature += compMesh->property(meanComp, v1) * projectedPoint[1];
      curvature += compMesh->property(meanComp, v2) * projectedPoint[2];
391

392
      const double curvatureDev = fabs(refMesh->property(meanRef, v_it) - curvature);
393

394
      meanCurvatures.push_back(curvatureDev);
395

396
      if (curvatureDev > maxMeanCurvatureDev_)
397
398
399
        maxMeanCurvatureDev_ = curvatureDev;
    }

400
401
    if (gaussCurvature) {

402
      TriMesh::Scalar curvature = 0.0;
Jan Möbius's avatar
Jan Möbius committed
403
      curvature =  compMesh->property(gaussComp, v0) * projectedPoint[0];
404
405
      curvature += compMesh->property(gaussComp, v1) * projectedPoint[1];
      curvature += compMesh->property(gaussComp, v2) * projectedPoint[2];
406

407
      const double curvatureDev = fabs(refMesh->property(gaussRef, v_it) - curvature);
408

409
      gaussCurvatures.push_back(curvatureDev);
410

411
412
413
      if (curvatureDev > maxGaussCurvatureDev_)
        maxGaussCurvatureDev_ = curvatureDev;
    }
414

415
416
  }

417
418
419
  // Generate the colors
  if ( pNode ) {

420
421
    tool_->minValue->setText( QString::number(0.0) );

422
    if ( tool_->distance->isChecked() ) {
Jan Möbius's avatar
Jan Möbius committed
423
      visualizeData(distances,maximalDistance_,pNode);
424
    } else if ( tool_->normalAngle->isChecked() ) {
Jan Möbius's avatar
Jan Möbius committed
425
      visualizeData(normalAngles,maxNormalDeviation_,pNode);
426
    } else if ( tool_->meanCurvature->isChecked() ) {
Jan Möbius's avatar
Jan Möbius committed
427
      visualizeData(meanCurvatures,maxMeanCurvatureDev_,pNode);
428
    } else if ( tool_->gaussCurvature->isChecked() ) {
Jan Möbius's avatar
Jan Möbius committed
429
430
      visualizeData(gaussCurvatures,maxGaussCurvatureDev_,pNode);
    }
431

Jan Möbius's avatar
Jan Möbius committed
432
433
    emit updateView();
  }
434

Jan Möbius's avatar
Jan Möbius committed
435
}
436

Jan Möbius's avatar
Jan Möbius committed
437
void MeshComparePlugin::visualizeData( const std::vector<double>& _data, double _maxValue, ACG::SceneGraph::PointNode* _pnode ) {
438

Jan Möbius's avatar
Jan Möbius committed
439
440
  // Set the current real maximal value in the label to show it to the user
  tool_->maxValue->setText( QString::number(_maxValue) );
441

Jan Möbius's avatar
Jan Möbius committed
442
443
444
445
446
447
448
449
  // If the clamping check box is set, we take the values from the spin boxes
  double min = 0.0;
  double max = 1.0;
  if ( tool_->doClamp->isChecked() ) {
    min = tool_->minVal->value();
    max = std::min(tool_->maxVal->value(),_maxValue);
  } else
    max = _maxValue;
450

Jan Möbius's avatar
Jan Möbius committed
451
452
453
454
  ACG::ColorCoder cCoder(min,max);

  for ( unsigned int i = 0 ; i < _data.size() ; ++i) {
    _pnode->add_color(cCoder.color_float4(_data[i]));
455
  }
456

Jan Möbius's avatar
Jan Möbius committed
457
458
459
460
461
462
#ifdef WITH_QWT
  plot_->setMinMax(min,max);
  plot_->setFunction( _data );
  plot_->replot();
#endif

463
464
465
}


Jan Möbius's avatar
Jan Möbius committed
466

467
468
Q_EXPORT_PLUGIN2( meshcompareplugin , MeshComparePlugin );