Developer Documentation

RemesherPlugin.cc
1 /*===========================================================================*\
2 * *
3 * OpenFlipper *
4 * Copyright (c) 2001-2015, RWTH-Aachen University *
5 * Department of Computer Graphics and Multimedia *
6 * All rights reserved. *
7 * www.openflipper.org *
8 * *
9 *---------------------------------------------------------------------------*
10 * This file is part of OpenFlipper. *
11 *---------------------------------------------------------------------------*
12 * *
13 * Redistribution and use in source and binary forms, with or without *
14 * modification, are permitted provided that the following conditions *
15 * are met: *
16 * *
17 * 1. Redistributions of source code must retain the above copyright notice, *
18 * this list of conditions and the following disclaimer. *
19 * *
20 * 2. Redistributions in binary form must reproduce the above copyright *
21 * notice, this list of conditions and the following disclaimer in the *
22 * documentation and/or other materials provided with the distribution. *
23 * *
24 * 3. Neither the name of the copyright holder nor the names of its *
25 * contributors may be used to endorse or promote products derived from *
26 * this software without specific prior written permission. *
27 * *
28 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS *
29 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *
30 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
31 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER *
32 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, *
33 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, *
34 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR *
35 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
36 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING *
37 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS *
38 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
39 * *
40 \*===========================================================================*/
41
42
43
44 #include "RemesherPlugin.hh"
45
46 #include "Algorithms/AdaptiveRemesherT.hh"
47 #include "Algorithms/UniformRemesherT.hh"
48
49 /*
50 * STILL \TODO:
51 *
52 * - Emit status updates when remeshing
53 */
54
55 // ----------------------------------------------------------------------------------------
56
57 RemesherPlugin::RemesherPlugin() :
58 progress_(0) {
59
60 if ( OpenFlipper::Options::gui() ) {
61 progress_ = new ProgressEmitter();
62
63 connect(progress_, SIGNAL(signalJobState(QString,int)), this, SIGNAL(setJobState(QString,int)), Qt::QueuedConnection);
64 connect(progress_, SIGNAL(changeDescription(QString,QString)), this, SIGNAL(setJobDescription(QString,QString)), Qt::QueuedConnection);
65 }
66 }
67
68 // ----------------------------------------------------------------------------------------
69
70 RemesherPlugin::~RemesherPlugin() {
71 if ( OpenFlipper::Options::gui() ) {
72 delete progress_;
73 }
74 }
75
76 // ----------------------------------------------------------------------------------------
77
79 void RemesherPlugin::pluginsInitialized(){
80
81 emit setSlotDescription("adaptiveRemeshing(int,double,double,double,uint,bool)", "Adaptive Remeshing with vertex selection",
82 QString("object_id,error,min_edge_length,max_edge_length,iterations,use_projection").split(","),
83 QString("id of an object,error,minimal target edge length,maximal target edge length,iterations,use projection method").split(","));
84 emit setSlotDescription("adaptiveRemeshing(int,double,double,double,uint)", "Adaptive Remeshing with vertex selection and projection method",
85 QString("object_id,error,min_edge_length,max_edge_length,iterations").split(","),
86 QString("id of an object,error,minimal target edge length,maximal target edge length,iterations").split(","));
87
88 emit setSlotDescription("adaptiveRemeshingFaceSelection(int,double,double,double,uint,bool)", "Adaptive Remeshing with face selection",
89 QString("object_id,error,min_edge_length,max_edge_length,iterations,use_projection").split(","),
90 QString("id of an object,error,minimal target edge length,maximal target edge length,iterations,use projection method").split(","));
91 emit setSlotDescription("adaptiveRemeshingFaceSelection(int,double,double,double,uint)", "Adaptive Remeshing with face selection and projection method",
92 QString("object_id,error,min_edge_length,max_edge_length,iterations").split(","),
93 QString("id of an object,error,minimal target edge length,maximal target edge length,iterations").split(","));
94
95
96 emit setSlotDescription("uniformRemeshing(int,double,uint,uint,bool)", "Uniform Remeshing with vertex selection",
97 QString("object_id,edge_length,iterations,area_iterations,use_projection").split(","),
98 QString("id of an object,target edge length,iterations,area iterations,use projection method").split(","));
99 emit setSlotDescription("uniformRemeshing(int,double,uint,uint)", "Uniform Remeshing with vertex selection and projection method",
100 QString("object_id,edge_length,iterations,area_iterations").split(","),
101 QString("id of an object,target edge length,iterations,area iterations").split(","));
102
103 emit setSlotDescription("uniformRemeshingFaceSelection(int,double,uint,uint,bool)", "Uniform Remeshing with face selection",
104 QString("object_id,edge_length,iterations,area_iterations,use_projection").split(","),
105 QString("id of an object,target edge length,iterations,area iterations,use projection method").split(","));
106 emit setSlotDescription("uniformRemeshingFaceSelection(int,double,uint,uint)", "Uniform Remeshing with face selection and projection method",
107 QString("object_id,edge_length,iterations,area_iterations").split(","),
108 QString("id of an object,target edge length,iterations,area iterations").split(","));
109 }
110
111 // ----------------------------------------------------------------------------------------
112
114 void RemesherPlugin::initializePlugin() {
115
116 if ( OpenFlipper::Options::gui() ) {
117 tool_ = new RemesherToolBox();
118
119 // Connect buttons
120 connect(tool_->adaptive_button, SIGNAL(clicked()), this, SLOT(adaptiveRemeshingButtonClicked()) );
121 //connect(tool_->anisotropic_button, SIGNAL(clicked()), this, SLOT(anisotropicRemeshingButtonClicked()) );
122 connect(tool_->uniform_button, SIGNAL(clicked()), this, SLOT(uniformRemeshingButtonClicked()) );
123
124 connect(tool_->adaptive_initial_values, SIGNAL(clicked()), this, SLOT(computeInitValues()));
125 connect(tool_->uniform_initial_values, SIGNAL(clicked()), this, SLOT(computeInitValues()));
126
127 tool_->tabWidget->setCurrentIndex(0);
128
129 toolIcon_ = new QIcon(OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator()+"remesher.png");
130 emit addToolbox( tr("Remesher") , tool_ , toolIcon_);
131 }
132
133 connect(this, SIGNAL( finishJob(QString)), this, SLOT(threadFinished(QString)), Qt::QueuedConnection);
134 }
135
136 // ----------------------------------------------------------------------------------------
137
138 void RemesherPlugin::threadFinished(QString _jobId) {
139
140 for ( PluginFunctions::ObjectIterator o_it(PluginFunctions::TARGET_OBJECTS,DataType(DATA_TRIANGLE_MESH | DATA_POLY_MESH)) ;
141 o_it != PluginFunctions::objectsEnd(); ++o_it) {
142
143 if ( operation_ == REMESH_ADAPTIVE ) {
144 emit updatedObject(o_it->id(), UPDATE_TOPOLOGY );
145 emit createBackup(o_it->id(), "Adaptive remeshing", UPDATE_TOPOLOGY);
146 } else if ( operation_ == REMESH_UNIFORM ) {
147 emit updatedObject(o_it->id(), UPDATE_TOPOLOGY );
148 emit createBackup(o_it->id(), "Uniform remeshing", UPDATE_TOPOLOGY);
149 }
150 }
151
152 // Detach job from progress emitter
153 progress_->detachJob();
154 }
155
156 // ----------------------------------------------------------------------------------------
157
158 void RemesherPlugin::computeInitValues() {
159
160 if(OpenFlipper::Options::nogui()) return;
161
162 double mean_edge = 0.0;
163 double max_feature_angle = 0.0;
164
165 //read one target objects
166 for ( PluginFunctions::ObjectIterator o_it(PluginFunctions::TARGET_OBJECTS, DataType(DATA_TRIANGLE_MESH | DATA_POLY_MESH)) ;
167 o_it != PluginFunctions::objectsEnd(); ++o_it) {
168
169 if(o_it->dataType() == DATA_TRIANGLE_MESH) {
170
171 TriMesh* mesh = PluginFunctions::triMesh(o_it->id());
172 if(!mesh) return;
173
174 mesh->update_face_normals();
175
176 for(TriMesh::EdgeIter e_it = mesh->edges_begin(); e_it != mesh->edges_end(); ++e_it) {
177
178 TriMesh::HalfedgeHandle he = mesh->halfedge_handle(*e_it, 0);
179
180 ACG::Vec3d vec_e = mesh->point(mesh->to_vertex_handle(he)) - mesh->point(mesh->from_vertex_handle(he));
181
182 mean_edge += vec_e.length();
183
184 // Estimate feature angle
185 TriMesh::FaceHandle fh1 = mesh->face_handle(he);
186 TriMesh::FaceHandle fh2 = mesh->face_handle(mesh->opposite_halfedge_handle(he));
187
188 // Boundary halfedge?
189 if ( !fh1.is_valid() || !fh2.is_valid() )
190 continue;
191
192 TriMesh::Normal n1 = mesh->normal(fh1);
193 TriMesh::Normal n2 = mesh->normal(fh2);
194
195 double feature_angle = (1.0 - (n1 | n2));
196
197 if(feature_angle > max_feature_angle) max_feature_angle = feature_angle;
198 }
199
200 mean_edge /= (double)mesh->n_edges();
201
202 } else {
203 // DATA_POLY_MESH
204
205 PolyMesh* mesh = PluginFunctions::polyMesh(o_it->id());
206 if(!mesh) return;
207
208 mesh->update_face_normals();
209
210 for(PolyMesh::EdgeIter e_it = mesh->edges_begin(); e_it != mesh->edges_end(); ++e_it) {
211
212 PolyMesh::HalfedgeHandle he = mesh->halfedge_handle(*e_it, 0);
213
214 ACG::Vec3d vec_e = mesh->point(mesh->to_vertex_handle(he)) - mesh->point(mesh->from_vertex_handle(he));
215
216 mean_edge += vec_e.length();
217
218 // Estimate feature angle
219 PolyMesh::FaceHandle fh1 = mesh->face_handle(he);
220 PolyMesh::FaceHandle fh2 = mesh->face_handle(mesh->opposite_halfedge_handle(he));
221
222 // Boundary halfedge?
223 if ( !fh1.is_valid() || !fh2.is_valid() )
224 continue;
225
226 PolyMesh::Normal n1 = mesh->normal(fh1);
227 PolyMesh::Normal n2 = mesh->normal(fh2);
228
229 double feature_angle = (1.0 - (n1 | n2));
230
231 if(feature_angle > max_feature_angle) max_feature_angle = feature_angle;
232 }
233
234 mean_edge /= (double)mesh->n_edges();
235 }
236
237 // Convert feature angle to radian
238 max_feature_angle = max_feature_angle * (M_PI / 2.0);
239
240 // Set adaptive values
241 tool_->adaptive_error->setValue(mean_edge * 0.1); // 10% of mean value
242 tool_->adaptive_min_edge->setValue(mean_edge - (mean_edge * 0.1)); // mean - 10%
243 tool_->adaptive_max_edge->setValue(mean_edge + (mean_edge * 0.1)); // mean + 10%
244
245 // Set uniform values
246 tool_->uniform_edge_length->setValue(mean_edge);
247
248 return; // Just take first object
249 }
250 }
251
252 // ----------------------- Adaptive Remeshing ---------------------------------------------
253
254 void RemesherPlugin::adaptiveRemeshingButtonClicked() {
255
256 QString jobId = name() + "AdaptiveRemeshing";
257 OpenFlipperThread* thread = new OpenFlipperThread(jobId);
258
259 connect(thread, SIGNAL( finished(QString)), this, SIGNAL(finishJob(QString)));
260 connect(thread, SIGNAL( function() ), this, SLOT(adaptiveRemeshing()),Qt::DirectConnection);
261
262
263 emit startJob( jobId, "Adaptive remeshing" , 0 , 100 , true);
264
265 // Attach job to progress emitter
266 progress_->attachJob(jobId);
267
268 thread->start();
269 thread->startProcessing();
270
271 }
272
273 // ----------------------------------------------------------------------------------------
274
275 void RemesherPlugin::adaptiveRemeshing() {
276
277 if(OpenFlipper::Options::nogui()) return;
278
279 //read one target objects
280 for ( PluginFunctions::ObjectIterator o_it(PluginFunctions::TARGET_OBJECTS, DataType(DATA_TRIANGLE_MESH)) ;
281 o_it != PluginFunctions::objectsEnd(); ++o_it) {
282
283 // Check data type.
284 // Note that adaptive remeshing is restricted
285 // to triangle meshes only since it relies
286 // on edge flips which are only defined
287 // for triangle configurations.
288
289 // Get parameters from GUI
290 double error = tool_->adaptive_error->value();
291 double min_edge = tool_->adaptive_min_edge->value();
292 double max_edge = tool_->adaptive_max_edge->value();
293 unsigned int iters = tool_->adaptive_iters->text().toInt();
294 bool projection = tool_->adaptive_projection->isChecked();
295 bool vertexSelection = (tool_->adaptive_selection->currentIndex() == 0);
296
297 slotAdaptiveRemeshing(o_it->id(), error, min_edge, max_edge, iters, projection,vertexSelection);
298
299 }
300 }
301
302 // ----------------------------------------------------------------------------------------
303
304 void RemesherPlugin::slotAdaptiveRemeshing(int _objectID,
305 double _error,
306 double _min_edge_length,
307 double _max_edge_length,
308 unsigned int _iters,
309 bool _use_projection,
310 bool _vertex_selection) {
311
312 operation_ = REMESH_ADAPTIVE;
313
314 BaseObjectData* object = 0;
315
316 if (PluginFunctions::getObject(_objectID, object)) {
317
318 // Check data type
319 if (object->dataType(DATA_TRIANGLE_MESH)) {
320
321 TriMesh* mesh = PluginFunctions::triMesh(object);
322
323 Remeshing::AdaptiveRemesherT<TriMesh> remesher(*mesh, progress_);
324
325 Remeshing::BaseRemesherT<TriMesh>::Selection selection = (_vertex_selection) ? Remeshing::BaseRemesherT<TriMesh>::VERTEX_SELECTION : Remeshing::BaseRemesherT<TriMesh>::FACE_SELECTION;
326
327 remesher.remesh(_error, _min_edge_length, _max_edge_length, _iters, _use_projection, selection);
328
329 mesh->update_normals();
330
331
332 QString projectionString = "\"FALSE\"";
333 if (_use_projection)
334 projectionString = "\"TRUE\"";
335
336 emit scriptInfo("adaptiveRemeshing(" + QString::number(_objectID) + ", "
337 + QString::number(_error) + ", "
338 + QString::number(_min_edge_length) + ", "
339 + QString::number(_max_edge_length) + ", "
340 + QString::number(_iters) + ", "
341 + projectionString + ")");
342
343 return;
344 }
345 }
346 }
347
348 // ----------------------- Uniform Remeshing ---------------------------------------------
349
350 void RemesherPlugin::uniformRemeshingButtonClicked() {
351
352 OpenFlipperThread* thread = new OpenFlipperThread(name() + "UniformRemeshing");
353
354 connect(thread, SIGNAL( state(QString, int)), this, SIGNAL(setJobState(QString, int)));
355 connect(thread, SIGNAL( finished(QString)), this, SIGNAL(finishJob(QString)));
356 connect(thread, SIGNAL( function() ), this, SLOT(uniformRemeshing()),Qt::DirectConnection);
357
358 emit startJob( name() + "UniformRemeshing", "Uniform remeshing" , 0 , 100 , true);
359
360 thread->start();
361 thread->startProcessing();
362
363 }
364
365 // ----------------------------------------------------------------------------------------
366
367 void RemesherPlugin::uniformRemeshing(){
368
369 if(OpenFlipper::Options::nogui()) return;
370
371 // Get parameters from GUI
372 double edge_length = tool_->uniform_edge_length->value();
373 unsigned int iters = tool_->uniform_iters->text().toInt();
374 unsigned int area_iters = tool_->uniform_area_iters->text().toInt();
375 bool projection = tool_->uniform_projection->isChecked();
376 bool vertex_selection = (tool_->uniform_selection->currentIndex() == 0);
377
378 //read one target objects
379 for ( PluginFunctions::ObjectIterator o_it(PluginFunctions::TARGET_OBJECTS, DataType(DATA_TRIANGLE_MESH)) ;
380 o_it != PluginFunctions::objectsEnd(); ++o_it) {
381
382 // Set incident vertices to feature edges to be feature vertices
383 TriMesh* mesh = PluginFunctions::triMesh(o_it->id());
384 if(!mesh) continue;
385 for(TriMesh::EdgeIter e_it = mesh->edges_begin(); e_it != mesh->edges_end(); ++e_it) {
386 if(mesh->status(*e_it).feature()) {
387 mesh->status(mesh->to_vertex_handle(mesh->halfedge_handle(*e_it, 0))).set_feature(true);
388 mesh->status(mesh->from_vertex_handle(mesh->halfedge_handle(*e_it, 0))).set_feature(true);
389 }
390 }
391
392 // Check data type.
393 // Note that uniform remeshing is restricted
394 // to triangle meshes only since it also relies
395 // on edge flips which are only defined
396 // for triangle configurations.
397
398 slotUniformRemeshing(o_it->id(), edge_length, iters, area_iters, projection,vertex_selection);
399 }
400 }
401
402 // ----------------------------------------------------------------------------------------
403
404 void RemesherPlugin::slotUniformRemeshing(int _objectID,
405 double _edge_length,
406 unsigned int _iters,
407 unsigned int _area_iters,
408 bool _use_projection,
409 bool _vertex_selection) {
410
411 operation_ = REMESH_UNIFORM;
412
413 BaseObjectData* object = 0;
414
415 if (PluginFunctions::getObject(_objectID, object)) {
416
417 // Check data type
418 if (object->dataType(DATA_TRIANGLE_MESH)) {
419
420 TriMesh* mesh = PluginFunctions::triMesh(object);
421
422 Remeshing::UniformRemesherT<TriMesh> remesher(*mesh, progress_);
423
424 Remeshing::BaseRemesherT<TriMesh>::Selection selection = (_vertex_selection) ? Remeshing::BaseRemesherT<TriMesh>::VERTEX_SELECTION : Remeshing::BaseRemesherT<TriMesh>::FACE_SELECTION;
425
426 remesher.remesh(_edge_length, _iters, _area_iters, _use_projection, selection);
427
428 mesh->update_normals();
429
430 QString projectionString = "\"FALSE\"";
431 if (_use_projection)
432 projectionString = "\"TRUE\"";
433
434 emit scriptInfo("uniformRemeshing(" + QString::number(_objectID) + ", "
435 + QString::number(_edge_length) + ", "
436 + QString::number(_iters) + ", "
437 + QString::number(_area_iters) + ", "
438 + QString::number(_iters) + ", "
439 + projectionString + ")");
440
441 return;
442 }
443 }
444
445 }
446
447 // ----------------------------------------------------------------------------------------
448
449 void RemesherPlugin::adaptiveRemeshing(int _objectID,
450 double _error,
451 double _min_edge_length,
452 double _max_edge_length,
453 unsigned int _iters,
454 bool _use_projection) {
455
456 slotAdaptiveRemeshing(_objectID,_error,_min_edge_length,_max_edge_length,_iters,_use_projection);
457 emit updatedObject(_objectID, UPDATE_TOPOLOGY );
458 emit createBackup(_objectID, "Adaptive remeshing", UPDATE_TOPOLOGY);
459
460 }
461
462 // ----------------------------------------------------------------------------------------
463
464 void RemesherPlugin::uniformRemeshing(int _objectID,
465 double _edge_length,
466 unsigned int _iters,
467 unsigned int _area_iters,
468 bool _use_projection) {
469
470 slotUniformRemeshing(_objectID,_edge_length,_iters,_area_iters,_use_projection);
471 emit updatedObject(_objectID, UPDATE_TOPOLOGY );
472 emit createBackup(_objectID, "Uniform remeshing", UPDATE_TOPOLOGY);
473
474 }
475
476 // ----------------------------------------------------------------------------------------
477
478 void RemesherPlugin::adaptiveRemeshingFaceSelection(int _objectID,
479 double _error,
480 double _min_edge_length,
481 double _max_edge_length,
482 unsigned int _iters,
483 bool _use_projection) {
484
485 slotAdaptiveRemeshing(_objectID,_error,_min_edge_length,_max_edge_length,_iters,_use_projection,false);
486 emit updatedObject(_objectID, UPDATE_TOPOLOGY );
487 emit createBackup(_objectID, "Adaptive remeshing", UPDATE_TOPOLOGY);
488
489 }
490
491 // ----------------------------------------------------------------------------------------
492
493 void RemesherPlugin::uniformRemeshingFaceSelection(int _objectID,
494 double _edge_length,
495 unsigned int _iters,
496 unsigned int _area_iters,
497 bool _use_projection) {
498
499 slotUniformRemeshing(_objectID,_edge_length,_iters,_area_iters,_use_projection,false);
500 emit updatedObject(_objectID, UPDATE_TOPOLOGY );
501 emit createBackup(_objectID, "Uniform remeshing", UPDATE_TOPOLOGY);
502
503 }
504
505 // ----------------------------------------------------------------------------------------
506
507
DATA_TRIANGLE_MESH
#define DATA_TRIANGLE_MESH
Definition: TriangleMesh.hh:60
DATA_POLY_MESH
#define DATA_POLY_MESH
Definition: PolyMesh.hh:59
PluginFunctions::polyMesh
PolyMesh * polyMesh(BaseObjectData *_object)
Get a poly mesh from an object.
Definition: PluginFunctionsPolyMesh.cc:128
ProgressEmitter
Definition: ProgressEmitter.hh:46
OpenMesh::PolyMeshT::Normal
Kernel::Normal Normal
Normal type.
Definition: PolyMeshT.hh:114
PluginFunctions::getObject
bool getObject(const int _identifier, BaseObject *&_object)
Get the object which has the given identifier.
Definition: PluginFunctions.cc:296
OpenMesh::TriMesh_ArrayKernelT
Definition: PolyMesh_ArrayKernelT.hh:73
OpenMesh::VectorT
Definition: Vector11T.hh:83
PluginFunctions::triMesh
TriMesh * triMesh(BaseObjectData *_object)
Get a triangle mesh from an object.
Definition: PluginFunctionsTriangleMesh.cc:134
OpenFlipperThread::startProcessing
void startProcessing()
start processing
Definition: OpenFlipperThread.cc:112
DataType
Predefined datatypes.
Definition: DataTypes.hh:83
RemesherPlugin::computeInitValues
void computeInitValues()
Compute mean edge length and set values.
Definition: RemesherPlugin.cc:158
OpenMesh::PolyMesh_ArrayKernelT
Definition: PolyMesh_ArrayKernelT.hh:94
BaseObject::dataType
bool dataType(DataType _type) const
Definition: BaseObject.cc:221
PluginFunctions::objectsEnd
DLLEXPORT ObjectIterator objectsEnd()
Return Iterator to Object End.
Definition: PluginFunctionsIterator.cc:177
RemesherPlugin::pluginsInitialized
void pluginsInitialized()
Initialize the plugin.
Definition: RemesherPlugin.cc:79
PluginFunctions::ObjectIterator
Core Data Iterator.
Definition: PluginFunctions.hh:441
RemesherPlugin::initializePlugin
void initializePlugin()
init the Toolbox
Definition: RemesherPlugin.cc:114
Remeshing::UniformRemesherT
Definition: UniformRemesherT.hh:69
Remeshing::BaseRemesherT
Definition: BaseRemesherT.hh:79
BaseObjectData
Definition: BaseObjectData.hh:102
OpenMesh::PolyMeshT::update_normals
void update_normals()
Compute normals for all primitives.
Definition: PolyMeshT_impl.hh:317
PluginFunctions::TARGET_OBJECTS
const QStringList TARGET_OBJECTS("target")
Iterable object range.
RemesherToolBox
Definition: RemesherToolbox.hh:48
OpenMesh::PolyMeshT::update_face_normals
void update_face_normals()
Update normal vectors for all faces.
Definition: PolyMeshT_impl.hh:335
UPDATE_TOPOLOGY
const UpdateType UPDATE_TOPOLOGY(UpdateTypeSet(1)<< 3)
Topology updated.
OpenMesh::VectorT::length
auto length() const -> decltype(std::declval< VectorT< S, DIM >>().norm())
compute squared euclidean norm
Definition: Vector11T.hh:423
Remeshing::AdaptiveRemesherT
Definition: AdaptiveRemesherT.hh:71
OpenFlipperThread
Thread handling class for OpenFlipper.
Definition: OpenFlipperThread.hh:77