RemesherPlugin.cc 22.4 KB
Newer Older
1
2
3
/*===========================================================================*\
*                                                                            *
*                              OpenFlipper                                   *
Jan Möbius's avatar
Jan Möbius committed
4
*      Copyright (C) 2001-2014 by Computer Graphics Group, RWTH Aachen       *
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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
*                           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/>.                                       *
*                                                                            *
\*===========================================================================*/

/*===========================================================================*\
*                                                                            *
*   $Revision$                                                       *
*   $LastChangedBy$                                                *
*   $Date$                    *
*                                                                            *
\*===========================================================================*/


#include "RemesherPlugin.hh"

#include "Algorithms/AdaptiveRemesherT.hh"
#include "Algorithms/UniformRemesherT.hh"

/*
 * STILL \TODO:
 *
 *  - Emit status updates when remeshing
 */

#include <OpenFlipper/BasePlugin/PluginFunctions.hh>
#include <OpenFlipper/common/GlobalDefines.hh>
#include <OpenFlipper/common/GlobalOptions.hh>

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

RemesherPlugin::RemesherPlugin() :
progress_(0) {

Matthias Möller's avatar
Matthias Möller committed
64
65
  if ( OpenFlipper::Options::gui() ) {
    progress_ = new ProgressEmitter();
66

Matthias Möller's avatar
Matthias Möller committed
67
68
69
    connect(progress_, SIGNAL(signalJobState(QString,int)), this, SIGNAL(setJobState(QString,int)), Qt::QueuedConnection);
    connect(progress_, SIGNAL(changeDescription(QString,QString)), this, SIGNAL(setJobDescription(QString,QString)), Qt::QueuedConnection);
  }
70
71
72
73
74
}

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

RemesherPlugin::~RemesherPlugin() {
Matthias Möller's avatar
Matthias Möller committed
75
76
77
  if ( OpenFlipper::Options::gui() ) {
    delete progress_;
  }
78
79
80
81
82
83
84
}

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

/// Initialize the plugin
void RemesherPlugin::pluginsInitialized(){

Matthias Möller's avatar
Matthias Möller committed
85
  emit setSlotDescription("adaptiveRemeshing(int,double,double,double,uint,bool)", "Adaptive Remeshing with vertex selection",
86
87
                          QString("object_id,error,min_edge_length,max_edge_length,iterations,use_projection").split(","),
                          QString("id of an object,error,minimal target edge length,maximal target edge length,iterations,use projection method").split(","));
Matthias Möller's avatar
Matthias Möller committed
88
89
90
91
92
93
94
95
96
97
98
99
100
  emit setSlotDescription("adaptiveRemeshing(int,double,double,double,uint)", "Adaptive Remeshing with vertex selection and projection method",
                            QString("object_id,error,min_edge_length,max_edge_length,iterations").split(","),
                            QString("id of an object,error,minimal target edge length,maximal target edge length,iterations").split(","));

  emit setSlotDescription("adaptiveRemeshingFaceSelection(int,double,double,double,uint,bool)", "Adaptive Remeshing with face selection",
                              QString("object_id,error,min_edge_length,max_edge_length,iterations,use_projection").split(","),
                              QString("id of an object,error,minimal target edge length,maximal target edge length,iterations,use projection method").split(","));
  emit setSlotDescription("adaptiveRemeshingFaceSelection(int,double,double,double,uint)", "Adaptive Remeshing with face selection and projection method",
                              QString("object_id,error,min_edge_length,max_edge_length,iterations").split(","),
                              QString("id of an object,error,minimal target edge length,maximal target edge length,iterations").split(","));


  emit setSlotDescription("uniformRemeshing(int,double,uint,uint,bool)", "Uniform Remeshing with vertex selection",
101
102
                          QString("object_id,edge_length,iterations,area_iterations,use_projection").split(","),
                          QString("id of an object,target edge length,iterations,area iterations,use projection method").split(","));
Matthias Möller's avatar
Matthias Möller committed
103
104
105
106
107
108
109
110
111
112
  emit setSlotDescription("uniformRemeshing(int,double,uint,uint)", "Uniform Remeshing with vertex selection and projection method",
                            QString("object_id,edge_length,iterations,area_iterations").split(","),
                            QString("id of an object,target edge length,iterations,area iterations").split(","));

  emit setSlotDescription("uniformRemeshingFaceSelection(int,double,uint,uint,bool)", "Uniform Remeshing with face selection",
                            QString("object_id,edge_length,iterations,area_iterations,use_projection").split(","),
                            QString("id of an object,target edge length,iterations,area iterations,use projection method").split(","));
  emit setSlotDescription("uniformRemeshingFaceSelection(int,double,uint,uint)", "Uniform Remeshing with face selection and projection method",
                              QString("object_id,edge_length,iterations,area_iterations").split(","),
                              QString("id of an object,target edge length,iterations,area iterations").split(","));
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
}

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

/// init the Toolbox
void RemesherPlugin::initializePlugin() {

  if ( OpenFlipper::Options::gui() ) {
    tool_ = new RemesherToolBox();

    // Connect buttons
    connect(tool_->adaptive_button,    SIGNAL(clicked()), this, SLOT(adaptiveRemeshingButtonClicked()) );
    //connect(tool_->anisotropic_button, SIGNAL(clicked()), this, SLOT(anisotropicRemeshingButtonClicked()) );
    connect(tool_->uniform_button,     SIGNAL(clicked()), this, SLOT(uniformRemeshingButtonClicked()) );

    connect(tool_->adaptive_initial_values, SIGNAL(clicked()), this, SLOT(computeInitValues()));
    connect(tool_->uniform_initial_values, SIGNAL(clicked()), this, SLOT(computeInitValues()));

    tool_->tabWidget->setCurrentIndex(0);

    toolIcon_ = new QIcon(OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator()+"remesher.png");
    emit addToolbox( tr("Remesher") , tool_ , toolIcon_);
  }
Jan Möbius's avatar
Jan Möbius committed
136
137

  connect(this,   SIGNAL( finishJob(QString)),    this, SLOT(threadFinished(QString)), Qt::QueuedConnection);
138
139
140
141
142
143
144
145
}

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

void RemesherPlugin::threadFinished(QString _jobId) {

  for ( PluginFunctions::ObjectIterator o_it(PluginFunctions::TARGET_OBJECTS,DataType(DATA_TRIANGLE_MESH | DATA_POLY_MESH)) ;
      o_it != PluginFunctions::objectsEnd(); ++o_it)  {
146
147
148
149
150
151
152
153

    if ( operation_ == REMESH_ADAPTIVE ) {
      emit updatedObject(o_it->id(), UPDATE_TOPOLOGY );
      emit createBackup(o_it->id(), "Adaptive remeshing", UPDATE_TOPOLOGY);
    } else if ( operation_ == REMESH_UNIFORM ) {
      emit updatedObject(o_it->id(), UPDATE_TOPOLOGY );
      emit createBackup(o_it->id(), "Uniform remeshing", UPDATE_TOPOLOGY);
    }
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
  }

  // Detach job from progress emitter
  progress_->detachJob();
}

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

void RemesherPlugin::computeInitValues() {

  if(OpenFlipper::Options::nogui()) return;

  double mean_edge = 0.0;
  double max_feature_angle = 0.0;

  //read one target objects
  for ( PluginFunctions::ObjectIterator o_it(PluginFunctions::TARGET_OBJECTS, DataType(DATA_TRIANGLE_MESH | DATA_POLY_MESH)) ;
      o_it != PluginFunctions::objectsEnd(); ++o_it)  {

    if(o_it->dataType() == DATA_TRIANGLE_MESH) {

      TriMesh* mesh = PluginFunctions::triMesh(o_it->id());
      if(!mesh) return;

      mesh->update_face_normals();

      for(TriMesh::EdgeIter e_it = mesh->edges_begin(); e_it != mesh->edges_end(); ++e_it) {

Jan Möbius's avatar
Jan Möbius committed
182
        TriMesh::HalfedgeHandle he = mesh->halfedge_handle(*e_it, 0);
183
184
185
186
187
188
189
190
191

        ACG::Vec3d vec_e = mesh->point(mesh->to_vertex_handle(he)) - mesh->point(mesh->from_vertex_handle(he));

        mean_edge += vec_e.length();

        // Estimate feature angle
        TriMesh::FaceHandle fh1 = mesh->face_handle(he);
        TriMesh::FaceHandle fh2 = mesh->face_handle(mesh->opposite_halfedge_handle(he));

192
        // Boundary halfedge?
193
        if ( !fh1.is_valid() || !fh2.is_valid() )
194
195
          continue;

196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
        TriMesh::Normal n1 = mesh->normal(fh1);
        TriMesh::Normal n2 = mesh->normal(fh2);

        double feature_angle = (1.0 - (n1 | n2));

        if(feature_angle > max_feature_angle) max_feature_angle = feature_angle;
      }

      mean_edge /= (double)mesh->n_edges();

    } else {
      // DATA_POLY_MESH

      PolyMesh* mesh = PluginFunctions::polyMesh(o_it->id());
      if(!mesh) return;

      mesh->update_face_normals();

      for(PolyMesh::EdgeIter e_it = mesh->edges_begin(); e_it != mesh->edges_end(); ++e_it) {

Jan Möbius's avatar
Jan Möbius committed
216
        PolyMesh::HalfedgeHandle he = mesh->halfedge_handle(*e_it, 0);
217
218
219
220
221
222
223
224
225

        ACG::Vec3d vec_e = mesh->point(mesh->to_vertex_handle(he)) - mesh->point(mesh->from_vertex_handle(he));

        mean_edge += vec_e.length();

        // Estimate feature angle
        PolyMesh::FaceHandle fh1 = mesh->face_handle(he);
        PolyMesh::FaceHandle fh2 = mesh->face_handle(mesh->opposite_halfedge_handle(he));

226
        // Boundary halfedge?
227
        if ( !fh1.is_valid() || !fh2.is_valid() )
228
229
          continue;

230
231
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
261
262
263
264
        PolyMesh::Normal n1 = mesh->normal(fh1);
        PolyMesh::Normal n2 = mesh->normal(fh2);

        double feature_angle = (1.0 - (n1 | n2));

        if(feature_angle > max_feature_angle) max_feature_angle = feature_angle;
      }

      mean_edge /= (double)mesh->n_edges();
    }

    // Convert feature angle to radian
    max_feature_angle = max_feature_angle * (M_PI / 2.0);

    // Set adaptive values
    tool_->adaptive_error->setValue(mean_edge * 0.1); // 10% of mean value
    tool_->adaptive_min_edge->setValue(mean_edge - (mean_edge * 0.1)); // mean - 10%
    tool_->adaptive_max_edge->setValue(mean_edge + (mean_edge * 0.1)); // mean + 10%

    // Set uniform values
    tool_->uniform_edge_length->setValue(mean_edge);

    return; // Just take first object
  }
}

// ----------------------- Adaptive Remeshing ---------------------------------------------

void RemesherPlugin::adaptiveRemeshingButtonClicked() {
    
  QString jobId = name() + "AdaptiveRemeshing";
  OpenFlipperThread* thread = new OpenFlipperThread(jobId);

  connect(thread, SIGNAL( finished(QString)),     this, SIGNAL(finishJob(QString)));
  connect(thread, SIGNAL( function() ),           this, SLOT(adaptiveRemeshing()),Qt::DirectConnection);
Jan Möbius's avatar
Jan Möbius committed
265

266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298

  emit startJob( jobId, "Adaptive remeshing" , 0 , 100 , true);

  // Attach job to progress emitter
  progress_->attachJob(jobId);

  thread->start();
  thread->startProcessing();

}

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

void RemesherPlugin::adaptiveRemeshing() {

  if(OpenFlipper::Options::nogui()) return;

  //read one target objects
  for ( PluginFunctions::ObjectIterator o_it(PluginFunctions::TARGET_OBJECTS, DataType(DATA_TRIANGLE_MESH)) ;
      o_it != PluginFunctions::objectsEnd(); ++o_it)  {

    // Check data type.
    // Note that adaptive remeshing is restricted
    // to triangle meshes only since it relies
    // on edge flips which are only defined
    // for triangle configurations.

    // Get parameters from GUI
    double error = tool_->adaptive_error->value();
    double min_edge = tool_->adaptive_min_edge->value();
    double max_edge = tool_->adaptive_max_edge->value();
    unsigned int iters = tool_->adaptive_iters->text().toInt();
    bool projection = tool_->adaptive_projection->isChecked();
Matthias Möller's avatar
Matthias Möller committed
299
    bool vertexSelection = (tool_->adaptive_selection->currentIndex() == 0);
300

Matthias Möller's avatar
Matthias Möller committed
301
    slotAdaptiveRemeshing(o_it->id(), error, min_edge, max_edge, iters, projection,vertexSelection);
302
303
304
305
306
307

  }
}

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

308
309
310
311
312
void RemesherPlugin::slotAdaptiveRemeshing(int           _objectID,
                                           double        _error,
                                           double        _min_edge_length,
                                           double        _max_edge_length,
                                           unsigned int  _iters,
Matthias Möller's avatar
Matthias Möller committed
313
314
                                           bool          _use_projection,
                                           bool          _vertex_selection) {
315
    
316
317
  operation_ = REMESH_ADAPTIVE;

318
319
320
321
322
323
324
325
326
327
328
  BaseObjectData* object = 0;

  if (PluginFunctions::getObject(_objectID, object)) {

    // Check data type
    if (object->dataType(DATA_TRIANGLE_MESH)) {

      TriMesh* mesh = PluginFunctions::triMesh(object);

      Remeshing::AdaptiveRemesherT<TriMesh> remesher(*mesh, progress_);

Matthias Möller's avatar
Matthias Möller committed
329
330
331
      Remeshing::BaseRemesherT<TriMesh>::Selection selection = (_vertex_selection) ? Remeshing::BaseRemesherT<TriMesh>::VERTEX_SELECTION : Remeshing::BaseRemesherT<TriMesh>::FACE_SELECTION;

      remesher.remesh(_error, _min_edge_length, _max_edge_length, _iters, _use_projection, selection);
332
333
334
335

      mesh->update_normals();


336
      QString projectionString = "\"FALSE\"";
337
      if (_use_projection)
338
        projectionString = "\"TRUE\"";
339
340
341
342
343
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

      emit scriptInfo("adaptiveRemeshing(" + QString::number(_objectID) + ", "
                                           + QString::number(_error) + ", "
                                           + QString::number(_min_edge_length) + ", "
                                           + QString::number(_max_edge_length) + ", "
                                           + QString::number(_iters) + ", "
                                           + projectionString + ")");

      return;
    }
  }
}

// ----------------------- Uniform Remeshing ---------------------------------------------

void RemesherPlugin::uniformRemeshingButtonClicked() {
    
  OpenFlipperThread* thread = new OpenFlipperThread(name() + "UniformRemeshing");

  connect(thread, SIGNAL( state(QString, int)),   this, SIGNAL(setJobState(QString, int)));
  connect(thread, SIGNAL( finished(QString)),     this, SIGNAL(finishJob(QString)));
  connect(thread, SIGNAL( function() ),           this, SLOT(uniformRemeshing()),Qt::DirectConnection);

  emit startJob( name() + "UniformRemeshing", "Uniform remeshing" , 0 , 100 , true);

  thread->start();
  thread->startProcessing();

}

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

void RemesherPlugin::uniformRemeshing(){

  if(OpenFlipper::Options::nogui()) return;

  // Get parameters from GUI
  double edge_length      = tool_->uniform_edge_length->value();
  unsigned int iters      = tool_->uniform_iters->text().toInt();
  unsigned int area_iters = tool_->uniform_area_iters->text().toInt();
  bool projection         = tool_->uniform_projection->isChecked();
Matthias Möller's avatar
Matthias Möller committed
380
  bool vertex_selection    = (tool_->uniform_selection->currentIndex() == 0);
381
382
383
384
385
386
387
388
389

  //read one target objects
  for ( PluginFunctions::ObjectIterator o_it(PluginFunctions::TARGET_OBJECTS, DataType(DATA_TRIANGLE_MESH)) ;
      o_it != PluginFunctions::objectsEnd(); ++o_it)  {

    // Set incident vertices to feature edges to be feature vertices
    TriMesh* mesh = PluginFunctions::triMesh(o_it->id());
    if(!mesh) continue;
    for(TriMesh::EdgeIter e_it = mesh->edges_begin(); e_it != mesh->edges_end(); ++e_it) {
Jan Möbius's avatar
Jan Möbius committed
390
391
392
      if(mesh->status(*e_it).feature()) {
        mesh->status(mesh->to_vertex_handle(mesh->halfedge_handle(*e_it, 0))).set_feature(true);
        mesh->status(mesh->from_vertex_handle(mesh->halfedge_handle(*e_it, 0))).set_feature(true);
393
394
395
396
397
398
399
400
401
      }
    }

    // Check data type.
    // Note that uniform remeshing is restricted
    // to triangle meshes only since it also relies
    // on edge flips which are only defined
    // for triangle configurations.

Matthias Möller's avatar
Matthias Möller committed
402
    slotUniformRemeshing(o_it->id(), edge_length, iters, area_iters, projection,vertex_selection);
403
404
405
406
407
  }
}

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

408
409
410
411
void RemesherPlugin::slotUniformRemeshing(int           _objectID,
                                          double        _edge_length,
                                          unsigned int  _iters,
                                          unsigned int  _area_iters,
Matthias Möller's avatar
Matthias Möller committed
412
413
                                          bool          _use_projection,
                                          bool          _vertex_selection) {
414
    
415
416
  operation_ = REMESH_UNIFORM;

417
418
419
420
421
422
423
424
425
426
427
  BaseObjectData* object = 0;

  if (PluginFunctions::getObject(_objectID, object)) {

    // Check data type
    if (object->dataType(DATA_TRIANGLE_MESH)) {

      TriMesh* mesh = PluginFunctions::triMesh(object);

      Remeshing::UniformRemesherT<TriMesh> remesher(*mesh, progress_);

Matthias Möller's avatar
Matthias Möller committed
428
429
430
      Remeshing::BaseRemesherT<TriMesh>::Selection selection = (_vertex_selection) ? Remeshing::BaseRemesherT<TriMesh>::VERTEX_SELECTION : Remeshing::BaseRemesherT<TriMesh>::FACE_SELECTION;

      remesher.remesh(_edge_length, _iters, _area_iters, _use_projection, selection);
431
432
433

      mesh->update_normals();

434
      QString projectionString = "\"FALSE\"";
435
      if (_use_projection)
436
        projectionString = "\"TRUE\"";
437

438
439
440
441
442
443
      emit scriptInfo("uniformRemeshing(" + QString::number(_objectID) + ", "
                                          + QString::number(_edge_length) + ", "
                                          + QString::number(_iters) + ", "
                                          + QString::number(_area_iters) + ", "
                                          + QString::number(_iters) + ", "
                                          + projectionString + ")");
444
445
446
447
448
449
450
451
452

      return;
    }
  }

}

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

453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
void RemesherPlugin::adaptiveRemeshing(int           _objectID,
                                       double        _error,
                                       double        _min_edge_length,
                                       double        _max_edge_length,
                                       unsigned int  _iters,
                                       bool          _use_projection) {

  slotAdaptiveRemeshing(_objectID,_error,_min_edge_length,_max_edge_length,_iters,_use_projection);
  emit updatedObject(_objectID, UPDATE_TOPOLOGY );
  emit createBackup(_objectID, "Adaptive remeshing", UPDATE_TOPOLOGY);

}

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

void RemesherPlugin::uniformRemeshing(int           _objectID,
                                      double        _edge_length,
                                      unsigned int  _iters,
                                      unsigned int  _area_iters,
                                      bool          _use_projection) {

  slotUniformRemeshing(_objectID,_edge_length,_iters,_area_iters,_use_projection);
  emit updatedObject(_objectID, UPDATE_TOPOLOGY );
  emit createBackup(_objectID, "Uniform remeshing", UPDATE_TOPOLOGY);

}

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

Matthias Möller's avatar
Matthias Möller committed
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
void RemesherPlugin::adaptiveRemeshingFaceSelection(int           _objectID,
                                                    double        _error,
                                                    double        _min_edge_length,
                                                    double        _max_edge_length,
                                                    unsigned int  _iters,
                                                    bool          _use_projection) {

  slotAdaptiveRemeshing(_objectID,_error,_min_edge_length,_max_edge_length,_iters,_use_projection,false);
  emit updatedObject(_objectID, UPDATE_TOPOLOGY );
  emit createBackup(_objectID, "Adaptive remeshing", UPDATE_TOPOLOGY);

}

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

void RemesherPlugin::uniformRemeshingFaceSelection(int           _objectID,
                                                   double        _edge_length,
                                                   unsigned int  _iters,
                                                   unsigned int  _area_iters,
                                                   bool          _use_projection) {

  slotUniformRemeshing(_objectID,_edge_length,_iters,_area_iters,_use_projection,false);
  emit updatedObject(_objectID, UPDATE_TOPOLOGY );
  emit createBackup(_objectID, "Uniform remeshing", UPDATE_TOPOLOGY);

}

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

Matthias Möller's avatar
Matthias Möller committed
511
512
513
#if QT_VERSION < 0x050000
  Q_EXPORT_PLUGIN2( remesherplugin, RemesherPlugin );
#endif
514