DGtal 2.1.0
Loading...
Searching...
No Matches
GenericLatticeConvexHull.h
1
16
17#pragma once
18
30
31#if defined(GenericLatticeConvexHull_RECURSES)
32#error Recursive header files inclusion detected in GenericLatticeConvexHull.h
33#else // defined(GenericLatticeConvexHull_RECURSES)
35#define GenericLatticeConvexHull_RECURSES
36
37#if !defined GenericLatticeConvexHull_h
39#define GenericLatticeConvexHull_h
40
42// Inclusions
43#include <iostream>
44#include <string>
45#include <vector>
46#include <queue>
47#include <set>
48#include "DGtal/base/Common.h"
49#include "DGtal/kernel/SpaceND.h"
50#include "DGtal/geometry/tools/AffineGeometry.h"
51#include "DGtal/geometry/tools/AffineBasis.h"
52#include "DGtal/geometry/tools/QuickHull.h"
53#include "DGtal/geometry/tools/QuickHullKernels.h"
54#include "DGtal/geometry/volumes/BoundedLatticePolytope.h"
55
56namespace DGtal
57{
58 // Forward declaration.
59 template < Dimension dim,
60 typename TCoordinateInteger,
61 typename TInternalInteger >
63
64 namespace detail {
65 template < Dimension dim,
66 typename TCoordinateInteger,
67 typename TInternalInteger,
68 Dimension K >
70 {
72 < K,TCoordinateInteger,TInternalInteger > Kernel;
74 < dim, TCoordinateInteger, TInternalInteger, K-1> LowerKernels;
75 typedef std::size_t Size;
77 typedef typename Point::Coordinate Integer;
82 TCoordinateInteger,
83 TInternalInteger > Computer;
85 static const Dimension dimension = K;
86
90 : ptr_gen_qhull( ptrGenQHull ), lower_kernels( ptrGenQHull ),
91 hull( Kernel(), ptrGenQHull->debug_level )
92 {
93 clear();
94 }
95
97 void clear()
98 {
99 hull.clear();
100 proj_points.clear();
101 polytope.clear();
102 lower_kernels.clear();
103 }
104
108 template <typename TInputPoint>
109 bool compute( const std::vector< Size >& I,
110 const std::vector< TInputPoint >& X )
111 {
112 typedef TInputPoint InputPoint;
113 typedef AffineGeometry< InputPoint > Affine;
114 typedef AffineBasis< InputPoint > Basis;
115 hull.clear();
116 polytope.clear();
117 if ( (I.size()-1) != dimension )
118 { // This kernel is not adapted => go to lower dimension
119 return lower_kernels.compute( I, X );
120 }
121 ptr_gen_qhull->affine_dimension = dimension;
122 auto& points = ptr_gen_qhull->points;
123 auto& ppoints = ptr_gen_qhull->projected_points;
124 auto& positions = ptr_gen_qhull->positions;
125 auto& v2p = ptr_gen_qhull->vertex2point;
126 auto& facets = ptr_gen_qhull->facets;
127 auto& dilation = ptr_gen_qhull->projected_dilation;
128 auto& aff_basis = ptr_gen_qhull->affine_basis;
129
130 Basis basis;
131 if ( dimension != ptr_gen_qhull->dimension )
132 { // Build the affine basis spanning the convex hull affine space.
133 if ( dimension == ptr_gen_qhull->dimension-1 )
134 { // codimension 1
135 // builds orthogonal vector
136 InputPoint normal;
137 functions::getOrthogonalVector( normal, X, I );
138 basis = Basis( X[0], normal, Basis::Type::ECHELON_REDUCED );
139 }
140 else
141 { // Generic case
142 basis = Basis( X, Basis::Type::SHORTEST_ECHELON_REDUCED );
143 }
144 }
145 // Build projected points on affine basis
146 dilation = basis.projectPoints( proj_points, X );
147
148 // Compute convex hull using quickhull.
149 bool ok_input = hull.setInput( proj_points, false );
150 bool ok_hull = hull.computeConvexHull( QHull::Status::VerticesCompleted );
151 if ( ! ok_hull || ! ok_input )
152 {
153 trace.error() << "[GenericLatticeConvexHullComputers::compute]"
154 << " Error in quick hull computation.\n"
155 << "qhull=" << hull << "\n";
156 return false;
157 }
159 points.resize( X.size() );
160 for ( Size i = 0; i < points.size(); i++ )
161 points[ i ] = Affine::transform( X[ i ] );
162 hull.getVertex2Point( v2p );
163 hull.getFacetVertices( facets );
164 positions.resize( v2p.size() );
165 for ( Size i = 0; i < positions.size(); i++ )
166 positions[ i ] = X[ v2p[ i ] ];
167 ppoints.resize( proj_points.size() );
168 for ( Size i = 0; i < ppoints.size(); i++ )
169 {
170 ppoints[ i ] = OutputPoint::zero;
171 for ( Dimension j = 0; j < Point::dimension; j++ )
172 ppoints[ i ][ j ] = proj_points[ i ][ j ];
173 }
174 aff_basis = AffineBasis<OutputPoint>( basis.origin(),
175 basis.basis(),
176 Basis::Type::SHORTEST_ECHELON_REDUCED,
177 true );
178 return true;
179 }
180
184 {
185 typedef typename LatticePolytope::Domain Domain;
186 typedef typename LatticePolytope::HalfSpace PolytopeHalfSpace;
187 typedef typename QHull::HalfSpace ConvexHullHalfSpace;
188 typedef AffineGeometry< Point > Affine;
189 if ( ptr_gen_qhull->affine_dimension != dimension )
190 { // This kernel is not adapted => go to lower dimension
191 return lower_kernels.makePolytope();
192 }
193 // If polytope is already initialized returns.
194 if ( polytope.nbHalfSpaces() > 0 ) return true;
195 // Compute domain
196 Point l = proj_points[ 0 ];
197 Point u = proj_points[ 0 ];
198 for ( std::size_t i = 1; i < proj_points.size(); i++ )
199 {
200 const auto& p = proj_points[ i ];
201 l = l.inf( p );
202 u = u.sup( p );
203 }
204 Domain domain( l, u );
205
206 // Initialize polytope
207 std::vector< ConvexHullHalfSpace > HS;
208 std::vector< PolytopeHalfSpace > PHS;
209 hull.getFacetHalfSpaces( HS );
210 PHS.reserve( HS.size() );
211 for ( auto& H : HS ) {
212 Point N;
213 Integer nu;
214 for ( Dimension i = 0; i < dimension; ++i )
216 ::cast( H.internalNormal()[ i ] );
217 Point Ns = Affine::simplifiedVector( N );
218 Integer g = N.norm1() / Ns.norm1();
219 nu = IntegerConverter< dimension, Integer >::cast( H.internalIntercept() );
220 PHS.emplace_back( Ns, nu / g );
221 }
222 polytope = LatticePolytope( domain, PHS.cbegin(), PHS.cend(), false, true );
223 return polytope.isValid();
224 }
225
234 {
235 if ( ptr_gen_qhull->affine_dimension != dimension )
236 { // This kernel is not adapted => go to lower dimension
237 return lower_kernels.count();
238 }
239 // If polytope is not initialized returns error.
240 if ( polytope.nbHalfSpaces() == 0 ) return -1;
241 return polytope.count();
242 }
243
254 {
255 if ( ptr_gen_qhull->affine_dimension != dimension )
256 { // This kernel is not adapted => go to lower dimension
257 return lower_kernels.countInterior();
258 }
259 // If polytope is not initialized returns error.
260 if ( polytope.nbHalfSpaces() == 0 ) return -1;
261 return polytope.countInterior();
262 }
263
274 {
275 if ( ptr_gen_qhull->affine_dimension != dimension )
276 { // This kernel is not adapted => go to lower dimension
277 return lower_kernels.countBoundary();
278 }
279 // If polytope is not initialized returns error.
280 if ( polytope.nbHalfSpaces() == 0 ) return -1;
281 return polytope.countBoundary();
282 }
283
300 {
301 if ( ptr_gen_qhull->affine_dimension != dimension )
302 { // This kernel is not adapted => go to lower dimension
303 return lower_kernels.countUpTo( max );
304 }
305 // If polytope is not initialized returns error.
306 if ( polytope.nbHalfSpaces() == 0 ) return -1;
307 return polytope.countUpTo( max );
308 }
309
310
314 std::vector< Point > proj_points;
316 };
317
318
319 template < Dimension dim,
320 typename TCoordinateInteger,
321 typename TInternalInteger >
322 struct GenericLatticeConvexHullComputers< dim, TCoordinateInteger, TInternalInteger, 1>
323 {
325 < 1,TCoordinateInteger,TInternalInteger > Kernel;
326 typedef Kernel Type;
327 typedef std::size_t Size;
329 typedef typename Point::Coordinate Integer;
332 TCoordinateInteger,
333 TInternalInteger > Computer;
335 static const Dimension dimension = 1;
336
340 : ptr_gen_qhull( ptrGenQHull )
341 {
342 clear();
343 }
344
346 void clear()
347 {
348 proj_points.clear();
349 }
350
354 template <typename TInputPoint>
355 bool compute( const std::vector< Size >& I,
356 const std::vector< TInputPoint >& X )
357 {
358 typedef TInputPoint InputPoint;
359 typedef AffineGeometry< InputPoint > Affine;
360 typedef AffineBasis< InputPoint > Basis;
361 typedef std::size_t Index;
362
363 auto& aff_dim = ptr_gen_qhull->affine_dimension;
364 auto& points = ptr_gen_qhull->points;
365 auto& ppoints = ptr_gen_qhull->projected_points;
366 auto& positions = ptr_gen_qhull->positions;
367 auto& v2p = ptr_gen_qhull->vertex2point;
368 auto& facets = ptr_gen_qhull->facets;
369 auto& dilation = ptr_gen_qhull->projected_dilation;
370 auto& aff_basis = ptr_gen_qhull->affine_basis;
371 facets.clear(); // no facets
372
373 if ( (I.size()-1) != dimension )
374 { // This kernel is not adapted => lower dimension is either
375 // 0, ie. 1 point, or -1, ie. 0 points.
376 if ( ! X.empty() )
377 {
378 aff_dim = 0;
379 points.resize( X.size() );
380 for ( Size i = 0; i < points.size(); i++ )
381 points[ i ] = Affine::transform( X[ i ] );
382 ppoints = points;
383 positions.resize( 1 );
384 positions[ 0 ] = X[ 0 ];
385 v2p.resize( 1 );
386 v2p[ 0 ] = 0;
387 nb_in_hull = 1;
388 }
389 else
390 {
391 aff_dim = -1;
392 points.clear();
393 ppoints.clear();
394 positions.clear();
395 v2p.clear();
396 nb_in_hull = 0;
397 }
398 return true;
399 }
400 // Generic 1D case.
401 aff_dim = dimension;
402 Basis basis;
403 if ( dimension != ptr_gen_qhull->dimension )
404 {
405 // Build the affine basis spanning the convex hull affine space.
406 basis = Basis( X, Basis::Type::SHORTEST_ECHELON_REDUCED );
407 }
408 // Build projected points on affine basis
409 dilation = basis.projectPoints( proj_points, X );
410 // Compute convex hull by looking at extremal points
411 Index left = 0;
412 Index right = 0;
413 for ( Index i = 1; i < proj_points.size(); i++ )
414 {
415 if ( proj_points[ i ][ 0 ] < proj_points[ left ][ 0 ] )
416 left = i;
417 else if ( proj_points[ i ][ 0 ] > proj_points[ right ][ 0 ] )
418 right = i;
419 }
420 points.resize( X.size() );
421 for ( Size i = 0; i < points.size(); i++ )
422 points[ i ] = Affine::transform( X[ i ] );
423 ppoints.resize( proj_points.size() );
424 for ( Size i = 0; i < ppoints.size(); i++ )
425 {
426 ppoints[ i ] = OutputPoint::zero;
427 ppoints[ i ][ 0 ] = proj_points[ i ][ 0 ];
428 }
429 v2p.resize( 2 );
430 v2p[ 0 ] = left;
431 v2p[ 1 ] = right;
432 positions.resize( 2 );
433 positions[ 0 ] = X[ v2p[ 0 ] ];
434 positions[ 1 ] = X[ v2p[ 1 ] ];
435 aff_basis = AffineBasis<OutputPoint>( basis.origin(),
436 basis.basis(),
437 Basis::Type::SHORTEST_ECHELON_REDUCED,
438 true );
439 auto dx = Affine::transform( points[ v2p[ 1 ] ] - points[ v2p[ 0 ] ] );
440 auto sdx = Affine::simplifiedVector( dx );
441 Integer n = dx.normInfinity() / sdx.normInfinity();
442 nb_in_hull = n+1;
443 return true;
444 }
445
448 {
449 return true;
450 }
451
457 {
458 return nb_in_hull;
459 }
460
465 {
466 return nb_in_hull >= 2 ? nb_in_hull - 2 : 0;
467 }
468
473 {
474 return nb_in_hull >= 2 ? 2 : nb_in_hull;
475 }
476
490 {
491 return nb_in_hull < max ? nb_in_hull : max;
492 }
493
495 std::vector< Point > proj_points;
497 };
498 }
499
501 // template class GenericLatticeConvexHull
502
518 template < Dimension dim,
519 typename TCoordinateInteger = DGtal::int64_t,
520 typename TInternalInteger = DGtal::int64_t >
522 {
524 TCoordinateInteger,
525 TInternalInteger > Kernel;
530 typedef std::size_t Index;
531 typedef std::size_t Size;
532 typedef std::vector< Index > IndexRange;
534 < dim, TCoordinateInteger, TInternalInteger, dim > GenericComputers;
535
537
538
539 // ----------------------- standard services --------------------------
540 public:
543
547 GenericLatticeConvexHull( const Kernel& K_ = Kernel(), int dbg = 0 )
548 : kernel( K_ ), debug_level( dbg ), generic_computers( this )
549 {
550 clear();
551 }
552
554 void clear()
555 {
556 generic_computers.clear();
557 points.clear();
558 projected_points.clear();
559 affine_dimension = -1;
560 positions.clear();
561 facets.clear();
562 vertex2point.clear();
565 polytope_computed = false;
566 }
567
568
569 // -------------------------- Convex hull services ----------------------------
570 public:
571
574
585 template < typename InputPoint >
586 bool compute( const std::vector< InputPoint >& input_points )
587 {
588 // Determine affine dimension of set of input points.
589 typedef AffineGeometry< InputPoint > Affine;
590 std::vector< Size > indices = Affine::affineSubset( input_points );
591 bool ok = generic_computers.compute( indices, input_points );
592 if ( ( ! ok ) || ( debug_level >= 1 ) )
593 {
594 std::cout << "Generic Convex hull #V=" << positions.size()
595 << " #F=" << facets.size() << "\n";
596 for ( Size i = 0; i < facets.size(); i++ )
597 {
598 std::cout << "F_" << i << " = (";
599 for ( auto v : facets[ i ] ) std::cout << " " << v;
600 std::cout << " )\n";
601 }
602 for ( Size i = 0; i < positions.size(); i++ )
603 std::cout << "V_" << i
604 << " pi(x)=" << projected_points[ vertex2point[ i ] ]
605 << " -> x=" << positions[ i ] << "\n";
606 }
607 return ok;
608 }
609
624 {
625 if ( ! polytope_computed )
626 polytope_computed = generic_computers.makePolytope();
627 if ( ! polytope_computed ) return -1;
628 return generic_computers.count();
629 }
630
647 {
648 if ( ! polytope_computed )
649 polytope_computed = generic_computers.makePolytope();
650 if ( ! polytope_computed ) return -1;
651 return generic_computers.countInterior();
652 }
653
670 {
671 if ( ! polytope_computed )
672 polytope_computed = generic_computers.makePolytope();
673 if ( ! polytope_computed ) return -1;
674 return generic_computers.countBoundary();
675 }
676
699 {
700 if ( ! polytope_computed )
701 polytope_computed = generic_computers.makePolytope();
702 if ( ! polytope_computed ) return -1;
703 return generic_computers.countUpTo( max );
704 }
705
706
708
709 // ----------------------- Interface --------------------------------------
710 public:
713
716 void selfDisplay ( std::ostream & out ) const
717 {
718 out << "[GenericLatticeConvexHull"
719 << " dim=" << dimension
720 << " #in=" << points.size()
721 << " aff_dim=" << affine_dimension
722 << " #V=" << positions.size()
723 << " #F=" << facets.size()
724 << "]";
725 }
726
729 bool isValid() const
730 {
731 return affine_dimension >= 0;
732 }
733
734
735 // ------------------------ public datas --------------------------
736 public:
739
740 public:
748
750 std::vector< OutputPoint > points;
752 std::vector< OutputPoint > projected_points;
756 std::vector< OutputPoint > positions;
758 std::vector< IndexRange > facets;
768 bool polytope_computed { false };
770
771 };
772
788 template < Dimension dim,
789 typename TCoordinateInteger,
790 typename TInternalInteger >
791 std::ostream&
792 operator<< ( std::ostream & out,
794 {
795 object.selfDisplay( out );
796 return out;
797 }
798
799} // namespace DGtal
800
802// Includes inline functions.
803// //
805
806#endif // !defined GenericLatticeConvexHull_h
807
808#undef GenericLatticeConvexHull_RECURSES
809#endif // else defined(GenericLatticeConvexHull_RECURSES)
ClosedIntegerHalfPlane< Space > HalfSpace
UnsignedComponent norm1() const
auto inf(const PointVector< dim, OtherComponent, OtherStorage > &aPoint) const -> decltype(DGtal::inf(*this, aPoint))
Implements the infimum (or greatest lower bound).
auto sup(const PointVector< dim, OtherComponent, OtherStorage > &aPoint) const -> decltype(DGtal::sup(*this, aPoint))
Implements the supremum (or least upper bound).
detail namespace gathers internal classes and functions.
static void getOrthogonalVector(TPoint &w, const std::vector< TInputPoint > &X, const TIndexRange &I, const double tolerance=1e-12)
DGtal is the top-level namespace which contains all DGtal functions and types.
std::int64_t int64_t
signed 94-bit integer.
Definition BasicTypes.h:73
std::ostream & operator<<(std::ostream &out, const ClosedIntegerHalfPlane< TSpace > &object)
DGtal::uint32_t Dimension
Definition Common.h:119
Trace trace
Aim: Utility class to determine the affine geometry of an input set of points. It provides exact resu...
Aim: Utility class to determine the affine geometry of an input set of points. It provides exact resu...
Aim: a geometric kernel to compute the convex hull of digital points with integer-only arithmetic.
Aim: Implements the quickhull algorithm by Barber et al. barber1996, a famous arbitrary dimensional c...
bool compute(const std::vector< InputPoint > &input_points)
GenericLatticeConvexHull(const Kernel &K_=Kernel(), int dbg=0)
detail::GenericLatticeConvexHullComputers< dim, TCoordinateInteger, TInternalInteger, dim > GenericComputers
void selfDisplay(std::ostream &out) const
void clear()
Clears the object as if no computations have been made.
ConvexHullIntegralKernel< dim, TCoordinateInteger, TInternalInteger > Kernel
----------— INTEGER/POINT CONVERSION SERVICES -----------------—
static Integer cast(Integer i)
Kernel::HalfSpace HalfSpace
Definition QuickHull.h:151
std::vector< Point > proj_points
the projected points, as points in lower dimension
bool compute(const std::vector< Size > &I, const std::vector< TInputPoint > &X)
detail::GenericLatticeConvexHullComputers< dim, TCoordinateInteger, TInternalInteger, K-1 > LowerKernels
void clear()
Clears the object as if no computations have been made.
bool compute(const std::vector< Size > &I, const std::vector< TInputPoint > &X)
ConvexHullIntegralKernel< K, TCoordinateInteger, TInternalInteger > Kernel
std::mt19937 g(rd())
int max(int a, int b)
KSpace K
Domain domain