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/AffineGeometry.h"
51#include "DGtal/geometry/tools/QuickHullKernels.h"
139 template <
typename TKernel >
143 typedef typename Kernel::CoordinatePoint
Point;
144 typedef typename Kernel::CoordinateVector
Vector;
145 typedef typename Kernel::CoordinateScalar
Scalar;
186 const auto it = std::find(
on_set.cbegin(),
on_set.cend(), p );
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;
198 for (
auto&& n :
on_set ) out <<
" " << n;
199 out <<
" }]" << std::endl;
217 if (
this != &other ) {
218 std::swap(
H, other.
H );
237 typedef std::pair< Index, Index >
Ridge;
293 M +=
sizeof( std::vector< Point > )
295 M +=
sizeof( std::vector< Index > )
297 M +=
sizeof( std::vector< Index > )
299 M +=
sizeof( std::vector< Index > )
302 M +=
sizeof( std::vector< Index > )
305 M +=
sizeof( std::vector< Facet > )
307 for (
const auto& f :
facets ) M += f.variableMemory();
309 M +=
sizeof( std::set< Index > )
312 M +=
sizeof( std::vector< Index > )
315 M +=
sizeof( std::vector< Index > )
318 M +=
sizeof( std::vector< double > )
319 +
timings.capacity() *
sizeof( double );
382 template <
typename InputPo
int >
383 bool setInput(
const std::vector< InputPoint >& input_points,
384 bool remove_duplicates =
true )
391 input_points, remove_duplicates );
416 trace.
error() <<
"[QuickHull::setInitialSimplex]"
417 <<
" not a full dimensional simplex" << std::endl;
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() ] );
471 if ( ! ok1 )
return false;
472 if (
status() == target )
return true;
479 if ( ! ok2 )
return false;
480 if (
status() == target )
return true;
487 if ( ! ok3 )
return false;
488 if (
status() == target )
return true;
508 if ( full_simplex.empty() ) {
527 std::queue< Index > Q;
533 trace.
info() <<
"---- Iteration " << n++ <<
" #Q=" << Q.size() << std::endl;
556 static const int MAX_NB_VPF = 10 *
dimension;
566 std::vector< IndexRange > p2f(
points.size() );
568 for (
auto&& p :
facets[ f ].on_set ) p2f[ p ].push_back( f );
574 const auto nbf = p2f[ p ].size();
612 template <
typename OutputPo
int >
615 vertex_positions.clear();
618 vertex_positions.resize(
v2p.size() );
619 for (
Index i = 0; i <
v2p.size(); i++ ) {
635 vertex_to_point.clear();
638 vertex_to_point =
v2p;
654 point_to_vertex.clear();
657 point_to_vertex =
p2v;
669 facet_vertices.clear();
672 facet_vertices.reserve(
nbFacets() );
675 for (
auto& v : ofacet ) v =
p2v[ v ];
676 facet_vertices.push_back( ofacet );
693 facet_halfspaces.clear();
696 facet_halfspaces.reserve(
nbFacets() );
698 facet_halfspaces.push_back(
facets[ f ].
H );
721 << (int)
status() << std::endl;
725 trace.
warning() <<
"[Quickhull::check] not all points processed: "
731 trace.
warning() <<
"[Quickhull::check] Check hull is invalid. "
736 trace.
warning() <<
"[Quickhull::check] Check facets is invalid. "
852 {
return kernel.height( F.
H, p ); }
858 {
return kernel.above( F.
H, p ); }
864 {
return kernel.aboveOrOn( F.
H, p ); }
870 {
return kernel.on( F.
H, p ); }
880 for (
auto& l : renumbering ) {
888 if ( ( renumbering[ f ] !=
UNASSIGNED ) && ( f != renumbering[ f ] ) )
891 for (
auto& F :
facets ) {
892 for (
auto& N : F.neighbors ) {
894 trace.
error() <<
"Invalid deleted neighboring facet." << std::endl;
895 else N = renumbering[ N ];
906 if ( !
kernel.hasInfiniteFacets() )
return;
911 for (
auto& l : renumbering ) {
912 if ( !
kernel.isHalfSpaceFacetInfinite(
facets[ j ].H ) ) l = i++;
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() );
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 ];
948 if ( Q.empty() )
return false;
956 trace.
info() <<
"---------------------------------------------" << std::endl;
957 trace.
info() <<
"---- ACTIVE FACETS---------------------------" << std::endl;
971 trace.
info() <<
"---------------------------------------------" << std::endl;
972 trace.
info() <<
"Processing facet " << F <<
" ";
978 auto furthest_h =
height( facet,
points[ furthest_v ] );
981 if ( h > furthest_h ) {
988 std::vector< Index > V;
990 std::queue< Index > E;
991 std::vector< Ridge >
H;
994 while ( ! E.empty() ) {
995 Index G = E.front(); E.pop();
997 for (
auto& N :
facets[ G ].neighbors ) {
999 if ( M.count( N ) )
continue;
1002 H.push_back( { G, N } );
1008 trace.
info() <<
"#Visible=" << V.size() <<
" #Horizon=" <<
H.size()
1009 <<
" furthest_v=" << furthest_v << std::endl;
1014 for (
Index i = 0; i <
H.size(); i++ )
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;
1025 base[ j++ ] = furthest_v;
1026 for (
auto&& v : ridge ) base[ j++ ] = v;
1028 trace.
error() <<
"Bad ridge between " << std::endl
1029 <<
"- facet " <<
H[i].first <<
" ";
1031 trace.
error() <<
"- facet " <<
H[i].second <<
" ";
1035 new_facets.push_back( nf );
1038 std::sort(
facets[ nf ].on_set.begin(),
facets[ nf ].on_set.end() );
1041 trace.
info() <<
"* New facet " << nf <<
" ";
1045 for (
Index k = 0; k < new_facets.size() - 1; k++ )
1048 trace.
info() <<
"Facets " << new_facets[ k ] <<
" and " << nf
1049 <<
" are parallel => merge." << std::endl;
1052 new_facets.pop_back();
1060 for (
Index i = 0; i < new_facets.size(); i++ )
1062 for (
Index j = i + 1; j < new_facets.size(); j++ )
1064 const Index nfi = new_facets[ i ];
1065 const Index nfj = new_facets[ j ];
1072 for (
auto&& vf : V ) {
1073 for (
auto&& v :
facets[ vf ].outside_set ) {
1074 if ( v != furthest_v ) {
1075 outside_pts.push_back( v );
1081 for (
Index i = 0; i < new_facets.size(); i++ ) {
1083 Index max_j = outside_pts.size();
1084 for (
Index j = 0; j < max_j; ) {
1085 const Index v = outside_pts[ j ];
1089 outside_pts[ j ] = outside_pts.back();
1090 outside_pts.pop_back();
1095 trace.
info() <<
"- New facet " << new_facets[ i ] <<
" ";
1104 for (
auto&& v : V ) {
1106 trace.
info() <<
"Delete facet " << v <<
" ";
1113 for (
Index i = 0; i < new_facets.size(); i++ )
1114 Q.push( new_facets[ i ] );
1123 <<
" / " <<
points.size() <<
" points processed." << std::endl;
1126 trace.
error() <<
"[computeFacet] Invalid convex hull" << std::endl;
1129 trace.
error() <<
"[computeFacet] Invalid facets" << std::endl;
1141 for (
auto v : F.
on_set )
1143 trace.
error() <<
"[QuickHull::checkFacet( " << f
1144 <<
") Invalid 'on' vertex " << v << std::endl;
1148 trace.
error() <<
"[QuickHull::checkFacet( " << f
1149 <<
") Not enough neighbors " << F.
neighbors.size() << std::endl;
1154 trace.
error() <<
"[QuickHull::checkFacet( " << f
1155 <<
") Invalid neighbor " << nf << std::endl;
1159 trace.
error() <<
"[QuickHull::checkFacet( " << f
1160 <<
") Bad below point " << F.
below << std::endl;
1165 trace.
error() <<
"[QuickHull::checkFacet( " << f
1166 <<
") Bad outside point " << ov << std::endl;
1169 for (
auto && v : F.
on_set ) {
1171 for (
auto&& N :
facets[ f ].neighbors )
1174 trace.
error() <<
"[QuickHull::checkFacet( " << f <<
") 'on' point " << v
1175 <<
" is a vertex of " << n <<
" facets "
1176 <<
"(should be >= " <<
dimension-1 <<
")" << std::endl;
1196 for (
auto n :
facets[ f ].neighbors )
1197 facets[ n ].subNeighbor( f );
1207 facets[ if1 ].addNeighbor( if2 );
1208 facets[ if2 ].addNeighbor( if1 );
1216 facets[ if1 ].subNeighbor( if2 );
1217 facets[ if2 ].subNeighbor( if1 );
1235 std::back_inserter( merge_idx ) );
1236 f1.
on_set.swap( merge_idx );
1238 if ( nf2 == if1 )
continue;
1239 facets[ nf2 ].subNeighbor( if2 );
1251 ASSERT( if1 != if2 );
1254 if (
kernel.equal( f1.
H, f2.
H ) )
return true;
1256 for (
auto&& v : f1.
on_set )
1257 if ( !
on( f2,
points[ v ] ) )
return false;
1275 if ( f1.
on_set[ i1 ] == f2.
on_set[ i2 ] ) { nb++; i1++; i2++; }
1296 for (
Size i = 0; i <
dimension; i++ ) simplex[ i ] = base[ i ];
1298 return Facet( plane, below );
1309 std::set_intersection( f1.
on_set.cbegin(), f1.
on_set.cend(),
1311 std::back_inserter( result ) );
1335 splx[ k ] = result[ k ];
1338 std::sort( result.begin()+
dimension-2, result.end(),
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 );
1348 splx[ k ] = result[ k ];
1364 auto & on_set =
facets[ f ].on_set;
1365 for (
Index i = 0; i < on_set.size(); )
1367 Index v = on_set[ i ];
1369 for (
auto&& N :
facets[ f ].neighbors )
1373 on_set[ i ] = on_set.back();
1377 std::sort( on_set.begin(), on_set.end() );
1389 const auto first_H =
kernel.compute(
points, splx, best.back() );
1390 auto best_volume =
kernel.volume ( first_H,
points[ best.back() ] );
1399 const Size nbtries = std::min( (
Size) 10, 1 + nb / 10 );
1401 const Size max_nbtries = 20;
1402 for (
Size i = 0; i < max_nbtries; i++ )
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 ) {
1411 <<
" new_volume = " << tmp_volume
1412 <<
" > " << best_volume << std::endl;
1415 best_volume = tmp_volume;
1417 if ( i >= nbtries && best_volume > 0 )
1423 trace.
info() <<
"[QuickHull::pickInitialSimplex] #affine subset = " << best.size() << std::endl;
1433 bool distinct =
false;
1434 while ( ! distinct )
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 ];
1458 for (
Index j = 0; j < full_simplex.size(); ++j )
1466 isimplex[ s ] = full_simplex[ i ];
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() );
1496 <<
" / " <<
points.size() <<
" points processed." << std::endl;
1499 trace.
error() <<
"[computeInitialSimplex] Invalid convex hull" << std::endl;
1502 trace.
error() <<
"[computeInitialSimplex] Invalid facets" << std::endl;
1520 out <<
"[QuickHull " <<
dimension <<
"D"
1555 template <
typename TKernel >
1560 object.selfDisplay( out );
1573#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
static std::vector< Size > affineSubset(const std::vector< TInputPoint > &X, const double tolerance=1e-12)
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
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).
QuickHull(const Kernel &K_=Kernel(), int dbg=0)
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)
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