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 > )
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 > )
305 +
facets.capacity() *
sizeof( 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 );
381 template <
typename InputPo
int >
382 bool setInput(
const std::vector< InputPoint >& input_points,
383 bool remove_duplicates =
true )
390 input_points, remove_duplicates );
382 bool setInput(
const std::vector< InputPoint >& input_points, {
…}
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;
565 std::vector< IndexRange > p2f(
points.size() );
567 for (
auto&& p :
facets[ f ].on_set ) p2f[ p ].push_back( f );
573 const auto nbf = p2f[ p ].size();
584 trace.info() <<
"#vertices=" <<
v2p.size() <<
" #facets=" <<
facets.size()
586 trace.info() <<
"#inc_facets/point= ";
588 trace.info() << std::endl;
611 template <
typename OutputPo
int >
614 vertex_positions.clear();
617 vertex_positions.resize(
v2p.size() );
618 for (
Index i = 0; i <
v2p.size(); 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 );
719 trace.warning() <<
"[Quickhull::check] invalid status="
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 ]
782 trace.info() << nbok <<
"/"
851 {
return kernel.height( F.H, p ); }
857 {
return kernel.above( F.H, p ); }
863 {
return kernel.aboveOrOn( F.H, p ); }
868 bool on(
const Facet& F,
const Point& p )
const
869 {
return kernel.on( F.H, p ); }
868 bool on(
const Facet& F,
const Point& p )
const {
…}
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 ];
905 if ( !
kernel.hasInfiniteFacets() )
return;
910 for (
auto& l : renumbering ) {
911 if ( !
kernel.isHalfSpaceFacetInfinite(
facets[ j ].H ) ) l = i++;
916 trace.error() <<
"[Quickhull::renumberInfiniteFacets]"
917 <<
" Error renumbering infinite facets "
918 <<
" up finite=" << i <<
" low infinite=" << k << std::endl;
919 std::vector< Facet > new_facets(
facets.size() );
921 new_facets[ renumbering[ f ] ].
swap(
facets[ f ] );
922 facets.swap( new_facets );
923 for (
auto& F :
facets ) {
924 for (
auto& N : F.neighbors ) {
925 N = renumbering[ N ];
947 if ( Q.empty() )
return false;
953 const Facet& facet =
facets[ F ];
955 trace.info() <<
"---------------------------------------------" << std::endl;
956 trace.info() <<
"---- ACTIVE FACETS---------------------------" << std::endl;
960 trace.info() <<
"- facet " << i <<
" ";
970 trace.info() <<
"---------------------------------------------" << std::endl;
971 trace.info() <<
"Processing facet " << F <<
" ";
972 facet.display(
trace.info() );
974 if ( facet.outside_set.empty() )
return true;
976 Index furthest_v = facet.outside_set[ 0 ];
977 auto furthest_h =
height( facet,
points[ furthest_v ] );
978 for (
Index v = 1; v < facet.outside_set.size(); v++ ) {
979 auto h =
height( facet,
points[ facet.outside_set[ v ] ] );
980 if ( h > furthest_h ) {
982 furthest_v = facet.outside_set[ v ];
987 std::vector< Index > V;
989 std::queue< Index > E;
990 std::vector< Ridge >
H;
993 while ( ! E.empty() ) {
994 Index G = E.front(); E.pop();
996 for (
auto& N :
facets[ G ].neighbors ) {
998 if ( M.count( N ) )
continue;
1001 H.push_back( { G, N } );
1007 trace.info() <<
"#Visible=" << V.size() <<
" #Horizon=" <<
H.size()
1008 <<
" furthest_v=" << furthest_v << std::endl;
1013 for (
Index i = 0; i <
H.size(); i++ )
1018 trace.info() <<
"Ridge (" <<
H[i].first <<
"," <<
H[i].second <<
") = {";
1019 for (
auto&& r : ridge )
trace.info() <<
" " << r;
1020 trace.info() <<
" } furthest_v=" << furthest_v << std::endl;
1024 base[ j++ ] = furthest_v;
1025 for (
auto&& v : ridge ) base[ j++ ] = v;
1027 trace.error() <<
"Bad ridge between " << std::endl
1028 <<
"- facet " <<
H[i].first <<
" ";
1030 trace.error() <<
"- facet " <<
H[i].second <<
" ";
1034 new_facets.push_back( nf );
1037 std::sort(
facets[ nf ].on_set.begin(),
facets[ nf ].on_set.end() );
1040 trace.info() <<
"* New facet " << nf <<
" ";
1044 for (
Index k = 0; k < new_facets.size() - 1; k++ )
1047 trace.info() <<
"Facets " << new_facets[ k ] <<
" and " << nf
1048 <<
" are parallel => merge." << std::endl;
1051 new_facets.pop_back();
1054 facets[ new_facets[ k ] ].display(
trace.info() );
1059 for (
Index i = 0; i < new_facets.size(); i++ )
1061 for (
Index j = i + 1; j < new_facets.size(); j++ )
1063 const Index nfi = new_facets[ i ];
1064 const Index nfj = new_facets[ j ];
1071 for (
auto&& vf : V ) {
1072 for (
auto&& v :
facets[ vf ].outside_set ) {
1073 if ( v != furthest_v ) {
1074 outside_pts.push_back( v );
1080 for (
Index i = 0; i < new_facets.size(); i++ ) {
1081 Facet& Fp =
facets[ new_facets[ i ] ];
1082 Index max_j = outside_pts.size();
1083 for (
Index j = 0; j < max_j; ) {
1084 const Index v = outside_pts[ j ];
1086 Fp.outside_set.push_back( v );
1088 outside_pts[ j ] = outside_pts.back();
1089 outside_pts.pop_back();
1094 trace.info() <<
"- New facet " << new_facets[ i ] <<
" ";
1095 Fp.display(
trace.info() );
1103 for (
auto&& v : V ) {
1105 trace.info() <<
"Delete facet " << v <<
" ";
1112 for (
Index i = 0; i < new_facets.size(); i++ )
1113 Q.push( new_facets[ i ] );
1122 <<
" / " <<
points.size() <<
" points processed." << std::endl;
1125 trace.error() <<
"[computeFacet] Invalid convex hull" << std::endl;
1128 trace.error() <<
"[computeFacet] Invalid facets" << std::endl;
1138 const Facet& F =
facets[ f ];
1140 for (
auto v : F.on_set )
1142 trace.error() <<
"[QuickHull::checkFacet( " << f
1143 <<
") Invalid 'on' vertex " << v << std::endl;
1147 trace.error() <<
"[QuickHull::checkFacet( " << f
1148 <<
") Not enough neighbors " << F.neighbors.size() << std::endl;
1151 for (
auto nf : F.neighbors )
1153 trace.error() <<
"[QuickHull::checkFacet( " << f
1154 <<
") Invalid neighbor " << nf << std::endl;
1158 trace.error() <<
"[QuickHull::checkFacet( " << f
1159 <<
") Bad below point " << F.below << std::endl;
1162 for (
auto ov : F.outside_set )
1164 trace.error() <<
"[QuickHull::checkFacet( " << f
1165 <<
") Bad outside point " << ov << std::endl;
1168 for (
auto && v : F.on_set ) {
1170 for (
auto&& N :
facets[ f ].neighbors )
1173 trace.error() <<
"[QuickHull::checkFacet( " << f <<
") 'on' point " << v
1174 <<
" is a vertex of " << n <<
" facets "
1175 <<
"(should be >= " <<
dimension-1 <<
")" << std::endl;
1187 facets.push_back( Facet() );
1195 for (
auto n :
facets[ f ].neighbors )
1196 facets[ n ].subNeighbor( f );
1206 facets[ if1 ].addNeighbor( if2 );
1207 facets[ if2 ].addNeighbor( if1 );
1215 facets[ if1 ].subNeighbor( if2 );
1216 facets[ if2 ].subNeighbor( if1 );
1227 Facet& f1 =
facets[ if1 ];
1228 Facet& f2 =
facets[ if2 ];
1229 std::copy( f2.outside_set.cbegin(), f2.outside_set.cend(),
1230 std::back_inserter( f1.outside_set ) );
1232 std::set_union( f1.on_set.cbegin(), f1.on_set.cend(),
1233 f2.on_set.cbegin(), f2.on_set.cend(),
1234 std::back_inserter( merge_idx ) );
1235 f1.on_set.swap( merge_idx );
1236 for (
auto && nf2 : f2.neighbors ) {
1237 if ( nf2 == if1 )
continue;
1238 facets[ nf2 ].subNeighbor( if2 );
1250 ASSERT( if1 != if2 );
1251 const Facet& f1 =
facets[ if1 ];
1252 const Facet& f2 =
facets[ if2 ];
1253 if (
kernel.equal( f1.H, f2.H ) )
return true;
1255 for (
auto&& v : f1.on_set )
1256 if ( !
on( f2,
points[ v ] ) )
return false;
1269 const Facet& f1 =
facets[ if1 ];
1270 const Facet& f2 =
facets[ if2 ];
1272 for (
Index i1 = 0, i2 = 0; i1 < f1.on_set.size() && i2 < f2.on_set.size(); )
1274 if ( f1.on_set[ i1 ] == f2.on_set[ i2 ] ) { nb++; i1++; i2++; }
1275 else if ( f1.on_set[ i1 ] < f2.on_set[ i2 ] ) i1++;
1295 for (
Size i = 0; i <
dimension; i++ ) simplex[ i ] = base[ i ];
1297 return Facet( plane, below );
1306 const Facet& f1 =
facets[
R.first ];
1307 const Facet& f2 =
facets[
R.second ];
1308 std::set_intersection( f1.on_set.cbegin(), f1.on_set.cend(),
1309 f2.on_set.cbegin(), f2.on_set.cend(),
1310 std::back_inserter( result ) );
1325 const Facet& F =
facets[ f ];
1334 splx[ k ] = result[ k ];
1335 std::sort( result.begin()+
dimension-2, result.end(),
1338 splx[ dimension-2 ] = i;
1339 splx[ dimension-1 ] = j;
1340 const auto H = kernel.compute( points, splx );
1341 const auto orient = kernel.dot( F.H, H );
1354 auto & on_set =
facets[ f ].on_set;
1355 for (
Index i = 0; i < on_set.size(); )
1357 Index v = on_set[ i ];
1359 for (
auto&& N :
facets[ f ].neighbors )
1363 on_set[ i ] = on_set.back();
1367 std::sort( on_set.begin(), on_set.end() );
1379 const auto first_H =
kernel.compute(
points, splx, best.back() );
1380 auto best_volume =
kernel.volume ( first_H,
points[ best.back() ] );
1381 const Size nbtries = std::min( (
Size) 10, 1 + nb / 10 );
1382 const Size max_nbtries = std::max( (
Size) 10, 2 * nb );
1383 for (
Size i = 0; i < max_nbtries; i++ )
1387 const auto tmp_H =
kernel.compute(
points, splx, tmp.back() );
1388 const auto tmp_volume =
kernel.volume ( tmp_H,
points[ tmp.back() ] );
1389 if ( best_volume < tmp_volume ) {
1391 trace.info() <<
"(" << i <<
")"
1392 <<
" new_volume = " << tmp_volume
1393 <<
" > " << best_volume << std::endl;
1396 best_volume = tmp_volume;
1398 if ( i >= nbtries && best_volume > 0 )
return best;
1409 bool distinct =
false;
1410 while ( ! distinct )
1413 for (
Index i = 0; i < d; i++ ) result[ i ] = rand() % n;
1414 std::sort( result.begin(), result.end() );
1415 for (
Index i = 1; distinct && i < d; i++ )
1416 distinct = result[ i-1 ] != result[ i ];
1434 for (
Index j = 0; j < full_simplex.size(); ++j )
1442 isimplex[ s ] = full_simplex[ i ];
1446 facets[ j ].neighbors = lsimplex;
1447 for (
auto&& v : isimplex )
facets[ j ].on_set.push_back( v );
1448 std::sort(
facets[ j ].on_set.begin(),
facets[ j ].on_set.end() );
1456 f.outside_set.push_back( v );
1467 for (
auto&& f :
facets ) f.display(
trace.info() );
1472 <<
" / " <<
points.size() <<
" points processed." << std::endl;
1475 trace.error() <<
"[computeInitialSimplex] Invalid convex hull" << std::endl;
1478 trace.error() <<
"[computeInitialSimplex] Invalid facets" << std::endl;
1496 out <<
"[QuickHull " <<
dimension <<
"D"
1506 for (
auto f :
facets ) f.display( out );
1507 for (
auto v :
v2p ) out <<
points[
v2p[ v ] ] << std::endl;
1531 template <
typename TKernel >
1536 object.selfDisplay( out );
1549#undef QuickHull_RECURSES
static const Dimension dimension
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. barber1996, a famous arbitrary dimensional c...
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)
bool setInitialSimplex(const IndexRange &full_splx)
Kernel::InternalScalar InternalScalar
std::vector< Index > IndexRange
bool on(const Facet &F, const Point &p) const
void filterVerticesOnFacet(const Index f)
IndexRange processed_points
bool checkFacet(Index f) const
BOOST_STATIC_ASSERT((Point::dimension==Vector::dimension))
Facet makeFacet(const IndexRange &base, Index below) const
IndexRange pointsOnRidge(const Ridge &R) const
Kernel::CombinatorialPlaneSimplex CombinatorialPlaneSimplex
bool computeSimplexConfiguration(const IndexRange &full_simplex)
std::vector< Facet > facets
bool getFacetVertices(std::vector< IndexRange > &facet_vertices) const
std::pair< Index, Index > Ridge
Kernel::CoordinatePoint Point
std::vector< Point > points
std::set< Index > 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
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
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
bool getPoint2Vertex(IndexRange &point_to_vertex)
void renumberInfiniteFacets()
bool computeConvexHull(Status target=Status::VerticesCompleted)
Kernel::HalfSpace HalfSpace
Kernel::CoordinateScalar Scalar
std::vector< double > timings
bool above(const Facet &F, const Point &p) const