27 #include <DGtal/base/Common.h>
28 #include <DGtal/helpers/StdDefs.h>
29 #include <DGtal/helpers/Shortcuts.h>
30 #include <DGtal/helpers/ShortcutsGeometry.h>
31 #include <DGtal/shapes/SurfaceMesh.h>
32 #include "DGtal/geometry/volumes/TangencyComputer.h"
33 #include "DGtal/geometry/tools/QuickHull.h"
34 #include "DGtal/geometry/surfaces/estimation/PlaneProbingTetrahedronEstimator.h"
35 #include "SymmetricConvexExpander.h"
37 #include "polyscope/pick.h"
38 #include <polyscope/polyscope.h>
39 #include <polyscope/surface_mesh.h>
40 #include <polyscope/point_cloud.h>
42 #include "ConfigExamples.h"
44 #include <Eigen/Dense>
45 #include <Eigen/Sparse>
47 using namespace DGtal;
61 polyscope::SurfaceMesh *
psMesh;
62 polyscope::SurfaceMesh *psDualMesh;
63 polyscope::SurfaceMesh *psTriangle;
64 polyscope::PointCloud* psCloud;
65 polyscope::PointCloud* psCloudCvx;
73 bool is_selected =
false;
74 Point selected_kpoint;
76 bool enforceFC =
true;
77 std::vector< Point > digital_points;
83 typedef std::vector< Index > Indices;
84 typedef double Scalar;
85 typedef std::vector< Scalar > Scalars;
87 struct UnorderedPointSetPredicate
90 const std::unordered_set< Point >* myS;
91 explicit UnorderedPointSetPredicate(
const std::unordered_set< Point >& S )
93 bool operator()(
const Point& p )
const
94 {
return myS->count( p ) != 0; }
97 std::unordered_set< Point > unorderedSet;
104 round( p[ 1 ] / gridstep + 0.5 ),
105 round( p[ 2 ] / gridstep + 0.5 ) );
106 return Point( sp[ 0 ], sp[ 1 ], sp[ 2 ] );
110 return RealPoint( gridstep * ( q[ 0 ] - 0.5 ),
111 gridstep * ( q[ 1 ] - 0.5 ),
112 gridstep * ( q[ 2 ] - 0.5 ) );
114 void embedPointels(
const std::vector< Point >& vq, std::vector< RealPoint >& vp )
116 vp.resize( vq.size() );
117 for (
auto i = 0; i < vp.size(); ++i )
118 vp[ i ] = pointelPoint2RealPoint( vq[ i ] );
120 void digitizePointels(
const std::vector< RealPoint >& vp, std::vector< Point >& vq )
122 vq.resize( vp.size() );
123 for (
auto i = 0; i < vq.size(); ++i )
124 vq[ i ] = pointelRealPoint2Point( vp[ i ] );
132 round( p[ 1 ] / gridstep ),
133 round( p[ 2 ] / gridstep ) );
134 return Point( sp[ 0 ], sp[ 1 ], sp[ 2 ] );
139 gridstep * ( q[ 1 ] ),
140 gridstep * ( q[ 2 ] ) );
142 void embedVoxels(
const std::vector< Point >& vq, std::vector< RealPoint >& vp )
144 vp.resize( vq.size() );
145 for (
auto i = 0; i < vp.size(); ++i )
146 vp[ i ] = voxelPoint2RealPoint( vq[ i ] );
148 void digitizeVoxels(
const std::vector< RealPoint >& vp, std::vector< Point >& vq )
150 vq.resize( vp.size() );
151 for (
auto i = 0; i < vq.size(); ++i )
152 vq[ i ] = voxelRealPoint2Point( vp[ i ] );
156 Indices tangentCone(
const Point& p )
158 return TC.getCotangentPoints( p );
161 Scalars distances(
const Point& p,
const Indices& idx )
163 Scalars D( idx.size() );
164 for (
Index i = 0; i < idx.size(); i++ )
165 D[ i ] = ( TC.point( i ) - p ).norm();
171 findCorners(
const std::unordered_set< Point >& S,
172 const std::vector< Vector >& In,
173 const std::vector< Vector >& Out )
175 std::vector< Point > C;
179 for (
auto&& n : In )
180 if ( ! S.count( p+n ) ) { corner =
false;
break; }
181 if ( ! corner )
continue;
182 for (
auto&& n : Out )
183 if ( S.count( p+n ) ) { corner =
false;
break; }
184 if ( corner ) C.push_back( p );
189 void computeQuadrant(
int q,
190 std::vector< Vector >& In,
191 std::vector< Vector >& Out )
195 In.push_back(
Vector( q & 0x1 ? 1 : -1, 0, 0 ) );
196 In.push_back(
Vector( 0, q & 0x2 ? 1 : -1, 0 ) );
197 In.push_back(
Vector( 0, 0, q & 0x4 ? 1 : -1 ) );
198 Out.push_back(
Vector( q & 0x1 ? 1 : -1, q & 0x2 ? 1 : -1, q & 0x4 ? 1 : -1 ) );
200 if ( D.
dot( Out[ 0 ] ) < 0.0 ) std::swap( In[ 1 ], In[ 2 ] );
201 In.push_back( In[ 0 ]+In[ 1 ] );
202 In.push_back( In[ 0 ]+In[ 2 ] );
203 In.push_back( In[ 1 ]+In[ 2 ] );
214 std::vector< RealPoint > positions;
216 std::vector< std::vector<SH3::SurfaceMesh::Vertex> > faces;
217 std::vector< Vector > In;
218 std::vector< Vector > Out;
219 std::unordered_set< Point > S( digital_points.cbegin(), digital_points.cend() );
220 UnorderedPointSetPredicate predS( S );
222 for (
int q = 0; q < 8; q++ )
224 computeQuadrant( q, In, Out );
225 std::vector< Point > corners = findCorners( S, In, Out );
226 std::cout <<
"Found " << corners.size() <<
" in Q" << q << std::endl;
227 std::array<Point, 3> m = { In[ 0 ], In[ 1 ], In[ 2 ] };
228 for (
auto&& p : corners )
234 std::vector<SH3::SurfaceMesh::Vertex> triangle { i, i+1, i+2 };
235 auto v = estimator.vertices();
236 faces.push_back( triangle );
240 while (estimator.advance().first) {
241 auto state = estimator.hexagonState();
242 if (state == Estimator::Neighborhood::HexagonState::Planar) {
243 auto v = estimator.vertices();
244 if ( S.count( v[ 0 ] ) && S.count( v[ 1 ] ) && S.count( v[ 2 ] ) )
246 std::vector< Point > X { v[ 0 ], v[ 1 ], v[ 2 ] };
263 embedPointels( vertices, positions );
264 psTriangle = polyscope::registerSurfaceMesh(
"Triangle", positions, faces);
268 void computeTangentCone()
270 if ( digital_points.empty() )
return;
271 if ( vertex_idx < 0 || vertex_idx >= digital_points.size() )
return;
274 auto local_X_idx = TC.getCotangentPoints( p );
275 std::cout <<
"#cone=" << local_X_idx.size() << std::endl;
277 std::vector< Point > local_X;
278 std::vector< RealPoint > emb_local_X;
279 for (
auto idx : local_X_idx )
280 local_X.push_back( TC.point( idx ) );
281 std::vector< double > values( local_X.size(), 0.0 );
283 embedPointels( local_X, emb_local_X );
284 psCloud = polyscope::registerPointCloud(
"Tangent cone", emb_local_X );
285 psCloud->setPointRadius( gridstep / 100.0 );
286 psCloud->addScalarQuantity(
"Classification", values );
290 std::vector< Point > positions;
291 std::vector< RealPoint > emb_positions;
294 embedPointels( positions, emb_positions );
295 psCloudCvx = polyscope::registerPointCloud(
"Tangent cone vertices", emb_positions );
296 psCloudCvx->setPointRadius( gridstep / 50.0 );
300 void computeSymmetricConvexSet()
302 if ( digital_points.empty() )
return;
303 if ( ! is_selected )
return;
307 UnorderedPointSetPredicate predS( unorderedSet );
309 ( predS, selected_kpoint, lo, up );
310 while ( SCE.advance( enforceFC ) )
311 if ( PSym && ! SCE.myPerfectSymmetry
312 && SCE.current().second >= SCE.myPerfectSymmetryRadius )
314 std::cout <<
"#symcvx=" << SCE.myPoints.size() << std::endl;
316 std::vector< Point > positions( SCE.myPoints.cbegin(), SCE.myPoints.cend() );
317 std::vector< RealPoint > emb_positions;
318 embedPointels( positions, emb_positions );
319 psCloud = polyscope::registerPointCloud(
"Symmetric convex set", emb_positions );
320 psCloud->setPointRadius( gridstep / 100.0 );
323 struct TriangleContext
335 Scalar bestTriangleAB( TriangleContext& ctx,
338 if ( ( ctx.best - ctx.cur_qAB ) > 4.0 * ctx.max_d )
return 0.0;
341 const Point a = TC.point( ctx.cone_P[ A ] );
342 const Point b = TC.point( ctx.cone_P[ B ] );
343 for ( cur_C = B + 1; cur_C < ctx.cone_P.size(); cur_C++ )
345 const Scalar d_PC = ctx.d_P[ cur_C ];
346 if ( d_PC < ctx.min_d )
continue;
347 const Point c = TC.point( ctx.cone_P[ cur_C ] );
348 const Scalar d_AC = ( c - a ).norm();
349 if ( d_AC < ctx.min_d )
continue;
350 const Scalar d_BC = ( c - b ).norm();
351 if ( d_BC < ctx.min_d )
continue;
352 const Scalar cur_q = ctx.cur_qAB + d_PC + d_AC + d_BC
356 if ( ! TC.arePointsCotangent( a, c ) )
continue;
357 if ( ! TC.arePointsCotangent( b, c ) )
continue;
358 C = cur_C; q = cur_q;
364 Scalar bestTriangleA( TriangleContext& ctx,
370 const Point a = TC.point( ctx.cone_P[ A ] );
371 for ( cur_B = A + 1; cur_B < ctx.cone_P.size(); cur_B++ )
373 const Scalar d_PB = ctx.d_P[ cur_B ];
374 if ( d_PB < ctx.min_d )
continue;
375 const Point b = TC.point( ctx.cone_P[ cur_B ] );
376 const Scalar d_AB = ( b - a ).norm();
377 if ( d_AB < ctx.min_d )
continue;
378 if ( ! TC.arePointsCotangent( a, b ) )
continue;
379 ctx.cur_qAB = ctx.cur_qA + d_PB + d_AB;
380 const Scalar result = bestTriangleAB( ctx, A, cur_B, cur_C );
383 B = cur_B; C = cur_C; q = result;
389 Scalar bestTriangle( TriangleContext& ctx,
392 Index cur_A, cur_B, cur_C;
393 for ( cur_A = 0; cur_A < ctx.cone_P.size(); cur_A++ )
395 std::cout <<
" " << cur_A;
397 const Scalar d_PA = ctx.d_P[ cur_A ];
398 if ( d_PA < ctx.min_d )
continue;
400 const Scalar result = bestTriangleA( ctx, cur_A, cur_B, cur_C );
401 if ( result > ctx.best )
403 A = cur_A; B = cur_B; C = cur_C;
410 void computeGreatTriangle()
412 if ( digital_points.empty() )
return;
413 if ( vertex_idx < 0 || vertex_idx >= digital_points.size() )
return;
417 ctx.cone_P = tangentCone( ctx.p );
418 std::cout <<
"#cone=" << ctx.cone_P.size() << std::endl;
420 ctx.d_P = distances( ctx.p, ctx.cone_P );
421 ctx.max_d = *( std::max_element( ctx.d_P.cbegin(), ctx.d_P.cend() ) );
422 ctx.min_d = ctx.max_d / 3.0;
425 Scalar d = bestTriangle( ctx, A, B, C );
427 std::cout <<
"Best triangle " << A <<
" " << B <<
" " << C
428 <<
" d=" << ctx.best << std::endl;
429 if ( d == 0 )
return;
430 std::vector< std::vector<SH3::SurfaceMesh::Vertex> > faces;
431 std::vector<SH3::SurfaceMesh::Vertex> triangle { 0, 1, 2 };
432 faces.push_back( triangle );
433 std::vector<RealPoint> positions;
435 { TC.point( ctx.cone_P[ A ] ),
436 TC.point( ctx.cone_P[ B ] ),
437 TC.point( ctx.cone_P[ C ] ) };
438 embedPointels( vertices, positions );
439 psTriangle = polyscope::registerSurfaceMesh(
"Triangle", positions, faces);
445 if (polyscope::pick::haveSelection()) {
446 bool goodSelection =
false;
447 auto selection = polyscope::pick::getSelection();
448 auto selectedSurface =
static_cast<polyscope::SurfaceMesh*
>(selection.first);
449 int idx = selection.second;
452 auto surf = polyscope::getSurfaceMesh(
"Input surface");
453 goodSelection = goodSelection || (selectedSurface == surf);
464 selected_kpoint = digital_points[
vertex_idx ] * 2;
465 std::ostringstream otext;
467 <<
" pos=" << selected_kpoint;
468 ImGui::Text(
"%s", otext.str().c_str() );
470 else if ( idx - nv < nf )
476 selected_kpoint += digital_points[ i ];
477 selected_kpoint /= 2;
478 std::ostringstream otext;
479 otext <<
"Selected face = " <<
face_idx
480 <<
" pos=" << selected_kpoint;
481 ImGui::Text(
"%s", otext.str().c_str() );
492 if (ImGui::Button(
"Compute tangent cone"))
493 computeTangentCone();
494 if (ImGui::Button(
"Compute symmetric convex set"))
495 computeSymmetricConvexSet();
496 ImGui::Checkbox(
"Perfect symmetry", &PSym );
497 ImGui::Checkbox(
"Full convexity", &enforceFC );
498 if (ImGui::Button(
"Compute great triangle"))
499 computeGreatTriangle();
500 if (ImGui::Button(
"Compute planes"))
502 ImGui::Text(
"Computation time = %f ms", Time );
505 int main(
int argc,
char* argv[] )
507 auto params = SH3::defaultParameters() | SHG3::defaultParameters() | SHG3::parametersGeometryEstimation();
508 params(
"surfaceComponents",
"All");
509 std::string filename = examplesPath + std::string(
"/samples/bunny-32.vol");
510 if ( argc > 1 ) filename = std::string( argv[ 1 ] );
511 auto binary_image = SH3::makeBinaryImage(filename, params );
514 auto primalSurface = SH3::makePrimalSurfaceMesh(
surface);
516 auto dualSurface = SH3::makeDualPolygonalSurface( s2i,
surface );
518 std::vector<std::vector<SH3::SurfaceMesh::Vertex>> faces;
519 std::vector<RealPoint> positions;
520 for(
auto face= 0 ; face < primalSurface->nbFaces(); ++face)
521 faces.push_back(primalSurface->incidentVertices( face ));
524 positions = primalSurface->positions();
530 std::vector<std::vector<SH3::SurfaceMesh::Vertex>> dual_faces;
531 std::vector<RealPoint> dual_positions;
532 for(
auto face= 0 ; face < dualSurface->nbFaces(); ++face)
533 dual_faces.push_back( dualSurface->verticesAroundFace( face ));
536 for (
auto vtx = 0; vtx < dualSurface->nbVertices(); ++vtx )
537 dual_positions.push_back( dualSurface->position( vtx ) );
539 dual_surfmesh =
SurfMesh(dual_positions.begin(),
540 dual_positions.end(),
544 std::cout <<
"number of non-manifold Edges = "
546 std::cout << dual_surfmesh << std::endl;
547 std::cout <<
"number of non-manifold Edges = "
550 digitizePointels( positions, digital_points );
551 trace.
info() <<
"Surface has " << digital_points.size() <<
" pointels." << std::endl;
554 TC.init( digital_points.cbegin(), digital_points.cend() );
556 ( digital_points.cbegin(), digital_points.cend(), 0 ).
starOfPoints();
557 trace.
info() <<
"#cell_cover = " << TC.cellCover().nbCells() << std::endl;
558 trace.
info() <<
"#lattice_cover = " << LS.
size() << std::endl;
561 unorderedSet = std::unordered_set< Point >( digital_points.cbegin(),
562 digital_points.cend() );
567 psMesh = polyscope::registerSurfaceMesh(
"Input surface", positions, faces);
568 psDualMesh = polyscope::registerSurfaceMesh(
"Input dual surface", dual_positions, dual_faces);
bool isFullySubconvex(const PointRange &Y, const LatticeSet &StarX) const
LatticePolytope makePolytope(const PointRange &X, bool make_minkowski_summable=false) const
Aim: This class is a model of CCellularGridSpaceND. It represents the cubical grid as a cell complex,...
Self starOfPoints() const
Aim: A class that locally estimates a normal on a digital set using only a predicate "does a point x ...
auto dot(const PointVector< dim, OtherComponent, OtherStorage > &v) const -> decltype(DGtal::dotProduct(*this, v))
Dot product with a PointVector.
static Self diagonal(Component val=1)
static Self zero
Static const for zero PointVector.
Aim: This class is used to simplify shape and surface creation. With it, you can create new shapes an...
Aim: This class is used to simplify shape and surface creation. With it, you can create new shapes an...
std::map< Surfel, IdxSurfel > Surfel2Index
Aim: SymmetricConvexExpander computes symmetric fully convex subsets of a given digital set.
Aim: A class that computes tangency to a given digital set. It provides services to compute all the c...
void beginBlock(const std::string &keyword="")
SurfaceMesh< RealPoint, RealVector > SurfMesh
polyscope::SurfaceMesh * psMesh
CountedPtr< SH3::DigitalSurface > surface
CountedPtr< SH3::BinaryImage > binary_image
Space::RealPoint RealPoint
DGtal is the top-level namespace which contains all DGtal functions and types.
auto crossProduct(PointVector< 3, LeftEuclideanRing, LeftContainer > const &lhs, PointVector< 3, RightEuclideanRing, RightContainer > const &rhs) -> decltype(DGtal::constructFromArithmeticConversion(lhs, rhs))
Cross product of two 3D Points/Vectors.
std::pair< typename graph_traits< DGtal::DigitalSurface< TDigitalSurfaceContainer > >::vertex_iterator, typename graph_traits< DGtal::DigitalSurface< TDigitalSurfaceContainer > >::vertex_iterator > vertices(const DGtal::DigitalSurface< TDigitalSurfaceContainer > &digSurf)
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 setInput(const std::vector< InputPoint > &input_points, bool remove_duplicates=true)
bool getVertexPositions(std::vector< OutputPoint > &vertex_positions)
bool computeConvexHull(Status target=Status::VerticesCompleted)
Aim: Represents an embedded mesh as faces and a list of vertices. Vertices may be shared among faces ...
const Vertices & incidentVertices(Face f) const
Edges computeNonManifoldEdges() const
int main(int argc, char **argv)
K init(Point(0, 0, 0), Point(512, 512, 512), true)
ShortcutsGeometry< Z3i::KSpace > SHG3