DGtal 2.1.0
Loading...
Searching...
No Matches
GenericLatticeConvexHull.h
1
17#pragma once
18
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 >
62 struct GenericLatticeConvexHull;
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();
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 }
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;
132 { // Build the affine basis spanning the convex hull affine space.
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;
190 { // This kernel is not adapted => go to lower dimension
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;
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 {
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 {
256 { // This kernel is not adapted => go to lower dimension
258 }
259 // If polytope is not initialized returns error.
260 if ( polytope.nbHalfSpaces() == 0 ) return -1;
261 return polytope.countInterior();
262 }
263
274 {
276 { // This kernel is not adapted => go to lower dimension
278 }
279 // If polytope is not initialized returns error.
280 if ( polytope.nbHalfSpaces() == 0 ) return -1;
281 return polytope.countBoundary();
282 }
283
300 {
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;
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 {
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 }
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 )
627 if ( ! polytope_computed ) return -1;
628 return generic_computers.count();
629 }
630
653
676
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 }
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)
unsigned int nbHalfSpaces() const
void clear()
Clears the polytope.
Integer countUpTo(Integer max) const
Aim: Implements basic operations that will be used in Point and Vector classes.
UnsignedComponent norm1() const
auto inf(const PointVector< dim, OtherComponent, OtherStorage > &aPoint) const -> decltype(DGtal::inf(*this, aPoint))
Implements the infimum (or greatest lower bound).
Component Coordinate
Type for Point elements.
auto sup(const PointVector< dim, OtherComponent, OtherStorage > &aPoint) const -> decltype(DGtal::sup(*this, aPoint))
Implements the supremum (or least upper bound).
static Self zero
Static const for zero PointVector.
static const Dimension dimension
Copy of the static dimension of the Point/Vector.
std::ostream & error()
SMesh::Index Index
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...
void selfDisplay(std::ostream &out) const
Aim: Utility class to determine the affine geometry of an input set of points. It provides exact resu...
Aim: A half-space specified by a vector N and a constant c. The half-space is the set .
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. , a famous arbitrary dimensional convex hull...
bool polytope_computed
When 'true', the polytope has been computed.
int64_t affine_dimension
The affine dimension of the input set.
bool compute(const std::vector< InputPoint > &input_points)
AffineBasis< OutputPoint > affine_basis
std::vector< OutputPoint > points
the set of input points, indexed as in the input
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.
std::vector< OutputPoint > positions
The positions of the vertices (a subset of the input points).
int debug_level
debug_level from 0:no to 2:verbose
IndexRange vertex2point
The indices of the vertices of the convex hull in the original set.
Integer projected_dilation
The factor of dilation d applied on every projected point coordinates.
Kernel kernel
The main quickhull kernel that is used for convex hull computations.
std::vector< OutputPoint > projected_points
the set of projected input points, indexed as in the input
std::vector< IndexRange > facets
The range giving for each facet the indices of its vertices.
ConvexHullIntegralKernel< dim, TCoordinateInteger, TInternalInteger > Kernel
----------— INTEGER/POINT CONVERSION SERVICES -----------------—
static Integer cast(Integer i)
bool setInput(const std::vector< InputPoint > &input_points, bool remove_duplicates=true)
Definition QuickHull.h:383
bool getFacetVertices(std::vector< IndexRange > &facet_vertices) const
Definition QuickHull.h:667
void clear()
Clears the object.
Definition QuickHull.h:271
bool getVertex2Point(IndexRange &vertex_to_point)
Definition QuickHull.h:633
bool getFacetHalfSpaces(std::vector< HalfSpace > &facet_halfspaces)
Definition QuickHull.h:691
bool computeConvexHull(Status target=Status::VerticesCompleted)
Definition QuickHull.h:461
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
GenericLatticeConvexHull< dim, TCoordinateInteger, TInternalInteger > Computer
LatticePolytope polytope
the polytope corresponding to the convex hull
LowerKernels lower_kernels
the computers of lower dimension
void clear()
Clears the object as if no computations have been made.
QHull hull
the quick hull object that computes the convex hull
bool compute(const std::vector< Size > &I, const std::vector< TInputPoint > &X)
ConvexHullIntegralKernel< K, TCoordinateInteger, TInternalInteger > Kernel
std::vector< Point > proj_points
the projected points, as points in lower dimension
Computer * ptr_gen_qhull
the pointer on the parent computer
std::mt19937 g(rd())
int max(int a, int b)
KSpace K
Domain domain
HyperRectDomain< Space > Domain