Developer Documentation
EdgeFunctions.cc
1
2/*===========================================================================*\
3 * *
4 * OpenFlipper *
5 * Copyright (c) 2001-2015, RWTH-Aachen University *
6 * Department of Computer Graphics and Multimedia *
7 * All rights reserved. *
8 * www.openflipper.org *
9 * *
10 *---------------------------------------------------------------------------*
11 * This file is part of OpenFlipper. *
12 *---------------------------------------------------------------------------*
13 * *
14 * Redistribution and use in source and binary forms, with or without *
15 * modification, are permitted provided that the following conditions *
16 * are met: *
17 * *
18 * 1. Redistributions of source code must retain the above copyright notice, *
19 * this list of conditions and the following disclaimer. *
20 * *
21 * 2. Redistributions in binary form must reproduce the above copyright *
22 * notice, this list of conditions and the following disclaimer in the *
23 * documentation and/or other materials provided with the distribution. *
24 * *
25 * 3. Neither the name of the copyright holder nor the names of its *
26 * contributors may be used to endorse or promote products derived from *
27 * this software without specific prior written permission. *
28 * *
29 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS *
30 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *
31 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
32 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER *
33 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, *
34 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, *
35 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR *
36 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
37 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING *
38 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS *
39 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
40 * *
41\*===========================================================================*/
42
43#include "MeshRepairPlugin.hh"
45
46//-----------------------------------------------------------------------------
47
48void MeshRepairPlugin::selectEdgesShorterThan(int _objectId,double _length) {
49 selectionEdgeLength(_objectId,_length,false);
50
51 emit scriptInfo( "selectEdgesShorterThan(" + QString::number(_objectId) + ", " + QString::number(_length) + ")" );
52}
53
54//-----------------------------------------------------------------------------
55
56void MeshRepairPlugin::selectEdgesLongerThan(int _objectId,double _length) {
57 selectionEdgeLength(_objectId,_length,true);
58
59 emit scriptInfo( "selectEdgesLongerThan(" + QString::number(_objectId) + ", " + QString::number(_length) + ")" );
60}
61
62//-----------------------------------------------------------------------------
63
64
65void MeshRepairPlugin::selectionEdgeLength(int _objectId, double _length, bool _larger) {
66
67 // get the target mesh
68 TriMesh* triMesh = 0;
69
70 PluginFunctions::getMesh(_objectId,triMesh);
71
72 if ( triMesh ) {
73 // Clear current edge selection
74 MeshSelection::clearEdgeSelection(triMesh);
75
76 // Iterate over all edges
77 for (auto e_it : triMesh->edges()) {
78 OpenMesh::SmartHalfedgeHandle he = e_it.h0();
79 TriMesh::Point p1 = triMesh->point( he.from() );
80 TriMesh::Point p2 = triMesh->point( he.to() );
81
82 if ( _larger ) {
83 if ( (p1 - p2).norm() > _length)
84 triMesh->status(e_it).set_selected(true);
85 } else {
86 if ( (p1 - p2).norm() < _length)
87 triMesh->status(e_it).set_selected(true);
88 }
89 }
90
91 emit updatedObject(_objectId,UPDATE_SELECTION);
92 emit createBackup( _objectId, "Select Edges", UPDATE_SELECTION);
93
94 return;
95 }
96
97 // get the target mesh
98 PolyMesh* polyMesh = 0;
99 PluginFunctions::getMesh(_objectId,polyMesh);
100
101 if ( polyMesh ) {
102 // Clear current edge selection
103 MeshSelection::clearEdgeSelection(polyMesh);
104
105 // Iterate over all edges
106 for (auto e_it : polyMesh->edges()) {
107 OpenMesh::SmartHalfedgeHandle he = e_it.h0();
108 PolyMesh::Point p1 = polyMesh->point( he.from() );
109 PolyMesh::Point p2 = polyMesh->point( he.to() );
110
111 if ( _larger ) {
112 if ( (p1 - p2).norm() > _length)
113 polyMesh->status(e_it).set_selected(true);
114 } else {
115 if ( (p1 - p2).norm() < _length)
116 polyMesh->status(e_it).set_selected(true);
117 }
118 }
119
120 emit updatedObject(_objectId,UPDATE_SELECTION);
121 emit createBackup( _objectId, "Select Edges", UPDATE_SELECTION);
122
123 return;
124 }
125
126 emit log( LOGERR,tr("Unsupported Object Type for edge selection") );
127
128}
129
130//-----------------------------------------------------------------------------
131
133
134 // get the target mesh
135 TriMesh* triMesh = 0;
136
137 PluginFunctions::getMesh(_objectId, triMesh);
138
139 if (triMesh) {
140
141 for (auto e_it : triMesh->edges()) {
142
143 if (!triMesh->status(e_it).deleted() && triMesh->status(e_it).selected()) {
144
145 const OpenMesh::SmartVertexHandle v0 = e_it.h0().to();
146 const OpenMesh::SmartVertexHandle v1 = e_it.h1().to();
147
148 const bool boundary0 = v0.is_boundary();
149 const bool boundary1 = v1.is_boundary();
150
151 const bool feature0 = triMesh->status(v0).feature();
152 const bool feature1 = triMesh->status(v1).feature();
153 const bool featureE = triMesh->status(e_it).feature();
154
155 // Collapsing v1 into vo:
156 // collapse is ok, if collapsed vertex is not a feature vertex or the target vertex is a feature
157 // and if we collapse along an feature edge or if the collapsed vertex is not a feature
158 if ((!boundary1 || boundary0) && (!feature1 || (feature0 && featureE)) && triMesh->is_collapse_ok(
159 e_it.h0()))
160 triMesh->collapse(e_it.h0());
161 else if ((!boundary0 || boundary1) && (!feature0 || (feature1 && featureE)) && triMesh->is_collapse_ok(
162 e_it.h1()))
163 triMesh->collapse(e_it.h1());
164 }
165 }
166
167 triMesh->garbage_collection();
168 triMesh->update_normals();
169
170 emit updatedObject(_objectId, UPDATE_ALL);
171 emit createBackup(_objectId, "Removed selected Edges", UPDATE_ALL);
172 emit scriptInfo("removeSelectedEdges(" + QString::number(_objectId) + ")");
173
174 return;
175 }
176
177 emit log(LOGERR, tr("Unsupported Object Type for edge removal"));
178
179}
180
181
182
183void MeshRepairPlugin::detectSkinnyTriangleByAngle(int _objectId, double _angle, bool _remove) {
184 // get the target mesh
185 TriMesh* triMesh = 0;
186
187 PluginFunctions::getMesh(_objectId, triMesh);
188
189 if (triMesh) {
190
191 // Clear current edge selection
192 MeshSelection::clearEdgeSelection(triMesh);
193
194 double maxAngle = cos(_angle * M_PI / 180.0);
195 double angle = 0.0;
196
197 for (auto e_it : triMesh->edges()) {
198
199 // Check prerequisites
200 if (!triMesh->status(e_it).deleted() && !triMesh->status(e_it).feature() && triMesh->is_flip_ok(e_it)) {
201
202 // For both halfedges
203 for (unsigned int h = 0; h < 2; ++h) {
204 TriMesh::HalfedgeHandle hh = triMesh->halfedge_handle(e_it, h);
205 const TriMesh::Point& a = triMesh->point(triMesh->from_vertex_handle(hh));
206 const TriMesh::Point& b = triMesh->point(triMesh->to_vertex_handle(hh));
207 hh = triMesh->next_halfedge_handle(hh);
208 const TriMesh::Point& c = triMesh->point(triMesh->to_vertex_handle(hh));
209
210 const double angle = ((a - c).normalize() | (b - c).normalize());
211
212 if (angle < maxAngle) {
213
214 // selcet it
215 triMesh->status(e_it).set_selected(true);
216
217 // remove it if requested
218 if (_remove)
219 triMesh->flip(e_it);
220 }
221 }
222 }
223 }
224
225 if (_remove) {
226 emit updatedObject(_objectId, UPDATE_ALL);
227 emit createBackup(_objectId, tr("Removed cap edges"), UPDATE_ALL);
228 } else {
229 emit updatedObject(_objectId, UPDATE_SELECTION);
230 emit createBackup(_objectId, tr("Selected cap edges"), UPDATE_ALL);
231 } emit
232 scriptInfo(
233 "detectSkinnyTriangleByAngle(" + QString::number(_objectId) + "," + QString::number(_angle) + ","
234 + (_remove?"TRUE":"FALSE") + ")");
235
236 return;
237 }
238
239 emit log(LOGERR, tr("Unsupported Object Type for Cap detection!"));
240}
241
242//-----------------------------------------------------------------------------
243
244
245void MeshRepairPlugin::detectFoldover(int _objectId, float _angle) {
246
247 // get the target mesh
248 TriMeshObject* trimesh_o = 0;
249 PolyMeshObject* polymesh_o = 0;
250
251 PluginFunctions::getObject(_objectId, trimesh_o);
252 PluginFunctions::getObject(_objectId, polymesh_o);
253
254 unsigned int count = 0;
255
256 if (trimesh_o != 0) {
257
258 // get the target mesh
259 TriMesh* mesh = trimesh_o->mesh();
260
261 if (mesh) {
262
263 // Clear current edge selection
264 MeshSelection::clearEdgeSelection(mesh);
265
267 TriMesh::Scalar a, cosa = cos(_angle / 180.0 * M_PI);
268
269 for (auto e_it : mesh->edges()) {
270 if (!e_it.is_boundary()) {
271 a = (mesh->normal(e_it.h0().face()) | mesh->normal(
272 e_it.h1().face()));
273
274 if (a < cosa) {
275 mesh->status(e_it.h0().edge()). set_selected(true);
276 ++count;
277 }
278 }
279 }
280 }
281 } else if (polymesh_o != 0) {
282
283 // get the target mesh
284 PolyMesh* mesh = polymesh_o->mesh();
285
286 if (mesh) {
287
288 // Clear current edge selection
289 MeshSelection::clearEdgeSelection(mesh);
290
292 PolyMesh::Scalar a, cosa = cos(_angle / 180.0 * M_PI);
293
294 for (auto e_it : mesh->edges()) {
295 if (!e_it.is_boundary()) {
296 a = (mesh->normal(e_it.h0().face()) | mesh->normal(
297 e_it.h1().face()));
298
299 if (a < cosa) {
300 mesh->status(e_it.h0().edge()). set_selected(true);
301 ++count;
302 }
303 }
304 }
305 }
306 }
307
308 if (count > 0) {
309 emit updatedObject(_objectId, UPDATE_SELECTION);
310 emit createBackup(_objectId, "Select edges", UPDATE_SELECTION);
311 emit scriptInfo("detectFoldover(" + QString::number(_objectId) + ", " + QString::number(_angle) + ")");
312 }
313 emit log(
314 "Selected " + QString::number(count) + " fold-overs on object " + QString::number(_objectId)
315 + " with angle greater than " + QString::number(_angle) + ".");
316}
317
318
319//-----------------------------------------------------------------------------
@ LOGERR
Functions for selection on a mesh.
MeshT * mesh()
return a pointer to the mesh
void removeSelectedEdges(int _objectId)
Removes all selected edges.
void detectSkinnyTriangleByAngle(int _objectId, double _angle, bool _remove)
Detect/Remove edges where neighboring faces form angle > _angle degrees.
void selectEdgesLongerThan(int _objectId, double _length)
Selects all edges of an object which are larger than the given length.
void detectFoldover(int _objectId, float _angle)
Detect folded-over configurations by the dihedral angle.
void selectEdgesShorterThan(int _objectId, double _length)
Selects all edges of an object which are shorter than the given length.
void selectionEdgeLength(int _objectId, double _length, bool _larger)
select edges based on length
Kernel::Scalar Scalar
Scalar type.
Definition: PolyMeshT.hh:110
Kernel::FaceHandle FaceHandle
Scalar type.
Definition: PolyMeshT.hh:139
Kernel::HalfedgeHandle HalfedgeHandle
Scalar type.
Definition: PolyMeshT.hh:137
Kernel::Point Point
Coordinate type.
Definition: PolyMeshT.hh:112
bool is_boundary() const
Returns true iff the handle is boundary.
Type for a Meshobject containing a poly mesh.
Definition: PolyMesh.hh:65
Type for a MeshObject containing a triangle mesh.
Definition: TriangleMesh.hh:67
const UpdateType UPDATE_ALL(UpdateTypeSet(1))
Identifier for all updates.
const UpdateType UPDATE_SELECTION(UpdateTypeSet(16))
Selection updated.
bool getMesh(int _identifier, PolyMesh *&_mesh)
Get the Poly Mesh which has the given identifier.
bool getObject(const int _identifier, BaseObject *&_object)
Get the object which has the given identifier.
SmartVertexHandle from() const
Returns vertex at start of halfedge.
SmartVertexHandle to() const
Returns vertex pointed to by halfedge.
Smart version of VertexHandle contains a pointer to the corresponding mesh and allows easier access t...