MeshComparePlugin.cc 18.2 KB
Newer Older
1 2 3
/*===========================================================================*\
*                                                                            *
*                              OpenFlipper                                   *
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 37 38
 *           Copyright (c) 2001-2015, RWTH-Aachen University                 *
 *           Department of Computer Graphics and Multimedia                  *
 *                          All rights reserved.                             *
 *                            www.openflipper.org                            *
 *                                                                           *
 *---------------------------------------------------------------------------*
 * This file is part of OpenFlipper.                                         *
 *---------------------------------------------------------------------------*
 *                                                                           *
 * Redistribution and use in source and binary forms, with or without        *
 * modification, are permitted provided that the following conditions        *
 * are met:                                                                  *
 *                                                                           *
 * 1. Redistributions of source code must retain the above copyright notice, *
 *    this list of conditions and the following disclaimer.                  *
 *                                                                           *
 * 2. Redistributions in binary form must reproduce the above copyright      *
 *    notice, this list of conditions and the following disclaimer in the    *
 *    documentation and/or other materials provided with the distribution.   *
 *                                                                           *
 * 3. Neither the name of the copyright holder nor the names of its          *
 *    contributors may be used to endorse or promote products derived from   *
 *    this software without specific prior written permission.               *
 *                                                                           *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS       *
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A           *
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER *
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,  *
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,       *
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR        *
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF    *
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING      *
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS        *
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.              *
39 40 41 42 43
*                                                                            *
\*===========================================================================*/

/*===========================================================================*\
*                                                                            *
Jan Möbius's avatar
Jan Möbius committed
44 45 46
*   $Revision$                                                       *
*   $LastChangedBy$                                                *
*   $Date$                     *
47 48 49 50 51 52 53 54 55 56 57
*                                                                            *
\*===========================================================================*/



#include "MeshComparePlugin.hh"


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

58
#include <ACG/Utils/ColorCoder.hh>
59

60

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

}

MeshComparePlugin::~MeshComparePlugin()
{

}

void MeshComparePlugin::initializePlugin()
{
81 82
  if ( OpenFlipper::Options::gui()) {
    tool_ = new MeshCompareToolbarWidget();
83

84
    connect( tool_->compare, SIGNAL(clicked()), this, SLOT(compareButton()) );
85
    connect( tool_->clear, SIGNAL(clicked()), this, SLOT(slotClear()) );
86

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

    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
100
    // Hide the extra frame as QWT is not available, we will not have an histogramm
101 102
    tool_->frame->hide();
#endif
103
  }
104 105 106 107
}

void MeshComparePlugin::pluginsInitialized() {
    
108
  //===========================================================
109
  // Describe scripting slots
110
  //===========================================================
111 112 113 114 115 116
  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("")));
117
  emit setSlotDescription(tr("lastMaximalMeanCurvatureDeviation()"), tr("Get the maximal mean curvature deviation between the meshes of the last comparison."),
118 119 120
      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("")));
121

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

  if ( OpenFlipper::Options::gui() && !meanCurvature )
    tool_->meanCurvature->setEnabled(false);
130 131 132 133 134 135 136 137 138

  //===========================================================
  // 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);
139 140
}

141
void MeshComparePlugin::compareButton() {
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 173 174 175

  // 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)  ) {
176 177 178 179
    compare(sourceObject->id(),targetObject->id(), tool_->distance->isChecked() ,
                                                   tool_->normalAngle->isChecked(),
                                                   tool_->gaussCurvature->isChecked(),
                                                   tool_->meanCurvature->isChecked() );
180 181 182 183 184 185 186
  } else {
    emit log(LOGERR,tr("Please select one source and one target mesh to compare! Source will be the reference mesh."));
  }


}

187 188 189 190 191 192 193 194 195 196 197 198 199 200
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");
  }

}

201 202 203 204 205 206 207 208 209 210
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");

  }

211 212
  emit updateView();

213 214
}

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

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


  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);

237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
  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");
    }
254

255 256 257
    // Clear but reserve memory for the required vertices
    pNode->clear();
    pNode->reserve(refMesh->n_vertices(),refMesh->n_vertices(),refMesh->n_vertices() );
258 259
  }

260 261 262 263
  // 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();

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

271 272 273 274 275 276 277 278 279 280 281 282 283
  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;
    }
284 285
  }

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

293 294 295 296 297 298 299 300 301 302 303 304 305
  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;
    }
306 307 308
  }


309 310

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


  // Remember distances for colorCoding after we know the maximal distance
  std::vector<double> distances;
  std::vector<double> normalAngles;
322
  std::vector<double> meanCurvatures;
323
  std::vector<double> gaussCurvatures;
324 325 326

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

Jan Möbius's avatar
Jan Möbius committed
327
    TriMeshObject::OMTriangleBSP::NearestNeighbor nearest = compBSP->nearest(refMesh->point(*v_it));
328 329 330
    TriMesh::FaceHandle closestFace = nearest.handle;

    // Remember the maximal distance between the meshes
331
    if (nearest.dist > maximalDistance_)
332
      maximalDistance_ = nearest.dist;
333

334 335
    // Remember distance for color coding
    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

Jan Möbius's avatar
Jan Möbius committed
340 341 342
    const TriMesh::Point& p0 = compMesh->point(*fv_it);
    const TriMesh::Normal n0 = compMesh->normal(*fv_it);
    const TriMesh::VertexHandle& v0 = *fv_it;
343

Jan Möbius's avatar
Jan Möbius committed
344 345 346
    const TriMesh::Point& p1 = compMesh->point(*(++fv_it));
    const TriMesh::Normal n1 = compMesh->normal(*fv_it);
    const TriMesh::VertexHandle& v1 = *fv_it;
347

Jan Möbius's avatar
Jan Möbius committed
348 349 350
    const TriMesh::Point& p2 = compMesh->point(*(++fv_it));
    const TriMesh::Normal n2 = compMesh->normal(*fv_it);
    const TriMesh::VertexHandle& v2 = *fv_it;
351 352 353

    // project original point to current mesh
    TriMesh::Point projectedPoint;
Jan Möbius's avatar
Jan Möbius committed
354
    ACG::Geometry::distPointTriangle(refMesh->point(*v_it), p0, p1, p2, projectedPoint);
355

356 357 358 359
    // Add the position to the point node
    if (pNode)
      pNode->add_point(projectedPoint);

360 361 362
    // compute Barycentric coordinates
    ACG::Geometry::baryCoord(projectedPoint, p0, p1, p2, projectedPoint);

363 364 365 366 367 368 369
    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();
370

371
      // Compute normal deviation in degrees
Jan Möbius's avatar
Jan Möbius committed
372
      double normalDeviation = (refMesh->normal(*v_it) | normal);
373

374 375 376 377
      if (normalDeviation < -1.0)
        normalDeviation = -1.0;
      else if (normalDeviation > 1.0)
        normalDeviation = 1.0;
378

379
      normalDeviation = 180.0 / M_PI * acos(normalDeviation);
380

381 382
      // Remember normal deviation for color coding
      normalAngles.push_back(normalDeviation);
383

384 385 386
      if (normalDeviation > maxNormalDeviation_)
        maxNormalDeviation_ = normalDeviation;
    }
387 388 389

    if (meanCurvature) {

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

Jan Möbius's avatar
Jan Möbius committed
394
      const double curvatureDev = fabs(refMesh->property(meanRef, *v_it) - curvature);
395

396
      meanCurvatures.push_back(curvatureDev);
397

398
      if (curvatureDev > maxMeanCurvatureDev_)
399 400 401
        maxMeanCurvatureDev_ = curvatureDev;
    }

402 403
    if (gaussCurvature) {

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

Jan Möbius's avatar
Jan Möbius committed
408
      const double curvatureDev = fabs(refMesh->property(gaussRef, *v_it) - curvature);
409

410
      gaussCurvatures.push_back(curvatureDev);
411

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

416 417
  }

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

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

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

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

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

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

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

Jan Möbius's avatar
Jan Möbius committed
443 444 445 446 447 448 449 450
  // 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;
451

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

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

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

464 465
}

466 467 468
#if QT_VERSION < 0x050000
  Q_EXPORT_PLUGIN2( meshcompareplugin , MeshComparePlugin );
#endif