MeshRepairPlugin.cc 37.1 KB
Newer Older
Jan Möbius's avatar
 
Jan Möbius committed
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
 *                                                                            *
 *                              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
33 34 35
\*===========================================================================*/

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

#include <QtGui>

#include "MeshRepairPlugin.hh"

#include <iostream>

#include <OpenFlipper/BasePlugin/PluginFunctions.hh>
#include "OpenFlipper/INIFile/INIFile.hh"

#include <MeshTools/MeshSelectionT.hh>
#include <ACG/Geometry/Algorithms.hh>
#include <Math_Tools/Math_Tools.hh>
Jan Möbius's avatar
Jan Möbius committed
55
#include "MeshFixingT.hh"
Jan Möbius's avatar
 
Jan Möbius committed
56 57 58

//-----------------------------------------------------------------------------

Jan Möbius's avatar
Jan Möbius committed
59 60 61 62 63 64 65 66
MeshRepairPlugin::MeshRepairPlugin() :
tool_(0),
toolIcon_(0)
{

}
//-----------------------------------------------------------------------------

Jan Möbius's avatar
 
Jan Möbius committed
67 68 69 70 71 72 73
void
MeshRepairPlugin::
initializePlugin()
{
  tool_ = new MeshRepairToolbarWidget();
  QSize size(300, 300);
  tool_->resize(size);
74 75


Jan Möbius's avatar
 
Jan Möbius committed
76
  // Vertex Selection/Removal
Jan Möbius's avatar
Jan Möbius committed
77
  connect(tool_->valenceThreeButton, SIGNAL(clicked()), this, SLOT(slotDetectFlatValence3Vertices()) );
Jan Möbius's avatar
 
Jan Möbius committed
78
  connect(tool_->repairRemoveVButton, SIGNAL(clicked()), this, SLOT(slotRemoveSelectedVal3Vertices()) );
79
  connect(tool_->snapBoundaryButton, SIGNAL(clicked()), this, SLOT(slotSnapBoundary()) );
Jan Möbius's avatar
 
Jan Möbius committed
80 81 82

  // Edge Selection/Repairing
  connect(tool_->detectEShorterButton, SIGNAL(clicked()), this, SLOT(slotDetectEdgesShorter()) );
83
  connect(tool_->detectELargerButton, SIGNAL(clicked()), this, SLOT(slotDetectEdgesLonger()) );
Jan Möbius's avatar
 
Jan Möbius committed
84
  connect(tool_->repairCollapseEButton, SIGNAL(clicked()), this, SLOT(slotRemoveSelectedEdges()) );
85 86
  connect(tool_->detectCapAngle, SIGNAL(clicked()), this, SLOT(slotDetectSkinnyTriangleByAngle()) );
  connect(tool_->repairFlipEButton, SIGNAL(clicked()), this, SLOT(slotRemoveSkinnyTriangleByAngle()) );
Jan Möbius's avatar
 
Jan Möbius committed
87
  connect(tool_->detectFoldoverButton, SIGNAL(clicked()), this, SLOT(slotDetectFoldover()) );
88

Jan Möbius's avatar
 
Jan Möbius committed
89 90 91
  //Face operations
  connect(tool_->triangleAspectButton,SIGNAL(clicked()),this,SLOT(slotDetectTriangleAspect()));
  connect(tool_->flipOrientation,SIGNAL(clicked()),this,SLOT(slotFlipOrientation()));
Jan Möbius's avatar
Jan Möbius committed
92
  connect(tool_->fixTopologyButton,SIGNAL(clicked()),this,SLOT(slotFixTopology()));
Jan Möbius's avatar
 
Jan Möbius committed
93

94 95 96 97
  //Normal operations
  connect(tool_->computeNormals,SIGNAL(clicked()),this,SLOT(slotUpdateNormals()));
  connect(tool_->computeVertexNormals,SIGNAL(clicked()),this,SLOT(slotUpdateVertexNormals()));
  connect(tool_->computeFaceNormals,SIGNAL(clicked()),this,SLOT(slotUpdateFaceNormals()));
98
  connect(tool_->computeHalfedgeNormals,SIGNAL(clicked()),this,SLOT(slotUpdateHalfedgeNormals()));
99

Jan Möbius's avatar
Jan Möbius committed
100 101 102
  // General Operations
  connect(tool_->fixMeshButton,SIGNAL(clicked()),this,SLOT(slotFixMesh()));

Jan Möbius's avatar
 
Jan Möbius committed
103 104 105 106
  toolIcon_ = new QIcon(OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator()+"meshrepair-toolbox.png");
  tool_->repairCollapseEButton->setIcon(*toolIcon_);
  tool_->repairFlipEButton->setIcon(*toolIcon_);
  tool_->repairRemoveVButton->setIcon(*toolIcon_);
107

Jan Möbius's avatar
 
Jan Möbius committed
108 109 110
  emit addToolbox( tr("Mesh Repair") , tool_,  toolIcon_);
}

111 112 113
//===========================================================================
// Button Slots
//===========================================================================
Jan Möbius's avatar
 
Jan Möbius committed
114

115
void MeshRepairPlugin::slotRemoveSelectedVal3Vertices() {
Jan Möbius's avatar
 
Jan Möbius committed
116

117
  for ( PluginFunctions::ObjectIterator o_it(PluginFunctions::TARGET_OBJECTS,DataType( DATA_TRIANGLE_MESH ) );  o_it != PluginFunctions::objectsEnd(); ++o_it)
118
    removeSelectedVal3Vertices(o_it->id());
Jan Möbius's avatar
 
Jan Möbius committed
119

120
  emit updateView();
Jan Möbius's avatar
 
Jan Möbius committed
121 122 123 124 125
}

//-----------------------------------------------------------------------------

void MeshRepairPlugin::slotRemoveSelectedEdges(){
126

Jan Möbius's avatar
 
Jan Möbius committed
127 128
  for ( PluginFunctions::ObjectIterator o_it(PluginFunctions::TARGET_OBJECTS,DataType( DATA_TRIANGLE_MESH ) );  o_it != PluginFunctions::objectsEnd(); ++o_it) 
    removeSelectedEdges(o_it->id());
129

Jan Möbius's avatar
 
Jan Möbius committed
130 131 132
  emit updateView();
}

133 134 135
//-----------------------------------------------------------------------------

void MeshRepairPlugin::slotDetectSkinnyTriangleByAngle()
Jan Möbius's avatar
 
Jan Möbius committed
136
{
137
  for ( PluginFunctions::ObjectIterator o_it(PluginFunctions::TARGET_OBJECTS,DataType( DATA_TRIANGLE_MESH | DATA_POLY_MESH ) );  o_it != PluginFunctions::objectsEnd(); ++o_it)
138
    detectSkinnyTriangleByAngle( o_it->id(), tool_->capAngleSpinbox->value(), false );
139

Jan Möbius's avatar
 
Jan Möbius committed
140 141 142
  emit updateView();
}

143 144 145
//-----------------------------------------------------------------------------

void MeshRepairPlugin::slotRemoveSkinnyTriangleByAngle()
Jan Möbius's avatar
 
Jan Möbius committed
146
{
147
  //rewrite!!!
148
  for ( PluginFunctions::ObjectIterator o_it(PluginFunctions::TARGET_OBJECTS,DataType( DATA_TRIANGLE_MESH | DATA_POLY_MESH ) );  o_it != PluginFunctions::objectsEnd(); ++o_it)
149
    detectSkinnyTriangleByAngle( o_it->id(), tool_->capAngleSpinbox->value(), true );
150

Jan Möbius's avatar
 
Jan Möbius committed
151 152 153 154 155 156 157
  emit updateView();
}

//-----------------------------------------------------------------------------

void MeshRepairPlugin::slotDetectFoldover() {

158
  for ( PluginFunctions::ObjectIterator o_it(PluginFunctions::TARGET_OBJECTS,DataType( DATA_TRIANGLE_MESH | DATA_POLY_MESH ) );  o_it != PluginFunctions::objectsEnd(); ++o_it)
159
    detectFoldover(o_it->id(), tool_->detectFoldoverSpinbox->value());
160 161

  emit updateView();
Jan Möbius's avatar
 
Jan Möbius committed
162 163 164 165 166 167
}

//-----------------------------------------------------------------------------

void MeshRepairPlugin::slotDetectTriangleAspect() {

168
  for ( PluginFunctions::ObjectIterator o_it(PluginFunctions::TARGET_OBJECTS,DataType( DATA_TRIANGLE_MESH | DATA_POLY_MESH ) );  o_it != PluginFunctions::objectsEnd(); ++o_it)
169
    detectTriangleAspect(o_it->id(), tool_->triangleAspectSpinbox->value());
170 171 172 173

  emit updateView();
}

174
//-----------------------------------------------------------------------------
175 176 177 178 179 180 181 182 183 184 185

void MeshRepairPlugin::slotFlipOrientation(){

  for ( PluginFunctions::ObjectIterator o_it(PluginFunctions::TARGET_OBJECTS,DataType( DATA_TRIANGLE_MESH | DATA_POLY_MESH ) );  o_it != PluginFunctions::objectsEnd(); ++o_it)
    flipOrientation(o_it->id());

  emit updateView();
}

//-----------------------------------------------------------------------------

Jan Möbius's avatar
Jan Möbius committed
186 187 188 189 190 191 192 193 194 195 196 197
void MeshRepairPlugin::slotFixMesh() {


  for ( PluginFunctions::ObjectIterator o_it(PluginFunctions::TARGET_OBJECTS,DataType( DATA_TRIANGLE_MESH ) );  o_it != PluginFunctions::objectsEnd(); ++o_it)
    fixMesh(o_it->id() , tool_->fixMeshBox->value() );

  emit updateView();

}

//-----------------------------------------------------------------------------

198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
void MeshRepairPlugin::slotUpdateVertexNormals() {
  for ( PluginFunctions::ObjectIterator o_it(PluginFunctions::TARGET_OBJECTS,DataType( DATA_TRIANGLE_MESH | DATA_POLY_MESH ) );  o_it != PluginFunctions::objectsEnd(); ++o_it)
    updateVertexNormals(o_it->id());

  emit updateView();
}

//-----------------------------------------------------------------------------

void MeshRepairPlugin::slotUpdateFaceNormals() {
  for ( PluginFunctions::ObjectIterator o_it(PluginFunctions::TARGET_OBJECTS,DataType( DATA_TRIANGLE_MESH | DATA_POLY_MESH ) );  o_it != PluginFunctions::objectsEnd(); ++o_it)
    updateFaceNormals(o_it->id());

  emit updateView();
}

//-----------------------------------------------------------------------------

216 217 218 219 220 221 222 223 224
void MeshRepairPlugin::slotUpdateHalfedgeNormals() {
  for ( PluginFunctions::ObjectIterator o_it(PluginFunctions::TARGET_OBJECTS,DataType( DATA_TRIANGLE_MESH | DATA_POLY_MESH ) );  o_it != PluginFunctions::objectsEnd(); ++o_it)
    updateHalfedgeNormals(o_it->id());

  emit updateView();
}

//-----------------------------------------------------------------------------

225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
void MeshRepairPlugin::slotUpdateNormals(){
  for ( PluginFunctions::ObjectIterator o_it(PluginFunctions::TARGET_OBJECTS,DataType( DATA_TRIANGLE_MESH | DATA_POLY_MESH ) );  o_it != PluginFunctions::objectsEnd(); ++o_it)
    updateNormals(o_it->id());

  emit updateView();
}

//-----------------------------------------------------------------------------

void MeshRepairPlugin::slotDetectEdgesShorter(){
  double length = tool_->edgeSpin->value();

  for ( PluginFunctions::ObjectIterator o_it(PluginFunctions::TARGET_OBJECTS,DataType( DATA_TRIANGLE_MESH | DATA_POLY_MESH ) );  o_it != PluginFunctions::objectsEnd(); ++o_it)
    selectEdgesShorterThan(o_it->id(),length);

  emit updateView();
}

//-----------------------------------------------------------------------------

void MeshRepairPlugin::slotDetectEdgesLonger(){
  double length = tool_->edgeSpin->value();

  for ( PluginFunctions::ObjectIterator o_it(PluginFunctions::TARGET_OBJECTS,DataType( DATA_TRIANGLE_MESH | DATA_POLY_MESH ) );  o_it != PluginFunctions::objectsEnd(); ++o_it)
    selectEdgesLongerThan(o_it->id(),length);

  emit updateView();
}

Jan Möbius's avatar
Jan Möbius committed
254 255 256 257 258 259 260 261 262 263 264
//-----------------------------------------------------------------------------

void MeshRepairPlugin::slotDetectFlatValence3Vertices() {
  double angle = tool_->valenceThreeSpinbox->value();

  for ( PluginFunctions::ObjectIterator o_it(PluginFunctions::TARGET_OBJECTS,DataType( DATA_TRIANGLE_MESH | DATA_POLY_MESH ) );  o_it != PluginFunctions::objectsEnd(); ++o_it)
    detectFlatValence3Vertices(o_it->id(), angle);

  emit updateView();
}

265 266 267 268 269 270 271 272 273 274
//-----------------------------------------------------------------------------

void MeshRepairPlugin::slotSnapBoundary()
{
  double eps = tool_->snapBoundarySpinBox->value();
  for ( PluginFunctions::ObjectIterator o_it(PluginFunctions::TARGET_OBJECTS,DataType( DATA_TRIANGLE_MESH | DATA_POLY_MESH ) );  o_it != PluginFunctions::objectsEnd(); ++o_it)
      snapBoundary(o_it->id(), eps);
  emit updateView();
}

Matthias Möller's avatar
Matthias Möller committed
275 276
//-----------------------------------------------------------------------------

Jan Möbius's avatar
Jan Möbius committed
277
void MeshRepairPlugin::slotFixTopology()
Matthias Möller's avatar
Matthias Möller committed
278 279 280 281 282 283
{
  for ( PluginFunctions::ObjectIterator o_it(PluginFunctions::TARGET_OBJECTS,DataType( DATA_TRIANGLE_MESH | DATA_POLY_MESH ) );  o_it != PluginFunctions::objectsEnd(); ++o_it)
    fixTopology(o_it->id());
  emit updateView();
}

284 285 286 287 288 289 290 291 292 293 294 295

//-----------------------------------------------------------------------------

/** \brief Initialization of the plugin when it is loaded by the core
 *
 */
void MeshRepairPlugin::pluginsInitialized() {

  emit setSlotDescription("updateFaceNormals(int)",tr("Recompute Face normals"),
                          QStringList(tr("objectId")),
                          QStringList(tr("ID of an object")));

296 297 298 299
  emit setSlotDescription("updateHalfedgeNormals(int)",tr("Recompute Halfedge normals"),
                          QStringList(tr("objectId")),
                          QStringList(tr("ID of an object")));

300 301 302 303 304 305 306 307 308 309 310 311
  emit setSlotDescription("updateVertexNormals(int)",tr("Recompute Vertex normals"),
                          QStringList(tr("objectId")),
                          QStringList(tr("ID of an object")));

  emit setSlotDescription("updateNormals(int)",tr("Recompute Face and Vertex normals"),
                          QStringList(tr("objectId")),
                          QStringList(tr("ID of an object")));

  emit setSlotDescription("flipOrientation(int)",tr("Flips the normals of all faces by changing the vertex order in each face"),
                          QStringList(tr("objectId")),
                          QStringList(tr("ID of an object")));

Jan Möbius's avatar
Jan Möbius committed
312 313 314 315
  emit setSlotDescription("fixMesh(int,double)",tr("Fixes a mesh."),
                          QString(tr("objectId,distance")).split(","),
                          QString(tr("ID of an object;Vertices with distance lower than epsilon will be treated as one")).split(";"));

316 317 318 319 320 321 322 323 324
  emit setSlotDescription("removeSelectedEdges(int)",tr("Remove the selected edges"),
                          QStringList(tr("objectId")),
                          QStringList(tr("ID of an object")));

  emit setSlotDescription("removeSelectedVal3Vertices(int)",tr("Remove all selected valence 3 vertices"),
                          QStringList(tr("objectId")),
                          QStringList(tr("ID of an object")));

  emit setSlotDescription("detectFoldover(int,float)",tr("Selects edges that are incident to folded over faces."),
325 326
                          QString(tr("objectId,angle")).split(","),
                          QString(tr("ID of an object;Minimum threshold angle for fold-overs")).split(";"));
327 328

  emit setSlotDescription("detectTriangleAspect(int,float)",tr("Selects all vertices that have a minimum aspect."),
329 330
                          QString(tr("objectId,aspect")).split(","),
                          QString(tr("ID of an object;The minimum aspect ratio")).split(";"));
331 332

  emit setSlotDescription("selectEdgesShorterThan(int,double)",tr("Selects all edges of an object which are shorter than the given length"),
333 334
                          QString(tr("objectId,length")).split(","),
                          QString(tr("ID of an object;All edges shorter than this length will be selected")).split(";"));
335 336

  emit setSlotDescription("selectEdgesLongerThan(int,double)",tr("Selects all edges of an object which are longer than the given length"),
337 338
                          QString(tr("objectId,length")).split(","),
                          QString(tr("ID of an object;All edges longer than this length will be selected")).split(";"));
339

Jan Möbius's avatar
Jan Möbius committed
340
  emit setSlotDescription("detectFlatValence3Vertices(int,double)",tr("Selects all vertices that have valence 3 and the normals of their neighboring faces have an angle less then the given angle"),
341 342
                          QString(tr("objectId,angle")).split(","),
                          QString(tr("ID of an object;the maximal angle between the adjacent faces")).split(";"));
343 344

  emit setSlotDescription("detectSkinnyTriangleByAngle(int,double,bool)",tr("Select or remove skinny triangles. Whether a triangle is skinny is determined by a minimum angle threshold."),
345 346
                          QString(tr("objectId,angle,remove")).split(","),
                          QString(tr("ID of an object;Minimum angle threshold")).split(";"));
347 348 349
  emit setSlotDescription("snapBoundary(int,double)",tr("Snaps selected and boundary vertices if the distance is less than the given max. distance."),
                          QString(tr("objectId,epsilon")).split(","),
                          QString(tr("ID of an object;Max Distance")).split(";"));
Jan Möbius's avatar
Jan Möbius committed
350

351 352 353 354 355 356 357 358
}



//===========================================================================
// Scriptable functions
//===========================================================================

359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380
void MeshRepairPlugin::snapBoundary(int _objectId, double _eps)
{
  TriMesh* triMesh = 0;
  PolyMesh* polyMesh = 0;

  PluginFunctions::getMesh(_objectId, triMesh);
  PluginFunctions::getMesh(_objectId, polyMesh);
  if (triMesh)
    snapBoundary(triMesh,_eps);
  else if (polyMesh)
    snapBoundary(polyMesh,_eps);
  else
  {
    emit log(LOGERR, tr("Unsupported Object Type."));
    return;
  }

  emit updatedObject(_objectId, UPDATE_ALL);
  emit createBackup(_objectId, "snapBoundary", UPDATE_ALL);
  emit scriptInfo("snapBoundary(" + QString::number(_objectId) + ", " + QString::number(_eps) +")");
}

Matthias Möller's avatar
Matthias Möller committed
381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403

void MeshRepairPlugin::fixTopology(int _objectId)
{
  TriMesh* triMesh = 0;
  PolyMesh* polyMesh = 0;

  PluginFunctions::getMesh(_objectId, triMesh);
  PluginFunctions::getMesh(_objectId, polyMesh);
  if (triMesh)
    fixTopology(triMesh);
  else if (polyMesh)
    fixTopology(polyMesh);
  else
  {
    emit log(LOGERR, tr("Unsupported Object Type."));
    return;
  }

  emit updatedObject(_objectId, UPDATE_ALL);
  emit createBackup(_objectId, "fixTopology", UPDATE_ALL);
  emit scriptInfo("fixTopology(" + QString::number(_objectId) + ")");
}

404 405
void MeshRepairPlugin::removeSelectedVal3Vertices(int _objectId) {

406
  unsigned int count = 0;
407

408 409 410
  // get the target mesh
  TriMesh* mesh = 0;
  PluginFunctions::getMesh(_objectId, mesh);
411

412
  if (mesh) {
413

414 415 416 417 418
    TriMesh::VertexIter v_it, v_end(mesh->vertices_end());
    TriMesh::VVIter vv_it;
    TriMesh::VFIter vf_it;
    int i;
    std::vector<TriMesh::VertexHandle> vh(3);
419

420 421 422 423 424
    for (v_it = mesh->vertices_begin(); v_it != v_end; ++v_it) {
      vf_it = mesh->vf_iter(v_it);
      if ((mesh->status(v_it).selected()) && !mesh->status(v_it).feature() && mesh->valence(v_it) == 3) {
        for (i = 0, vv_it = mesh->vv_iter(v_it); vv_it; ++vv_it, ++i)
          vh[2 - i] = vv_it.handle();
425

426 427
        mesh->delete_vertex(v_it, false);
        mesh->add_face(vh);
428

429 430
        ++count;
      }
431
    }
432 433 434
    if (count > 0)
      mesh->garbage_collection();
  }
435

436 437 438 439 440
  if (count > 0) {
    emit updatedObject(_objectId, UPDATE_ALL);
    emit createBackup(_objectId, "Delete/merge selected vertices", UPDATE_ALL);
  }
  emit log("Deleted " + QString::number(count) + " vertices on object " + QString::number(_objectId) + ".");
441 442 443 444
}

void MeshRepairPlugin::removeSelectedEdges(int _objectId) {

445 446
  // get the target mesh
  TriMesh* triMesh = 0;
447

448
  PluginFunctions::getMesh(_objectId, triMesh);
449

450
  if (triMesh) {
451

452
    TriMesh::EdgeIter e_it, e_end = triMesh->edges_end();
453

454
    for (e_it = triMesh->edges_begin(); e_it != e_end; ++e_it) {
455

456
      if (!triMesh->status(e_it).deleted() && triMesh->status(e_it).selected()) {
457

458 459
        const TriMesh::VHandle v0 = triMesh->to_vertex_handle(triMesh->halfedge_handle(e_it, 0));
        const TriMesh::VHandle v1 = triMesh->to_vertex_handle(triMesh->halfedge_handle(e_it, 1));
460

461 462
        const bool boundary0 = triMesh->is_boundary(v0);
        const bool boundary1 = triMesh->is_boundary(v1);
463

464 465 466
        const bool feature0 = triMesh->status(v0).feature();
        const bool feature1 = triMesh->status(v1).feature();
        const bool featureE = triMesh->status(e_it).feature();
467

468 469 470 471 472 473 474 475 476 477 478
        // Collapsing v1 into vo:
        // collapse is ok, if collapsed vertex is not a feature vertex or the target vertex is a feature
        // and if we collapse along an feature edge or if the collapsed vertex is not a feature
        if ((!boundary1 || boundary0) && (!feature1 || (feature0 && featureE)) && triMesh->is_collapse_ok(
            triMesh->halfedge_handle(e_it, 0)))
          triMesh->collapse(triMesh->halfedge_handle(e_it, 0));
        else if ((!boundary0 || boundary1) && (!feature0 || (feature1 && featureE)) && triMesh->is_collapse_ok(
            triMesh->halfedge_handle(e_it, 1)))
          triMesh->collapse(triMesh->halfedge_handle(e_it, 1));
      }
    }
479

480 481
    triMesh->garbage_collection();
    triMesh->update_normals();
482

483 484 485
    emit updatedObject(_objectId, UPDATE_ALL);
    emit createBackup(_objectId, "Removed selected Edges", UPDATE_ALL);
    emit scriptInfo("removeSelectedEdges(" + QString::number(_objectId) + ")");
486

487 488
    return;
  }
489

490
  emit log(LOGERR, tr("Unsupported Object Type for edge removal"));
491 492 493 494 495 496

}

//-----------------------------------------------------------------------------

void MeshRepairPlugin::detectSkinnyTriangleByAngle(int _objectId, double _angle, bool _remove) {
497 498
  // get the target mesh
  TriMesh* triMesh = 0;
499

500
  PluginFunctions::getMesh(_objectId, triMesh);
501

502
  if (triMesh) {
503

504 505
    // Clear current edge selection
    MeshSelection::clearEdgeSelection(triMesh);
506

507 508 509
    double maxAngle = cos(_angle * M_PI / 180.0);
    double angle = 0.0;
    TriMesh::EdgeIter e_it, e_end = triMesh->edges_end();
510

511
    for (e_it = triMesh->edges_begin(); e_it != e_end; ++e_it) {
512

513 514
      // Check prerequisites
      if (!triMesh->status(e_it).deleted() && !triMesh->status(e_it).feature() && triMesh->is_flip_ok(e_it)) {
515

516 517 518 519 520 521 522
        // For both halfedges
        for (unsigned int h = 0; h < 2; ++h) {
          TriMesh::HalfedgeHandle hh = triMesh->halfedge_handle(e_it.handle(), h);
          const TriMesh::Point& a = triMesh->point(triMesh->from_vertex_handle(hh));
          const TriMesh::Point& b = triMesh->point(triMesh->to_vertex_handle(hh));
          hh = triMesh->next_halfedge_handle(hh);
          const TriMesh::Point& c = triMesh->point(triMesh->to_vertex_handle(hh));
523

524
          angle = ((a - c).normalize() | (b - c).normalize());
525

526
          if (angle < maxAngle) {
527

528 529
            // selcet it
            triMesh->status(e_it).set_selected(true);
530

531 532 533 534
            // remove it if requested
            if (_remove)
              triMesh->flip(e_it);
          }
535
        }
536
      }
537 538
    }

539 540 541 542 543 544 545 546 547 548 549 550 551 552 553
    if (_remove) {
      emit updatedObject(_objectId, UPDATE_ALL);
      emit createBackup(_objectId, tr("Removed cap edges"), UPDATE_ALL);
    } else {
      emit updatedObject(_objectId, UPDATE_SELECTION);
      emit createBackup(_objectId, tr("Selected cap edges"), UPDATE_ALL);
    } emit
    scriptInfo(
        "detectSkinnyTriangleByAngle(" + QString::number(_objectId) + "," + QString::number(_angle) + ","
        + _remove + ")");

    return;
  }

  emit log(LOGERR, tr("Unsupported Object Type for Cap detection!"));
554 555 556 557 558 559
}

//-----------------------------------------------------------------------------

void MeshRepairPlugin::detectFoldover(int _objectId, float _angle) {

560 561 562
  // get the target mesh
  TriMeshObject* trimesh_o = 0;
  PolyMeshObject* polymesh_o = 0;
563

564 565
  PluginFunctions::getObject(_objectId, trimesh_o);
  PluginFunctions::getObject(_objectId, polymesh_o);
566

567
  unsigned int count = 0;
568

569
  if (trimesh_o != 0) {
570

571 572
    // get the target mesh
    TriMesh* mesh = trimesh_o->mesh();
573

574
    if (mesh) {
575

576 577
      // Clear current edge selection
      MeshSelection::clearEdgeSelection(mesh);
578

579 580 581
      TriMesh::EdgeIter e_it, e_end(mesh->edges_end());
      TriMesh::FaceHandle fh;
      TriMesh::Scalar a, cosa = cos(_angle / 180.0 * M_PI);
582

583 584 585 586
      for (e_it = mesh->edges_begin(); e_it != e_end; ++e_it) {
        if (!mesh->is_boundary(e_it)) {
          a = (mesh->normal(mesh->face_handle(mesh->halfedge_handle(e_it, 0))) | mesh->normal(
              mesh->face_handle(mesh->halfedge_handle(e_it, 1))));
587

588 589 590 591
          if (a < cosa) {
            mesh->status(mesh->edge_handle(mesh->halfedge_handle(e_it, 0))). set_selected(true);
            ++count;
          }
592
        }
593 594 595
      }
    }
  } else if (polymesh_o != 0) {
596

597 598
    // get the target mesh
    PolyMesh* mesh = polymesh_o->mesh();
599

600
    if (mesh) {
601

602 603
      // Clear current edge selection
      MeshSelection::clearEdgeSelection(mesh);
604

605 606 607
      PolyMesh::EdgeIter e_it, e_end(mesh->edges_end());
      PolyMesh::FaceHandle fh;
      PolyMesh::Scalar a, cosa = cos(_angle / 180.0 * M_PI);
608

609 610 611 612
      for (e_it = mesh->edges_begin(); e_it != e_end; ++e_it) {
        if (!mesh->is_boundary(e_it)) {
          a = (mesh->normal(mesh->face_handle(mesh->halfedge_handle(e_it, 0))) | mesh->normal(
              mesh->face_handle(mesh->halfedge_handle(e_it, 1))));
613

614 615 616 617
          if (a < cosa) {
            mesh->status(mesh->edge_handle(mesh->halfedge_handle(e_it, 0))). set_selected(true);
            ++count;
          }
618
        }
619
      }
620
    }
621
  }
622

623 624 625 626 627 628 629 630
  if (count > 0) {
    emit updatedObject(_objectId, UPDATE_SELECTION);
    emit createBackup(_objectId, "Select edges", UPDATE_SELECTION);
    emit scriptInfo("detectFoldover(" + QString::number(_objectId) + ", " + QString::number(_angle) + ")");
  }
  emit log(
      "Selected " + QString::number(count) + " fold-overs on object " + QString::number(_objectId)
  + " with angle greater than " + QString::number(_angle) + ".");
631 632 633 634
}

//-----------------------------------------------------------------------------

635 636 637 638 639 640
void MeshRepairPlugin::updateFaceNormals(int _objectId) {
  BaseObjectData* object = 0;
  PluginFunctions::getObject(_objectId,object);

  if ( object == 0) {
    emit log(LOGERR,tr("updateFaceNormals: Unable to get object %1. ").arg(_objectId) );
641
    return;
642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660
  }

  if ( object->dataType(DATA_TRIANGLE_MESH) ) {
    TriMesh* mesh = PluginFunctions::triMesh(object);
    mesh->update_face_normals();
    emit updatedObject(_objectId, UPDATE_ALL);
    emit createBackup( _objectId, "Updated Face Normals", UPDATE_ALL);
    emit scriptInfo( "updateFaceNormals(" + QString::number(_objectId) + ")" );
  } else if ( object->dataType(DATA_POLY_MESH) ) {
    PolyMesh* mesh = PluginFunctions::polyMesh(object);
    mesh->update_face_normals();
    emit updatedObject(_objectId, UPDATE_ALL);
    emit createBackup( _objectId, "Updated Face Normals", UPDATE_ALL);
    emit scriptInfo( "updateFaceNormals(" + QString::number(_objectId) + ")" );
  } else
    emit log(LOGERR,tr("updateFaceNormals: MeshRepair only works on triangle and poly meshes!") );

}

661 662 663 664 665 666 667 668 669

//-----------------------------------------------------------------------------

void MeshRepairPlugin::updateHalfedgeNormals(int _objectId) {
  BaseObjectData* object = 0;
  PluginFunctions::getObject(_objectId,object);

  if ( object == 0) {
    emit log(LOGERR,tr("updateFaceNormals: Unable to get object %1. ").arg(_objectId) );
670
    return;
671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692
  }

  if ( object->dataType(DATA_TRIANGLE_MESH) ) {
    TriMesh* mesh = PluginFunctions::triMesh(object);
    mesh->request_halfedge_normals();
    mesh->update_halfedge_normals();
    emit updatedObject(_objectId, UPDATE_ALL);
    emit createBackup( _objectId, "Updated Face Normals", UPDATE_ALL);
    emit scriptInfo( "updateFaceNormals(" + QString::number(_objectId) + ")" );
  } else if ( object->dataType(DATA_POLY_MESH) ) {
    PolyMesh* mesh = PluginFunctions::polyMesh(object);
    mesh->request_halfedge_normals();
    mesh->update_halfedge_normals();
    emit updatedObject(_objectId, UPDATE_ALL);
    emit createBackup( _objectId, "Updated Face Normals", UPDATE_ALL);
    emit scriptInfo( "updateFaceNormals(" + QString::number(_objectId) + ")" );
  } else
    emit log(LOGERR,tr("updateFaceNormals: MeshRepair only works on triangle and poly meshes!") );

}


693 694 695 696 697 698 699 700 701
//-----------------------------------------------------------------------------


void MeshRepairPlugin::updateVertexNormals(int _objectId){
  BaseObjectData* object = 0;
  PluginFunctions::getObject(_objectId,object);

  if ( object == 0) {
    emit log(LOGERR,tr("updateVertexNormals: Unable to get object %1. ").arg(_objectId) );
702
    return;
703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728
  }

  if ( object->dataType(DATA_TRIANGLE_MESH) ) {
    TriMesh* mesh = PluginFunctions::triMesh(object);
    mesh->update_vertex_normals();
    emit updatedObject(_objectId, UPDATE_ALL);
    emit createBackup( _objectId, "Updated Vertex Normals", UPDATE_ALL);
    emit scriptInfo( "updateVertexNormals(" + QString::number(_objectId) + ")" );
  } else if ( object->dataType(DATA_POLY_MESH) ) {
    PolyMesh* mesh = PluginFunctions::polyMesh(object);
    mesh->update_vertex_normals();
    emit updatedObject(_objectId, UPDATE_ALL);
    emit createBackup( _objectId, "Updated Vertex Normals", UPDATE_ALL);
    emit scriptInfo( "updateVertexNormals(" + QString::number(_objectId) + ")" );
  } else
    emit log(LOGERR,tr("updateVertexNormals: MeshRepair only works on triangle and poly meshes!") );
}

//-----------------------------------------------------------------------------

void MeshRepairPlugin::updateNormals(int _objectId) {
  BaseObjectData* object = 0;
  PluginFunctions::getObject(_objectId,object);

  if ( object == 0) {
    emit log(LOGERR,tr("updateNormals: Unable to get object %1. ").arg(_objectId) );
729
    return;
730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748
  }

  if ( object->dataType(DATA_TRIANGLE_MESH) ) {
    TriMesh* mesh = PluginFunctions::triMesh(object);
    mesh->update_normals();
    emit scriptInfo( "updateNormals(" + QString::number(_objectId) + ")" );
  } else if ( object->dataType(DATA_POLY_MESH) ) {
    PolyMesh* mesh = PluginFunctions::polyMesh(object);
    mesh->update_normals();
    emit updatedObject(_objectId, UPDATE_ALL);
    emit createBackup( _objectId, "Updated All Normals", UPDATE_ALL);
    emit scriptInfo( "updateNormals(" + QString::number(_objectId) + ")" );
  } else
    emit log(LOGERR,tr("updateNormals: MeshRepair only works on triangle and poly meshes!") );
}

//-----------------------------------------------------------------------------

void
749 750
MeshRepairPlugin::detectTriangleAspect(int _objectId, float _aspect) {

751 752
  // get the target mesh
  TriMesh* mesh = 0;
753

754
  PluginFunctions::getMesh(_objectId, mesh);
755

756
  if (mesh) {
757

758
    unsigned int count(0);
759

760 761
    // Clear current face selection
    MeshSelection::clearFaceSelection(mesh);
762

763 764 765
    TriMesh::FaceIter f_it, f_end(mesh->faces_end());
    TriMesh::FVIter fv_it;
    TriMesh::FEIter fe_it;
766

767 768
    for (f_it = mesh->faces_begin(); f_it != f_end; ++f_it) {
      fv_it = mesh->fv_iter(f_it);
769

770 771 772
      const TriMesh::Point& p0 = mesh->point(fv_it);
      const TriMesh::Point& p1 = mesh->point(++fv_it);
      const TriMesh::Point& p2 = mesh->point(++fv_it);
773

774 775 776 777
      if (ACG::Geometry::aspectRatio(p0, p1, p2) > _aspect) {
        mesh->status(f_it).set_selected(true);
        ++count;
      }
778
    }
779 780 781 782 783 784 785 786 787 788 789
    if (count > 0) {
      emit updatedObject(_objectId, UPDATE_SELECTION);
      emit createBackup(_objectId, "Select triangles", UPDATE_SELECTION);
      emit scriptInfo( "detectTriangleAspect(" + QString::number(_objectId) + ", " + QString::number(_aspect) + ")" );
    }
    emit log(
        "Selected " + QString::number(count) + " triangles on object " + QString::number(_objectId)
    + " with aspect ratio greater than " + QString::number(_aspect) + ".");
  } else {
    emit log("Cannot detect skinny triangles on non-trimesh " + QString::number(_objectId) + ".");
  }
790 791 792 793 794 795 796
}

//-----------------------------------------------------------------------------

void
MeshRepairPlugin::flipOrientation(int _objectId) {

797 798 799 800
  // get the target mesh
  TriMesh* triMesh = 0;
  PolyMesh* polyMesh = 0;

801
  PluginFunctions::getMesh(_objectId,triMesh);
802 803
  PluginFunctions::getMesh(_objectId,polyMesh);

804 805 806 807 808 809
  if (triMesh)
    flipOrientationSelected(triMesh);
  else if (polyMesh)
    flipOrientationSelected(polyMesh);
  else
    emit log( LOGERR,tr("Unsupported Object Type for normal flipping!") );
810 811


812 813 814
  emit updatedObject(_objectId, UPDATE_ALL);
  emit createBackup( _objectId, "Flipped Normals", UPDATE_ALL);
  emit scriptInfo( "flipOrientation(" + QString::number(_objectId) + ")" );
815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901
}

//-----------------------------------------------------------------------------

void MeshRepairPlugin::selectEdgesShorterThan(int _objectId,double _length) {
  selectionEdgeLength(_objectId,_length,false);

  emit scriptInfo( "selectEdgesShorterThan(" + QString::number(_objectId) + ", " + QString::number(_length) + ")" );
}

//-----------------------------------------------------------------------------

void MeshRepairPlugin::selectEdgesLongerThan(int _objectId,double _length) {
  selectionEdgeLength(_objectId,_length,true);

  emit scriptInfo( "selectEdgesLongerThan(" + QString::number(_objectId) + ", " + QString::number(_length) + ")" );
}

//-----------------------------------------------------------------------------

void MeshRepairPlugin::selectionEdgeLength(int _objectId, double _length, bool _larger) {

  // get the target mesh
  TriMesh* triMesh = 0;

  PluginFunctions::getMesh(_objectId,triMesh);

  if ( triMesh ) {
    TriMesh::EdgeIter e_it, e_end=triMesh->edges_end();

    // Clear current edge selection
    MeshSelection::clearEdgeSelection(triMesh);

    // Iterate over all edges
    for (e_it = triMesh->edges_begin(); e_it != e_end ; ++e_it) {
      TriMesh::HalfedgeHandle he = triMesh->halfedge_handle( e_it, 0 );
      TriMesh::Point p1 = triMesh->point( triMesh->from_vertex_handle( he ) );
      TriMesh::Point p2 = triMesh->point( triMesh->to_vertex_handle( he )   );

      if ( _larger ) {
        if ( (p1 - p2).norm() > _length)
          triMesh->status(e_it).set_selected(true);
      } else {
        if ( (p1 - p2).norm() < _length)
          triMesh->status(e_it).set_selected(true);
      }
    }

    emit updatedObject(_objectId,UPDATE_SELECTION);
    emit createBackup( _objectId, "Select Edges", UPDATE_SELECTION);

    return;
  }

  // get the target mesh
  PolyMesh* polyMesh = 0;
  PluginFunctions::getMesh(_objectId,polyMesh);

  if ( polyMesh ) {
    PolyMesh::EdgeIter e_it, e_end=polyMesh->edges_end();

    // Clear current edge selection
    MeshSelection::clearEdgeSelection(polyMesh);

    // Iterate over all edges
    for (e_it = polyMesh->edges_begin(); e_it != e_end ; ++e_it) {
      PolyMesh::HalfedgeHandle he = polyMesh->halfedge_handle( e_it, 0 );
      PolyMesh::Point p1 = polyMesh->point( polyMesh->from_vertex_handle( he ) );
      PolyMesh::Point p2 = polyMesh->point( polyMesh->to_vertex_handle( he )   );

      if ( _larger ) {
        if ( (p1 - p2).norm() > _length)
          polyMesh->status(e_it).set_selected(true);
      } else {
        if ( (p1 - p2).norm() < _length)
          polyMesh->status(e_it).set_selected(true);
      }
    }

    emit updatedObject(_objectId,UPDATE_SELECTION);
    emit createBackup( _objectId, "Select Edges", UPDATE_SELECTION);

    return;
  }

  emit log( LOGERR,tr("Unsupported Object Type for edge selection") );

Jan Möbius's avatar
 
Jan Möbius committed
902 903
}

904 905


Jan Möbius's avatar
Jan Möbius committed
906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937
//-----------------------------------------------------------------------------

void MeshRepairPlugin::detectFlatValence3Vertices(int _objectId, double _angle) {

  unsigned int count(0);

  // get the target mesh
  TriMesh* mesh = 0;
  PluginFunctions::getMesh(_objectId,mesh);

  if ( mesh ) {

    // Clear current triangle selection
    MeshSelection::clearVertexSelection(mesh);

    TriMesh::VertexIter                 v_it, v_end(mesh->vertices_end());
    TriMesh::VVIter                     vv_it;
    TriMesh::VFIter                     vf_it;
    TriMesh::FaceHandle                 fh;
    std::vector<TriMesh::VertexHandle>  vh(3);
    TriMesh::Scalar                     cosangle(cos(_angle/180.0*M_PI));

    for (v_it=mesh->vertices_begin(); v_it!=v_end; ++v_it)
    {
      if (!mesh->status(v_it).deleted() && !mesh->is_boundary(v_it) && mesh->valence(v_it) == 3)
      {
        vf_it = mesh->vf_iter(v_it);
        const TriMesh::Normal& n0 = mesh->normal(vf_it);
        const TriMesh::Normal& n1 = mesh->normal(++vf_it);
        const TriMesh::Normal& n2 = mesh->normal(++vf_it);

        if ( (n0|n1) > cosangle &&
938 939
            (n0|n2) > cosangle &&
            (n1|n2) > cosangle )
Jan Möbius's avatar
Jan Möbius committed
940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961
        {

          mesh->status(v_it).set_selected(true);
          ++count;
        }
      }
    }
  }
  else {
    emit log(LOGERR, "Cannot detect flat triangles on non-trimesh " + QString::number(_objectId) + ".");
  }

  if (count > 0) {
    emit updatedObject(_objectId, UPDATE_SELECTION);
    emit createBackup(_objectId, "Select vertices", UPDATE_SELECTION);
  }

  emit log (LOGINFO,"Selected " + QString::number(count) + " vertices on object " + QString::number(_objectId) + " with face angle difference smaller than " + QString::number(_angle) + ".");
  emit scriptInfo( "detectFlatValence3Vertices(" + QString::number(_objectId) + ", " + QString::number(_angle) + ")" );

}

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

Jan Möbius's avatar
Jan Möbius committed
963 964 965 966 967 968 969 970 971
void
MeshRepairPlugin::fixMesh(int _objectId, double _epsilon) {
  // get the target mesh
   TriMesh* triMesh = 0;

   PluginFunctions::getMesh(_objectId,triMesh);

   if (triMesh) {
     MeshFixing<TriMesh> fixer(*triMesh,_epsilon);
972

Jan Möbius's avatar
Jan Möbius committed
973 974 975 976 977 978 979 980 981 982 983 984 985 986 987
     if ( !fixer.fix() )
       emit log(LOGERR, "Fixmesh encountered Problems! Object: " + QString::number(_objectId) + ".");

     // Recompute normals
     triMesh->update_normals();

     emit updatedObject(_objectId, UPDATE_ALL);
     emit createBackup(_objectId, "Fixed mesh", UPDATE_ALL);

     emit scriptInfo( "fixMesh(" + QString::number(_objectId) + ", " + QString::number(_epsilon) + ")" );

   } else
     emit log( LOGERR,tr("Unsupported Object Type for mesh fixing!") );

}
988

Jan Möbius's avatar
 
Jan Möbius committed
989 990 991
//-----------------------------------------------------------------------------

Q_EXPORT_PLUGIN2( meshrepairplugin , MeshRepairPlugin );