MeshRepairPlugin.cc 31.3 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 55
\*===========================================================================*/

#include <QtGui>

#include "MeshRepairPlugin.hh"

#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
56 57 58 59 60 61 62 63
MeshRepairPlugin::MeshRepairPlugin() :
tool_(0),
toolIcon_(0)
{

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

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


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

  // Edge Selection/Repairing
  connect(tool_->detectEShorterButton, SIGNAL(clicked()), this, SLOT(slotDetectEdgesShorter()) );
80
  connect(tool_->detectELargerButton, SIGNAL(clicked()), this, SLOT(slotDetectEdgesLonger()) );
Jan Möbius's avatar
 
Jan Möbius committed
81
  connect(tool_->repairCollapseEButton, SIGNAL(clicked()), this, SLOT(slotRemoveSelectedEdges()) );
82 83
  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
84
  connect(tool_->detectFoldoverButton, SIGNAL(clicked()), this, SLOT(slotDetectFoldover()) );
85

Jan Möbius's avatar
 
Jan Möbius committed
86 87 88 89
  //Face operations
  connect(tool_->triangleAspectButton,SIGNAL(clicked()),this,SLOT(slotDetectTriangleAspect()));
  connect(tool_->flipOrientation,SIGNAL(clicked()),this,SLOT(slotFlipOrientation()));

90 91 92 93 94 95 96 97



  // General Operations
  connect(tool_->fixMeshButton,SIGNAL(clicked()),this,SLOT(slotFixMesh()));


  //==================
98
  //Normal operations
99
  //==================
100 101 102
  connect(tool_->computeNormals,SIGNAL(clicked()),this,SLOT(slotUpdateNormals()));
  connect(tool_->computeVertexNormals,SIGNAL(clicked()),this,SLOT(slotUpdateVertexNormals()));
  connect(tool_->computeFaceNormals,SIGNAL(clicked()),this,SLOT(slotUpdateFaceNormals()));
103
  connect(tool_->computeHalfedgeNormals,SIGNAL(clicked()),this,SLOT(slotUpdateHalfedgeNormals()));
104

105 106 107 108
  //==================
  // General
  //==================
  connect(tool_->fixNonManifoldVerticesButton,SIGNAL(clicked()),this,SLOT(slotFixNonManifoldVertices()));
Jan Möbius's avatar
Jan Möbius committed
109

Jan Möbius's avatar
 
Jan Möbius committed
110 111 112 113
  toolIcon_ = new QIcon(OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator()+"meshrepair-toolbox.png");
  tool_->repairCollapseEButton->setIcon(*toolIcon_);
  tool_->repairFlipEButton->setIcon(*toolIcon_);
  tool_->repairRemoveVButton->setIcon(*toolIcon_);
114

Jan Möbius's avatar
 
Jan Möbius committed
115 116 117
  emit addToolbox( tr("Mesh Repair") , tool_,  toolIcon_);
}

118 119 120
//===========================================================================
// Button Slots
//===========================================================================
Jan Möbius's avatar
 
Jan Möbius committed
121

122
void MeshRepairPlugin::slotRemoveSelectedVal3Vertices() {
Jan Möbius's avatar
 
Jan Möbius committed
123

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

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

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

void MeshRepairPlugin::slotRemoveSelectedEdges(){
133

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

Jan Möbius's avatar
 
Jan Möbius committed
137 138 139
  emit updateView();
}

140 141 142
//-----------------------------------------------------------------------------

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

Jan Möbius's avatar
 
Jan Möbius committed
147 148 149
  emit updateView();
}

150 151 152
//-----------------------------------------------------------------------------

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

Jan Möbius's avatar
 
Jan Möbius committed
158 159 160 161 162 163 164
  emit updateView();
}

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

void MeshRepairPlugin::slotDetectFoldover() {

165
  for ( PluginFunctions::ObjectIterator o_it(PluginFunctions::TARGET_OBJECTS,DataType( DATA_TRIANGLE_MESH | DATA_POLY_MESH ) );  o_it != PluginFunctions::objectsEnd(); ++o_it)
166
    detectFoldover(o_it->id(), tool_->detectFoldoverSpinbox->value());
167 168

  emit updateView();
Jan Möbius's avatar
 
Jan Möbius committed
169 170 171 172 173 174
}

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

void MeshRepairPlugin::slotDetectTriangleAspect() {

175
  for ( PluginFunctions::ObjectIterator o_it(PluginFunctions::TARGET_OBJECTS,DataType( DATA_TRIANGLE_MESH | DATA_POLY_MESH ) );  o_it != PluginFunctions::objectsEnd(); ++o_it)
176
    detectTriangleAspect(o_it->id(), tool_->triangleAspectSpinbox->value());
177 178 179 180

  emit updateView();
}

181
//-----------------------------------------------------------------------------
182 183 184 185 186 187 188 189 190 191 192

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
193 194 195 196 197 198 199 200 201 202 203 204
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();

}

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

205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
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();
}

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

223 224 225 226 227 228 229 230 231
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();
}

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

232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
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
261 262 263 264 265 266 267 268 269 270 271
//-----------------------------------------------------------------------------

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

272 273 274 275 276 277 278 279 280 281
//-----------------------------------------------------------------------------

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
282 283
//-----------------------------------------------------------------------------

284
void MeshRepairPlugin::slotFixNonManifoldVertices()
Matthias Möller's avatar
Matthias Möller committed
285 286
{
  for ( PluginFunctions::ObjectIterator o_it(PluginFunctions::TARGET_OBJECTS,DataType( DATA_TRIANGLE_MESH | DATA_POLY_MESH ) );  o_it != PluginFunctions::objectsEnd(); ++o_it)
287
    fixNonManifoldVertices(o_it->id());
Matthias Möller's avatar
Matthias Möller committed
288 289 290
  emit updateView();
}

291 292 293 294 295 296 297 298

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

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

299
  // TODO: Check and update
300

301

302 303 304 305 306

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

307

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

309 310 311 312 313 314 315 316 317
  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."),
318 319
                          QString(tr("objectId,angle")).split(","),
                          QString(tr("ID of an object;Minimum threshold angle for fold-overs")).split(";"));
320 321

  emit setSlotDescription("detectTriangleAspect(int,float)",tr("Selects all vertices that have a minimum aspect."),
322 323
                          QString(tr("objectId,aspect")).split(","),
                          QString(tr("ID of an object;The minimum aspect ratio")).split(";"));
324 325

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

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

Jan Möbius's avatar
Jan Möbius committed
333
  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"),
334 335
                          QString(tr("objectId,angle")).split(","),
                          QString(tr("ID of an object;the maximal angle between the adjacent faces")).split(";"));
336 337

  emit setSlotDescription("detectSkinnyTriangleByAngle(int,double,bool)",tr("Select or remove skinny triangles. Whether a triangle is skinny is determined by a minimum angle threshold."),
338 339
                          QString(tr("objectId,angle,remove")).split(","),
                          QString(tr("ID of an object;Minimum angle threshold")).split(";"));
340

341 342 343
  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
344

345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381




  // ===============================
  // Normal Fixing
  // ===============================

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

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

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


  // ===============================
  // General Mesh fixing
  // ===============================

  emit setSlotDescription("(int)",tr("Fixes non manifold vertices."),
                            QString(tr("objectId")).split(","),
                            QString(tr("ID of an object;Non manifold vertices are splitted.")).split(";"));

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

382 383 384 385 386 387 388 389
}



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

Matthias Möller's avatar
Matthias Möller committed
390 391


392 393
void MeshRepairPlugin::removeSelectedVal3Vertices(int _objectId) {

394
  unsigned int count = 0;
395

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

400
  if (mesh) {
401

402 403 404 405 406
    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);
407

408 409 410 411 412
    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();
413

414 415
        mesh->delete_vertex(v_it, false);
        mesh->add_face(vh);
416

417 418
        ++count;
      }
419
    }
420 421 422
    if (count > 0)
      mesh->garbage_collection();
  }
423

424 425 426 427 428
  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) + ".");
429 430 431 432
}

void MeshRepairPlugin::removeSelectedEdges(int _objectId) {

433 434
  // get the target mesh
  TriMesh* triMesh = 0;
435

436
  PluginFunctions::getMesh(_objectId, triMesh);
437

438
  if (triMesh) {
439

440
    TriMesh::EdgeIter e_it, e_end = triMesh->edges_end();
441

442
    for (e_it = triMesh->edges_begin(); e_it != e_end; ++e_it) {
443

444
      if (!triMesh->status(e_it).deleted() && triMesh->status(e_it).selected()) {
445

446 447
        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));
448

449 450
        const bool boundary0 = triMesh->is_boundary(v0);
        const bool boundary1 = triMesh->is_boundary(v1);
451

452 453 454
        const bool feature0 = triMesh->status(v0).feature();
        const bool feature1 = triMesh->status(v1).feature();
        const bool featureE = triMesh->status(e_it).feature();
455

456 457 458 459 460 461 462 463 464 465 466
        // 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));
      }
    }
467

468 469
    triMesh->garbage_collection();
    triMesh->update_normals();
470

471 472 473
    emit updatedObject(_objectId, UPDATE_ALL);
    emit createBackup(_objectId, "Removed selected Edges", UPDATE_ALL);
    emit scriptInfo("removeSelectedEdges(" + QString::number(_objectId) + ")");
474

475 476
    return;
  }
477

478
  emit log(LOGERR, tr("Unsupported Object Type for edge removal"));
479 480 481 482 483 484

}

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

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

488
  PluginFunctions::getMesh(_objectId, triMesh);
489

490
  if (triMesh) {
491

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

495 496 497
    double maxAngle = cos(_angle * M_PI / 180.0);
    double angle = 0.0;
    TriMesh::EdgeIter e_it, e_end = triMesh->edges_end();
498

499
    for (e_it = triMesh->edges_begin(); e_it != e_end; ++e_it) {
500

501 502
      // Check prerequisites
      if (!triMesh->status(e_it).deleted() && !triMesh->status(e_it).feature() && triMesh->is_flip_ok(e_it)) {
503

504 505 506 507 508 509 510
        // 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));
511

512
          angle = ((a - c).normalize() | (b - c).normalize());
513

514
          if (angle < maxAngle) {
515

516 517
            // selcet it
            triMesh->status(e_it).set_selected(true);
518

519 520 521 522
            // remove it if requested
            if (_remove)
              triMesh->flip(e_it);
          }
523
        }
524
      }
525 526
    }

527 528 529 530 531 532 533 534 535 536 537 538 539 540 541
    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!"));
542 543 544 545 546 547
}

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

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

548 549 550
  // get the target mesh
  TriMeshObject* trimesh_o = 0;
  PolyMeshObject* polymesh_o = 0;
551

552 553
  PluginFunctions::getObject(_objectId, trimesh_o);
  PluginFunctions::getObject(_objectId, polymesh_o);
554

555
  unsigned int count = 0;
556

557
  if (trimesh_o != 0) {
558

559 560
    // get the target mesh
    TriMesh* mesh = trimesh_o->mesh();
561

562
    if (mesh) {
563

564 565
      // Clear current edge selection
      MeshSelection::clearEdgeSelection(mesh);
566

567 568 569
      TriMesh::EdgeIter e_it, e_end(mesh->edges_end());
      TriMesh::FaceHandle fh;
      TriMesh::Scalar a, cosa = cos(_angle / 180.0 * M_PI);
570

571 572 573 574
      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))));
575

576 577 578 579
          if (a < cosa) {
            mesh->status(mesh->edge_handle(mesh->halfedge_handle(e_it, 0))). set_selected(true);
            ++count;
          }
580
        }
581 582 583
      }
    }
  } else if (polymesh_o != 0) {
584

585 586
    // get the target mesh
    PolyMesh* mesh = polymesh_o->mesh();
587

588
    if (mesh) {
589

590 591
      // Clear current edge selection
      MeshSelection::clearEdgeSelection(mesh);
592

593 594 595
      PolyMesh::EdgeIter e_it, e_end(mesh->edges_end());
      PolyMesh::FaceHandle fh;
      PolyMesh::Scalar a, cosa = cos(_angle / 180.0 * M_PI);
596

597 598 599 600
      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))));
601

602 603 604 605
          if (a < cosa) {
            mesh->status(mesh->edge_handle(mesh->halfedge_handle(e_it, 0))). set_selected(true);
            ++count;
          }
606
        }
607
      }
608
    }
609
  }
610

611 612 613 614 615 616 617 618
  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) + ".");
619 620
}

621

622

623 624 625
//-----------------------------------------------------------------------------

void
626 627
MeshRepairPlugin::detectTriangleAspect(int _objectId, float _aspect) {

628 629
  // get the target mesh
  TriMesh* mesh = 0;
630

631
  PluginFunctions::getMesh(_objectId, mesh);
632

633
  if (mesh) {
634

635
    unsigned int count(0);
636

637 638
    // Clear current face selection
    MeshSelection::clearFaceSelection(mesh);
639

640 641 642
    TriMesh::FaceIter f_it, f_end(mesh->faces_end());
    TriMesh::FVIter fv_it;
    TriMesh::FEIter fe_it;
643

644 645
    for (f_it = mesh->faces_begin(); f_it != f_end; ++f_it) {
      fv_it = mesh->fv_iter(f_it);
646

647 648 649
      const TriMesh::Point& p0 = mesh->point(fv_it);
      const TriMesh::Point& p1 = mesh->point(++fv_it);
      const TriMesh::Point& p2 = mesh->point(++fv_it);
650

651 652 653 654
      if (ACG::Geometry::aspectRatio(p0, p1, p2) > _aspect) {
        mesh->status(f_it).set_selected(true);
        ++count;
      }
655
    }
656 657 658 659 660 661 662 663 664 665 666
    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) + ".");
  }
667 668 669 670 671 672 673
}

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

void
MeshRepairPlugin::flipOrientation(int _objectId) {

674 675 676 677
  // get the target mesh
  TriMesh* triMesh = 0;
  PolyMesh* polyMesh = 0;

678
  PluginFunctions::getMesh(_objectId,triMesh);
679 680
  PluginFunctions::getMesh(_objectId,polyMesh);

681 682 683 684 685 686
  if (triMesh)
    flipOrientationSelected(triMesh);
  else if (polyMesh)
    flipOrientationSelected(polyMesh);
  else
    emit log( LOGERR,tr("Unsupported Object Type for normal flipping!") );
687 688


689 690 691
  emit updatedObject(_objectId, UPDATE_ALL);
  emit createBackup( _objectId, "Flipped Normals", UPDATE_ALL);
  emit scriptInfo( "flipOrientation(" + QString::number(_objectId) + ")" );
692 693 694 695 696 697 698 699 700 701 702 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 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778
}

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

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
779 780
}

781 782


Jan Möbius's avatar
Jan Möbius committed
783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814
//-----------------------------------------------------------------------------

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 &&
815 816
            (n0|n2) > cosangle &&
            (n1|n2) > cosangle )
Jan Möbius's avatar
Jan Möbius committed
817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838
        {

          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
839

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

Jan Möbius's avatar
 
Jan Möbius committed
841 842

Q_EXPORT_PLUGIN2( meshrepairplugin , MeshRepairPlugin );