DGtal 2.1.0
Loading...
Searching...
No Matches
ConvexityHelper.ih
1/**
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.
6 *
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.
11 *
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/>.
14 *
15 **/
16
17/**
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
21 *
22 * @date 2020/01/02
23 *
24 * Implementation of inline methods defined in ConvexityHelper.h
25 *
26 * This file is part of the DGtal library.
27 */
28
29
30//////////////////////////////////////////////////////////////////////////////
31#include <cstdlib>
32#include "DGtal/kernel/IntegerConverter.h"
33//////////////////////////////////////////////////////////////////////////////
34
35///////////////////////////////////////////////////////////////////////////////
36// IMPLEMENTATION of inline methods.
37///////////////////////////////////////////////////////////////////////////////
38
39///////////////////////////////////////////////////////////////////////////////
40// ----------------------- Standard services ------------------------------
41
42//-----------------------------------------------------------------------------
43template < int dim, typename TInteger, typename TInternalInteger >
44typename DGtal::ConvexityHelper< dim, TInteger, TInternalInteger>::LatticePolytope
45DGtal::ConvexityHelper< dim, TInteger, TInternalInteger>::computeSimplex
46( const PointRange& input_points,
47 bool remove_duplicates )
48{
49 PointRange X;
50 if ( remove_duplicates )
51 {
52 std::set<Point> S;
53 for ( auto&& p : input_points ) S.insert( p );
54 X = PointRange( S.cbegin(), S.cend() );
55 }
56 else X = input_points;
57 LatticePolytope P( X.cbegin(), X.cend() );
58 if ( P.nbHalfSpaces() != 0 )
59 return P;
60 else
61 return computeDegeneratedLatticePolytope( X );
62}
63
64//-----------------------------------------------------------------------------
65template < int dim, typename TInteger, typename TInternalInteger >
66typename DGtal::ConvexityHelper< dim, TInteger, TInternalInteger>::LatticePolytope
67DGtal::ConvexityHelper< dim, TInteger, TInternalInteger>::
68computeDegeneratedLatticePolytope
69( PointRange& input_points )
70{
71 typedef typename LatticePolytope::Domain Domain;
72 typedef typename LatticePolytope::HalfSpace PolytopeHalfSpace;
73 typedef QuickHull< LatticeConvexHullKernel > ConvexHull;
74 typedef typename ConvexHull::HalfSpace ConvexHullHalfSpace;
75
76 // Rewrite computeDegeneratedLatticePolytope using AffineSubset
77 typedef PointVector<dim, InternalInteger> InternalPoint;
78 typedef AffineGeometry<InternalPoint> Affine;
79 auto indices = Affine::affineSubset( input_points );
80 auto ref_basis = Affine::affineBasis ( input_points, indices );
81 auto ref = ref_basis.first;
82 auto& basis = ref_basis.second;
83 if ( basis.size() >= dimension )
84 {
85 trace.error() << "[ConvexityHelper::computeDegeneratedLatticePolytope]"
86 << " Weird error: found initial full dimensional simplex" << std::endl;
87 return LatticePolytope();
88 }
89 if ( basis.size() == 0 )
90 { // Polytope is degenerated to one point (nD)
91 PointRange X = { ref };
92 return LatticePolytope( X.cbegin(), X.cend() );
93 }
94 if ( basis.size() == 1 )
95 { // Polytope is a 1-dimensional straight segment (nD).
96 // Find the two extremal points.
97 std::vector< InternalInteger > alpha( input_points.size() );
98 for ( std::size_t i = 0; i < input_points.size(); i++ )
99 alpha[ i ] = basis[ 0 ].dot( input_points[ i ] - ref );
100 Index a = 0, b = 0;
101 for ( std::size_t i = 1; i < input_points.size(); i++ )
102 {
103 if ( alpha[ i ] < alpha[ a ] ) a = i;
104 if ( alpha[ i ] > alpha[ b ] ) b = i;
105 }
106 PointRange X = { input_points[ a ], input_points[ b ] };
107 return LatticePolytope( X.cbegin(), X.cend() );
108 }
109 if ( basis.size()+1 != dimension )
110 {
111 trace.error() << "[ConvexityHelper::computeDegeneratedLatticePolytope] "
112 << "computation of a " << basis.size() << "-dimensional polytope"
113 << " in " << dimension << "D is not implemented yet." << std::endl;
114 return LatticePolytope();
115 }
116 // Polytope is d-1 dimensional (nD)
117 // Compute an independent vector to the basis (one of the canonic vector)
118 Point e = Affine::template independentVector<Point>( basis );
119 // Compute an orthogonal vector to the basis
120 Affine::completeBasis( basis, true );
121 InternalPoint n = basis.back(); // used to compute the two half-spaces that sandwich the polytope
122
123 // Compute domain
124 Point l = input_points[ 0 ];
125 Point u = input_points[ 0 ];
126 for ( const auto& p : input_points ) {
127 l = l.inf( p );
128 u = u.sup( p );
129 }
130 Domain domain( l, u );
131
132 // We complete the affine subset so that it is full dimensional.
133 // We could use `ref + n` but it induces bigger integers.
134 // Now the set of input points should be full dimensional.
135 input_points.push_back( ref + e );
136
137 // Compute convex hull
138 ConvexHull hull;
139 hull.setInput( input_points, false );
140 const auto target = ConvexHull::Status::FacetsCompleted;
141 // the array `indices` provides the initial full dimensional simplex,
142 // if we had the last point.
143 indices.push_back( input_points.size() - 1 );
144 bool ok_init = hull.setInitialSimplex( indices );
145 if ( ! ok_init )
146 {
147 std::cerr << "[ConvexityHelper::computeDegeneratedLatticePolytope]"
148 << "Weird error in hull.setInitialSimplex" << std::endl;
149 return LatticePolytope();
150 }
151 bool ok_hull = hull.computeConvexHull( target );
152 if ( ! ok_hull )
153 {
154 std::cerr << "[ConvexityHelper::computeDegeneratedLatticePolytope]"
155 << "Weird error in hull.computeConvexHull" << std::endl;
156 return LatticePolytope();
157 }
158 // Initialize polytope
159 std::vector< ConvexHullHalfSpace > HS;
160 std::vector< PolytopeHalfSpace > PHS;
161 hull.getFacetHalfSpaces( HS );
162 PHS.reserve( HS.size()+2 );
163 for ( auto& H : HS ) {
164 Vector N;
165 Integer nu;
166 for ( Dimension ii = 0; ii < dim; ++ii )
167 N[ ii ] = IntegerConverter< dimension, Integer >::cast( H.internalNormal()[ ii ] );
168 nu = IntegerConverter< dimension, Integer >::cast( H.internalIntercept() );
169 PHS.emplace_back( N, nu );
170 }
171 // Add top constraint.
172 Vector tn = IntegerConverter< dimension, Integer >::cast( n );
173 Integer nu0 = input_points[ 0 ].dot( tn );
174 PHS.emplace_back( tn, nu0 );
175 return LatticePolytope( domain, PHS.cbegin(), PHS.cend(),
176 false, false );
177}
178
179//-----------------------------------------------------------------------------
180template < int dim, typename TInteger, typename TInternalInteger >
181typename DGtal::ConvexityHelper< dim, TInteger, TInternalInteger>::LatticePolytope
182DGtal::ConvexityHelper< dim, TInteger, TInternalInteger>::compute3DTriangle
183( const Point& a, const Point& b, const Point& c,
184 bool make_minkowski_summable )
185{
186 if constexpr( dim != 3 ) return LatticePolytope();
187 using Op = detail::BoundedRationalPolytopeSpecializer< dimension, Integer>;
188 typedef typename LatticePolytope::Domain Domain;
189 typedef typename LatticePolytope::HalfSpace PolytopeHalfSpace;
190 // Compute domain
191 const Vector ab = b - a;
192 const Vector bc = c - b;
193 const Vector ca = a - c;
194 const Vector n = Op::crossProduct( ab, bc );
195 if ( n == Vector::zero )
196 return computeDegeneratedTriangle( a, b, c );
197 const Point low = a.inf( b ).inf( c );
198 const Point high = a.sup( b ).sup( c );
199 // Initialize polytope
200 std::vector< PolytopeHalfSpace > PHS;
201 PHS.reserve( make_minkowski_summable ? 11 : 5 );
202 const Integer n_a = n.dot( a );
203 const Vector u = Op::crossProduct( ab, n );
204 const Vector v = Op::crossProduct( bc, n );
205 const Vector w = Op::crossProduct( ca, n );
206 PHS.emplace_back( n, n_a );
207 PHS.emplace_back( -n, -n_a );
208 if ( ! make_minkowski_summable )
209 { // It is enough to have one constraint per edge.
210 PHS.emplace_back( u, u.dot( a ) );
211 PHS.emplace_back( v, v.dot( b ) );
212 PHS.emplace_back( w, w.dot( c ) );
213 }
214 else
215 { // Compute additionnal constraints on edges so that the
216 // Minkowski sum with axis-aligned edges is valid.
217 for ( Integer d = -1; d <= 1; d += 2 )
218 for ( Dimension k = 0; k < dim; k++ )
219 {
220 const Vector i = Vector::base( k, d );
221 const Vector eab = Op::crossProduct( ab, i );
222 const Integer eab_a = eab.dot( a );
223 if ( eab.dot( c ) < eab_a ) // c must be below plane (a,eab)
224 PHS.emplace_back( eab, eab_a );
225 const Vector ebc = Op::crossProduct( bc, i );
226 const Integer ebc_b = ebc.dot( b );
227 if ( ebc.dot( a ) < ebc_b ) // a must be below plane (b,ebc)
228 PHS.emplace_back( ebc, ebc_b );
229 const Vector eca = Op::crossProduct( ca, i );
230 const Integer eca_c = eca.dot( c );
231 if ( eca.dot( b ) < eca_c ) // b must be below plane (c,eca)
232 PHS.emplace_back( eca, eca_c );
233 }
234 }
235 return LatticePolytope( Domain( low, high ),
236 PHS.cbegin(), PHS.cend(),
237 make_minkowski_summable, false );
238}
239
240//-----------------------------------------------------------------------------
241template < int dim, typename TInteger, typename TInternalInteger >
242typename DGtal::ConvexityHelper< dim, TInteger, TInternalInteger>::LatticePolytope
243DGtal::ConvexityHelper< dim, TInteger, TInternalInteger>::compute3DOpenTriangle
244( const Point& a, const Point& b, const Point& c,
245 bool make_minkowski_summable )
246{
247 using Op = detail::BoundedRationalPolytopeSpecializer< dimension, Integer>;
248 const Vector ab = b - a;
249 const Vector bc = c - b;
250 const Vector n = Op::crossProduct( ab, bc );
251 if ( n == Vector::zero )
252 return LatticePolytope(); // empty polytope
253 auto P = compute3DTriangle( a, b, c, make_minkowski_summable );
254 // Compute domain
255 for ( auto k = 0; k < P.nbHalfSpaces(); k++ )
256 {
257 if ( Op::crossProduct( P.getA( k ), n ) != Vector::zero )
258 P.setStrict( k );
259 }
260 return P;
261}
262
263
264//-----------------------------------------------------------------------------
265template < int dim, typename TInteger, typename TInternalInteger >
266typename DGtal::ConvexityHelper< dim, TInteger, TInternalInteger>::LatticePolytope
267DGtal::ConvexityHelper< dim, TInteger, TInternalInteger>::
268computeDegeneratedTriangle
269( const Point& a, const Point& b, const Point& c )
270{
271 if ( a == b ) return computeSegment( a, c );
272 if ( ( a == c ) || ( b == c ) ) return computeSegment( a, b );
273 // The three points are distinct, hence aligned. One is in-between the two others.
274 const Point low = a.inf( b ).inf( c );
275 const Point high = a.sup( b ).sup( c );
276 for ( Dimension k = 0; k < dim; k++ )
277 {
278 const auto lk = low [ k ];
279 const auto hk = high[ k ];
280 if ( ( a[ k ] != lk ) && ( a[ k ] != hk ) ) return computeSegment( b, c );
281 if ( ( b[ k ] != lk ) && ( b[ k ] != hk ) ) return computeSegment( a, c );
282 if ( ( c[ k ] != lk ) && ( c[ k ] != hk ) ) return computeSegment( a, b );
283 }
284 trace.error() << "[ConvexityHelper::computeSegmentFromDegeneratedTriangle] "
285 << "Should never arrive here." << std::endl;
286 return computeSegment( a, a );
287}
288
289
290//-----------------------------------------------------------------------------
291template < int dim, typename TInteger, typename TInternalInteger >
292typename DGtal::ConvexityHelper< dim, TInteger, TInternalInteger>::LatticePolytope
293DGtal::ConvexityHelper< dim, TInteger, TInternalInteger>::computeOpenSegment
294( const Point& a, const Point& b )
295{
296
297 if constexpr( dim != 3 ) return LatticePolytope();
298 using Op = detail::BoundedRationalPolytopeSpecializer< dimension, Integer>;
299 typedef typename LatticePolytope::Domain Domain;
300 typedef typename LatticePolytope::HalfSpace PolytopeHalfSpace;
301 // Compute domain
302 const Point low = a.inf( b );
303 const Point high = a.sup( b );
304 const Vector ab = b - a;
305 bool degenerate = ( ab == Vector::zero );
306 if ( degenerate ) return LatticePolytope();
307 // Initialize polytope
308 std::vector< PolytopeHalfSpace > PHS;
309 PHS.reserve( 4*dim );
310 // Compute additionnal constraints on domain boundary to make it open.
311 for ( Dimension k = 0; k < dim; k++ )
312 {
313 const Vector bp = Vector::base( k, 1 );
314 PHS.emplace_back( bp, high[ k ] );
315 const Vector bn = Vector::base( k, -1 );
316 PHS.emplace_back( bn, -low[ k ] );
317 }
318 // Compute additionnal constraints on edges so that the
319 // Minkowski sum with axis-aligned edges is valid.
320 for ( Integer d = -1; d <= 1; d += 2 )
321 for ( Dimension k = 0; k < dim; k++ )
322 {
323 const Vector i = Vector::base( k, d );
324 const Vector e = Op::crossProduct( ab, i );
325 if ( e != Vector::zero )
326 {
327 const Integer e_a = e.dot( a );
328 PHS.emplace_back( e, e_a );
329 }
330 }
331 auto P = LatticePolytope( Domain( low, high ), PHS.cbegin(), PHS.cend(), true, false );
332 // Fix < inequalities
333 for ( auto k = 2 * dim; k < 4 * dim; k++ )
334 {
335 auto V = P.getA( k );
336 if ( V.dot( a ) != V.dot( b ) ) P.setStrict( k );
337 }
338 return P;
339
340}
341
342//-----------------------------------------------------------------------------
343template < int dim, typename TInteger, typename TInternalInteger >
344typename DGtal::ConvexityHelper< dim, TInteger, TInternalInteger>::LatticePolytope
345DGtal::ConvexityHelper< dim, TInteger, TInternalInteger>::computeSegment
346( const Point& a, const Point& b )
347{
348 if constexpr( dim != 3 ) return LatticePolytope();
349 using Op = detail::BoundedRationalPolytopeSpecializer< dimension, Integer>;
350 typedef typename LatticePolytope::Domain Domain;
351 typedef typename LatticePolytope::HalfSpace PolytopeHalfSpace;
352 // Compute domain
353 const Point low = a.inf( b );
354 const Point high = a.sup( b );
355 const Vector ab = b - a;
356 bool degenerate = ( ab == Vector::zero );
357 // Initialize polytope
358 std::vector< PolytopeHalfSpace > PHS;
359 if ( degenerate )
360 return LatticePolytope( Domain( low, high ), PHS.cbegin(), PHS.cend(), true, false );
361 PHS.reserve( 2*dim );
362 // Compute additionnal constraints on edges so that the
363 // Minkowski sum with axis-aligned edges is valid.
364 for ( Integer d = -1; d <= 1; d += 2 )
365 for ( Dimension k = 0; k < dim; k++ )
366 {
367 const Vector i = Vector::base( k, d );
368 const Vector e = Op::crossProduct( ab, i );
369 if ( e != Vector::zero )
370 {
371 const Integer e_a = e.dot( a );
372 PHS.emplace_back( e, e_a );
373 }
374 }
375 return LatticePolytope( Domain( low, high ), PHS.cbegin(), PHS.cend(), true, false );
376}
377
378
379//-----------------------------------------------------------------------------
380template < int dim, typename TInteger, typename TInternalInteger >
381typename DGtal::ConvexityHelper< dim, TInteger, TInternalInteger>::PointRange
382DGtal::ConvexityHelper< dim, TInteger, TInternalInteger>::
383computeDegeneratedConvexHullVertices
384( PointRange& input_points )
385{
386 typedef QuickHull< LatticeConvexHullKernel > ConvexHull;
387
388 // Rewrite computeDegeneratedLatticePolytope using AffineSubset
389 typedef PointVector<dim, TInternalInteger> InternalPoint;
390 typedef AffineGeometry<InternalPoint> Affine;
391 auto indices = Affine::affineSubset( input_points );
392 auto ref_basis = Affine::affineBasis ( input_points, indices );
393 auto ref = ref_basis.first;
394 auto& basis = ref_basis.second;
395 if ( basis.size() >= dimension )
396 {
397 trace.error() << "[ConvexityHelper::computeDegeneratedLatticePolytope]"
398 << " Weird error: found initial full dimensional simplex" << std::endl;
399 return PointRange();
400 }
401 if ( basis.size() == 0 )
402 { // Polytope is degenerated to one point (nD)
403 PointRange X = { ref };
404 return X;
405 }
406 if ( basis.size() == 1 )
407 { // Polytope is a 1-dimensional straight segment (nD).
408 // Find the two extremal points.
409 std::vector< Integer > alpha( input_points.size() );
410 for ( std::size_t i = 0; i < input_points.size(); i++ )
411 alpha[ i ] = basis[ 0 ].dot( input_points[ i ] - ref );
412 Index a = 0, b = 0;
413 for ( std::size_t i = 1; i < input_points.size(); i++ )
414 {
415 if ( alpha[ i ] < alpha[ a ] ) a = i;
416 if ( alpha[ i ] > alpha[ b ] ) b = i;
417 }
418 PointRange X = { input_points[ a ], input_points[ b ] };
419 return X;
420 }
421 if ( basis.size()+1 != dimension )
422 {
423 trace.error() << "[ConvexityHelper::computeDegeneratedLatticePolytope] "
424 << "computation of a " << basis.size() << "-dimensional polytope"
425 << " in " << dimension << "D is not implemented yet." << std::endl;
426 return PointRange();
427 }
428 // Polytope is d-1 dimensional (nD)
429
430 // old code
431 // typedef QuickHull< LatticeConvexHullKernel > ConvexHull;
432 // // input_points is a range of points with no duplicates, but which
433 // // seems to be not full dimensional.
434 // if ( input_points.size() <= 1 )
435 // return input_points;
436 // // At least 1-dimensional
437 // std::vector< Vector > basis;
438 // std::vector< Integer > alpha;
439 // basis.push_back( input_points[ 1 ] - input_points[ 0 ] );
440 // const auto n0 = basis[ 0 ].norm();
441 // alpha.push_back( Integer( 0 ) );
442 // alpha.push_back( basis[ 0 ].dot( basis[ 0 ] ) );
443 // Index i = 2;
444 // while ( i < input_points.size() ) {
445 // Vector v = input_points[ i ] - input_points[ 0 ];
446 // alpha.push_back( basis[ 0 ].dot( v ) );
447 // const auto ni = v.norm();
448 // const double alignment =
449 // fabs( fabs( NumberTraits< Integer >::castToDouble( alpha.back() ) )
450 // - ( n0 * ni ) );
451 // if ( alignment > 1e-8 ) break;
452 // i++;
453 // }
454 // if ( i == input_points.size() )
455 // { // 1-dimensional
456 // Index a = 0, b = 0;
457 // for ( i = 1; i < input_points.size(); i++ )
458 // {
459 // if ( alpha[ i ] < alpha[ a ] ) a = i;
460 // if ( alpha[ i ] > alpha[ b ] ) b = i;
461 // }
462 // PointRange X( 2 );
463 // X[ 0 ] = input_points[ a ];
464 // X[ 1 ] = input_points[ b ];
465 // return X;
466 // }
467 // // at least 2-dimensional
468 // ASSERT( dimension > 1 );
469 // if ( dimension == 2 )
470 // {
471 // std::cerr << "[ConvexityHelper::computeDegeneratedConvexHullVertices]"
472 // << " Weird error: found initial full dimensional simplex" << std::endl;
473 // return PointRange();
474 // }
475 // if ( dimension >= 4 )
476 // {
477 // std::cerr << "[ConvexityHelper::computeDegeneratedConvexHullVertices]"
478 // << "Degenerated lattice polytope in nD, n >= 4 is not implemented"
479 // << std::endl;
480 // return PointRange();
481 // }
482
483 // Compute an independent vector to the basis (one of the canonic vector)
484 Point e = Affine::template independentVector<Point>( basis );
485 if ( e == Point::zero )
486 {
487 std::cerr << "[ConvexityHelper::computeDegeneratedConvexHullVertices]"
488 << "Weird numerical error, the basis is not full dimensional and there is no independent canonic vector !"
489 << std::endl;
490 return PointRange();
491 }
492 // We complete the affine subset so that it is full dimensional.
493 // Now the set of input points should be full dimensional.
494 input_points.push_back( ref + e );
495 // Compute convex hull
496 ConvexHull hull;
497 hull.setInput( input_points, false );
498 const auto target = ConvexHull::Status::VerticesCompleted; //< to get vertices
499 // the array `indices` provides the initial full dimensional simplex,
500 // if we had the last point.
501 indices.push_back( input_points.size() - 1 );
502 bool ok_init = hull.setInitialSimplex( indices );
503 if ( ! ok_init )
504 {
505 std::cerr << "[ConvexityHelper::computeDegeneratedLatticePolytope]"
506 << "Weird error in hull.setInitialSimplex" << std::endl;
507 return PointRange();
508 }
509 bool ok_hull = hull.computeConvexHull( target );
510 if ( ! ok_hull )
511 {
512 std::cerr << "[ConvexityHelper::computeDegeneratedLatticePolytope]"
513 << "Weird error in hull.computeConvexHull" << std::endl;
514 return PointRange();
515 }
516
517 // old code
518 // basis.push_back( input_points[ i ] - input_points[ 0 ] );
519 // Vector n = detail::BoundedLatticePolytopeSpecializer< dimension, Integer>
520 // ::crossProduct( basis[ 0 ], basis[ 1 ] );
521 // if ( n == Vector::zero )
522 // {
523 // std::cerr << "[ConvexityHelper::computeDegeneratedConvexHullVertices]"
524 // << "Weird numerical error, u . v != |u| |v| but u x v != 0"
525 // << std::endl;
526 // return PointRange();
527 // }
528 // // Now the set of input points should be full dimensional.
529 // input_points.push_back( input_points[ 0 ] + n );
530 // // Compute convex hull
531 // ConvexHull hull;
532 // hull.setInput( input_points, false );
533 // const auto target = ConvexHull::Status::VerticesCompleted;
534 // IndexRange full_splx = { 0, 1, i, input_points.size() - 1 };
535 // bool ok_init = hull.setInitialSimplex( full_splx );
536 // if ( ! ok_init )
537 // {
538 // std::cerr << "[ConvexityHelper::computeDegeneratedConvexHullVertices]"
539 // << "Weird error in hull.setInitialSimplex" << std::endl;
540 // return PointRange();
541 // }
542 // bool ok_hull = hull.computeConvexHull( target );
543 // if ( ! ok_hull )
544 // {
545 // std::cerr << "[ConvexityHelper::computeDegeneratedConvexHullVertices]"
546 // << "Weird error in hull.computeConvexHull" << std::endl;
547 // return PointRange();
548 // }
549
550 // Get convex hull vertices and remove top point
551 PointRange X;
552 hull.getVertexPositions( X );
553 const std::size_t nX = X.size();
554 for ( std::size_t j = 0; j < nX; j++ )
555 if ( X[ j ] == input_points.back() )
556 {
557 X[ j ] = X.back();
558 break;
559 }
560 X.resize( nX - 1 );
561 return X;
562}
563
564//-----------------------------------------------------------------------------
565template < int dim, typename TInteger, typename TInternalInteger >
566typename DGtal::ConvexityHelper< dim, TInteger, TInternalInteger>::LatticePolytope
567DGtal::ConvexityHelper< dim, TInteger, TInternalInteger>::
568computeLatticePolytope
569( const PointRange& input_points,
570 bool remove_duplicates,
571 bool make_minkowski_summable )
572{
573 typedef typename LatticePolytope::Domain Domain;
574 typedef typename LatticePolytope::HalfSpace PolytopeHalfSpace;
575 typedef QuickHull< LatticeConvexHullKernel > ConvexHull;
576 typedef typename ConvexHull::HalfSpace ConvexHullHalfSpace;
577 typedef typename ConvexHull::Ridge Ridge;
578 if ( input_points.empty() ) return LatticePolytope();
579 if ( input_points.size() <= ( dimension + 1) )
580 return computeSimplex( input_points, remove_duplicates );
581 // Compute domain
582 Point l = input_points[ 0 ];
583 Point u = input_points[ 0 ];
584 for ( std::size_t i = 1; i < input_points.size(); i++ )
585 {
586 const auto& p = input_points[ i ];
587 l = l.inf( p );
588 u = u.sup( p );
589 }
590 Domain domain( l, u );
591 // Compute convex hull
592 ConvexHull hull;
593 hull.setInput( input_points, remove_duplicates );
594 const auto target = ( make_minkowski_summable && dimension == 3 )
595 ? ConvexHull::Status::VerticesCompleted
596 : ConvexHull::Status::FacetsCompleted;
597 bool ok = hull.computeConvexHull( target );
598 if ( ! ok ) // set of points is not full dimensional
599 return computeDegeneratedLatticePolytope( hull.points );
600 // Initialize polytope
601 std::vector< ConvexHullHalfSpace > HS;
602 std::vector< PolytopeHalfSpace > PHS;
603 hull.getFacetHalfSpaces( HS );
604 PHS.reserve( HS.size() );
605 for ( auto& H : HS ) {
606 Vector N;
607 Integer nu;
608 for ( Dimension i = 0; i < dim; ++i )
609 N[ i ] = IntegerConverter< dimension, Integer >::cast( H.internalNormal()[ i ] );
610 nu = IntegerConverter< dimension, Integer >::cast( H.internalIntercept() );
611 PHS.emplace_back( N, nu );
612 }
613 if ( make_minkowski_summable && dimension >= 4 )
614 trace.warning() << "[ConvexityHelper::computeLatticePolytope]"
615 << " Not implemented starting from dimension 4."
616 << std::endl;
617 if ( make_minkowski_summable && dimension == 3 )
618 {
619 // Compute ridge vertices to add edge constraints.
620 PointRange positions;
621 std::vector< IndexRange > facet_vertices;
622 std::vector< IndexRange > ridge_vertices;
623 std::map< Ridge, Index > ridge2index;
624 hull.getVertexPositions( positions );
625 computeFacetAndRidgeVertices( hull, facet_vertices,
626 ridge2index, ridge_vertices );
627 for ( auto p : ridge2index ) {
628 const auto r = p.first;
629 // Copy by value since PHS may be reallocated during the iteration.
630 const auto U = PHS[ r.first ].N; // normal of facet 1
631 const auto V = PHS[ r.second ].N; // normal of facet 2
632 const auto& S = ridge_vertices[ p.second ]; // vertices along facets 1, 2
633 ASSERT( S.size() == 2 && "Invalid ridge" );
634 const auto& P0 = positions[ S[ 0 ] ];
635 const auto& P1 = positions[ S[ 1 ] ];
636 auto E = P1 - P0; // edge 1, 2
637 const auto UxV =
638 detail::BoundedLatticePolytopeSpecializer< dimension, Integer>
639 ::crossProduct( U, V ); // parallel to E
640 ASSERT( E.dot( UxV ) != 0 && "Invalid E / UxV" );
641 if ( E.dot( UxV ) <= 0 ) E = -E; // force correct orientation
642 const auto E1 =
643 detail::BoundedLatticePolytopeSpecializer< dimension, Integer>
644 ::crossProduct( U, E ); // edge on facet 1
645 const auto E2 =
646 detail::BoundedLatticePolytopeSpecializer< dimension, Integer>
647 ::crossProduct( E, V ); // edge on facet 2
648 ASSERT( E1.dot( U ) == 0 && "Invalid E1 / U" );
649 ASSERT( E1.dot( V ) < 0 && "Invalid E1 / V" );
650 ASSERT( E2.dot( V ) == 0 && "Invalid E1 / V" );
651 ASSERT( E2.dot( U ) < 0 && "Invalid E1 / U" );
652 for ( Dimension k = 0; k < dimension; ++k ) {
653 const auto W = U[ k ] * V - V[ k ] * U;
654 const auto nn1 = W.dot( E1 );
655 const auto nn2 = W.dot( E2 );
656 if ( nn1 > 0 && nn2 > 0 ) {
657 PHS.emplace_back( -W, -W.dot( P0 ) );
658 ASSERT( E1.dot(-W ) < 0 && "Invalid E1 /-W" );
659 ASSERT( E2.dot(-W ) < 0 && "Invalid E2 /-W" );
660 }
661 else if ( nn1 < 0 && nn2 < 0 ) {
662 PHS.emplace_back( W, W.dot( P0 ) );
663 ASSERT( E1.dot( W ) < 0 && "Invalid E1 / W" );
664 ASSERT( E2.dot( W ) < 0 && "Invalid E2 / W" );
665 }
666 }
667 }
668 }
669 return LatticePolytope( domain, PHS.cbegin(), PHS.cend(),
670 make_minkowski_summable && ( dimension <= 3 ), true );
671}
672
673//-----------------------------------------------------------------------------
674template < int dim, typename TInteger, typename TInternalInteger >
675typename DGtal::ConvexityHelper< dim, TInteger, TInternalInteger>::PointRange
676DGtal::ConvexityHelper< dim, TInteger, TInternalInteger>::
677computeConvexHullVertices
678( const PointRange& input_points,
679 bool remove_duplicates )
680{
681 typedef QuickHull< LatticeConvexHullKernel > ConvexHull;
682 PointRange positions;
683 ConvexHull hull;
684 hull.setInput( input_points, remove_duplicates );
685 bool ok = hull.computeConvexHull( ConvexHull::Status::VerticesCompleted );
686 if ( !ok )
687 {
688 PointRange Z( input_points );
689 return computeDegeneratedConvexHullVertices( Z );
690 }
691 hull.getVertexPositions( positions );
692 return positions;
693}
694
695//-----------------------------------------------------------------------------
696template < int dim, typename TInteger, typename TInternalInteger >
697template < typename TSurfaceMesh >
698bool
699DGtal::ConvexityHelper< dim, TInteger, TInternalInteger>::computeConvexHullBoundary
700( TSurfaceMesh& mesh,
701 const PointRange& input_points,
702 bool remove_duplicates )
703{
704 typedef TSurfaceMesh SurfaceMesh;
705 typedef QuickHull< LatticeConvexHullKernel > ConvexHull;
706 ConvexHull hull;
707 hull.setInput( input_points, remove_duplicates );
708 bool ok = hull.computeConvexHull( ConvexHull::Status::VerticesCompleted );
709 std::cout << "[ConvexityHelper::computeConvexHullBoundary(SurfMesh)] status=" << (int) hull.status() << "\n";
710 if ( !ok ) return false;
711 std::vector< RealPoint > positions;
712 hull.getVertexPositions( positions );
713 std::vector< IndexRange > faces;
714 hull.getFacetVertices( faces );
715 for ( auto p : positions ) std::cout << " " << p;
716 for ( auto f : faces ) {
717 std::cout << " (";
718 for ( auto i : f ) std::cout << " " << i;
719 std::cout << " )";
720 }
721 std::cout << "\n";
722 mesh = SurfaceMesh( positions.cbegin(), positions.cend(),
723 faces.cbegin(), faces.cend() );
724 return true;
725}
726
727//-----------------------------------------------------------------------------
728template < int dim, typename TInteger, typename TInternalInteger >
729bool
730DGtal::ConvexityHelper< dim, TInteger, TInternalInteger>::computeConvexHullBoundary
731( PolygonalSurface< Point >& polysurf,
732 const PointRange& input_points,
733 bool remove_duplicates )
734{
735 typedef QuickHull< LatticeConvexHullKernel > ConvexHull;
736 ConvexHull hull;
737 hull.setInput( input_points, remove_duplicates );
738 bool ok = hull.computeConvexHull( ConvexHull::Status::VerticesCompleted );
739 std::cout << "[ConvexityHelper::computeConvexHullBoundary(PolySurf)] status=" << (int) hull.status() << "\n";
740 if ( !ok ) return false;
741 PointRange positions;
742 hull.getVertexPositions( positions );
743 std::vector< IndexRange > faces;
744 hull.getFacetVertices( faces );
745 for ( auto p : positions ) std::cout << " " << p;
746 for ( auto f : faces ) {
747 std::cout << " (";
748 for ( auto i : f ) std::cout << " " << i;
749 std::cout << " )";
750 }
751 std::cout << "\n";
752 // build polygonal surface
753 polysurf.clear();
754 for ( auto p : positions ) polysurf.addVertex( p );
755 for ( auto f : faces ) polysurf.addPolygonalFace( f );
756 return polysurf.build();
757}
758
759//-----------------------------------------------------------------------------
760template < int dim, typename TInteger, typename TInternalInteger >
761bool
762DGtal::ConvexityHelper< dim, TInteger, TInternalInteger>::computeConvexHullCellComplex
763( ConvexCellComplex< Point >& cell_complex,
764 const PointRange& input_points,
765 bool remove_duplicates )
766{
767 typedef QuickHull< LatticeConvexHullKernel > ConvexHull;
768 typedef typename ConvexCellComplex< Point >::FaceRange FaceRange;
769 ConvexHull hull;
770 hull.setInput( input_points, remove_duplicates );
771 bool ok = hull.computeConvexHull( ConvexHull::Status::VerticesCompleted );
772 cell_complex.clear();
773 if ( ! ok ) return false;
774 // Build complex, only 1 finite cell and as many faces as convex hull facets.
775 // Taking care of faces for each cell (here one cell borders all faces).
776 std::vector< IndexRange > faces;
777 hull.getFacetVertices( faces );
778 FaceRange all_faces;
779 for ( Index i = 0; i < faces.size(); i++ )
780 all_faces.push_back( { i, true } );
781 cell_complex.cell_faces.push_back( all_faces );
782 // Vertices of this unique cell will be computed lazily on request.
783 // Taking care of each face.
784 for ( Index i = 0; i < faces.size(); i++ )
785 {
786 // every inner face borders cell 0
787 cell_complex.true_face_cell.push_back( 0 );
788 // every outer face borders the infinite cell
789 cell_complex.false_face_cell.push_back( cell_complex.infiniteCell() );
790 }
791 // Taking care of vertices (in consistent order) of every face
792 cell_complex.true_face_vertices.swap( faces );
793 // Taking care of vertex positions
794 hull.getVertexPositions( cell_complex.vertex_position );
795 return true;
796}
797
798//-----------------------------------------------------------------------------
799template < int dim, typename TInteger, typename TInternalInteger >
800bool
801DGtal::ConvexityHelper< dim, TInteger, TInternalInteger>::computeDelaunayCellComplex
802( ConvexCellComplex< Point >& cell_complex,
803 const PointRange& input_points,
804 bool remove_duplicates )
805{
806 typedef QuickHull< LatticeDelaunayKernel > Delaunay;
807 typedef typename Delaunay::Ridge Ridge;
808 typedef typename ConvexCellComplex< Point >::FaceRange FaceRange;
809
810 Delaunay del;
811 del.setInput( input_points, remove_duplicates );
812 bool ok = del.computeConvexHull( Delaunay::Status::VerticesCompleted );
813 cell_complex.clear();
814 if ( ! ok ) return false;
815
816 // Build complex, as many maximal cells as convex hull facets.
817 // convex hull facet -> cell of complex
818 // convex hull ridge -> face of complex
819 // (1) Get cell vertices, count ridges/faces and compute their vertices
820 std::map< Ridge, Index > r2f;
821 computeFacetAndRidgeVertices( del,
822 cell_complex.cell_vertices,
823 r2f,
824 cell_complex.true_face_vertices );
825 // (2) assign ridges/faces to cell and conversely
826 const Index nb_r = r2f.size();
827 cell_complex.true_face_cell .resize( nb_r, cell_complex.infiniteCell() );
828 cell_complex.false_face_cell.resize( nb_r, cell_complex.infiniteCell() );
829 cell_complex.true_face_vertices.resize( nb_r );
830 for ( Index cur_f = 0; cur_f < del.nbFiniteFacets(); ++cur_f ) {
831 const auto& facet = del.facets[ cur_f ];
832 FaceRange current_faces;
833 for ( auto neigh_f : facet.neighbors ) {
834 const Ridge r { std::min( cur_f, neigh_f ), std::max( cur_f, neigh_f ) };
835 const bool pos = cur_f < neigh_f;
836 const Index cur_r = r2f[ r ];
837 cell_complex.true_face_cell [ cur_r ] = r.first;
838 if ( r.second >= del.nbFiniteFacets() )
839 cell_complex.false_face_cell[ cur_r ] = cell_complex.infiniteCell();
840 else
841 cell_complex.false_face_cell[ cur_r ] = r.second;
842 current_faces.emplace_back( cur_r, pos );
843 }
844 cell_complex.cell_faces.push_back( current_faces );
845 }
846 // (3) Takes care of vertex positions
847 del.getVertexPositions( cell_complex.vertex_position );
848 return true;
849}
850
851
852//-----------------------------------------------------------------------------
853template < int dim, typename TInteger, typename TInternalInteger >
854typename DGtal::ConvexityHelper< dim, TInteger, TInternalInteger>::RationalPolytope
855DGtal::ConvexityHelper< dim, TInteger, TInternalInteger>::computeRationalPolytope
856( const std::vector< RealPoint >& input_points,
857 Integer denominator,
858 bool remove_duplicates,
859 bool make_minkowski_summable )
860{
861 if ( denominator < 1 )
862 trace.error() << "Invalid denominator " << denominator
863 << ". Should be greater or equal to 1." << std::endl;
864 typedef typename RationalPolytope::Domain Domain;
865 typedef typename RationalPolytope::HalfSpace PolytopeHalfSpace;
866 typedef QuickHull< RealConvexHullKernel > ConvexHull;
867 typedef typename ConvexHull::HalfSpace ConvexHullHalfSpace;
868 typedef typename ConvexHull::Ridge Ridge;
869 if ( input_points.empty() ) return RationalPolytope();
870 // Compute convex hull
871 ConvexHull hull( denominator );
872 hull.setInput( input_points, remove_duplicates );
873 const auto target = ( make_minkowski_summable && dimension == 3 )
874 ? ConvexHull::Status::VerticesCompleted
875 : ConvexHull::Status::FacetsCompleted;
876 bool ok = hull.computeConvexHull( target );
877 if ( ! ok ) return RationalPolytope();
878 // Compute domain (as a lattice domain)
879 auto l = hull.points[ 0 ];
880 auto u = hull.points[ 0 ];
881 for ( const auto& p : hull.points ) {
882 l = l.inf( p );
883 u = u.sup( p );
884 }
885 Domain domain( l, u );
886 trace.info() << "Domain l=" << l << " u=" << u << std::endl;
887 // Initialize polytope
888 std::vector< ConvexHullHalfSpace > HS;
889 std::vector< PolytopeHalfSpace > PHS;
890 hull.getFacetHalfSpaces( HS );
891 PHS.reserve( HS.size() );
892 for ( auto& H : HS ) {
893 Vector N;
894 Integer nu;
895 for ( Dimension i = 0; i < dim; ++i )
896 N[ i ] = (Integer) H.internalNormal()[ i ];
897 nu = (Integer) H.internalIntercept();
898 PHS.emplace_back( N, nu );
899 }
900 if ( make_minkowski_summable && dimension >= 4 )
901 trace.warning() << "[ConvexityHelper::computeRationalPolytope]"
902 << " Not implemented starting from dimension 4."
903 << std::endl;
904 if ( make_minkowski_summable && dimension == 3 )
905 {
906 // Compute ridge vertices to add edge constraints.
907 PointRange positions;
908 std::vector< IndexRange > facet_vertices;
909 std::vector< IndexRange > ridge_vertices;
910 std::map< Ridge, Index > ridge2index;
911 hull.getVertexPositions( positions );
912 computeFacetAndRidgeVertices( hull, facet_vertices,
913 ridge2index, ridge_vertices );
914 for ( auto p : ridge2index ) {
915 const auto r = p.first;
916 // Copy by value since PHS may be reallocated during the iteration.
917 const auto U = PHS[ r.first ].N; // normal of facet 1
918 const auto V = PHS[ r.second ].N; // normal of facet 2
919 const auto& S = ridge_vertices[ p.second ]; // vertices along facets 1, 2
920 ASSERT( S.size() == 2 && "Invalid ridge" );
921 const auto& P0 = positions[ S[ 0 ] ];
922 const auto& P1 = positions[ S[ 1 ] ];
923 auto E = P1 - P0; // edge 1, 2
924 const auto UxV =
925 detail::BoundedRationalPolytopeSpecializer< dimension, Integer>
926 ::crossProduct( U, V ); // parallel to E
927 ASSERT( E.dot( UxV ) != 0 && "Invalid E / UxV" );
928 if ( E.dot( UxV ) <= 0 ) E = -E; // force correct orientation
929 const auto E1 =
930 detail::BoundedRationalPolytopeSpecializer< dimension, Integer>
931 ::crossProduct( U, E ); // edge on facet 1
932 const auto E2 =
933 detail::BoundedRationalPolytopeSpecializer< dimension, Integer>
934 ::crossProduct( E, V ); // edge on facet 2
935 ASSERT( E1.dot( U ) == 0 && "Invalid E1 / U" );
936 ASSERT( E1.dot( V ) < 0 && "Invalid E1 / V" );
937 ASSERT( E2.dot( V ) == 0 && "Invalid E1 / V" );
938 ASSERT( E2.dot( U ) < 0 && "Invalid E1 / U" );
939 for ( Dimension k = 0; k < dimension; ++k ) {
940 const auto W = U[ k ] * V - V[ k ] * U;
941 const auto nn1 = W.dot( E1 );
942 const auto nn2 = W.dot( E2 );
943 if ( nn1 > 0 && nn2 > 0 ) {
944 PHS.emplace_back( -W, -W.dot( P0 ) );
945 ASSERT( E1.dot(-W ) < 0 && "Invalid E1 /-W" );
946 ASSERT( E2.dot(-W ) < 0 && "Invalid E2 /-W" );
947 }
948 else if ( nn1 < 0 && nn2 < 0 ) {
949 PHS.emplace_back( W, W.dot( P0 ) );
950 ASSERT( E1.dot( W ) < 0 && "Invalid E1 / W" );
951 ASSERT( E2.dot( W ) < 0 && "Invalid E2 / W" );
952 }
953 }
954 }
955 }
956 return RationalPolytope( denominator, domain, PHS.cbegin(), PHS.cend(),
957 make_minkowski_summable && ( dimension <= 3 ), true );
958}
959
960
961//-----------------------------------------------------------------------------
962template < int dim, typename TInteger, typename TInternalInteger >
963template < typename TSurfaceMesh >
964bool
965DGtal::ConvexityHelper< dim, TInteger, TInternalInteger>::computeConvexHullBoundary
966( TSurfaceMesh& mesh,
967 const std::vector< RealPoint >& input_points,
968 double precision,
969 bool remove_duplicates )
970{
971 typedef TSurfaceMesh SurfaceMesh;
972 typedef QuickHull< RealConvexHullKernel > ConvexHull;
973 ConvexHull hull( precision );
974 hull.setInput( input_points, remove_duplicates );
975 bool ok = hull.computeConvexHull( ConvexHull::Status::VerticesCompleted );
976 if ( !ok ) return false;
977 std::vector< RealPoint > positions;
978 hull.getVertexPositions( positions );
979 std::vector< IndexRange > faces;
980 hull.getFacetVertices( faces );
981 mesh = SurfaceMesh( positions.cbegin(), positions.cend(),
982 faces.cbegin(), faces.cend() );
983 return true;
984}
985
986
987//-----------------------------------------------------------------------------
988template < int dim, typename TInteger, typename TInternalInteger >
989bool
990DGtal::ConvexityHelper< dim, TInteger, TInternalInteger>::computeConvexHullBoundary
991( PolygonalSurface< RealPoint >& polysurf,
992 const std::vector< RealPoint >& input_points,
993 double precision,
994 bool remove_duplicates )
995{
996 typedef QuickHull< RealConvexHullKernel > ConvexHull;
997 ConvexHull hull( precision );
998 hull.setInput( input_points, remove_duplicates );
999 bool ok = hull.computeConvexHull( ConvexHull::Status::VerticesCompleted );
1000 if ( !ok ) return false;
1001 std::vector< RealPoint > positions;
1002 hull.getVertexPositions( positions );
1003 std::vector< IndexRange > faces;
1004 hull.getFacetVertices( faces );
1005 // build polygonal surface
1006 polysurf.clear();
1007 for ( auto p : positions ) polysurf.addVertex( p );
1008 for ( auto f : faces ) polysurf.addPolygonalFace( f );
1009 return polysurf.build();
1010}
1011
1012//-----------------------------------------------------------------------------
1013template < int dim, typename TInteger, typename TInternalInteger >
1014bool
1015DGtal::ConvexityHelper< dim, TInteger, TInternalInteger>::computeConvexHullCellComplex
1016( ConvexCellComplex< RealPoint >& cell_complex,
1017 const std::vector< RealPoint >& input_points,
1018 double precision,
1019 bool remove_duplicates )
1020{
1021 typedef QuickHull< RealConvexHullKernel > ConvexHull;
1022 typedef typename ConvexCellComplex< RealPoint >::FaceRange FaceRange;
1023 ConvexHull hull( precision );
1024 hull.setInput( input_points, remove_duplicates );
1025 bool ok = hull.computeConvexHull( ConvexHull::Status::VerticesCompleted );
1026 cell_complex.clear();
1027 if ( ! ok ) return false;
1028 // Build complex, only 1 finite cell and as many faces as convex hull facets.
1029 // Taking care of faces for each cell (here one cell borders all faces).
1030 std::vector< IndexRange > faces;
1031 hull.getFacetVertices( faces );
1032 FaceRange all_faces;
1033 for ( Index i = 0; i < faces.size(); i++ )
1034 all_faces.push_back( { i, true } );
1035 cell_complex.cell_faces.push_back( all_faces );
1036 // Vertices of this unique cell will be computed lazily on request.
1037 // Taking care of each face.
1038 for ( Index i = 0; i < faces.size(); i++ )
1039 {
1040 // every inner face borders cell 0
1041 cell_complex.true_face_cell.push_back( 0 );
1042 // every outer face borders the infinite cell
1043 cell_complex.false_face_cell.push_back( cell_complex.infiniteCell() );
1044 }
1045 // Taking care of vertices (in consistent order) of every face
1046 cell_complex.true_face_vertices.swap( faces );
1047 // Taking care of vertex positions
1048 hull.getVertexPositions( cell_complex.vertex_position );
1049 return true;
1050}
1051
1052
1053//-----------------------------------------------------------------------------
1054template < int dim, typename TInteger, typename TInternalInteger >
1055bool
1056DGtal::ConvexityHelper< dim, TInteger, TInternalInteger>::computeDelaunayCellComplex
1057( ConvexCellComplex< RealPoint >& cell_complex,
1058 const std::vector< RealPoint >& input_points,
1059 double precision,
1060 bool remove_duplicates )
1061{
1062 typedef QuickHull< RealDelaunayKernel > Delaunay;
1063 typedef typename Delaunay::Ridge Ridge;
1064 typedef typename ConvexCellComplex< RealPoint >::FaceRange FaceRange;
1065
1066 Delaunay del( precision );
1067 del.setInput( input_points, remove_duplicates );
1068 bool ok = del.computeConvexHull( Delaunay::Status::VerticesCompleted );
1069 cell_complex.clear();
1070 if ( ! ok ) return false;
1071
1072 // Build complex, as many maximal cells as convex hull facets.
1073 // convex hull facet -> cell of complex
1074 // convex hull ridge -> face of complex
1075 // (1) Get cell vertices, count ridges/faces and compute their vertices
1076 std::map< Ridge, Index > r2f;
1077 computeFacetAndRidgeVertices( del,
1078 cell_complex.cell_vertices,
1079 r2f,
1080 cell_complex.true_face_vertices );
1081 // (2) assign ridges/faces to cell and conversely
1082 const Index nb_r = r2f.size();
1083 cell_complex.true_face_cell .resize( nb_r, cell_complex.infiniteCell() );
1084 cell_complex.false_face_cell.resize( nb_r, cell_complex.infiniteCell() );
1085 cell_complex.true_face_vertices.resize( nb_r );
1086 for ( Index cur_f = 0; cur_f < del.nbFiniteFacets(); ++cur_f ) {
1087 const auto& facet = del.facets[ cur_f ];
1088 FaceRange current_faces;
1089 for ( auto neigh_f : facet.neighbors ) {
1090 const Ridge r { std::min( cur_f, neigh_f ), std::max( cur_f, neigh_f ) };
1091 const bool pos = cur_f < neigh_f;
1092 const Index cur_r = r2f[ r ];
1093 cell_complex.true_face_cell [ cur_r ] = r.first;
1094 if ( r.second >= del.nbFiniteFacets() )
1095 cell_complex.false_face_cell[ cur_r ] = cell_complex.infiniteCell();
1096 else
1097 cell_complex.false_face_cell[ cur_r ] = r.second;
1098 current_faces.emplace_back( cur_r, pos );
1099 }
1100 cell_complex.cell_faces.push_back( current_faces );
1101 }
1102 // (3) Takes care of vertex positions
1103 del.getVertexPositions( cell_complex.vertex_position );
1104 return true;
1105}
1106
1107
1108//-----------------------------------------------------------------------------
1109template < int dim, typename TInteger, typename TInternalInteger >
1110template < typename QHull >
1111void
1112DGtal::ConvexityHelper< dim, TInteger, TInternalInteger>::computeFacetAndRidgeVertices
1113( const QHull& hull,
1114 std::vector< IndexRange >& cell_vertices,
1115 std::map< typename QHull::Ridge, Index >& r2f,
1116 std::vector< IndexRange >& face_vertices )
1117{
1118 typedef typename QHull::Ridge Ridge;
1119
1120 ASSERT( hull.status() >= QHull::Status::VerticesCompleted
1121 && hull.status() <= QHull::Status::AllCompleted );
1122
1123 // Get cell vertices and sort them
1124 bool ok_fv = hull.getFacetVertices( cell_vertices );
1125 if ( ! ok_fv )
1126 trace.error() << "[ConvexityHelper::computeFacetAndRidgeVertices]"
1127 << " method hull.getFacetVertices failed."
1128 << " Maybe QuickHull was not computed till VerticesCompleted."
1129 << std::endl;
1130 std::vector< IndexRange > sorted_cell_vertices = cell_vertices;
1131 for ( auto& vtcs : sorted_cell_vertices )
1132 std::sort( vtcs.begin(), vtcs.end() );
1133 cell_vertices.resize( hull.nbFiniteFacets() );
1134
1135 // Count ridges/faces and compute their vertices
1136 Index nb_r = 0;
1137 face_vertices.clear();
1138 for ( Index cur_f = 0; cur_f < hull.nbFiniteFacets(); ++cur_f ) {
1139 const auto& facet = hull.facets[ cur_f ];
1140 for ( auto neigh_f : facet.neighbors ) {
1141 const Ridge r { std::min( cur_f, neigh_f ), std::max( cur_f, neigh_f ) };
1142 auto itr = r2f.find( r );
1143 if ( itr == r2f.end() ) {
1144 IndexRange result;
1145 std::set_intersection( sorted_cell_vertices[ cur_f ].cbegin(),
1146 sorted_cell_vertices[ cur_f ].cend(),
1147 sorted_cell_vertices[ neigh_f ].cbegin(),
1148 sorted_cell_vertices[ neigh_f ].cend(),
1149 std::back_inserter( result ) );
1150 face_vertices.push_back( result );
1151 r2f[ r ] = nb_r++;
1152 }
1153 }
1154 }
1155}
1156
1157// //
1158///////////////////////////////////////////////////////////////////////////////