DGtal 2.1.0
Loading...
Searching...
No Matches
QuickHull.h
1
17#pragma once
18
31#if defined(QuickHull_RECURSES)
32#error Recursive header files inclusion detected in QuickHull.h
33#else // defined(QuickHull_RECURSES)
35#define QuickHull_RECURSES
36
37#if !defined QuickHull_h
39#define QuickHull_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/base/Clock.h"
50#include "DGtal/geometry/tools/AffineGeometry.h"
51#include "DGtal/geometry/tools/QuickHullKernels.h"
52
53namespace DGtal
54{
56 // template class QuickHull
57
139 template < typename TKernel >
141 {
142 typedef TKernel Kernel;
143 typedef typename Kernel::CoordinatePoint Point;
144 typedef typename Kernel::CoordinateVector Vector;
145 typedef typename Kernel::CoordinateScalar Scalar;
146 typedef typename Kernel::InternalScalar InternalScalar;
147 typedef std::size_t Index;
148 typedef std::size_t Size;
149 BOOST_STATIC_ASSERT(( Point::dimension == Vector::dimension ));
150 typedef std::vector< Index > IndexRange;
151 typedef typename Kernel::HalfSpace HalfSpace;
152 typedef typename Kernel::CombinatorialPlaneSimplex CombinatorialPlaneSimplex;
153 static const Size dimension = Point::dimension;
154
156 enum { UNASSIGNED = (Index) -1 };
157
161 struct Facet {
167
168 Facet() = default;
169 Facet( const Facet& ) = default;
170 Facet( Facet&& ) = default;
171 Facet& operator=( Facet&& ) = default;
172 Facet& operator=( const Facet& ) = default;
173 Facet( const HalfSpace& aH, Index b )
174 : H( aH ), below( b ) {}
175
176 void clear()
177 {
178 H = HalfSpace();
179 neighbors.clear();
180 outside_set.clear();
181 on_set.clear();
183 }
185 {
186 const auto it = std::find( on_set.cbegin(), on_set.cend(), p );
187 if ( it == on_set.cend() ) on_set.push_back( p );
188 }
189 void display( std::ostream& out ) const
190 {
191 const auto N = H.internalNormal();
192 out << "[Facet iN=(" << N[0];
193 for ( Dimension i = 1; i < N.dimension; i++ ) out << "," << N[ i ];
194 out << ") c=" << H.internalIntercept() << " b=" << below << " n={";
195 for ( auto&& n : neighbors ) out << " " << n;
196 out << " } #out=" << outside_set.size();
197 out << " on={";
198 for ( auto&& n : on_set ) out << " " << n;
199 out << " }]" << std::endl;
200 }
201
203 {
204 const auto it = std::find( neighbors.cbegin(), neighbors.cend(), n );
205 if ( it == neighbors.cend() ) neighbors.push_back( n );
206 }
208 {
209 auto it = std::find( neighbors.begin(), neighbors.end(), n );
210 if ( it != neighbors.end() ) {
211 std::swap( *it, neighbors.back() );
212 neighbors.pop_back();
213 }
214 }
215 void swap( Facet& other )
216 {
217 if ( this != &other ) {
218 std::swap( H, other.H );
219 neighbors.swap ( other.neighbors );
220 outside_set.swap( other.outside_set );
221 on_set.swap ( other.on_set );
222 std::swap( below, other.below );
223 }
224 }
226 {
227 Size M;
228 M += neighbors.capacity() * sizeof( Index );
229 M += outside_set.capacity() * sizeof( Index );
230 M += on_set.capacity() * sizeof( Index );
231 return M;
232 }
233 };
234
237 typedef std::pair< Index, Index > Ridge;
238
240 enum class Status {
241 Uninitialized = 0,
242 InputInitialized = 1,
243 SimplexCompleted = 2,
244 FacetsCompleted = 3,
246 AllCompleted = 5,
248 InvalidRidge = 11,
250 };
251
252 // ----------------------- standard services --------------------------
253 public:
256
260 QuickHull( const Kernel& K_ = Kernel(), int dbg = 0 )
261 : kernel( K_ ), debug_level( dbg ), myStatus( Status::Uninitialized )
262 {}
263
268 { return myStatus; }
269
271 void clear()
272 {
274 points.clear();
275 processed_points.clear();
276 input2comp.clear();
277 comp2input.clear();
278 assignment.clear();
279 facets.clear();
280 deleted_facets.clear();
281 p2v.clear();
282 v2p.clear();
283 timings.clear();
284 }
285
286
288 Size memory() const
289 {
290 // int debug_level;
291 Size M = sizeof( kernel ) + sizeof( int );
292 // std::vector< Point > points;
293 M += sizeof( std::vector< Point > )
294 + points.capacity() * sizeof( Point );
295 M += sizeof( std::vector< Index > )
296 + processed_points.capacity() * sizeof( Index );
297 M += sizeof( std::vector< Index > )
298 + input2comp.capacity() * sizeof( Index );
299 M += sizeof( std::vector< Index > )
300 + comp2input.capacity() * sizeof( Index );
301 // std::vector< Index > assignment;
302 M += sizeof( std::vector< Index > )
303 + assignment.capacity() * sizeof( Index );
304 // std::vector< Facet > facets;
305 M += sizeof( std::vector< Facet > )
306 + facets.capacity() * sizeof( Facet );
307 for ( const auto& f : facets ) M += f.variableMemory();
308 // std::set< Index > deleted_facets;
309 M += sizeof( std::set< Index > )
310 + deleted_facets.size() * ( sizeof( Index ) + 2*sizeof(Index*) );
311 // IndexRange p2v;
312 M += sizeof( std::vector< Index > )
313 + p2v.capacity() * sizeof( Index );
314 // IndexRange v2p;
315 M += sizeof( std::vector< Index > )
316 + v2p.capacity() * sizeof( Index );
317 // std::vector< double > timings;
318 M += sizeof( std::vector< double > )
319 + timings.capacity() * sizeof( double );
320 return M;
321 }
322
324 Size nbPoints() const
325 { return points.size(); }
326
329 Size nbFacets() const
330 {
333 return facets.size();
334 }
335
339 {
342 return v2p.size();
343 }
344
348 {
351 return nb_finite_facets;
352 }
353
357 {
360 return nb_infinite_facets;
361 }
362
364 // -------------------------- Convex hull services ----------------------------
365 public:
368
382 template < typename InputPoint >
383 bool setInput( const std::vector< InputPoint >& input_points,
384 bool remove_duplicates = true )
385 {
386 Clock tic;
387 tic.startClock();
388 clear();
389 timings.clear();
390 kernel.makeInput( points, input2comp, comp2input,
391 input_points, remove_duplicates );
392 timings.push_back( tic.stopClock() );
393 if ( points.size() <= dimension ) {
395 return false;
396 }
398 return true;
399 }
400
411 bool setInitialSimplex( const IndexRange& full_splx )
412 {
413 if ( status() != Status::InputInitialized ) return false;
414 if ( full_splx.size() != dimension + 1 )
415 {
416 trace.error() << "[QuickHull::setInitialSimplex]"
417 << " not a full dimensional simplex" << std::endl;
419 return false;
420 }
422 for ( Index j = 0; j < dimension; ++j )
423 splx[ j ] = full_splx[ j ];
424 const auto H = kernel.compute( points, splx, full_splx.back() );
425 const auto volume = kernel.volume( H, points[ full_splx.back() ] );
426 if ( volume > 0 )
427 return computeSimplexConfiguration( full_splx );
429 return false;
430 }
431
433 // -------------------------- Convex hull services ----------------------------
434 public:
437
462 {
463 if ( target < Status::InputInitialized || target > Status::AllCompleted )
464 return false;
465 Clock tic;
467 { // Initialization
468 tic.startClock();
469 bool ok1 = computeInitialSimplex();
470 timings.push_back( tic.stopClock() );
471 if ( ! ok1 ) return false;
472 if ( status() == target ) return true;
473 }
475 { // Computes facets
476 tic.startClock();
477 bool ok2 = computeFacets();
478 timings.push_back( tic.stopClock() );
479 if ( ! ok2 ) return false;
480 if ( status() == target ) return true;
481 }
483 { // Computes vertices
484 tic.startClock();
485 bool ok3 = computeVertices();
486 timings.push_back( tic.stopClock() );
487 if ( ! ok3 ) return false;
488 if ( status() == target ) return true;
489 }
490 if ( target == Status::AllCompleted
492 { // for now, Status::VerticesCompleted and
493 // Status::AllCompleted are the same.
495 return true;
496 }
497 return false;
498 }
499
506 {
507 const auto full_simplex = pickInitialSimplex();
508 if ( full_simplex.empty() ) {
510 return false;
511 }
512 return computeSimplexConfiguration( full_simplex );
513 }
514
525 {
526 if ( status() != Status::SimplexCompleted ) return false;
527 std::queue< Index > Q;
528 for ( Index fi = 0; fi < facets.size(); ++fi )
529 Q.push( fi );
530 Index n = 0;
531 while ( processFacet( Q ) ) {
532 if ( debug_level >= 1 )
533 trace.info() << "---- Iteration " << n++ << " #Q=" << Q.size() << std::endl;
534 }
535 cleanFacets();
536 if ( debug_level >= 2 ) {
537 trace.info() << ".... #facets=" << facets.size()
538 << " #deleted=" << deleted_facets.size() << std::endl;
539 }
541 return true;
542 }
543
555 {
556 static const int MAX_NB_VPF = 10 * dimension;
557 if ( status() != Status::FacetsCompleted ) return false;
558
559 // Renumber infinite facets in case of Delaunay triangulation computation.
561
562 // Builds the maps v2p: vertex -> point, and p2v : point -> vertex.
563 facet_counter = IndexRange( MAX_NB_VPF, 0 );
564 v2p.clear();
565 p2v.resize( points.size() );
566 std::vector< IndexRange > p2f( points.size() );
567 for ( Index f = 0; f < facets.size(); ++f ) {
568 for ( auto&& p : facets[ f ].on_set ) p2f[ p ].push_back( f );
569 }
570
571 // vertices belong to at least d facets
572 Index v = 0;
573 for ( Index p = 0; p < points.size(); ++p ) {
574 const auto nbf = p2f[ p ].size();
575 facet_counter[ std::min( (int) nbf, MAX_NB_VPF-1 ) ] += 1;
576 if ( nbf >= dimension ) {
577 v2p.push_back( p );
578 p2v[ p ] = v++;
579 }
580 else p2v[ p ] = UNASSIGNED;
581 }
582
583 // Display debug informations
584 if ( debug_level >= 1 ) {
585 trace.info() << "#vertices=" << v2p.size() << " #facets=" << facets.size()
586 << std::endl;
587 trace.info() << "#inc_facets/point= ";
588 for ( auto n : facet_counter ) trace.info() << n << " ";
589 trace.info() << std::endl;
590 }
592 return true;
593 }
594
595
597 // -------------------------- Output services ----------------------------
598 public:
601
612 template < typename OutputPoint >
613 bool getVertexPositions( std::vector< OutputPoint >& vertex_positions )
614 {
615 vertex_positions.clear();
617 && status() <= Status::AllCompleted ) ) return false;
618 vertex_positions.resize( v2p.size() );
619 for ( Index i = 0; i < v2p.size(); i++ ) {
620 kernel.convertPointTo( points[ v2p[ i ] ], vertex_positions[ i ] );
621 }
622 return true;
623 }
624
633 bool getVertex2Point( IndexRange& vertex_to_point )
634 {
635 vertex_to_point.clear();
637 && status() <= Status::AllCompleted ) ) return false;
638 vertex_to_point = v2p;
639 return true;
640 }
641
652 bool getPoint2Vertex( IndexRange& point_to_vertex )
653 {
654 point_to_vertex.clear();
656 && status() <= Status::AllCompleted ) ) return false;
657 point_to_vertex = p2v;
658 return true;
659 }
660
667 bool getFacetVertices( std::vector< IndexRange >& facet_vertices ) const
668 {
669 facet_vertices.clear();
671 && status() <= Status::AllCompleted ) ) return false;
672 facet_vertices.reserve( nbFacets() );
673 for ( Index f = 0; f < nbFacets(); ++f ) {
674 IndexRange ofacet = orientedFacetPoints( f );
675 for ( auto& v : ofacet ) v = p2v[ v ];
676 facet_vertices.push_back( ofacet );
677 }
678 return true;
679 }
680
691 bool getFacetHalfSpaces( std::vector< HalfSpace >& facet_halfspaces )
692 {
693 facet_halfspaces.clear();
694 if ( ! ( status() >= Status::FacetsCompleted
695 && status() <= Status::AllCompleted ) ) return false;
696 facet_halfspaces.reserve( nbFacets() );
697 for ( Index f = 0; f < nbFacets(); ++f ) {
698 facet_halfspaces.push_back( facets[ f ].H );
699 }
700 return true;
701 }
702
703
705 // -------------------------- Check hull services ----------------------------
706 public:
709
716 bool check()
717 {
718 bool ok = true;
720 trace.warning() << "[Quickhull::check] invalid status="
721 << (int)status() << std::endl;
722 ok = false;
723 }
724 if ( processed_points.size() != points.size() ) {
725 trace.warning() << "[Quickhull::check] not all points processed: "
726 << processed_points.size() << " / " << points.size()
727 << std::endl;
728 ok = false;
729 }
730 if ( ! checkHull() ) {
731 trace.warning() << "[Quickhull::check] Check hull is invalid. "
732 << std::endl;
733 ok = false;
734 }
735 if ( ! checkFacets() ) {
736 trace.warning() << "[Quickhull::check] Check facets is invalid. "
737 << std::endl;
738 ok = false;
739 }
740 return ok;
741 }
742
745 {
746 Size nb = 0;
747 Size nbok = 0;
748 for ( Index f = 0; f < facets.size(); ++f )
749 if ( deleted_facets.count( f ) == 0 ) {
750 bool ok = checkFacet( f );
751 nbok += ok ? 1 : 0;
752 nb += 1;
753 }
754 return nb == nbok;
755 }
756
764 {
765 Index nbok = 0;
766 // for ( Index v = 0; v < points.size(); ++v ) {
767 for ( auto v : processed_points ) {
768 bool ok = true;
769 for ( Index f = 0; f < facets.size(); ++f )
770 if ( deleted_facets.count( f ) == 0 ) {
771 if ( above( facets[ f ], points[ v ] ) ) {
772 ok = false;
773 trace.error() << "- bad vertex " << v << " " << points[ v ]
774 << " dist="
775 << ( kernel.height( facets[ f ].H, points[ v ] ) )
776 << std::endl;
777 break;
778 }
779 }
780 nbok += ok ? 1 : 0;
781 }
782 if ( debug_level >= 2 ) {
783 trace.info() << nbok << "/"
784 << processed_points.size() << " vertices inside convex hull."
785 << std::endl;
786 }
788 return nbok == processed_points.size();
789 }
790
792 // ------------------------ public datas --------------------------
793 public:
796
797 public:
799 mutable Kernel kernel;
803 std::vector< Point > points;
813 std::vector< Index > assignment;
815 std::vector< Facet > facets;
817 std::set< Index > deleted_facets;
827 std::vector< double > timings;
829 std::vector< Size > facet_counter;
830
832 // ------------------------ protected datas --------------------------
833 protected:
836
841
843 // --------------------- protected services --------------------------
844 protected:
847
851 InternalScalar height( const Facet& F, const Point& p ) const
852 { return kernel.height( F.H, p ); }
853
857 bool above( const Facet& F, const Point& p ) const
858 { return kernel.above( F.H, p ); }
859
863 bool aboveOrOn( const Facet& F, const Point& p ) const
864 { return kernel.aboveOrOn( F.H, p ); }
865
869 bool on( const Facet& F, const Point& p ) const
870 { return kernel.on( F.H, p ); }
871
875 {
876 if ( deleted_facets.empty() ) return;
877 IndexRange renumbering( facets.size() );
878 Index i = 0;
879 Index j = 0;
880 for ( auto& l : renumbering ) {
881 if ( ! deleted_facets.count( j ) ) l = i++;
882 else l = UNASSIGNED;
883 j++;
884 }
885 const Index nf = facets.size() - deleted_facets.size();
886 deleted_facets.clear();
887 for ( Index f = 0; f < facets.size(); f++ )
888 if ( ( renumbering[ f ] != UNASSIGNED ) && ( f != renumbering[ f ] ) )
889 facets[ renumbering[ f ] ] = facets[ f ];
890 facets.resize( nf );
891 for ( auto& F : facets ) {
892 for ( auto& N : F.neighbors ) {
893 if ( renumbering[ N ] == UNASSIGNED )
894 trace.error() << "Invalid deleted neighboring facet." << std::endl;
895 else N = renumbering[ N ];
896 }
897 }
898 }
899
903 {
904 nb_finite_facets = facets.size();
906 if ( ! kernel.hasInfiniteFacets() ) return;
907 IndexRange renumbering( facets.size() );
908 Index i = 0;
909 Index k = facets.size();
910 Index j = 0;
911 for ( auto& l : renumbering ) {
912 if ( ! kernel.isHalfSpaceFacetInfinite( facets[ j ].H ) ) l = i++;
913 else l = --k;
914 j++;
915 }
916 if ( i != k )
917 trace.error() << "[Quickhull::renumberInfiniteFacets]"
918 << " Error renumbering infinite facets "
919 << " up finite=" << i << " low infinite=" << k << std::endl;
920 std::vector< Facet > new_facets( facets.size() );
921 for ( Index f = 0; f < facets.size(); f++ )
922 new_facets[ renumbering[ f ] ].swap( facets[ f ] );
923 facets.swap( new_facets );
924 for ( auto& F : facets ) {
925 for ( auto& N : F.neighbors ) {
926 N = renumbering[ N ];
927 }
928 }
929 // Assign correct number of facets.
932 }
933
934
945 bool processFacet( std::queue< Index >& Q )
946 {
947 // If Q empty, we are done
948 if ( Q.empty() ) return false;
949 Index F = Q.front();
950 Q.pop();
951 // If F is already deleted, proceed to next in queue.
952 if ( deleted_facets.count( F ) ) return true;
953 // Take car of current facet.
954 const Facet& facet = facets[ F ];
955 if ( debug_level >= 3 ) {
956 trace.info() << "---------------------------------------------" << std::endl;
957 trace.info() << "---- ACTIVE FACETS---------------------------" << std::endl;
958 bool ok = true;
959 for ( Index i = 0; i < facets.size(); i++ )
960 if ( ! deleted_facets.count( i ) ) {
961 trace.info() << "- facet " << i << " ";
962 facets[ i ].display( trace.info() );
963 ok = ok && checkFacet( i );
964 }
965 if ( ! ok ) { // should not happen.
967 return false;
968 }
969 }
970 if ( debug_level >= 2 ) {
971 trace.info() << "---------------------------------------------" << std::endl;
972 trace.info() << "Processing facet " << F << " ";
973 facet.display( trace.info() );
974 }
975 if ( facet.outside_set.empty() ) return true;
976 // Selects furthest vertex
977 Index furthest_v = facet.outside_set[ 0 ];
978 auto furthest_h = height( facet, points[ furthest_v ] );
979 for ( Index v = 1; v < facet.outside_set.size(); v++ ) {
980 auto h = height( facet, points[ facet.outside_set[ v ] ] );
981 if ( h > furthest_h ) {
982 furthest_h = h;
983 furthest_v = facet.outside_set[ v ];
984 }
985 }
986 const Point& p = points[ furthest_v ];
987 // Extracts Visible facets V and Horizon Ridges H
988 std::vector< Index > V; // visible facets
989 std::set< Index > M; // marked facets (are in E or were in E)
990 std::queue< Index > E; // queue to extract visible facets
991 std::vector< Ridge > H; // visible facets
992 E.push ( F );
993 M.insert( F );
994 while ( ! E.empty() ) {
995 Index G = E.front(); E.pop();
996 V.push_back( G );
997 for ( auto& N : facets[ G ].neighbors ) {
998 if ( aboveOrOn( facets[ N ], p ) ) {
999 if ( M.count( N ) ) continue;
1000 E.push( N );
1001 } else {
1002 H.push_back( { G, N } );
1003 }
1004 M.insert( N );
1005 }
1006 } // while ( ! E.empty() )
1007 if ( debug_level >= 1 ) {
1008 trace.info() << "#Visible=" << V.size() << " #Horizon=" << H.size()
1009 << " furthest_v=" << furthest_v << std::endl;
1010 }
1011 // Create new facets
1012 IndexRange new_facets;
1013 // For each ridge R in H
1014 for ( Index i = 0; i < H.size(); i++ )
1015 {
1016 // Create a new facet from R and p
1017 IndexRange ridge = pointsOnRidge( H[ i ] );
1018 if ( debug_level >= 3 ) {
1019 trace.info() << "Ridge (" << H[i].first << "," << H[i].second << ") = {";
1020 for ( auto&& r : ridge ) trace.info() << " " << r;
1021 trace.info() << " } furthest_v=" << furthest_v << std::endl;
1022 }
1023 IndexRange base( 1 + ridge.size() );
1024 Index j = 0;
1025 base[ j++ ] = furthest_v;
1026 for ( auto&& v : ridge ) base[ j++ ] = v;
1027 if ( j < dimension ) {
1028 trace.error() << "Bad ridge between " << std::endl
1029 << "- facet " << H[i].first << " ";
1030 facets[ H[i].first ].display( trace.error() );
1031 trace.error() << "- facet " << H[i].second << " ";
1032 facets[ H[i].second ].display( trace.error() );
1033 }
1034 Index nf = newFacet();
1035 new_facets.push_back( nf );
1036 facets[ nf ] = makeFacet( base, facets[ H[i].first ].below );
1037 facets[ nf ].on_set = IndexRange { base.cbegin(), base.cend() };
1038 std::sort( facets[ nf ].on_set.begin(), facets[ nf ].on_set.end() );
1039 makeNeighbors( nf, H[ i ].second );
1040 if ( debug_level >= 3 ) {
1041 trace.info() << "* New facet " << nf << " ";
1042 facets[ nf ].display( trace.info() );
1043 }
1044 // Checks that the facet is not parallel to another in the Horizon
1045 for ( Index k = 0; k < new_facets.size() - 1; k++ )
1046 if ( areFacetsParallel( new_facets[ k ], nf ) ) {
1047 if ( debug_level >= 1 ) {
1048 trace.info() << "Facets " << new_facets[ k ] << " and " << nf
1049 << " are parallel => merge." << std::endl;
1050 }
1051 mergeParallelFacets( new_facets[ k ], nf );
1052 new_facets.pop_back();
1053 deleteFacet( nf );
1054 if ( debug_level >= 3 ) {
1055 facets[ new_facets[ k ] ].display( trace.info() );
1056 }
1057 }
1058 }
1059 // For each new facet
1060 for ( Index i = 0; i < new_facets.size(); i++ )
1061 { // link the new facet to its neighbors
1062 for ( Index j = i + 1; j < new_facets.size(); j++ )
1063 {
1064 const Index nfi = new_facets[ i ];
1065 const Index nfj = new_facets[ j ];
1066 if ( areFacetsNeighbor( nfi, nfj ) )
1067 makeNeighbors( nfi, nfj );
1068 }
1069 }
1070 // Extracts all outside points from visible facets V
1071 IndexRange outside_pts;
1072 for ( auto&& vf : V ) {
1073 for ( auto&& v : facets[ vf ].outside_set ) {
1074 if ( v != furthest_v ) {
1075 outside_pts.push_back( v );
1076 assignment[ v ] = UNASSIGNED;
1077 }
1078 }
1079 }
1080 // For each new facet F'
1081 for ( Index i = 0; i < new_facets.size(); i++ ) {
1082 Facet& Fp = facets[ new_facets[ i ] ];
1083 Index max_j = outside_pts.size();
1084 for ( Index j = 0; j < max_j; ) {
1085 const Index v = outside_pts[ j ];
1086 if ( above( Fp, points[ v ] ) ) {
1087 Fp.outside_set.push_back( v );
1088 assignment[ v ] = new_facets[ i ];
1089 outside_pts[ j ] = outside_pts.back();
1090 outside_pts.pop_back();
1091 max_j--;
1092 } else j++;
1093 }
1094 if ( debug_level >= 3 ) {
1095 trace.info() << "- New facet " << new_facets[ i ] << " ";
1096 Fp.display( trace.info() );
1097 }
1098 }
1099 // Update processed points
1100 processed_points.push_back( furthest_v );
1101 for ( auto v : outside_pts ) processed_points.push_back( v );
1102
1103 // Delete the facets in V
1104 for ( auto&& v : V ) {
1105 if ( debug_level >= 2 ) {
1106 trace.info() << "Delete facet " << v << " ";
1107 facets[ v ].display( trace.info() );
1108 }
1109 deleteFacet( v );
1110 }
1111
1112 // Add new facets to queue
1113 for ( Index i = 0; i < new_facets.size(); i++ )
1114 Q.push( new_facets[ i ] );
1115 if ( debug_level >= 1 ) {
1116 trace.info() << "#facets=" << facets.size()
1117 << " #deleted=" << deleted_facets.size() << std::endl;
1118 }
1119
1120 // Checks that everything is ok.
1121 if ( debug_level >= 1 ) {
1122 trace.info() << "[CHECK INVARIANT] " << processed_points.size()
1123 << " / " << points.size() << " points processed." << std::endl;
1124 bool okh = checkHull();
1125 if ( ! okh )
1126 trace.error() << "[computeFacet] Invalid convex hull" << std::endl;
1127 bool okf = checkFacets();
1128 if ( ! okf )
1129 trace.error() << "[computeFacet] Invalid facets" << std::endl;
1130 if ( ! ( okh && okf ) ) myStatus = Status::InvalidConvexHull;
1131 }
1132
1133 return status() == Status::SimplexCompleted;
1134 }
1135
1137 bool checkFacet( Index f ) const
1138 {
1139 const Facet& F = facets[ f ];
1140 bool ok = F.on_set.size() >= dimension;
1141 for ( auto v : F.on_set )
1142 if ( ! on( F, points[ v ] ) ) {
1143 trace.error() << "[QuickHull::checkFacet( " << f
1144 << ") Invalid 'on' vertex " << v << std::endl;
1145 ok = false;
1146 }
1147 if ( F.neighbors.size() < dimension ) {
1148 trace.error() << "[QuickHull::checkFacet( " << f
1149 << ") Not enough neighbors " << F.neighbors.size() << std::endl;
1150 ok = false;
1151 }
1152 for ( auto nf : F.neighbors )
1153 if ( ! areFacetsNeighbor( f, nf ) ) {
1154 trace.error() << "[QuickHull::checkFacet( " << f
1155 << ") Invalid neighbor " << nf << std::endl;
1156 ok = false;
1157 }
1158 if ( aboveOrOn( F, points[ F.below ] ) ) {
1159 trace.error() << "[QuickHull::checkFacet( " << f
1160 << ") Bad below point " << F.below << std::endl;
1161 ok = false;
1162 }
1163 for ( auto ov : F.outside_set )
1164 if ( ! above( F, points[ ov ] ) ) {
1165 trace.error() << "[QuickHull::checkFacet( " << f
1166 << ") Bad outside point " << ov << std::endl;
1167 ok = false;
1168 }
1169 for ( auto && v : F.on_set ) {
1170 Size n = 0;
1171 for ( auto&& N : facets[ f ].neighbors )
1172 if ( on( facets[ N ], points[ v ] ) ) n += 1;
1173 if ( n < dimension-1 ) {
1174 trace.error() << "[QuickHull::checkFacet( " << f << ") 'on' point " << v
1175 << " is a vertex of " << n << " facets "
1176 << "(should be >= " << dimension-1 << ")" << std::endl;
1177 ok = false;
1178 }
1179 }
1180 return ok;
1181 }
1182
1185 {
1186 // SLightly faster to postpone deletion of intermediate facets.
1187 const Index f = facets.size();
1188 facets.push_back( Facet() );
1189 return f;
1190 }
1191
1195 {
1196 for ( auto n : facets[ f ].neighbors )
1197 facets[ n ].subNeighbor( f );
1198 deleted_facets.insert( f );
1199 facets[ f ].clear();
1200 }
1201
1205 void makeNeighbors( const Index if1, const Index if2 )
1206 {
1207 facets[ if1 ].addNeighbor( if2 );
1208 facets[ if2 ].addNeighbor( if1 );
1209 }
1210
1214 void unmakeNeighbors( const Index if1, const Index if2 )
1215 {
1216 facets[ if1 ].subNeighbor( if2 );
1217 facets[ if2 ].subNeighbor( if1 );
1218 }
1219
1226 Index mergeParallelFacets( const Index if1, const Index if2 )
1227 {
1228 Facet& f1 = facets[ if1 ];
1229 Facet& f2 = facets[ if2 ];
1230 std::copy( f2.outside_set.cbegin(), f2.outside_set.cend(),
1231 std::back_inserter( f1.outside_set ) );
1232 IndexRange merge_idx;
1233 std::set_union( f1.on_set.cbegin(), f1.on_set.cend(),
1234 f2.on_set.cbegin(), f2.on_set.cend(),
1235 std::back_inserter( merge_idx ) );
1236 f1.on_set.swap( merge_idx );
1237 for ( auto && nf2 : f2.neighbors ) {
1238 if ( nf2 == if1 ) continue;
1239 facets[ nf2 ].subNeighbor( if2 );
1240 makeNeighbors( if1, nf2 );
1241 }
1242 return if2;
1243 }
1244
1249 bool areFacetsParallel( const Index if1, const Index if2 ) const
1250 {
1251 ASSERT( if1 != if2 );
1252 const Facet& f1 = facets[ if1 ];
1253 const Facet& f2 = facets[ if2 ];
1254 if ( kernel.equal( f1.H, f2.H ) ) return true;
1255 // Need to check if one N is a multiple of the other.
1256 for ( auto&& v : f1.on_set )
1257 if ( ! on( f2, points[ v ] ) ) return false;
1258 return true;
1259 }
1260
1268 bool areFacetsNeighbor( const Index if1, const Index if2 ) const
1269 {
1270 const Facet& f1 = facets[ if1 ];
1271 const Facet& f2 = facets[ if2 ];
1272 Index nb = 0;
1273 for ( Index i1 = 0, i2 = 0; i1 < f1.on_set.size() && i2 < f2.on_set.size(); )
1274 {
1275 if ( f1.on_set[ i1 ] == f2.on_set[ i2 ] ) { nb++; i1++; i2++; }
1276 else if ( f1.on_set[ i1 ] < f2.on_set[ i2 ] ) i1++;
1277 else i2++;
1278 }
1279 return nb >= ( dimension - 1 );
1280 }
1281
1293 Facet makeFacet( const IndexRange& base, Index below ) const
1294 {
1296 for ( Size i = 0; i < dimension; i++ ) simplex[ i ] = base[ i ];
1297 auto plane = kernel.compute( points, simplex, below );
1298 return Facet( plane, below );
1299 }
1300
1304 {
1305 // Slightly faster to look at points instead at true geometry.
1306 IndexRange result;
1307 const Facet& f1 = facets[ R.first ];
1308 const Facet& f2 = facets[ R.second ];
1309 std::set_intersection( f1.on_set.cbegin(), f1.on_set.cend(),
1310 f2.on_set.cbegin(), f2.on_set.cend(),
1311 std::back_inserter( result ) );
1312 return result;
1313 }
1314
1325 {
1326 const Facet& F = facets[ f ];
1327 IndexRange result = F.on_set;
1328 // Sort a facet such that its points, taken in order, have
1329 // always the same orientation of the facet. More precisely,
1330 // facets span a `dimension-1` vector space. There are thus
1331 // dimension-2 fixed points, and the last ones (at least two)
1332 // may be reordered.
1334 for ( Dimension k = 0; k < dimension-2; k++ )
1335 splx[ k ] = result[ k ];
1336 // std::cout << "Orienting face " << f << " (";
1337 // for ( auto i : result ) std::cout << " " << i;
1338 std::sort( result.begin()+dimension-2, result.end(),
1339 [&] ( Index i, Index j )
1340 {
1341 splx[ dimension-2 ] = i;
1342 splx[ dimension-1 ] = j;
1343 const auto H = kernel.compute( points, splx );
1344 const auto orient = kernel.dot( F.H, H );
1345 return orient > 0;
1346 } );
1347 for ( Dimension k = 0; k < dimension; k++ )
1348 splx[ k ] = result[ k ];
1349 // const auto H = kernel.compute( points, splx );
1350 // const auto orient = kernel.dot( F.H, H );
1351 // std::cout << " ) => (";
1352 // for ( auto i : result ) std::cout << " " << i;
1353 // std::cout << " ) N=" << H.internalNormal() << " dot=" << orient << "\n";
1354 return result;
1355 }
1356
1357
1363 {
1364 auto & on_set = facets[ f ].on_set;
1365 for ( Index i = 0; i < on_set.size(); )
1366 {
1367 Index v = on_set[ i ];
1368 Size n = 0;
1369 for ( auto&& N : facets[ f ].neighbors )
1370 if ( on( facets[ N ], points[ v ] ) ) n += 1;
1371 if ( n >= dimension-1 ) i++;
1372 else {
1373 on_set[ i ] = on_set.back();
1374 on_set.pop_back();
1375 }
1376 }
1377 std::sort( on_set.begin(), on_set.end() );
1378 }
1379
1383 {
1384 const Size nb = points.size();
1385 if ( nb < dimension + 1 ) return IndexRange();
1386 IndexRange best = pickIntegers( dimension + 1, nb );
1388 for ( Index j = 0; j < dimension; ++j ) splx[ j ] = best[ j ];
1389 const auto first_H = kernel.compute( points, splx, best.back() );
1390 auto best_volume = kernel.volume ( first_H, points[ best.back() ] );
1391 // Randomized approach to find full dimensional simplex.
1392 //
1393 // Let a be the proportion of full dimensional simplices among
1394 // all simplices of the input points. Let p be the desired
1395 // probability to find a full dimensional simplex after t tries.
1396 // Then t >= log(1-p)/log(1-a)
1397 // For a=0.25, p=99% we found 16 tries.
1398 // For a=0.20, p=99% we found 20 tries.
1399 const Size nbtries = std::min( (Size) 10, 1 + nb / 10 );
1400 // const Size max_nbtries = std::max( (Size) 10, 2 * nb );
1401 const Size max_nbtries = 20;
1402 for ( Size i = 0; i < max_nbtries; i++ )
1403 {
1404 IndexRange tmp = pickIntegers( dimension + 1, nb );
1405 for ( Index j = 0; j < dimension; ++j ) splx[ j ] = tmp[ j ];
1406 const auto tmp_H = kernel.compute( points, splx, tmp.back() );
1407 const auto tmp_volume = kernel.volume ( tmp_H, points[ tmp.back() ] );
1408 if ( best_volume < tmp_volume ) {
1409 if ( debug_level >= 1 ) {
1410 trace.info() << "(" << i << ")"
1411 << " new_volume = " << tmp_volume
1412 << " > " << best_volume << std::endl;
1413 }
1414 best = tmp;
1415 best_volume = tmp_volume;
1416 }
1417 if ( i >= nbtries && best_volume > 0 )
1418 return best;
1419 }
1420 // If not found, we adopt a deterministic algorithm based on Gauss reduction.
1422 if ( debug_level >= 1 )
1423 trace.info() << "[QuickHull::pickInitialSimplex] #affine subset = " << best.size() << std::endl;
1424 return ( best.size() == (dimension+1) ) ? best : IndexRange();
1425 }
1426
1430 static IndexRange pickIntegers( const Size d, const Size n )
1431 {
1432 IndexRange result( d );
1433 bool distinct = false;
1434 while ( ! distinct )
1435 {
1436 distinct = true;
1437 for ( Index i = 0; i < d; i++ ) result[ i ] = rand() % n;
1438 std::sort( result.begin(), result.end() );
1439 for ( Index i = 1; distinct && i < d; i++ )
1440 distinct = result[ i-1 ] != result[ i ];
1441 }
1442 return result;
1443 }
1444
1453 bool computeSimplexConfiguration( const IndexRange& full_simplex )
1454 {
1455 assignment = std::vector< Index >( points.size(), UNASSIGNED );
1456 facets.resize( dimension + 1 );
1457 deleted_facets.clear();
1458 for ( Index j = 0; j < full_simplex.size(); ++j )
1459 {
1460 IndexRange lsimplex( dimension );
1461 IndexRange isimplex( dimension );
1462 Index s = 0;
1463 for ( Index i = 0; i <= dimension; i++ )
1464 if ( i != j ) {
1465 lsimplex[ s ] = i;
1466 isimplex[ s ] = full_simplex[ i ];
1467 s++;
1468 }
1469 facets[ j ] = makeFacet( isimplex, full_simplex[ j ] );
1470 facets[ j ].neighbors = lsimplex;
1471 for ( auto&& v : isimplex ) facets[ j ].on_set.push_back( v );
1472 std::sort( facets[ j ].on_set.begin(), facets[ j ].on_set.end() );
1473 }
1474 // List of unassigned vertices
1475 for ( Index fi = 0; fi < facets.size(); ++fi ) {
1476 Facet& f = facets[ fi ];
1477 for ( Index v = 0; v < points.size(); v++ )
1478 {
1479 if ( assignment[ v ] == UNASSIGNED && above( f, points[ v ] ) ) {
1480 f.outside_set.push_back( v );
1481 assignment[ v ] = fi;
1482 }
1483 }
1484 }
1485 for ( Index v = 0; v < points.size(); v++ )
1486 if ( assignment[ v ] == UNASSIGNED )
1487 processed_points.push_back( v );
1488
1489 // Display some information
1490 if ( debug_level >= 2 ) {
1491 for ( auto&& f : facets ) f.display( trace.info() );
1492 }
1494 if ( debug_level >= 1 ) {
1495 trace.info() << "[CHECK INVARIANT] " << processed_points.size()
1496 << " / " << points.size() << " points processed." << std::endl;
1497 bool okh = checkHull();
1498 if ( ! okh )
1499 trace.error() << "[computeInitialSimplex] Invalid convex hull" << std::endl;
1500 bool okf = checkFacets();
1501 if ( ! okf )
1502 trace.error() << "[computeInitialSimplex] Invalid facets" << std::endl;
1503 if ( ! ( okh && okf ) ) myStatus = Status::InvalidConvexHull;
1504 }
1505 return status() == Status::SimplexCompleted;
1506 }
1507
1509 // ----------------------- Interface --------------------------------------
1510 public:
1513
1518 void selfDisplay ( std::ostream & out ) const
1519 {
1520 out << "[QuickHull " << dimension << "D"
1521 // << " status=" << status()
1522 << " #P=" << nbPoints();
1524 out << " #F=" << nbFacets();
1526 out << " #V=" << nbVertices();
1527 out << "]";
1528 // if ( status() >= Status::FacetsCompleted && status() <= Status::AllCompleted
1529 // && nbFacets() == 24 ) {
1530 // for ( auto f : facets ) f.display( out );
1531 // for ( auto v : v2p ) out << points[ v2p[ v ] ] << std::endl;
1532 // }
1533 }
1534
1539 bool isValid() const
1540 {
1541 return status() >= Status::Uninitialized
1543 }
1545
1546 };
1547
1555 template < typename TKernel >
1556 std::ostream&
1557 operator<< ( std::ostream & out,
1558 const QuickHull< TKernel > & object )
1559 {
1560 object.selfDisplay( out );
1561 return out;
1562 }
1563
1564} // namespace DGtal
1565
1567// Includes inline functions.
1568// //
1570
1571#endif // !defined QuickHull_h
1572
1573#undef QuickHull_RECURSES
1574#endif // else defined(QuickHull_RECURSES)
1575
void tic()
Starts timer.
void startClock()
std::ostream & warning()
std::ostream & error()
std::ostream & info()
DGtal is the top-level namespace which contains all DGtal functions and types.
void swap(UnorderedSetByBlock< Key, TSplitter, Hash, KeyEqual, UnorderedMapAllocator > &s1, UnorderedSetByBlock< Key, TSplitter, Hash, KeyEqual, UnorderedMapAllocator > &s2) noexcept
std::ostream & operator<<(std::ostream &out, const ClosedIntegerHalfPlane< TSpace > &object)
DGtal::uint32_t Dimension
Definition Common.h:119
Trace trace
static std::vector< Size > affineSubset(const std::vector< TInputPoint > &X, const double tolerance=1e-12)
void subNeighbor(Index n)
Definition QuickHull.h:207
Facet & operator=(Facet &&)=default
void addPointOn(Index p)
Definition QuickHull.h:184
Facet(const HalfSpace &aH, Index b)
Definition QuickHull.h:173
void display(std::ostream &out) const
Definition QuickHull.h:189
Facet(Facet &&)=default
void addNeighbor(Index n)
Definition QuickHull.h:202
Index below
index of point that is below this facet
Definition QuickHull.h:166
Facet(const Facet &)=default
HalfSpace H
the facet geometry
Definition QuickHull.h:162
IndexRange outside_set
outside set, i.e. points above this facet
Definition QuickHull.h:164
void swap(Facet &other)
Definition QuickHull.h:215
IndexRange neighbors
neighbor facets
Definition QuickHull.h:163
Facet & operator=(const Facet &)=default
IndexRange on_set
on set, i.e. points on this facet, sorted
Definition QuickHull.h:165
Size variableMemory() const
Definition QuickHull.h:225
Aim: Implements the quickhull algorithm by Barber et al. , a famous arbitrary dimensional convex hull...
Definition QuickHull.h:141
bool computeInitialSimplex()
Definition QuickHull.h:505
bool computeFacets()
Definition QuickHull.h:524
void unmakeNeighbors(const Index if1, const Index if2)
Definition QuickHull.h:1214
bool setInput(const std::vector< InputPoint > &input_points, bool remove_duplicates=true)
Definition QuickHull.h:383
Kernel::CoordinateVector Vector
Definition QuickHull.h:144
bool areFacetsNeighbor(const Index if1, const Index if2) const
Definition QuickHull.h:1268
Kernel kernel
Kernel that is duplicated for computing facet geometries.
Definition QuickHull.h:799
int debug_level
debug_level from 0:no to 2
Definition QuickHull.h:801
bool setInitialSimplex(const IndexRange &full_splx)
Definition QuickHull.h:411
Kernel::InternalScalar InternalScalar
Definition QuickHull.h:146
std::vector< Index > IndexRange
Definition QuickHull.h:150
Size nb_infinite_facets
Number of infinite facets (!= 0 only for specific kernels)
Definition QuickHull.h:825
bool on(const Facet &F, const Point &p) const
Definition QuickHull.h:869
void filterVerticesOnFacet(const Index f)
Definition QuickHull.h:1362
IndexRange processed_points
Points already processed (and within the convex hull).
Definition QuickHull.h:811
QuickHull(const Kernel &K_=Kernel(), int dbg=0)
Definition QuickHull.h:260
bool checkFacet(Index f) const
Definition QuickHull.h:1137
BOOST_STATIC_ASSERT((Point::dimension==Vector::dimension))
Facet makeFacet(const IndexRange &base, Index below) const
Definition QuickHull.h:1293
Size nb_finite_facets
Number of finite facets.
Definition QuickHull.h:823
IndexRange pointsOnRidge(const Ridge &R) const
Definition QuickHull.h:1303
IndexRange v2p
vertex index -> point index
Definition QuickHull.h:821
Kernel::CombinatorialPlaneSimplex CombinatorialPlaneSimplex
Definition QuickHull.h:152
bool computeSimplexConfiguration(const IndexRange &full_simplex)
Definition QuickHull.h:1453
std::vector< Facet > facets
the current set of facets.
Definition QuickHull.h:815
bool getFacetVertices(std::vector< IndexRange > &facet_vertices) const
Definition QuickHull.h:667
std::size_t Index
Definition QuickHull.h:147
std::pair< Index, Index > Ridge
Definition QuickHull.h:237
Kernel::CoordinatePoint Point
Definition QuickHull.h:143
std::vector< Point > points
the set of points, indexed as in the array.
Definition QuickHull.h:803
IndexRange input2comp
Definition QuickHull.h:806
std::set< Index > deleted_facets
set of deleted facets
Definition QuickHull.h:817
void clear()
Clears the object.
Definition QuickHull.h:271
Size nbFiniteFacets() const
Definition QuickHull.h:347
bool areFacetsParallel(const Index if1, const Index if2) const
Definition QuickHull.h:1249
bool computeVertices()
Definition QuickHull.h:554
bool getVertex2Point(IndexRange &vertex_to_point)
Definition QuickHull.h:633
IndexRange pickInitialSimplex() const
Definition QuickHull.h:1382
bool isValid() const
Definition QuickHull.h:1539
IndexRange orientedFacetPoints(Index f) const
Definition QuickHull.h:1324
std::vector< Size > facet_counter
Counts the number of facets with a given number of vertices.
Definition QuickHull.h:829
Status status() const
Definition QuickHull.h:267
bool processFacet(std::queue< Index > &Q)
Definition QuickHull.h:945
static IndexRange pickIntegers(const Size d, const Size n)
Definition QuickHull.h:1430
Size nbVertices() const
Definition QuickHull.h:338
std::size_t Size
Definition QuickHull.h:148
void deleteFacet(Index f)
Definition QuickHull.h:1194
bool getVertexPositions(std::vector< OutputPoint > &vertex_positions)
Definition QuickHull.h:613
std::vector< Index > assignment
assignment of points to facets
Definition QuickHull.h:813
static const Size dimension
Definition QuickHull.h:153
Status
Represents the status of a QuickHull object.
Definition QuickHull.h:240
@ InvalidRidge
Error: some ridge is not consistent (probably integer overflow).
@ InvalidConvexHull
Error: the convex hull does not contain all its points (probably integer overflow).
@ SimplexCompleted
An initial full-dimensional simplex has been found. QuickHull core algorithm can start.
@ FacetsCompleted
All facets of the convex hull are identified.
@ NotFullDimensional
Error: the initial simplex is not full dimensional.
@ VerticesCompleted
All vertices of the convex hull are determined.
@ InputInitialized
A range of input points has been given to QuickHull.
@ AllCompleted
Same as VerticesCompleted.
@ Uninitialized
QuickHull is empty and has just been instantiated.
Size nbInfiniteFacets() const
Definition QuickHull.h:356
InternalScalar height(const Facet &F, const Point &p) const
Definition QuickHull.h:851
bool getFacetHalfSpaces(std::vector< HalfSpace > &facet_halfspaces)
Definition QuickHull.h:691
Index mergeParallelFacets(const Index if1, const Index if2)
Definition QuickHull.h:1226
void makeNeighbors(const Index if1, const Index if2)
Definition QuickHull.h:1205
void selfDisplay(std::ostream &out) const
Definition QuickHull.h:1518
bool aboveOrOn(const Facet &F, const Point &p) const
Definition QuickHull.h:863
IndexRange p2v
point index -> vertex index (or UNASSIGNED)
Definition QuickHull.h:819
Size memory() const
Definition QuickHull.h:288
Size nbFacets() const
Definition QuickHull.h:329
bool getPoint2Vertex(IndexRange &point_to_vertex)
Definition QuickHull.h:652
void renumberInfiniteFacets()
Definition QuickHull.h:902
bool computeConvexHull(Status target=Status::VerticesCompleted)
Definition QuickHull.h:461
Kernel::HalfSpace HalfSpace
Definition QuickHull.h:151
Kernel::CoordinateScalar Scalar
Definition QuickHull.h:145
IndexRange comp2input
Definition QuickHull.h:809
std::vector< double > timings
Timings of the different phases: 0: init, 1: facets, 2: vertices.
Definition QuickHull.h:827
bool above(const Facet &F, const Point &p) const
Definition QuickHull.h:857
Size nbPoints() const
Definition QuickHull.h:324