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"
45#include <Eigen/Sparse>
61polyscope::SurfaceMesh *
psMesh;
62polyscope::SurfaceMesh *psDualMesh;
63polyscope::SurfaceMesh *psTriangle;
64polyscope::PointCloud* psCloud;
65polyscope::PointCloud* psCloudCvx;
73bool is_selected =
false;
77std::vector< Point > digital_points;
83typedef std::vector< Index > Indices;
85typedef std::vector< Scalar > Scalars;
87struct 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; }
97std::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 ) );
114void 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 ] );
120void 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 ] ) );
142void 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 ] );
148void 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 ] );
156Indices tangentCone(
const Point& p )
158 return TC.getCotangentPoints( p );
161Scalars 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();
171findCorners(
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 );
189void 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);
268void 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;
276 local_X_idx.push_back( vertex_idx );
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 );
300void 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 );
323struct TriangleContext
335Scalar 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;
364Scalar 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;
389Scalar 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;
410void 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;
419 ctx.cone_P.push_back( vertex_idx );
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);
454 const auto nv = selectedSurface->nVertices();
455 const auto nf = selectedSurface->nFaces();
456 const auto ne = selectedSurface->nEdges();
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 )
475 for (
auto i : selectedSurface->faces[
face_idx ] )
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() );
483 else if ( idx - nv - nf < ne )
488 for (
auto i : selectedSurface->edgeIndices[
edge_idx ] )
489 selected_kpoint += digital_points[ i ];
490 selected_kpoint /= 2;
491 std::ostringstream otext;
492 otext <<
"Selected edge = " <<
edge_idx
493 <<
" pos=" << selected_kpoint;
494 ImGui::Text(
"%s", otext.str().c_str() );
505 if (ImGui::Button(
"Compute tangent cone"))
506 computeTangentCone();
507 if (ImGui::Button(
"Compute symmetric convex set"))
508 computeSymmetricConvexSet();
509 ImGui::Checkbox(
"Perfect symmetry", &PSym );
510 ImGui::Checkbox(
"Full convexity", &enforceFC );
511 if (ImGui::Button(
"Compute great triangle"))
512 computeGreatTriangle();
513 if (ImGui::Button(
"Compute planes"))
515 ImGui::Text(
"Computation time = %f ms", Time );
518int main(
int argc,
char* argv[] )
521 params(
"surfaceComponents",
"All");
522 std::string filename = examplesPath + std::string(
"/samples/bunny-32.vol");
523 if ( argc > 1 ) filename = std::string( argv[ 1 ] );
531 std::vector<std::vector<SH3::SurfaceMesh::Vertex>> faces;
532 std::vector<RealPoint> positions;
533 for(
auto face= 0 ; face < primalSurface->nbFaces(); ++face)
534 faces.push_back(primalSurface->incidentVertices( face ));
537 positions = primalSurface->positions();
543 std::vector<std::vector<SH3::SurfaceMesh::Vertex>> dual_faces;
544 std::vector<RealPoint> dual_positions;
545 for(
auto face= 0 ; face < dualSurface->nbFaces(); ++face)
546 dual_faces.push_back( dualSurface->verticesAroundFace( face ));
549 for (
auto vtx = 0; vtx < dualSurface->nbVertices(); ++vtx )
550 dual_positions.push_back( dualSurface->position( vtx ) );
552 dual_surfmesh =
SurfMesh(dual_positions.begin(),
553 dual_positions.end(),
557 std::cout <<
"number of non-manifold Edges = "
559 std::cout << dual_surfmesh << std::endl;
560 std::cout <<
"number of non-manifold Edges = "
563 digitizePointels( positions, digital_points );
564 trace.
info() <<
"Surface has " << digital_points.size() <<
" pointels." << std::endl;
567 TC.init( digital_points.cbegin(), digital_points.cend() );
569 ( digital_points.cbegin(), digital_points.cend(), 0 ).
starOfPoints();
570 trace.
info() <<
"#cell_cover = " << TC.cellCover().nbCells() << std::endl;
571 trace.
info() <<
"#lattice_cover = " << LS.
size() << std::endl;
574 unorderedSet = std::unordered_set< Point >( digital_points.cbegin(),
575 digital_points.cend() );
580 psMesh = polyscope::registerSurfaceMesh(
"Input surface", positions, faces);
581 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)
Aim: This class is used to simplify shape and surface creation. With it, you can create new shapes an...
static Parameters parametersGeometryEstimation()
static Parameters defaultParameters()
Aim: This class is used to simplify shape and surface creation. With it, you can create new shapes an...
static KSpace getKSpace(const Point &low, const Point &up, Parameters params=parametersKSpace())
std::map< Surfel, IdxSurfel > Surfel2Index
static CountedPtr< DigitalSurface > makeDigitalSurface(CountedPtr< TPointPredicate > bimage, const KSpace &K, const Parameters ¶ms=parametersDigitalSurface())
static CountedPtr< PolygonalSurface > makeDualPolygonalSurface(Surfel2Index &s2i, CountedPtr< ::DGtal::DigitalSurface< TContainer > > aSurface)
static Parameters defaultParameters()
static CountedPtr< SurfaceMesh > makePrimalSurfaceMesh(Cell2Index &c2i, CountedPtr< ::DGtal::DigitalSurface< TContainer > > aSurface)
static CountedPtr< BinaryImage > makeBinaryImage(Domain shapeDomain)
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="")
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. barber1996, a famous arbitrary dimensional c...
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 ...
Edges computeNonManifoldEdges() const