Developer Documentation
IsotropicRemesherT_impl.hh
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
45#define ISOTROPICREMESHER_C
46
47#include "IsotropicRemesherT.hh"
48
49#include <OpenMesh/Core/Mesh/PolyConnectivity.hh>
50
51#include <ACG/Geometry/Algorithms.hh>
52
53// -------------------- BSP
54#include <ACG/Geometry/bsp/TriangleBSPT.hh>
55
57template< class MeshT >
58void IsotropicRemesher< MeshT >::remesh( MeshT& _mesh, const double _targetEdgeLength )
59{
60 const double low = (4.0 / 5.0) * _targetEdgeLength;
61 const double high = (4.0 / 3.0) * _targetEdgeLength;
62
63 MeshT meshCopy = _mesh;
64 OpenMeshTriangleBSPT< MeshT >* triangleBSP = getTriangleBSP(meshCopy);
65
66 for (int i=0; i < 10; i++){
67 if (prgEmt_)
68 prgEmt_->sendProgressSignal(10*i + 0);
69// std::cerr << "Iteration = " << i << std::endl;
70
71 splitLongEdges(_mesh, high);
72 if (prgEmt_)
73 prgEmt_->sendProgressSignal(10*i + 2);
74
75// std::cerr << "collapse" << std::endl;
76 collapseShortEdges(_mesh, low, high);
77 if (prgEmt_)
78 prgEmt_->sendProgressSignal(10*i + 4);
79
80// std::cerr << "equal" << std::endl;
81 equalizeValences(_mesh);
82 if (prgEmt_)
83 prgEmt_->sendProgressSignal(10*i + 6);
84
85// std::cerr << "relax" << std::endl;
86 tangentialRelaxation(_mesh);
87 if (prgEmt_)
88 prgEmt_->sendProgressSignal(10*i + 8);
89
90// std::cerr << "project" << std::endl;
91 projectToSurface(_mesh, meshCopy, triangleBSP);
92 if (prgEmt_)
93 prgEmt_->sendProgressSignal(10*i + 10);
94 }
95
96 delete triangleBSP;
97}
98
99template< class MeshT >
101{
102 // create Triangle BSP
104
105 // build Triangle BSP
106 triangle_bsp->reserve(_mesh.n_faces());
107
108 for (auto f_it : _mesh.faces())
109 triangle_bsp->push_back( f_it );
110
111 triangle_bsp->build(10, 100);
112
113 // return pointer to triangle bsp
114 return triangle_bsp;
115}
116
118template< class MeshT >
119void IsotropicRemesher< MeshT >::splitLongEdges( MeshT& _mesh, const double _maxEdgeLength )
120{
121
122 const double _maxEdgeLengthSqr = _maxEdgeLength * _maxEdgeLength;
123
124 //iterate over all edges
125 for (auto e_it : _mesh.edges()) {
126 const typename OpenMesh::SmartHalfedgeHandle & hh = e_it.h0();
127
128 const typename MeshT::VertexHandle & v0 = hh.from();
129 const typename MeshT::VertexHandle & v1 = hh.to();
130
131 typename MeshT::Point vec = _mesh.point(v1) - _mesh.point(v0);
132
133 //edge to long?
134 if ( vec.sqrnorm() > _maxEdgeLengthSqr ){
135
136 const typename MeshT::Point midPoint = _mesh.point(v0) + ( 0.5 * vec );
137
138 //split at midpoint
139 typename MeshT::VertexHandle vh = _mesh.add_vertex( midPoint );
140
141 bool hadFeature = _mesh.status(e_it).feature();
142
143 _mesh.split(e_it, vh);
144
145 if ( hadFeature ){
146
147 for (auto voh_it : _mesh.voh_range(vh))
148 if ( voh_it.to() == v0 || voh_it.to() == v1 )
149 _mesh.status( voh_it.edge() ).set_feature( true );
150 }
151 }
152 }
153}
154
156template< class MeshT >
157void IsotropicRemesher< MeshT >::collapseShortEdges( MeshT& _mesh, const double _minEdgeLength, const double _maxEdgeLength )
158{
159 const double _minEdgeLengthSqr = _minEdgeLength * _minEdgeLength;
160 const double _maxEdgeLengthSqr = _maxEdgeLength * _maxEdgeLength;
161
162 //add checked property
164 if ( !_mesh.get_property_handle(checked, "Checked Property") )
165 _mesh.add_property(checked,"Checked Property" );
166
167 //init property
168 for (auto e_it : _mesh.edges())
169 _mesh.property(checked, e_it) = false;
170
171
172 bool finished = false;
173
174 while( !finished ){
175
176 finished = true;
177
178 for (auto e_it : _mesh.edges()) {
179
180 if ( _mesh.property(checked, e_it) )
181 continue;
182
183 _mesh.property(checked, e_it) = true;
184
185 const typename OpenMesh::SmartHalfedgeHandle & hh = e_it.h0();
186
187 const typename MeshT::VertexHandle & v0 = hh.from();
188 const typename MeshT::VertexHandle & v1 = hh.to();
189
190 const typename MeshT::Point vec = _mesh.point(v1) - _mesh.point(v0);
191
192 const double edgeLength = vec.sqrnorm();
193
194 // edge too short but don't try to collapse edges that have length 0
195 if ( (edgeLength < _minEdgeLengthSqr) && (edgeLength > DBL_EPSILON) ){
196
197 //check if the collapse is ok
198 const typename MeshT::Point & B = _mesh.point(v1);
199
200 bool collapse_ok = true;
201
202 for (auto voh_it : _mesh.voh_range(v0))
203 if ( (( B - _mesh.point( voh_it.to() ) ).sqrnorm() > _maxEdgeLengthSqr )
204 || ( _mesh.status( voh_it.edge() ).feature())
205 || ( voh_it.edge().is_boundary() ) ){
206 collapse_ok = false;
207 break;
208 }
209
210 if( collapse_ok && _mesh.is_collapse_ok(hh) ) {
211 _mesh.collapse( hh );
212
213 finished = false;
214 }
215
216 }
217 }
218
219 }
220
221 _mesh.remove_property(checked);
222
223 _mesh.garbage_collection();
224}
225
226template< class MeshT >
228{
229
230 for (auto e_it : _mesh.edges()) {
231
232 if ( !_mesh.is_flip_ok(e_it) ) continue;
233 if ( _mesh.status( e_it ).feature() ) continue;
234
235 const typename OpenMesh::SmartHalfedgeHandle & h0 = e_it.h0();
236 const typename OpenMesh::SmartHalfedgeHandle & h1 = e_it.h1();
237
238 if (h0.is_valid() && h1.is_valid())
239
240 if (h0.face().is_valid() && h1.face().is_valid()){
241 //get vertices of corresponding faces
242 //const typename MeshT::VertexHandle & a = h0.to();
243 const typename OpenMesh::SmartVertexHandle & a = h0.to();
244 const typename OpenMesh::SmartVertexHandle & b = h1.to();
245 const typename OpenMesh::SmartVertexHandle & c = h0.next().to();
246 const typename OpenMesh::SmartVertexHandle & d = h1.next().to();
247
248 const int deviation_pre = abs((int)(a.valence() - targetValence(_mesh, a)))
249 +abs((int)(b.valence() - targetValence(_mesh, b)))
250 +abs((int)(c.valence() - targetValence(_mesh, c)))
251 +abs((int)(d.valence() - targetValence(_mesh, d)));
252 _mesh.flip(e_it);
253
254 const int deviation_post = abs((int)(a.valence() - targetValence(_mesh, a)))
255 +abs((int)(b.valence() - targetValence(_mesh, b)))
256 +abs((int)(c.valence() - targetValence(_mesh, c)))
257 +abs((int)(d.valence() - targetValence(_mesh, d)));
258
259 if (deviation_pre <= deviation_post)
260 _mesh.flip(e_it);
261 }
262 }
263}
264
266template< class MeshT >
267inline
268int IsotropicRemesher< MeshT >::targetValence( MeshT& _mesh, const typename MeshT::VertexHandle& _vh ){
269
270 if (isBoundary(_mesh,_vh))
271 return 4;
272 else
273 return 6;
274}
275
276template< class MeshT >
277inline
278bool IsotropicRemesher< MeshT >::isBoundary( MeshT& _mesh, const typename MeshT::VertexHandle& _vh ){
279
280 for (auto voh_it : _mesh.voh_range(_vh))
281 if ( voh_it.edge().is_boundary() )
282 return true;
283
284 return false;
285}
286
287template< class MeshT >
288inline
289bool IsotropicRemesher< MeshT >::isFeature( MeshT& _mesh, const typename MeshT::VertexHandle& _vh ){
290
291 for (auto voh_it : _mesh.voh_range(_vh))
292 if ( _mesh.status( voh_it.edge()).feature() )
293 return true;
294
295 return false;
296}
297
298template< class MeshT >
300{
301 _mesh.update_normals();
302
303 //add checked property
305 if ( !_mesh.get_property_handle(q, "q Property") )
306 _mesh.add_property(q,"q Property" );
307
308 //first compute barycenters
309 for (auto v_it : _mesh.vertices()){
310
311 typename MeshT::Point tmp(0.0, 0.0, 0.0);
312 uint N = 0;
313
314 for (auto vv_it : v_it.vertices()){
315 tmp += _mesh.point(vv_it);
316 N++;
317 }
318
319 if (N > 0)
320 tmp /= (double) N;
321
322 _mesh.property(q, v_it) = tmp;
323 }
324
325 //move to new position
326 for (auto v_it : _mesh.vertices()){
327 if ( !v_it.is_boundary() && !v_it.feature() )
328 _mesh.set_point(v_it, _mesh.property(q, v_it) + (_mesh.normal(v_it)| (_mesh.point(v_it) - _mesh.property(q, v_it) ) ) * _mesh.normal(v_it));
329 }
330
331 _mesh.remove_property(q);
332}
333
334template <class MeshT>
335template <class SpatialSearchT>
336typename MeshT::Point
338 const typename MeshT::Point& _point,
339 typename MeshT::FaceHandle& _fh,
340 SpatialSearchT* _ssearch,
341 double* _dbest)
342{
343
344 typename MeshT::Point p_best = _mesh.point(_mesh.vertex_handle(0));
345 typename MeshT::Scalar d_best = (_point-p_best).sqrnorm();
346
347 typename MeshT::FaceHandle fh_best;
348
349 if( _ssearch == 0 )
350 {
351 // exhaustive search
352 for (auto cf_it : _mesh.faces()) {
353 typename MeshT::ConstFaceVertexIter cfv_it = _mesh.cfv_iter(cf_it);
354
355 const typename MeshT::Point& pt0 = _mesh.point( *cfv_it);
356 const typename MeshT::Point& pt1 = _mesh.point( *(++cfv_it));
357 const typename MeshT::Point& pt2 = _mesh.point( *(++cfv_it));
358
359 typename MeshT::Point ptn;
360
361 typename MeshT::Scalar d = ACG::Geometry::distPointTriangleSquared( _point,
362 pt0,
363 pt1,
364 pt2,
365 ptn );
366
367 if( d < d_best && d >= 0.0)
368 {
369 d_best = d;
370 p_best = ptn;
371
372 fh_best = cf_it;
373 }
374 }
375
376 // return facehandle
377 _fh = fh_best;
378
379 // return distance
380 if(_dbest)
381 *_dbest = sqrt(d_best);
382
383
384 return p_best;
385 }
386 else
387 {
388 typename MeshT::FaceHandle fh = _ssearch->nearest(_point).handle;
389 typename MeshT::CFVIter fv_it = _mesh.cfv_iter(fh);
390
391 const typename MeshT::Point& pt0 = _mesh.point( *( fv_it));
392 const typename MeshT::Point& pt1 = _mesh.point( *(++fv_it));
393 const typename MeshT::Point& pt2 = _mesh.point( *(++fv_it));
394
395 // project
396 d_best = ACG::Geometry::distPointTriangleSquared(_point, pt0, pt1, pt2, p_best);
397
398 // return facehandle
399 _fh = fh;
400
401 // return distance
402 if(_dbest)
403 *_dbest = sqrt(d_best);
404
405 return p_best;
406 }
407}
408
409
410template< class MeshT >
411template< class SpatialSearchT >
412void IsotropicRemesher< MeshT >::projectToSurface( MeshT& _mesh, MeshT& _original, SpatialSearchT* _ssearch )
413{
414
415 for (auto v_it : _mesh.vertices()){
416
417 if ( v_it.is_boundary() ) continue;
418 if ( v_it.feature() ) continue;
419
420 typename MeshT::Point p = _mesh.point(v_it);
421 typename MeshT::FaceHandle fhNear;
422 double distance;
423
424 typename MeshT::Point pNear = findNearestPoint(_original, p, fhNear, _ssearch, &distance);
425
426 _mesh.set_point(v_it, pNear);
427 }
428}
429
void collapseShortEdges(MeshT &_mesh, const double _minEdgeLength, const double _maxEdgeLength)
collapse edges shorter than minEdgeLength if collapsing doesn't result in new edge longer than maxEdg...
void remesh(MeshT &_mesh, const double _targetEdgeLength)
do the remeshing
void splitLongEdges(MeshT &_mesh, const double _maxEdgeLength)
performs edge splits until all edges are shorter than the threshold
int targetValence(MeshT &_mesh, const typename MeshT::VertexHandle &_vh)
returns 4 for boundary vertices and 6 otherwise
bool is_valid() const
The handle is valid iff the index is not negative.
Definition: Handles.hh:72
Scalar sqrnorm(const VectorT< Scalar, DIM > &_v)
Definition: Vector11T.hh:756
VertexHandle add_vertex(const VecT &_p)
Add a geometric point to the mesh.
PointT normal(HalfFaceHandle _hfh) const
size_t n_faces() const override
Get number of faces in mesh.
void build(unsigned int _max_handles, unsigned int _max_depth)
void push_back(Handle _h)
Add a handle to the BSP.
void reserve(size_t _n)
Reserve memory for _n entries.
SmartFaceHandle face() const
Returns incident face of halfedge.
SmartVertexHandle from() const
Returns vertex at start of halfedge.
SmartHalfedgeHandle next() const
Returns next halfedge handle.
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...
uint valence() const
Returns valence of the vertex.