2 * This program is free software: you can redistribute it and/or modify
3 * it under the terms of the GNU Lesser General Public License as
4 * published by the Free Software Foundation, either version 3 of the
5 * License, or (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 * @file ConvexityHelper.ih
19 * @author Jacques-Olivier Lachaud (\c jacques-olivier.lachaud@univ-savoie.fr )
20 * Laboratory of Mathematics (CNRS, UMR 5127), University of Savoie, France
24 * Implementation of inline methods defined in ConvexityHelper.h
26 * This file is part of the DGtal library.
30 //////////////////////////////////////////////////////////////////////////////
34 //////////////////////////////////////////////////////////////////////////////
36 ///////////////////////////////////////////////////////////////////////////////
37 // IMPLEMENTATION of inline methods.
38 ///////////////////////////////////////////////////////////////////////////////
40 ///////////////////////////////////////////////////////////////////////////////
41 // ----------------------- Standard services ------------------------------
43 //-----------------------------------------------------------------------------
44 template < int dim, typename TInteger, typename TInternalInteger >
45 typename DGtal::ConvexityHelper< dim, TInteger, TInternalInteger>::LatticePolytope
46 DGtal::ConvexityHelper< dim, TInteger, TInternalInteger>::computeLatticePolytope
47 ( const std::vector< Point >& input_points,
48 bool remove_duplicates,
49 bool make_minkowski_summable )
51 typedef typename LatticePolytope::Domain Domain;
52 typedef typename LatticePolytope::HalfSpace PolytopeHalfSpace;
53 typedef QuickHull< LatticeConvexHullKernel > ConvexHull;
54 typedef typename ConvexHull::HalfSpace ConvexHullHalfSpace;
55 typedef typename ConvexHull::Ridge Ridge;
56 if ( input_points.empty() ) return LatticePolytope();
58 Point l = input_points[ 0 ];
59 Point u = input_points[ 0 ];
60 for ( const auto& p : input_points ) {
64 Domain domain( l, u );
65 // Compute convex hull
67 hull.setInput( input_points, remove_duplicates );
68 const auto target = ( make_minkowski_summable && dimension == 3 )
69 ? ConvexHull::Status::VerticesCompleted
70 : ConvexHull::Status::FacetsCompleted;
71 bool ok = hull.computeConvexHull( target );
72 if ( ! ok ) return LatticePolytope();
73 // Initialize polytope
74 std::vector< ConvexHullHalfSpace > HS;
75 std::vector< PolytopeHalfSpace > PHS;
76 hull.getFacetHalfSpaces( HS );
77 PHS.reserve( HS.size() );
78 for ( auto& H : HS ) {
81 for ( Dimension i = 0; i < dim; ++i )
82 N[ i ] = (Integer) H.internalNormal()[ i ];
83 nu = (Integer) H.internalIntercept();
84 PHS.emplace_back( N, nu );
86 if ( make_minkowski_summable && dimension >= 4 )
87 trace.warning() << "[ConvexityHelper::computeLatticePolytope]"
88 << " Not implemented starting from dimension 4."
90 if ( make_minkowski_summable && dimension == 3 )
92 // Compute ridge vertices to add edge constraints.
93 std::vector< Point > positions;
94 std::vector< IndexRange > facet_vertices;
95 std::vector< IndexRange > ridge_vertices;
96 std::map< Ridge, Index > ridge2index;
97 hull.getVertexPositions( positions );
98 computeFacetAndRidgeVertices( hull, facet_vertices,
99 ridge2index, ridge_vertices );
100 for ( auto p : ridge2index ) {
101 const auto r = p.first;
102 // Copy by value since PHS may be reallocated during the iteration.
103 const auto U = PHS[ r.first ].N; // normal of facet 1
104 const auto V = PHS[ r.second ].N; // normal of facet 2
105 const auto& S = ridge_vertices[ p.second ]; // vertices along facets 1, 2
106 ASSERT( S.size() == 2 && "Invalid ridge" );
107 const auto& P0 = positions[ S[ 0 ] ];
108 const auto& P1 = positions[ S[ 1 ] ];
109 auto E = P1 - P0; // edge 1, 2
111 detail::BoundedLatticePolytopeSpecializer< dimension, Integer>
112 ::crossProduct( U, V ); // parallel to E
113 ASSERT( E.dot( UxV ) != 0 && "Invalid E / UxV" );
114 if ( E.dot( UxV ) <= 0 ) E = -E; // force correct orientation
116 detail::BoundedLatticePolytopeSpecializer< dimension, Integer>
117 ::crossProduct( U, E ); // edge on facet 1
119 detail::BoundedLatticePolytopeSpecializer< dimension, Integer>
120 ::crossProduct( E, V ); // edge on facet 2
121 ASSERT( E1.dot( U ) == 0 && "Invalid E1 / U" );
122 ASSERT( E1.dot( V ) < 0 && "Invalid E1 / V" );
123 ASSERT( E2.dot( V ) == 0 && "Invalid E1 / V" );
124 ASSERT( E2.dot( U ) < 0 && "Invalid E1 / U" );
125 for ( Dimension k = 0; k < dimension; ++k ) {
126 const auto W = U[ k ] * V - V[ k ] * U;
127 const auto nn1 = W.dot( E1 );
128 const auto nn2 = W.dot( E2 );
129 if ( nn1 > 0 && nn2 > 0 ) {
130 PHS.emplace_back( -W, -W.dot( P0 ) );
131 ASSERT( E1.dot(-W ) < 0 && "Invalid E1 /-W" );
132 ASSERT( E2.dot(-W ) < 0 && "Invalid E2 /-W" );
134 else if ( nn1 < 0 && nn2 < 0 ) {
135 PHS.emplace_back( W, W.dot( P0 ) );
136 ASSERT( E1.dot( W ) < 0 && "Invalid E1 / W" );
137 ASSERT( E2.dot( W ) < 0 && "Invalid E2 / W" );
142 return LatticePolytope( domain, PHS.cbegin(), PHS.cend(),
143 make_minkowski_summable && ( dimension <= 3 ), true );
146 //-----------------------------------------------------------------------------
147 template < int dim, typename TInteger, typename TInternalInteger >
148 template < typename TSurfaceMesh >
150 DGtal::ConvexityHelper< dim, TInteger, TInternalInteger>::computeConvexHullBoundary
151 ( TSurfaceMesh& mesh,
152 const std::vector< Point >& input_points,
153 bool remove_duplicates )
155 typedef TSurfaceMesh SurfaceMesh;
156 typedef QuickHull< LatticeConvexHullKernel > ConvexHull;
157 typedef typename ConvexHull::IndexRange IndexRange;
159 hull.setInput( input_points, remove_duplicates );
160 bool ok = hull.computeConvexHull( ConvexHull::Status::VerticesCompleted );
161 if ( !ok ) return false;
162 std::vector< RealPoint > positions;
163 hull.getVertexPositions( positions );
164 std::vector< IndexRange > faces;
165 hull.getFacetVertices( faces );
166 mesh = SurfaceMesh( positions.cbegin(), positions.cend(),
167 faces.cbegin(), faces.cend() );
171 //-----------------------------------------------------------------------------
172 template < int dim, typename TInteger, typename TInternalInteger >
174 DGtal::ConvexityHelper< dim, TInteger, TInternalInteger>::computeConvexHullBoundary
175 ( PolygonalSurface< Point >& polysurf,
176 const std::vector< Point >& input_points,
177 bool remove_duplicates )
179 typedef QuickHull< LatticeConvexHullKernel > ConvexHull;
180 typedef typename ConvexHull::IndexRange IndexRange;
182 hull.setInput( input_points, remove_duplicates );
183 bool ok = hull.computeConvexHull( ConvexHull::Status::VerticesCompleted );
184 if ( !ok ) return false;
185 std::vector< Point > positions;
186 hull.getVertexPositions( positions );
187 std::vector< IndexRange > faces;
188 hull.getFacetVertices( faces );
189 // build polygonal surface
191 for ( auto p : positions ) polysurf.addVertex( p );
192 for ( auto f : faces ) polysurf.addPolygonalFace( f );
193 return polysurf.build();
196 //-----------------------------------------------------------------------------
197 template < int dim, typename TInteger, typename TInternalInteger >
199 DGtal::ConvexityHelper< dim, TInteger, TInternalInteger>::computeConvexHullCellComplex
200 ( ConvexCellComplex< Point >& cell_complex,
201 const std::vector< Point >& input_points,
202 bool remove_duplicates )
204 typedef QuickHull< LatticeConvexHullKernel > ConvexHull;
205 typedef typename ConvexHull::IndexRange IndexRange;
206 typedef typename ConvexCellComplex< Point >::FaceRange FaceRange;
208 hull.setInput( input_points, remove_duplicates );
209 bool ok = hull.computeConvexHull( ConvexHull::Status::VerticesCompleted );
210 cell_complex.clear();
211 if ( ! ok ) return false;
212 // Build complex, only 1 finite cell and as many faces as convex hull facets.
213 // Taking care of faces for each cell (here one cell borders all faces).
214 std::vector< IndexRange > faces;
215 hull.getFacetVertices( faces );
217 for ( Index i = 0; i < faces.size(); i++ )
218 all_faces.push_back( { i, true } );
219 cell_complex.cell_faces.push_back( all_faces );
220 // Vertices of this unique cell will be computed lazily on request.
221 // Taking care of each face.
222 for ( const auto& vtcs : faces )
224 // every inner face borders cell 0
225 cell_complex.true_face_cell.push_back( 0 );
226 // every outer face borders the infinite cell
227 cell_complex.false_face_cell.push_back( cell_complex.infiniteCell() );
229 // Taking care of vertices (in consistent order) of every face
230 cell_complex.true_face_vertices.swap( faces );
231 // Taking care of vertex positions
232 hull.getVertexPositions( cell_complex.vertex_position );
236 //-----------------------------------------------------------------------------
237 template < int dim, typename TInteger, typename TInternalInteger >
239 DGtal::ConvexityHelper< dim, TInteger, TInternalInteger>::computeDelaunayCellComplex
240 ( ConvexCellComplex< Point >& cell_complex,
241 const std::vector< Point >& input_points,
242 bool remove_duplicates )
244 typedef QuickHull< LatticeDelaunayKernel > Delaunay;
245 typedef typename Delaunay::Ridge Ridge;
246 typedef typename ConvexCellComplex< Point >::FaceRange FaceRange;
249 del.setInput( input_points, remove_duplicates );
250 bool ok = del.computeConvexHull( Delaunay::Status::VerticesCompleted );
251 cell_complex.clear();
252 if ( ! ok ) return false;
254 // Build complex, as many maximal cells as convex hull facets.
255 // convex hull facet -> cell of complex
256 // convex hull ridge -> face of complex
257 // (1) Get cell vertices, count ridges/faces and compute their vertices
258 std::map< Ridge, Index > r2f;
259 computeFacetAndRidgeVertices( del,
260 cell_complex.cell_vertices,
262 cell_complex.true_face_vertices );
263 // (2) assign ridges/faces to cell and conversely
264 const Index nb_r = r2f.size();
265 cell_complex.true_face_cell .resize( nb_r, cell_complex.infiniteCell() );
266 cell_complex.false_face_cell.resize( nb_r, cell_complex.infiniteCell() );
267 cell_complex.true_face_vertices.resize( nb_r );
268 for ( Index cur_f = 0; cur_f < del.nbFiniteFacets(); ++cur_f ) {
269 const auto& facet = del.facets[ cur_f ];
270 FaceRange current_faces;
271 for ( auto neigh_f : facet.neighbors ) {
272 const Ridge r { std::min( cur_f, neigh_f ), std::max( cur_f, neigh_f ) };
273 const bool pos = cur_f < neigh_f;
274 const Index cur_r = r2f[ r ];
275 cell_complex.true_face_cell [ cur_r ] = r.first;
276 if ( r.second >= del.nbFiniteFacets() )
277 cell_complex.false_face_cell[ cur_r ] = cell_complex.infiniteCell();
279 cell_complex.false_face_cell[ cur_r ] = r.second;
280 current_faces.emplace_back( cur_r, pos );
282 cell_complex.cell_faces.push_back( current_faces );
284 // (3) Takes care of vertex positions
285 del.getVertexPositions( cell_complex.vertex_position );
290 //-----------------------------------------------------------------------------
291 template < int dim, typename TInteger, typename TInternalInteger >
292 typename DGtal::ConvexityHelper< dim, TInteger, TInternalInteger>::RationalPolytope
293 DGtal::ConvexityHelper< dim, TInteger, TInternalInteger>::computeRationalPolytope
294 ( const std::vector< RealPoint >& input_points,
296 bool remove_duplicates,
297 bool make_minkowski_summable )
299 if ( denominator < 1 )
300 trace.error() << "Invalid denominator " << denominator
301 << ". Should be greater or equal to 1." << std::endl;
302 typedef typename RationalPolytope::Domain Domain;
303 typedef typename RationalPolytope::HalfSpace PolytopeHalfSpace;
304 typedef QuickHull< RealConvexHullKernel > ConvexHull;
305 typedef typename ConvexHull::HalfSpace ConvexHullHalfSpace;
306 typedef typename ConvexHull::Ridge Ridge;
307 if ( input_points.empty() ) return RationalPolytope();
308 // Compute convex hull
309 ConvexHull hull( denominator );
310 hull.setInput( input_points, remove_duplicates );
311 const auto target = ( make_minkowski_summable && dimension == 3 )
312 ? ConvexHull::Status::VerticesCompleted
313 : ConvexHull::Status::FacetsCompleted;
314 bool ok = hull.computeConvexHull( target );
315 if ( ! ok ) return RationalPolytope();
316 // Compute domain (as a lattice domain)
317 auto l = hull.points[ 0 ];
318 auto u = hull.points[ 0 ];
319 for ( const auto& p : hull.points ) {
323 Domain domain( l, u );
324 trace.info() << "Domain l=" << l << " u=" << u << std::endl;
325 // Initialize polytope
326 std::vector< ConvexHullHalfSpace > HS;
327 std::vector< PolytopeHalfSpace > PHS;
328 hull.getFacetHalfSpaces( HS );
329 PHS.reserve( HS.size() );
330 for ( auto& H : HS ) {
333 for ( Dimension i = 0; i < dim; ++i )
334 N[ i ] = (Integer) H.internalNormal()[ i ];
335 nu = (Integer) H.internalIntercept();
336 PHS.emplace_back( N, nu );
338 if ( make_minkowski_summable && dimension >= 4 )
339 trace.warning() << "[ConvexityHelper::computeRationalPolytope]"
340 << " Not implemented starting from dimension 4."
342 if ( make_minkowski_summable && dimension == 3 )
344 // Compute ridge vertices to add edge constraints.
345 std::vector< Point > positions;
346 std::vector< IndexRange > facet_vertices;
347 std::vector< IndexRange > ridge_vertices;
348 std::map< Ridge, Index > ridge2index;
349 hull.getVertexPositions( positions );
350 computeFacetAndRidgeVertices( hull, facet_vertices,
351 ridge2index, ridge_vertices );
352 for ( auto p : ridge2index ) {
353 const auto r = p.first;
354 // Copy by value since PHS may be reallocated during the iteration.
355 const auto U = PHS[ r.first ].N; // normal of facet 1
356 const auto V = PHS[ r.second ].N; // normal of facet 2
357 const auto& S = ridge_vertices[ p.second ]; // vertices along facets 1, 2
358 ASSERT( S.size() == 2 && "Invalid ridge" );
359 const auto& P0 = positions[ S[ 0 ] ];
360 const auto& P1 = positions[ S[ 1 ] ];
361 auto E = P1 - P0; // edge 1, 2
363 detail::BoundedRationalPolytopeSpecializer< dimension, Integer>
364 ::crossProduct( U, V ); // parallel to E
365 ASSERT( E.dot( UxV ) != 0 && "Invalid E / UxV" );
366 if ( E.dot( UxV ) <= 0 ) E = -E; // force correct orientation
368 detail::BoundedRationalPolytopeSpecializer< dimension, Integer>
369 ::crossProduct( U, E ); // edge on facet 1
371 detail::BoundedRationalPolytopeSpecializer< dimension, Integer>
372 ::crossProduct( E, V ); // edge on facet 2
373 ASSERT( E1.dot( U ) == 0 && "Invalid E1 / U" );
374 ASSERT( E1.dot( V ) < 0 && "Invalid E1 / V" );
375 ASSERT( E2.dot( V ) == 0 && "Invalid E1 / V" );
376 ASSERT( E2.dot( U ) < 0 && "Invalid E1 / U" );
377 for ( Dimension k = 0; k < dimension; ++k ) {
378 const auto W = U[ k ] * V - V[ k ] * U;
379 const auto nn1 = W.dot( E1 );
380 const auto nn2 = W.dot( E2 );
381 if ( nn1 > 0 && nn2 > 0 ) {
382 PHS.emplace_back( -W, -W.dot( P0 ) );
383 ASSERT( E1.dot(-W ) < 0 && "Invalid E1 /-W" );
384 ASSERT( E2.dot(-W ) < 0 && "Invalid E2 /-W" );
386 else if ( nn1 < 0 && nn2 < 0 ) {
387 PHS.emplace_back( W, W.dot( P0 ) );
388 ASSERT( E1.dot( W ) < 0 && "Invalid E1 / W" );
389 ASSERT( E2.dot( W ) < 0 && "Invalid E2 / W" );
394 return RationalPolytope( denominator, domain, PHS.cbegin(), PHS.cend(),
395 make_minkowski_summable && ( dimension <= 3 ), true );
399 //-----------------------------------------------------------------------------
400 template < int dim, typename TInteger, typename TInternalInteger >
401 template < typename TSurfaceMesh >
403 DGtal::ConvexityHelper< dim, TInteger, TInternalInteger>::computeConvexHullBoundary
404 ( TSurfaceMesh& mesh,
405 const std::vector< RealPoint >& input_points,
407 bool remove_duplicates )
409 typedef TSurfaceMesh SurfaceMesh;
410 typedef QuickHull< RealConvexHullKernel > ConvexHull;
411 typedef typename ConvexHull::IndexRange IndexRange;
412 ConvexHull hull( precision );
413 hull.setInput( input_points, remove_duplicates );
414 bool ok = hull.computeConvexHull( ConvexHull::Status::VerticesCompleted );
415 if ( !ok ) return false;
416 std::vector< RealPoint > positions;
417 hull.getVertexPositions( positions );
418 std::vector< IndexRange > faces;
419 hull.getFacetVertices( faces );
420 mesh = SurfaceMesh( positions.cbegin(), positions.cend(),
421 faces.cbegin(), faces.cend() );
426 //-----------------------------------------------------------------------------
427 template < int dim, typename TInteger, typename TInternalInteger >
429 DGtal::ConvexityHelper< dim, TInteger, TInternalInteger>::computeConvexHullBoundary
430 ( PolygonalSurface< RealPoint >& polysurf,
431 const std::vector< RealPoint >& input_points,
433 bool remove_duplicates )
435 typedef QuickHull< RealConvexHullKernel > ConvexHull;
436 typedef typename ConvexHull::IndexRange IndexRange;
437 ConvexHull hull( precision );
438 hull.setInput( input_points, remove_duplicates );
439 bool ok = hull.computeConvexHull( ConvexHull::Status::VerticesCompleted );
440 if ( !ok ) return false;
441 std::vector< RealPoint > positions;
442 hull.getVertexPositions( positions );
443 std::vector< IndexRange > faces;
444 hull.getFacetVertices( faces );
445 // build polygonal surface
447 for ( auto p : positions ) polysurf.addVertex( p );
448 for ( auto f : faces ) polysurf.addPolygonalFace( f );
449 return polysurf.build();
452 //-----------------------------------------------------------------------------
453 template < int dim, typename TInteger, typename TInternalInteger >
455 DGtal::ConvexityHelper< dim, TInteger, TInternalInteger>::computeConvexHullCellComplex
456 ( ConvexCellComplex< RealPoint >& cell_complex,
457 const std::vector< RealPoint >& input_points,
459 bool remove_duplicates )
461 typedef QuickHull< RealConvexHullKernel > ConvexHull;
462 typedef typename ConvexHull::IndexRange IndexRange;
463 typedef typename ConvexCellComplex< RealPoint >::FaceRange FaceRange;
464 ConvexHull hull( precision );
465 hull.setInput( input_points, remove_duplicates );
466 bool ok = hull.computeConvexHull( ConvexHull::Status::VerticesCompleted );
467 cell_complex.clear();
468 if ( ! ok ) return false;
469 // Build complex, only 1 finite cell and as many faces as convex hull facets.
470 // Taking care of faces for each cell (here one cell borders all faces).
471 std::vector< IndexRange > faces;
472 hull.getFacetVertices( faces );
474 for ( Index i = 0; i < faces.size(); i++ )
475 all_faces.push_back( { i, true } );
476 cell_complex.cell_faces.push_back( all_faces );
477 // Vertices of this unique cell will be computed lazily on request.
478 // Taking care of each face.
479 for ( const auto& vtcs : faces )
481 // every inner face borders cell 0
482 cell_complex.true_face_cell.push_back( 0 );
483 // every outer face borders the infinite cell
484 cell_complex.false_face_cell.push_back( cell_complex.infiniteCell() );
486 // Taking care of vertices (in consistent order) of every face
487 cell_complex.true_face_vertices.swap( faces );
488 // Taking care of vertex positions
489 hull.getVertexPositions( cell_complex.vertex_position );
494 //-----------------------------------------------------------------------------
495 template < int dim, typename TInteger, typename TInternalInteger >
497 DGtal::ConvexityHelper< dim, TInteger, TInternalInteger>::computeDelaunayCellComplex
498 ( ConvexCellComplex< RealPoint >& cell_complex,
499 const std::vector< RealPoint >& input_points,
501 bool remove_duplicates )
503 typedef QuickHull< RealDelaunayKernel > Delaunay;
504 typedef typename Delaunay::Ridge Ridge;
505 typedef typename ConvexCellComplex< RealPoint >::FaceRange FaceRange;
507 Delaunay del( precision );
508 del.setInput( input_points, remove_duplicates );
509 bool ok = del.computeConvexHull( Delaunay::Status::VerticesCompleted );
510 cell_complex.clear();
511 if ( ! ok ) return false;
513 // Build complex, as many maximal cells as convex hull facets.
514 // convex hull facet -> cell of complex
515 // convex hull ridge -> face of complex
516 // (1) Get cell vertices, count ridges/faces and compute their vertices
517 std::map< Ridge, Index > r2f;
518 computeFacetAndRidgeVertices( del,
519 cell_complex.cell_vertices,
521 cell_complex.true_face_vertices );
522 // (2) assign ridges/faces to cell and conversely
523 const Index nb_r = r2f.size();
524 cell_complex.true_face_cell .resize( nb_r, cell_complex.infiniteCell() );
525 cell_complex.false_face_cell.resize( nb_r, cell_complex.infiniteCell() );
526 cell_complex.true_face_vertices.resize( nb_r );
527 for ( Index cur_f = 0; cur_f < del.nbFiniteFacets(); ++cur_f ) {
528 const auto& facet = del.facets[ cur_f ];
529 FaceRange current_faces;
530 for ( auto neigh_f : facet.neighbors ) {
531 const Ridge r { std::min( cur_f, neigh_f ), std::max( cur_f, neigh_f ) };
532 const bool pos = cur_f < neigh_f;
533 const Index cur_r = r2f[ r ];
534 cell_complex.true_face_cell [ cur_r ] = r.first;
535 if ( r.second >= del.nbFiniteFacets() )
536 cell_complex.false_face_cell[ cur_r ] = cell_complex.infiniteCell();
538 cell_complex.false_face_cell[ cur_r ] = r.second;
539 current_faces.emplace_back( cur_r, pos );
541 cell_complex.cell_faces.push_back( current_faces );
543 // (3) Takes care of vertex positions
544 del.getVertexPositions( cell_complex.vertex_position );
549 //-----------------------------------------------------------------------------
550 template < int dim, typename TInteger, typename TInternalInteger >
551 template < typename QHull >
553 DGtal::ConvexityHelper< dim, TInteger, TInternalInteger>::computeFacetAndRidgeVertices
555 std::vector< IndexRange >& cell_vertices,
556 std::map< typename QHull::Ridge, Index >& r2f,
557 std::vector< IndexRange >& face_vertices )
559 typedef typename QHull::Ridge Ridge;
561 ASSERT( hull.status() >= QHull::Status::VerticesCompleted
562 && hull.status() <= QHull::Status::AllCompleted );
564 // Get cell vertices and sort them
565 bool ok_fv = hull.getFacetVertices( cell_vertices );
567 trace.error() << "[ConvexityHelper::computeFacetAndRidgeVertices]"
568 << " method hull.getFacetVertices failed."
569 << " Maybe QuickHull was not computed till VerticesCompleted."
571 std::vector< IndexRange > sorted_cell_vertices = cell_vertices;
572 for ( auto& vtcs : sorted_cell_vertices )
573 std::sort( vtcs.begin(), vtcs.end() );
574 cell_vertices.resize( hull.nbFiniteFacets() );
576 // Count ridges/faces and compute their vertices
578 face_vertices.clear();
579 for ( Index cur_f = 0; cur_f < hull.nbFiniteFacets(); ++cur_f ) {
580 const auto& facet = hull.facets[ cur_f ];
581 for ( auto neigh_f : facet.neighbors ) {
582 const Ridge r { std::min( cur_f, neigh_f ), std::max( cur_f, neigh_f ) };
583 auto itr = r2f.find( r );
584 if ( itr == r2f.end() ) {
586 std::set_intersection( sorted_cell_vertices[ cur_f ].cbegin(),
587 sorted_cell_vertices[ cur_f ].cend(),
588 sorted_cell_vertices[ neigh_f ].cbegin(),
589 sorted_cell_vertices[ neigh_f ].cend(),
590 std::back_inserter( result ) );
591 face_vertices.push_back( result );
599 ///////////////////////////////////////////////////////////////////////////////