31#if defined(QuickHull_RECURSES)
32#error Recursive header files inclusion detected in QuickHull.h
35#define QuickHull_RECURSES
37#if !defined QuickHull_h
48#include "DGtal/base/Common.h"
49#include "DGtal/base/Clock.h"
50#include "DGtal/geometry/tools/QuickHullKernels.h"
138 template <
typename TKernel >
142 typedef typename Kernel::CoordinatePoint
Point;
143 typedef typename Kernel::CoordinateVector
Vector;
144 typedef typename Kernel::CoordinateScalar
Scalar;
185 const auto it = std::find(
on_set.cbegin(),
on_set.cend(), p );
190 const auto N =
H.internalNormal();
191 out <<
"[Facet iN=(" << N[0];
192 for (
Dimension i = 1; i < N.dimension; i++ ) out <<
"," << N[ i ];
193 out <<
") c=" <<
H.internalIntercept() <<
" b=" <<
below <<
" n={";
194 for (
auto&& n :
neighbors ) out <<
" " << n;
197 for (
auto&& n :
on_set ) out <<
" " << n;
198 out <<
" }]" << std::endl;
216 if (
this != &other ) {
217 std::swap(
H, other.
H );
236 typedef std::pair< Index, Index >
Ridge;
292 M +=
sizeof( std::vector< Point > )
293 + points.capacity() *
sizeof(
Point );
294 M +=
sizeof( std::vector< Index > )
296 M +=
sizeof( std::vector< Index > )
298 M +=
sizeof( std::vector< Index > )
301 M +=
sizeof( std::vector< Index > )
304 M +=
sizeof( std::vector< Facet > )
306 for (
const auto& f :
facets ) M += f.variableMemory();
308 M +=
sizeof( std::set< Index > )
311 M +=
sizeof( std::vector< Index > )
314 M +=
sizeof( std::vector< Index > )
317 M +=
sizeof( std::vector< double > )
318 +
timings.capacity() *
sizeof( double );
324 {
return points.size(); }
381 template <
typename InputPo
int >
382 bool setInput(
const std::vector< InputPoint >& input_points,
383 bool remove_duplicates =
true )
390 input_points, remove_duplicates );
415 trace.
error() <<
"[QuickHull::setInitialSimplex]"
416 <<
" not a full dimensional simplex" << std::endl;
422 splx[ j ] = full_splx[ j ];
423 const auto H =
kernel.compute( points, splx, full_splx.back() );
424 const auto volume =
kernel.volume(
H, points[ full_splx.back() ] );
470 if ( ! ok1 )
return false;
471 if (
status() == target )
return true;
478 if ( ! ok2 )
return false;
479 if (
status() == target )
return true;
486 if ( ! ok3 )
return false;
487 if (
status() == target )
return true;
507 if ( full_simplex.empty() ) {
526 std::queue< Index > Q;
532 trace.
info() <<
"---- Iteration " << n++ <<
" #Q=" << Q.size() << std::endl;
555 static const int MAX_NB_VPF = 10 *
dimension;
564 p2v.resize( points.size() );
565 std::vector< IndexRange > p2f( points.size() );
567 for (
auto&& p :
facets[ f ].on_set ) p2f[ p ].push_back( f );
572 for (
Index p = 0; p < points.size(); ++p ) {
573 const auto nbf = p2f[ p ].size();
611 template <
typename OutputPo
int >
614 vertex_positions.clear();
617 vertex_positions.resize(
v2p.size() );
618 for (
Index i = 0; i <
v2p.size(); i++ ) {
619 kernel.convertPointTo( points[
v2p[ i ] ], vertex_positions[ i ] );
634 vertex_to_point.clear();
637 vertex_to_point =
v2p;
653 point_to_vertex.clear();
656 point_to_vertex =
p2v;
668 facet_vertices.clear();
671 facet_vertices.reserve(
nbFacets() );
674 for (
auto& v : ofacet ) v =
p2v[ v ];
675 facet_vertices.push_back( ofacet );
692 facet_halfspaces.clear();
695 facet_halfspaces.reserve(
nbFacets() );
697 facet_halfspaces.push_back(
facets[ f ].
H );
720 << (int)
status() << std::endl;
724 trace.
warning() <<
"[Quickhull::check] not all points processed: "
730 trace.
warning() <<
"[Quickhull::check] Check hull is invalid. "
735 trace.
warning() <<
"[Quickhull::check] Check facets is invalid. "
772 trace.
error() <<
"- bad vertex " << v <<
" " << points[ v ]
851 {
return kernel.height( F.
H, p ); }
857 {
return kernel.above( F.
H, p ); }
863 {
return kernel.aboveOrOn( F.
H, p ); }
869 {
return kernel.on( F.
H, p ); }
879 for (
auto& l : renumbering ) {
887 if ( ( renumbering[ f ] !=
UNASSIGNED ) && ( f != renumbering[ f ] ) )
890 for (
auto& F :
facets ) {
891 for (
auto& N : F.neighbors ) {
893 trace.
error() <<
"Invalid deleted neighboring facet." << std::endl;
894 else N = renumbering[ N ];
903 if ( !
kernel.hasInfiniteFacets() )
return;
907 for (
auto& l : renumbering ) {
908 if ( !
kernel.isHalfSpaceFacetInfinite(
facets[ j ].H ) ) l = i++;
914 if ( ( renumbering[ f ] !=
UNASSIGNED ) && ( f != renumbering[ f ] ) )
917 for (
auto& F :
facets ) {
918 for (
auto& N : F.neighbors ) {
919 N = renumbering[ N ];
930 if ( !
kernel.hasInfiniteFacets() )
return;
935 for (
auto& l : renumbering ) {
936 if ( !
kernel.isHalfSpaceFacetInfinite(
facets[ j ].H ) ) l = i++;
941 trace.
error() <<
"[Quickhull::renumberInfiniteFacets]"
942 <<
" Error renumbering infinite facets "
943 <<
" up finite=" << i <<
" low infinite=" << k << std::endl;
944 std::vector< Facet > new_facets(
facets.size() );
946 new_facets[ renumbering[ f ] ].
swap(
facets[ f ] );
947 facets.swap( new_facets );
948 for (
auto& F :
facets ) {
949 for (
auto& N : F.neighbors ) {
950 N = renumbering[ N ];
972 if ( Q.empty() )
return false;
980 trace.
info() <<
"---------------------------------------------" << std::endl;
981 trace.
info() <<
"---- ACTIVE FACETS---------------------------" << std::endl;
995 trace.
info() <<
"---------------------------------------------" << std::endl;
996 trace.
info() <<
"Processing facet " << F <<
" ";
1002 auto furthest_h = height( facet, points[ furthest_v ] );
1004 auto h = height( facet, points[ facet.
outside_set[ v ] ] );
1005 if ( h > furthest_h ) {
1010 const Point& p = points[ furthest_v ];
1012 std::vector< Index > V;
1013 std::set< Index > M;
1014 std::queue< Index > E;
1015 std::vector< Ridge >
H;
1018 while ( ! E.empty() ) {
1019 Index G = E.front(); E.pop();
1021 for (
auto& N :
facets[ G ].neighbors ) {
1023 if ( M.count( N ) )
continue;
1026 H.push_back( { G, N } );
1032 trace.
info() <<
"#Visible=" << V.size() <<
" #Horizon=" <<
H.size()
1033 <<
" furthest_v=" << furthest_v << std::endl;
1038 for (
Index i = 0; i <
H.size(); i++ )
1043 trace.
info() <<
"Ridge (" <<
H[i].first <<
"," <<
H[i].second <<
") = {";
1044 for (
auto&& r : ridge )
trace.
info() <<
" " << r;
1045 trace.
info() <<
" } furthest_v=" << furthest_v << std::endl;
1049 base[ j++ ] = furthest_v;
1050 for (
auto&& v : ridge ) base[ j++ ] = v;
1052 trace.
error() <<
"Bad ridge between " << std::endl
1053 <<
"- facet " <<
H[i].first <<
" ";
1055 trace.
error() <<
"- facet " <<
H[i].second <<
" ";
1059 new_facets.push_back( nf );
1062 std::sort(
facets[ nf ].on_set.begin(),
facets[ nf ].on_set.end() );
1065 trace.
info() <<
"* New facet " << nf <<
" ";
1069 for (
Index k = 0; k < new_facets.size() - 1; k++ )
1072 trace.
info() <<
"Facets " << new_facets[ k ] <<
" and " << nf
1073 <<
" are parallel => merge." << std::endl;
1076 new_facets.pop_back();
1084 for (
Index i = 0; i < new_facets.size(); i++ )
1086 for (
Index j = i + 1; j < new_facets.size(); j++ )
1088 const Index nfi = new_facets[ i ];
1089 const Index nfj = new_facets[ j ];
1096 for (
auto&& vf : V ) {
1097 for (
auto&& v :
facets[ vf ].outside_set ) {
1098 if ( v != furthest_v ) {
1099 outside_pts.push_back( v );
1105 for (
Index i = 0; i < new_facets.size(); i++ ) {
1107 Index max_j = outside_pts.size();
1108 for (
Index j = 0; j < max_j; ) {
1109 const Index v = outside_pts[ j ];
1110 if (
above( Fp, points[ v ] ) ) {
1113 outside_pts[ j ] = outside_pts.back();
1114 outside_pts.pop_back();
1119 trace.
info() <<
"- New facet " << new_facets[ i ] <<
" ";
1128 for (
auto&& v : V ) {
1130 trace.
info() <<
"Delete facet " << v <<
" ";
1137 for (
Index i = 0; i < new_facets.size(); i++ )
1138 Q.push( new_facets[ i ] );
1147 <<
" / " << points.size() <<
" points processed." << std::endl;
1150 trace.
error() <<
"[computeFacet] Invalid convex hull" << std::endl;
1153 trace.
error() <<
"[computeFacet] Invalid facets" << std::endl;
1165 for (
auto v : F.
on_set )
1166 if ( !
on( F, points[ v ] ) ) {
1167 trace.
error() <<
"[QuickHull::checkFacet( " << f
1168 <<
") Invalid 'on' vertex " << v << std::endl;
1172 trace.
error() <<
"[QuickHull::checkFacet( " << f
1173 <<
") Not enough neighbors " << F.
neighbors.size() << std::endl;
1178 trace.
error() <<
"[QuickHull::checkFacet( " << f
1179 <<
") Invalid neighbor " << nf << std::endl;
1183 trace.
error() <<
"[QuickHull::checkFacet( " << f
1184 <<
") Bad below point " << F.
below << std::endl;
1188 if ( !
above( F, points[ ov ] ) ) {
1189 trace.
error() <<
"[QuickHull::checkFacet( " << f
1190 <<
") Bad outside point " << ov << std::endl;
1193 for (
auto && v : F.
on_set ) {
1195 for (
auto&& N :
facets[ f ].neighbors )
1196 if (
on(
facets[ N ], points[ v ] ) ) n += 1;
1198 trace.
error() <<
"[QuickHull::checkFacet( " << f <<
") 'on' point " << v
1199 <<
" is a vertex of " << n <<
" facets "
1200 <<
"(should be >= " <<
dimension-1 <<
")" << std::endl;
1220 for (
auto n :
facets[ f ].neighbors )
1221 facets[ n ].subNeighbor( f );
1231 facets[ if1 ].addNeighbor( if2 );
1232 facets[ if2 ].addNeighbor( if1 );
1240 facets[ if1 ].subNeighbor( if2 );
1241 facets[ if2 ].subNeighbor( if1 );
1259 std::back_inserter( merge_idx ) );
1260 f1.
on_set.swap( merge_idx );
1262 if ( nf2 == if1 )
continue;
1263 facets[ nf2 ].subNeighbor( if2 );
1275 ASSERT( if1 != if2 );
1278 if (
kernel.equal( f1.
H, f2.
H ) )
return true;
1280 for (
auto&& v : f1.
on_set )
1281 if ( !
on( f2, points[ v ] ) )
return false;
1299 if ( f1.
on_set[ i1 ] == f2.
on_set[ i2 ] ) { nb++; i1++; i2++; }
1320 for (
Size i = 0; i <
dimension; i++ ) simplex[ i ] = base[ i ];
1321 auto plane =
kernel.compute( points, simplex, below );
1322 return Facet( plane, below );
1333 std::set_intersection( f1.
on_set.cbegin(), f1.
on_set.cend(),
1335 std::back_inserter( result ) );
1359 splx[ k ] = result[ k ];
1360 std::sort( result.begin()+
dimension-2, result.end(),
1363 splx[ dimension-2 ] = i;
1364 splx[ dimension-1 ] = j;
1365 const auto H = kernel.compute( points, splx );
1366 const auto orient = kernel.dot( F.H, H );
1379 auto & on_set =
facets[ f ].on_set;
1380 for (
Index i = 0; i < on_set.size(); )
1382 Index v = on_set[ i ];
1384 for (
auto&& N :
facets[ f ].neighbors )
1385 if (
on(
facets[ N ], points[ v ] ) ) n += 1;
1388 on_set[ i ] = on_set.back();
1392 std::sort( on_set.begin(), on_set.end() );
1399 const Size nb = points.size();
1404 const auto first_H =
kernel.compute( points, splx, best.back() );
1405 auto best_volume =
kernel.volume ( first_H, points[ best.back() ] );
1406 const Size nbtries = std::min( (
Size) 10, 1 + nb / 10 );
1407 const Size max_nbtries = std::max( (
Size) 10, 2 * nb );
1408 for (
Size i = 0; i < max_nbtries; i++ )
1412 const auto tmp_H =
kernel.compute( points, splx, tmp.back() );
1413 const auto tmp_volume =
kernel.volume ( tmp_H, points[ tmp.back() ] );
1414 if ( best_volume < tmp_volume ) {
1417 <<
" new_volume = " << tmp_volume
1418 <<
" > " << best_volume << std::endl;
1421 best_volume = tmp_volume;
1423 if ( i >= nbtries && best_volume > 0 )
return best;
1434 bool distinct =
false;
1435 while ( ! distinct )
1438 for (
Index i = 0; i < d; i++ ) result[ i ] = rand() % n;
1439 std::sort( result.begin(), result.end() );
1440 for (
Index i = 1; distinct && i < d; i++ )
1441 distinct = result[ i-1 ] != result[ i ];
1459 for (
Index j = 0; j < full_simplex.size(); ++j )
1467 isimplex[ s ] = full_simplex[ i ];
1471 facets[ j ].neighbors = lsimplex;
1472 for (
auto&& v : isimplex )
facets[ j ].on_set.push_back( v );
1473 std::sort(
facets[ j ].on_set.begin(),
facets[ j ].on_set.end() );
1478 for (
Index v = 0; v < points.size(); v++ )
1481 f.outside_set.push_back( v );
1486 for (
Index v = 0; v < points.size(); v++ )
1497 <<
" / " << points.size() <<
" points processed." << std::endl;
1500 trace.
error() <<
"[computeInitialSimplex] Invalid convex hull" << std::endl;
1503 trace.
error() <<
"[computeInitialSimplex] Invalid facets" << std::endl;
1521 out <<
"[QuickHull " <<
dimension <<
"D"
1531 for (
auto f :
facets ) f.display( out );
1532 for (
auto v :
v2p ) out << points[
v2p[ v ] ] << std::endl;
1556 template <
typename TKernel >
1561 object.selfDisplay( out );
1574#undef QuickHull_RECURSES
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
void subNeighbor(Index n)
Facet & operator=(Facet &&)=default
Facet(const HalfSpace &aH, Index b)
void display(std::ostream &out) const
void addNeighbor(Index n)
Index below
index of point that is below this facet
Facet(const Facet &)=default
HalfSpace H
the facet geometry
IndexRange outside_set
outside set, i.e. points above this facet
IndexRange neighbors
neighbor facets
Facet & operator=(const Facet &)=default
IndexRange on_set
on set, i.e. points on this facet, sorted
Size variableMemory() const
Aim: Implements the quickhull algorithm by Barber et al. , a famous arbitrary dimensional convex hull...
bool computeInitialSimplex()
void unmakeNeighbors(const Index if1, const Index if2)
bool setInput(const std::vector< InputPoint > &input_points, bool remove_duplicates=true)
Kernel::CoordinateVector Vector
bool areFacetsNeighbor(const Index if1, const Index if2) const
QuickHull(const Kernel &K=Kernel(), int dbg=0)
Kernel kernel
Kernel that is duplicated for computing facet geometries.
int debug_level
debug_level from 0:no to 2
bool setInitialSimplex(const IndexRange &full_splx)
Kernel::InternalScalar InternalScalar
std::vector< Index > IndexRange
Size nb_infinite_facets
Number of infinite facets (!= 0 only for specific kernels)
bool on(const Facet &F, const Point &p) const
void filterVerticesOnFacet(const Index f)
IndexRange processed_points
Points already processed (and within the convex hull).
bool checkFacet(Index f) const
BOOST_STATIC_ASSERT((Point::dimension==Vector::dimension))
Facet makeFacet(const IndexRange &base, Index below) const
Size nb_finite_facets
Number of finite facets.
IndexRange pointsOnRidge(const Ridge &R) const
IndexRange v2p
vertex index -> point index
Kernel::CombinatorialPlaneSimplex CombinatorialPlaneSimplex
bool computeSimplexConfiguration(const IndexRange &full_simplex)
void cleanInfiniteFacets()
std::vector< Facet > facets
the current set of facets.
bool getFacetVertices(std::vector< IndexRange > &facet_vertices) const
std::pair< Index, Index > Ridge
Kernel::CoordinatePoint Point
std::vector< Point > points
the set of points, indexed as in the array.
std::set< Index > deleted_facets
set of deleted facets
void clear()
Clears the object.
Size nbFiniteFacets() const
bool areFacetsParallel(const Index if1, const Index if2) const
bool getVertex2Point(IndexRange &vertex_to_point)
IndexRange pickInitialSimplex() const
IndexRange orientedFacetPoints(Index f) const
std::vector< Size > facet_counter
Counts the number of facets with a given number of vertices.
bool processFacet(std::queue< Index > &Q)
static IndexRange pickIntegers(const Size d, const Size n)
void deleteFacet(Index f)
bool getVertexPositions(std::vector< OutputPoint > &vertex_positions)
std::vector< Index > assignment
assignment of points to facets
static const Size dimension
Status
Represents the status of a QuickHull object.
@ 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
InternalScalar height(const Facet &F, const Point &p) const
bool getFacetHalfSpaces(std::vector< HalfSpace > &facet_halfspaces)
Index mergeParallelFacets(const Index if1, const Index if2)
void makeNeighbors(const Index if1, const Index if2)
void selfDisplay(std::ostream &out) const
bool aboveOrOn(const Facet &F, const Point &p) const
IndexRange p2v
point index -> vertex index (or UNASSIGNED)
bool getPoint2Vertex(IndexRange &point_to_vertex)
void renumberInfiniteFacets()
bool computeConvexHull(Status target=Status::VerticesCompleted)
Kernel::HalfSpace HalfSpace
Kernel::CoordinateScalar Scalar
std::vector< double > timings
Timings of the different phases: 0: init, 1: facets, 2: vertices.
bool above(const Facet &F, const Point &p) const