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//////////////////////////////////////////////////////////////////////////////
32#include "DGtal/kernel/IntegerConverter.h"
33//////////////////////////////////////////////////////////////////////////////
35///////////////////////////////////////////////////////////////////////////////
36// IMPLEMENTATION of inline methods.
37///////////////////////////////////////////////////////////////////////////////
39///////////////////////////////////////////////////////////////////////////////
40// ----------------------- Standard services ------------------------------
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 )
50 if ( remove_duplicates )
53 for ( auto&& p : input_points ) S.insert( p );
54 X = PointRange( S.cbegin(), S.cend() );
56 else X = input_points;
57 LatticePolytope P( X.cbegin(), X.cend() );
58 if ( P.nbHalfSpaces() != 0 )
61 return computeDegeneratedLatticePolytope( X );
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 )
71 typedef typename LatticePolytope::Domain Domain;
72 typedef typename LatticePolytope::HalfSpace PolytopeHalfSpace;
73 typedef QuickHull< LatticeConvexHullKernel > ConvexHull;
74 typedef typename ConvexHull::HalfSpace ConvexHullHalfSpace;
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 )
85 trace.error() << "[ConvexityHelper::computeDegeneratedLatticePolytope]"
86 << " Weird error: found initial full dimensional simplex" << std::endl;
87 return LatticePolytope();
89 if ( basis.size() == 0 )
90 { // Polytope is degenerated to one point (nD)
91 PointRange X = { ref };
92 return LatticePolytope( X.cbegin(), X.cend() );
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 );
101 for ( std::size_t i = 1; i < input_points.size(); i++ )
103 if ( alpha[ i ] < alpha[ a ] ) a = i;
104 if ( alpha[ i ] > alpha[ b ] ) b = i;
106 PointRange X = { input_points[ a ], input_points[ b ] };
107 return LatticePolytope( X.cbegin(), X.cend() );
109 if ( basis.size()+1 != dimension )
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();
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
124 Point l = input_points[ 0 ];
125 Point u = input_points[ 0 ];
126 for ( const auto& p : input_points ) {
130 Domain domain( l, u );
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 );
137 // Compute convex 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 );
147 std::cerr << "[ConvexityHelper::computeDegeneratedLatticePolytope]"
148 << "Weird error in hull.setInitialSimplex" << std::endl;
149 return LatticePolytope();
151 bool ok_hull = hull.computeConvexHull( target );
154 std::cerr << "[ConvexityHelper::computeDegeneratedLatticePolytope]"
155 << "Weird error in hull.computeConvexHull" << std::endl;
156 return LatticePolytope();
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 ) {
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 );
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(),
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 )
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;
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 ) );
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++ )
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 );
235 return LatticePolytope( Domain( low, high ),
236 PHS.cbegin(), PHS.cend(),
237 make_minkowski_summable, false );
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 )
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 );
255 for ( auto k = 0; k < P.nbHalfSpaces(); k++ )
257 if ( Op::crossProduct( P.getA( k ), n ) != Vector::zero )
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 )
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++ )
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 );
284 trace.error() << "[ConvexityHelper::computeSegmentFromDegeneratedTriangle] "
285 << "Should never arrive here." << std::endl;
286 return computeSegment( a, a );
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 )
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;
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++ )
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 ] );
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++ )
323 const Vector i = Vector::base( k, d );
324 const Vector e = Op::crossProduct( ab, i );
325 if ( e != Vector::zero )
327 const Integer e_a = e.dot( a );
328 PHS.emplace_back( e, e_a );
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++ )
335 auto V = P.getA( k );
336 if ( V.dot( a ) != V.dot( b ) ) P.setStrict( k );
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 )
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;
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;
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++ )
367 const Vector i = Vector::base( k, d );
368 const Vector e = Op::crossProduct( ab, i );
369 if ( e != Vector::zero )
371 const Integer e_a = e.dot( a );
372 PHS.emplace_back( e, e_a );
375 return LatticePolytope( Domain( low, high ), PHS.cbegin(), PHS.cend(), true, false );
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 )
386 typedef QuickHull< LatticeConvexHullKernel > ConvexHull;
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 )
397 trace.error() << "[ConvexityHelper::computeDegeneratedLatticePolytope]"
398 << " Weird error: found initial full dimensional simplex" << std::endl;
401 if ( basis.size() == 0 )
402 { // Polytope is degenerated to one point (nD)
403 PointRange X = { ref };
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 );
413 for ( std::size_t i = 1; i < input_points.size(); i++ )
415 if ( alpha[ i ] < alpha[ a ] ) a = i;
416 if ( alpha[ i ] > alpha[ b ] ) b = i;
418 PointRange X = { input_points[ a ], input_points[ b ] };
421 if ( basis.size()+1 != dimension )
423 trace.error() << "[ConvexityHelper::computeDegeneratedLatticePolytope] "
424 << "computation of a " << basis.size() << "-dimensional polytope"
425 << " in " << dimension << "D is not implemented yet." << std::endl;
428 // Polytope is d-1 dimensional (nD)
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 ] ) );
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() ) )
451 // if ( alignment > 1e-8 ) break;
454 // if ( i == input_points.size() )
455 // { // 1-dimensional
456 // Index a = 0, b = 0;
457 // for ( i = 1; i < input_points.size(); i++ )
459 // if ( alpha[ i ] < alpha[ a ] ) a = i;
460 // if ( alpha[ i ] > alpha[ b ] ) b = i;
462 // PointRange X( 2 );
463 // X[ 0 ] = input_points[ a ];
464 // X[ 1 ] = input_points[ b ];
467 // // at least 2-dimensional
468 // ASSERT( dimension > 1 );
469 // if ( dimension == 2 )
471 // std::cerr << "[ConvexityHelper::computeDegeneratedConvexHullVertices]"
472 // << " Weird error: found initial full dimensional simplex" << std::endl;
473 // return PointRange();
475 // if ( dimension >= 4 )
477 // std::cerr << "[ConvexityHelper::computeDegeneratedConvexHullVertices]"
478 // << "Degenerated lattice polytope in nD, n >= 4 is not implemented"
480 // return PointRange();
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 )
487 std::cerr << "[ConvexityHelper::computeDegeneratedConvexHullVertices]"
488 << "Weird numerical error, the basis is not full dimensional and there is no independent canonic vector !"
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
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 );
505 std::cerr << "[ConvexityHelper::computeDegeneratedLatticePolytope]"
506 << "Weird error in hull.setInitialSimplex" << std::endl;
509 bool ok_hull = hull.computeConvexHull( target );
512 std::cerr << "[ConvexityHelper::computeDegeneratedLatticePolytope]"
513 << "Weird error in hull.computeConvexHull" << std::endl;
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 )
523 // std::cerr << "[ConvexityHelper::computeDegeneratedConvexHullVertices]"
524 // << "Weird numerical error, u . v != |u| |v| but u x v != 0"
526 // return PointRange();
528 // // Now the set of input points should be full dimensional.
529 // input_points.push_back( input_points[ 0 ] + n );
530 // // Compute convex 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 );
538 // std::cerr << "[ConvexityHelper::computeDegeneratedConvexHullVertices]"
539 // << "Weird error in hull.setInitialSimplex" << std::endl;
540 // return PointRange();
542 // bool ok_hull = hull.computeConvexHull( target );
545 // std::cerr << "[ConvexityHelper::computeDegeneratedConvexHullVertices]"
546 // << "Weird error in hull.computeConvexHull" << std::endl;
547 // return PointRange();
550 // Get convex hull vertices and remove top point
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() )
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 )
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 );
582 Point l = input_points[ 0 ];
583 Point u = input_points[ 0 ];
584 for ( std::size_t i = 1; i < input_points.size(); i++ )
586 const auto& p = input_points[ i ];
590 Domain domain( l, u );
591 // Compute convex 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 ) {
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 );
613 if ( make_minkowski_summable && dimension >= 4 )
614 trace.warning() << "[ConvexityHelper::computeLatticePolytope]"
615 << " Not implemented starting from dimension 4."
617 if ( make_minkowski_summable && dimension == 3 )
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
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
643 detail::BoundedLatticePolytopeSpecializer< dimension, Integer>
644 ::crossProduct( U, E ); // edge on facet 1
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" );
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" );
669 return LatticePolytope( domain, PHS.cbegin(), PHS.cend(),
670 make_minkowski_summable && ( dimension <= 3 ), true );
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 )
681 typedef QuickHull< LatticeConvexHullKernel > ConvexHull;
682 PointRange positions;
684 hull.setInput( input_points, remove_duplicates );
685 bool ok = hull.computeConvexHull( ConvexHull::Status::VerticesCompleted );
688 PointRange Z( input_points );
689 return computeDegeneratedConvexHullVertices( Z );
691 hull.getVertexPositions( positions );
695//-----------------------------------------------------------------------------
696template < int dim, typename TInteger, typename TInternalInteger >
697template < typename TSurfaceMesh >
699DGtal::ConvexityHelper< dim, TInteger, TInternalInteger>::computeConvexHullBoundary
701 const PointRange& input_points,
702 bool remove_duplicates )
704 typedef TSurfaceMesh SurfaceMesh;
705 typedef QuickHull< LatticeConvexHullKernel > ConvexHull;
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 ) {
718 for ( auto i : f ) std::cout << " " << i;
722 mesh = SurfaceMesh( positions.cbegin(), positions.cend(),
723 faces.cbegin(), faces.cend() );
727//-----------------------------------------------------------------------------
728template < int dim, typename TInteger, typename TInternalInteger >
730DGtal::ConvexityHelper< dim, TInteger, TInternalInteger>::computeConvexHullBoundary
731( PolygonalSurface< Point >& polysurf,
732 const PointRange& input_points,
733 bool remove_duplicates )
735 typedef QuickHull< LatticeConvexHullKernel > ConvexHull;
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 ) {
748 for ( auto i : f ) std::cout << " " << i;
752 // build polygonal surface
754 for ( auto p : positions ) polysurf.addVertex( p );
755 for ( auto f : faces ) polysurf.addPolygonalFace( f );
756 return polysurf.build();
759//-----------------------------------------------------------------------------
760template < int dim, typename TInteger, typename TInternalInteger >
762DGtal::ConvexityHelper< dim, TInteger, TInternalInteger>::computeConvexHullCellComplex
763( ConvexCellComplex< Point >& cell_complex,
764 const PointRange& input_points,
765 bool remove_duplicates )
767 typedef QuickHull< LatticeConvexHullKernel > ConvexHull;
768 typedef typename ConvexCellComplex< Point >::FaceRange FaceRange;
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 );
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++ )
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() );
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 );
798//-----------------------------------------------------------------------------
799template < int dim, typename TInteger, typename TInternalInteger >
801DGtal::ConvexityHelper< dim, TInteger, TInternalInteger>::computeDelaunayCellComplex
802( ConvexCellComplex< Point >& cell_complex,
803 const PointRange& input_points,
804 bool remove_duplicates )
806 typedef QuickHull< LatticeDelaunayKernel > Delaunay;
807 typedef typename Delaunay::Ridge Ridge;
808 typedef typename ConvexCellComplex< Point >::FaceRange FaceRange;
811 del.setInput( input_points, remove_duplicates );
812 bool ok = del.computeConvexHull( Delaunay::Status::VerticesCompleted );
813 cell_complex.clear();
814 if ( ! ok ) return false;
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,
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();
841 cell_complex.false_face_cell[ cur_r ] = r.second;
842 current_faces.emplace_back( cur_r, pos );
844 cell_complex.cell_faces.push_back( current_faces );
846 // (3) Takes care of vertex positions
847 del.getVertexPositions( cell_complex.vertex_position );
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,
858 bool remove_duplicates,
859 bool make_minkowski_summable )
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 ) {
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 ) {
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 );
900 if ( make_minkowski_summable && dimension >= 4 )
901 trace.warning() << "[ConvexityHelper::computeRationalPolytope]"
902 << " Not implemented starting from dimension 4."
904 if ( make_minkowski_summable && dimension == 3 )
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
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
930 detail::BoundedRationalPolytopeSpecializer< dimension, Integer>
931 ::crossProduct( U, E ); // edge on facet 1
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" );
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" );
956 return RationalPolytope( denominator, domain, PHS.cbegin(), PHS.cend(),
957 make_minkowski_summable && ( dimension <= 3 ), true );
961//-----------------------------------------------------------------------------
962template < int dim, typename TInteger, typename TInternalInteger >
963template < typename TSurfaceMesh >
965DGtal::ConvexityHelper< dim, TInteger, TInternalInteger>::computeConvexHullBoundary
967 const std::vector< RealPoint >& input_points,
969 bool remove_duplicates )
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() );
987//-----------------------------------------------------------------------------
988template < int dim, typename TInteger, typename TInternalInteger >
990DGtal::ConvexityHelper< dim, TInteger, TInternalInteger>::computeConvexHullBoundary
991( PolygonalSurface< RealPoint >& polysurf,
992 const std::vector< RealPoint >& input_points,
994 bool remove_duplicates )
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
1007 for ( auto p : positions ) polysurf.addVertex( p );
1008 for ( auto f : faces ) polysurf.addPolygonalFace( f );
1009 return polysurf.build();
1012//-----------------------------------------------------------------------------
1013template < int dim, typename TInteger, typename TInternalInteger >
1015DGtal::ConvexityHelper< dim, TInteger, TInternalInteger>::computeConvexHullCellComplex
1016( ConvexCellComplex< RealPoint >& cell_complex,
1017 const std::vector< RealPoint >& input_points,
1019 bool remove_duplicates )
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++ )
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() );
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 );
1053//-----------------------------------------------------------------------------
1054template < int dim, typename TInteger, typename TInternalInteger >
1056DGtal::ConvexityHelper< dim, TInteger, TInternalInteger>::computeDelaunayCellComplex
1057( ConvexCellComplex< RealPoint >& cell_complex,
1058 const std::vector< RealPoint >& input_points,
1060 bool remove_duplicates )
1062 typedef QuickHull< RealDelaunayKernel > Delaunay;
1063 typedef typename Delaunay::Ridge Ridge;
1064 typedef typename ConvexCellComplex< RealPoint >::FaceRange FaceRange;
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;
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,
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();
1097 cell_complex.false_face_cell[ cur_r ] = r.second;
1098 current_faces.emplace_back( cur_r, pos );
1100 cell_complex.cell_faces.push_back( current_faces );
1102 // (3) Takes care of vertex positions
1103 del.getVertexPositions( cell_complex.vertex_position );
1108//-----------------------------------------------------------------------------
1109template < int dim, typename TInteger, typename TInternalInteger >
1110template < typename QHull >
1112DGtal::ConvexityHelper< dim, TInteger, TInternalInteger>::computeFacetAndRidgeVertices
1114 std::vector< IndexRange >& cell_vertices,
1115 std::map< typename QHull::Ridge, Index >& r2f,
1116 std::vector< IndexRange >& face_vertices )
1118 typedef typename QHull::Ridge Ridge;
1120 ASSERT( hull.status() >= QHull::Status::VerticesCompleted
1121 && hull.status() <= QHull::Status::AllCompleted );
1123 // Get cell vertices and sort them
1124 bool ok_fv = hull.getFacetVertices( cell_vertices );
1126 trace.error() << "[ConvexityHelper::computeFacetAndRidgeVertices]"
1127 << " method hull.getFacetVertices failed."
1128 << " Maybe QuickHull was not computed till VerticesCompleted."
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() );
1135 // Count ridges/faces and compute their vertices
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() ) {
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 );
1158///////////////////////////////////////////////////////////////////////////////