38 #include "DGtal/base/Common.h"
39 #include "DGtal/base/CountedPtr.h"
40 #include "DGtal/helpers/StdDefs.h"
41 #include "DGtal/math/MPolynomial.h"
42 #include "DGtal/io/readers/MPolynomialReader.h"
43 #include "DGtal/io/DrawWithDisplay3DModifier.h"
44 #include "DGtal/io/viewers/Viewer3D.h"
45 #include "DGtal/topology/KhalimskySpaceND.h"
46 #include "DGtal/topology/CubicalComplex.h"
47 #include "DGtal/topology/CubicalComplexFunctions.h"
48 #include "DGtal/topology/SetOfSurfels.h"
49 #include "DGtal/topology/DigitalSurface.h"
50 #include "DGtal/topology/helpers/Surfaces.h"
51 #include "DGtal/shapes/GaussDigitizer.h"
52 #include "DGtal/shapes/Mesh.h"
53 #include "DGtal/shapes/implicit/ImplicitPolynomial3Shape.h"
58 using namespace DGtal;
129 template <
typename TSpace3,
typename TSpace4,
typename TPolynomial3>
130 struct ImplicitSurface4DProductExtension {
131 typedef TSpace3 Space3;
132 typedef TSpace4 Space4;
133 typedef TPolynomial3 Polynomial3;
134 typedef typename Space3::RealPoint RealPoint3;
135 typedef typename Space3::RealVector RealVector3;
136 typedef typename Space3::Integer Integer;
137 typedef typename RealPoint3::Coordinate Ring;
138 typedef typename Space4::RealPoint RealPoint4;
139 typedef typename Space4::RealVector RealVector4;
140 typedef RealPoint4 RealPoint;
141 typedef RealVector4 RealVector;
143 ImplicitSurface4DProductExtension( Clone<Polynomial3> poly )
146 fx = derivative<0>( f );
147 fy = derivative<1>( f );
148 fz = derivative<2>( f );
149 fxx = derivative<0>( fx );
150 fxy = derivative<0>( fy );
151 fxz = derivative<0>( fz );
152 fyy = derivative<1>( fy );
153 fyz = derivative<1>( fz );
154 fzz = derivative<2>( fz );
161 Ring operator()(
const RealPoint &aPoint)
const
163 Ring x = aPoint[ 0 ];
164 Ring y = aPoint[ 1 ];
165 Ring z = aPoint[ 2 ];
166 Ring t = aPoint[ 3 ];
167 RealVector3 grad( fx(x)(y)(z), fy(x)(y)(z), fz(x)(y)(z) );
168 return f(x)(y)(z) - t * grad.norm();
175 bool isInside(
const RealPoint &aPoint)
const
177 return this->operator()( aPoint ) <= NumberTraits<Ring>::ZERO;
186 Orientation orientation(
const RealPoint &aPoint)
const
188 Ring v = this->operator()( aPoint );
189 if ( v > NumberTraits<Ring>::ZERO )
return OUTSIDE;
190 else if ( v < NumberTraits<Ring>::ZERO )
return INSIDE;
199 RealVector gradient(
const RealPoint &aPoint )
const
201 Ring x = aPoint[ 0 ];
202 Ring y = aPoint[ 1 ];
203 Ring z = aPoint[ 2 ];
204 Ring t = aPoint[ 3 ];
205 RealVector3 grad( fx(x)(y)(z), fy(x)(y)(z), fz(x)(y)(z) );
206 Ring ngrad = grad.norm();
208 RealVector d_gradx( fxx(x)(y)(z), fxy(x)(y)(z),fxz(x)(y)(z) );
209 RealVector d_grady( fxy(x)(y)(z), fyy(x)(y)(z),fyz(x)(y)(z) );
210 RealVector d_gradz( fxz(x)(y)(z), fyz(x)(y)(z),fzz(x)(y)(z) );
211 return RealVector( grad[ 0 ] - t * d_gradx.dot( grad ),
212 grad[ 1 ] - t * d_grady.dot( grad ),
213 grad[ 2 ] - t * d_gradz.dot( grad ),
235 template <
typename TSpace3,
typename TSpace4,
typename TPolynomial3>
236 struct ImplicitSurface4DExtension {
237 typedef TSpace3 Space3;
238 typedef TSpace4 Space4;
239 typedef TPolynomial3 Polynomial3;
240 typedef typename Space3::RealPoint RealPoint3;
241 typedef typename Space3::RealVector RealVector3;
242 typedef typename Space3::Integer Integer;
243 typedef typename RealPoint3::Coordinate Ring;
244 typedef typename Space4::RealPoint RealPoint4;
245 typedef typename Space4::RealVector RealVector4;
246 typedef RealPoint4 RealPoint;
247 typedef RealVector4 RealVector;
249 ImplicitSurface4DExtension( Clone<Polynomial3> poly )
252 fx = derivative<0>( f );
253 fy = derivative<1>( f );
254 fz = derivative<2>( f );
255 fxx = derivative<0>( fx );
256 fxy = derivative<0>( fy );
257 fxz = derivative<0>( fz );
258 fyy = derivative<1>( fy );
259 fyz = derivative<1>( fz );
260 fzz = derivative<2>( fz );
267 Ring operator()(
const RealPoint &aPoint)
const
269 Ring x = aPoint[ 0 ];
270 Ring y = aPoint[ 1 ];
271 Ring z = aPoint[ 2 ];
272 Ring t = aPoint[ 3 ];
274 return f(x)(y)(z) - t;
281 bool isInside(
const RealPoint &aPoint)
const
283 return this->operator()( aPoint ) <= NumberTraits<Ring>::ZERO;
292 Orientation orientation(
const RealPoint &aPoint)
const
294 Ring v = this->operator()( aPoint );
295 if ( v > NumberTraits<Ring>::ZERO )
return OUTSIDE;
296 else if ( v < NumberTraits<Ring>::ZERO )
return INSIDE;
305 RealVector gradient(
const RealPoint &aPoint )
const
307 Ring x = aPoint[ 0 ];
308 Ring y = aPoint[ 1 ];
309 Ring z = aPoint[ 2 ];
310 Ring t = aPoint[ 3 ];
311 return RealVector( fx(x)(y)(z), fy(x)(y)(z), fz(x)(y)(z), -1.0 );
328 template <
typename CellOutputIterator,
typename DigitalSurface>
330 analyzeSurface( CellOutputIterator itSure, CellOutputIterator itUnsure,
331 DigitalSurface surface )
333 typedef typename DigitalSurface::KSpace KSpace;
334 typedef typename DigitalSurface::Surfel Surfel;
335 typedef typename DigitalSurface::ConstIterator ConstIterator;
336 typedef typename DigitalSurface::DigitalSurfaceContainer Container;
337 typedef typename DigitalSurface::DigitalSurfaceTracker Tracker;
338 typedef typename KSpace::Integer Integer;
339 typedef typename KSpace::Cell Cell;
340 const Dimension t = KSpace::dimension - 1;
341 const Container& C = surface.container();
342 const KSpace& K = surface.container().space();
344 for ( ConstIterator it = surface.begin(), itE = surface.end(); it != itE; ++it )
347 Cell is = K.unsigns( s );
348 Integer s_xt = K.sKCoord( s, t );
349 if ( s_xt == 0 ) *itUnsure++ = is;
350 else if ( s_xt == -1 )
352 CountedPtr<Tracker> tracker( C.newTracker( s ) );
353 if ( tracker->adjacent( s2, t,
true ) != 0 )
355 Integer s2_xt = K.sKCoord( s2, t );
356 Cell ic = K.uIncident( is, t,
true );
357 if ( s2_xt > 0 ) *itSure++ = ic;
358 else *itUnsure++ = ic;
361 else if ( s_xt == 1 )
363 CountedPtr<Tracker> tracker( C.newTracker( s ) );
364 if ( tracker->adjacent( s2, t,
false ) != 0 )
366 Integer s2_xt = K.sKCoord( s2, t );
367 Cell ic = K.uIncident( is, t,
false );
368 if ( s2_xt < 0 ) *itSure++ = ic;
369 else *itUnsure++ = ic;
372 else cout <<
" " << s_xt;
384 template <
typename ImplicitSurface,
typename RealPo
int>
385 RealPoint projectNewton(
const ImplicitSurface & is,
387 typename RealPoint::Coordinate epsilon,
388 unsigned int max_iter )
390 typedef typename RealPoint::Coordinate Scalar;
393 Scalar eps2 = epsilon * epsilon;
394 while ( max_iter-- != 0 )
397 if ( abs( f ) < epsilon )
return x;
398 gx = is.gradient( x );
400 if ( g2 < eps2 )
return x;
407 template <
typename CubicalComplex4,
typename ImplicitShape4,
408 typename ImplicitDigitalShape4,
typename ImplicitShape3>
409 void projectComplex( std::vector< typename ImplicitShape3::RealPoint >& points,
410 const CubicalComplex4& complex4,
411 const ImplicitShape4& shape,
412 const ImplicitDigitalShape4& dshape,
413 const ImplicitShape3& shape3,
415 unsigned int max_iter )
417 typedef typename CubicalComplex4::Cell Cell4;
418 typedef typename CubicalComplex4::Point Point4;
419 typedef typename CubicalComplex4::CellMapConstIterator CellMapConstIterator;
420 typedef typename ImplicitShape4::RealPoint RealPoint4;
421 typedef typename ImplicitShape4::Ring Ring;
422 typedef typename ImplicitShape3::RealPoint RealPoint3;
424 for ( CellMapConstIterator it = complex4.begin( 0 ), itE = complex4.end( 0 ); it != itE; ++it )
426 Cell4 cell = it->first;
427 Point4 dp = complex4.space().uCoords( cell );
428 RealPoint4 p = dshape->embed( dp );
429 RealPoint3 p3 = RealPoint3( p[ 0 ], p[ 1 ], p[ 2 ] );
430 RealPoint3 q = projectNewton( shape3, p3, epsilon, max_iter );
431 points.push_back( q );
435 template <
typename CubicalComplex4,
typename ImplicitShape4,
436 typename ImplicitDigitalShape4,
typename ImplicitShape3>
437 void doNotProjectComplex( std::vector< typename ImplicitShape3::RealPoint >& points,
438 const CubicalComplex4& complex4,
439 const ImplicitShape4& shape,
440 const ImplicitDigitalShape4& dshape,
441 const ImplicitShape3& shape3 )
443 typedef typename CubicalComplex4::Cell Cell4;
444 typedef typename CubicalComplex4::Point Point4;
445 typedef typename CubicalComplex4::CellMapConstIterator CellMapConstIterator;
446 typedef typename ImplicitShape4::RealPoint RealPoint4;
447 typedef typename ImplicitShape4::Ring Ring;
448 typedef typename ImplicitShape3::RealPoint RealPoint3;
449 for ( CellMapConstIterator it = complex4.begin( 0 ), itE = complex4.end( 0 ); it != itE; ++it )
451 Cell4 cell = it->first;
452 Point4 dp = complex4.space().uCoords( cell );
453 RealPoint4 p = dshape->embed( dp );
454 RealPoint3 p3 = RealPoint3( p[ 0 ], p[ 1 ], p[ 2 ] );
455 points.push_back( p3 );
460 int main(
int argc,
char** argv )
463 typedef SpaceND<3,Integer> Space3;
464 typedef KhalimskySpaceND<3,Integer> KSpace3;
465 typedef KSpace3::Cell Cell3;
466 typedef std::map<Cell3, CubicalCellData> Map3;
467 typedef CubicalComplex< KSpace3, Map3 > CC3;
468 typedef Space3::Point Point3;
469 typedef Space3::RealPoint RealPoint3;
470 typedef RealPoint3::Coordinate Ring;
472 typedef MPolynomial<3, Ring> Polynomial3;
473 typedef MPolynomialReader<3, Ring> Polynomial3Reader;
474 typedef ImplicitPolynomial3Shape<Space3> ImplicitShape3;
475 typedef SpaceND<4,Integer> Space4;
476 typedef KhalimskySpaceND<4,Integer> KSpace4;
477 typedef KSpace4::Cell Cell4;
478 typedef KSpace4::Cells Cells4;
479 typedef KSpace4::SCell SCell4;
480 typedef Space4::Point Point4;
481 typedef Space4::RealPoint RealPoint4;
482 typedef Space4::RealVector RealVector4;
483 typedef ImplicitSurface4DProductExtension<Space3,Space4,Polynomial3>
485 typedef GaussDigitizer< Space4, ImplicitShape4 >
486 ImplicitDigitalShape4;
487 typedef ImplicitDigitalShape4::Domain Domain4;
488 typedef std::map<Cell4, CubicalCellData> Map4;
489 typedef CubicalComplex< KSpace4, Map4 > CC4;
490 typedef CC4::CellMapIterator CellMapIterator;
491 typedef CC4::CellMapConstIterator CellMapConstIterator;
492 typedef CC4::Cells Cells4;
498 std::string outputFileName {
"result.raw"};
499 DGtal::int64_t rescaleInputMin {0};
500 DGtal::int64_t rescaleInputMax {255};
506 std::string project {
"Newton"};
507 double epsilon {1e-6};
508 unsigned int max_iter {500};
509 std::string view {
"Normal"};
510 app.description(
"Computes the zero level set of the given polynomial. Usage: 3dImplicitSurfaceExtractorBy4DExtension -p <polynomial> [options]\n Example:\n 3dImplicitSurfaceExtractorBy4DExtension -p \"-0.9*(y^2+z^2-1)^2-(x^2+y^2-1)^3\" -g 0.06125 -a -2 -A 2 -v Singular -t 0.02 \n - whitney : x^2-y*z^2 \n - 4lines : x*y*(y-x)*(y-z*x) \n - cone : z^2-x^2-y^2 \n - simonU : x^2-z*y^2+x^4+y^4 \n - cayley3 : 4*(x^2 + y^2 + z^2) + 16*x*y*z - 1 \n - crixxi : -0.9*(y^2+z^2-1)^2-(x^2+y^2-1)^3 \n Some other examples (more difficult): \n 3dImplicitSurfaceExtractorBy4DExtension -a -2 -A 2 -p \"((y^2+z^2-1)^2-(x^2+y^2-1)^3)*(y*(x-1)^2-z*(x+1))^2\" -g 0.025 -e 1e-6 -n 50000 -v Singular -t 0.5 -P Newton \n 3dImplicitSurfaceExtractorBy4DExtension -a -2 -A 2 -p \"(x^5-4*z^3*y^2)*((x+y)^2-(z-x)^3)\" -g 0.025 -e 1e-6 -n 50000 -v Singular -t 0.05 -P Newton ");
511 app.add_option(
"-p,--polynomial,1", poly_str,
"the implicit polynomial whose zero-level defines the shape of interest." )
513 app.add_option(
"--minAABB,-a",min_x,
"the min value of the AABB bounding box (domain)" ,
true);
514 app.add_option(
"--maxAABB,-A",max_x,
"the max value of the AABB bounding box (domain)" ,
true);
515 app.add_option(
"--gridstep,-g", h,
"the gridstep that defines the digitization in the 4th dimension (small is generally a good idea, default is 1e-6). ",
true);
516 app.add_option(
"--timestep,-t",t,
"the thickening parameter for the implicit surface." ,
true);
517 app.add_option(
"--project,-P", project,
"defines the projection: either No or Newton.",
true)
518 -> check(CLI::IsMember({
"No",
"Newton"}));
519 app.add_option(
"--epsilon,-e", epsilon,
"the maximum precision relative to the implicit surface in the Newton approximation of F=0.",
true);
520 app.add_option(
"--max_iter,-n", max_iter,
"the maximum number of iteration in the Newton approximation of F=0.",
true );
521 app.add_option(
"--view,-v", view,
"specifies if the surface is viewed as is (Normal) or if places close to singularities are highlighted (Singular), or if unsure places should not be displayed (Hide).",
true )
522 -> check(CLI::IsMember({
"Singular",
"Normal",
"Hide"}));
524 app.get_formatter()->column_width(40);
525 CLI11_PARSE(app, argc, argv);
530 trace.beginBlock(
"Reading polynomial and creating 4D implicit function" );
532 Polynomial3Reader reader;
533 string::const_iterator iter = reader.read( poly, poly_str.begin(), poly_str.end() );
534 if ( iter != poly_str.end() )
536 trace.error() <<
"ERROR reading polynomial: I read only <" << poly_str.substr( 0, iter - poly_str.begin() )
537 <<
">, and I built P=" << poly << std::endl;
541 ImplicitShape3 shape3( poly );
542 CountedPtr<ImplicitShape4> shape(
new ImplicitShape4( poly ) );
545 RealPoint4 p1( min_x, min_x, min_x, -t*h );
546 RealPoint4 p2( max_x, max_x, max_x, 0 );
548 CountedPtr<ImplicitDigitalShape4> dshape(
new ImplicitDigitalShape4() );
549 dshape->attach( *shape );
550 dshape->init( p1, p2, RealVector4( h, h, h, t ) );
551 Domain4 domain4 = dshape->getDomain();
553 K4.init( domain4.lowerBound(), domain4.upperBound(),
true );
554 trace.info() <<
"- domain is " << domain4 << std::endl;
558 trace.beginBlock(
"Extracting isosurface 0 of 4D polynomial. " );
560 std::vector<SCell4> surface_cells;
561 std::back_insert_iterator< std::vector<SCell4> > outItSurface( surface_cells );
562 Surfaces<KSpace4>::sWriteBoundary( outItSurface,
563 K4, *dshape, domain4.lowerBound(), domain4.upperBound() );
564 trace.info() <<
"- 4D surface has " << surface_cells.size() <<
" 3-cells." << endl;
565 typedef SetOfSurfels<KSpace4> SurfaceContainer;
566 SurfaceContainer* ptrF0 =
new SurfaceContainer( K4, SurfelAdjacency< KSpace4::dimension >(
false ) );
567 ptrF0->surfelSet().insert( surface_cells.begin(), surface_cells.end() );
568 DigitalSurface<SurfaceContainer> surface( ptrF0 );
569 std::vector<Cell4> sure_cells;
570 std::vector<Cell4> unsure_cells;
571 analyzeSurface( std::back_inserter( sure_cells ), std::back_inserter( unsure_cells ),
573 trace.info() <<
"- sure cells = " << sure_cells.size() << endl;
574 trace.info() <<
"- unsure cells = " << unsure_cells.size() << endl;
575 CubicalCellData unsure_data( 0 );
576 CubicalCellData sure_data( CC4::FIXED );
577 complex4.insertCells( sure_cells.begin(), sure_cells.end(), sure_data );
578 complex4.insertCells( unsure_cells.begin(), unsure_cells.end(), unsure_data );
579 surface_cells.clear();
583 trace.beginBlock(
"Get boundary and inner cells. " );
584 std::vector<Cell4> inner;
585 std::vector<Cell4> bdry;
587 trace.info() <<
"- K=" << complex4 << endl;
588 functions::filterCellsWithinBounds
589 ( complex4, K4.uKCoords( K4.lowerCell() ), K4.uKCoords( K4.upperCell() ),
590 std::back_inserter( bdry ), std::back_inserter( inner ) );
591 trace.info() <<
"- there are " << inner.size() <<
" inner cells." << endl;
592 trace.info() <<
"- there are " << bdry.size() <<
" boundary cells." << endl;
596 trace.beginBlock(
"Compute priority function. " );
597 Dimension d = complex4.dim();
598 for ( Dimension i = 0; i <= d; ++i )
600 for ( CellMapIterator it = complex4.begin( i ), itE = complex4.end( i ); it != itE; ++it )
602 Cell4 cell = it->first;
603 Point4 dp = K4.uCoords( cell );
604 RealPoint4 p = dshape->embed( dp );
605 Ring v = (*shape)( p );
607 if ( v > 1000000.0 ) v = 1000000.0;
608 it->second.data &= ~CC4::VALUE;
609 it->second.data |= (DGtal::uint32_t) floor( v );
616 trace.beginBlock(
"Collapse boundary. " );
617 typename CC4::DefaultCellMapIteratorPriority priority;
618 CC4 bdry_complex4( K4 );
619 for ( std::vector<Cell4>::const_iterator it = bdry.begin(), itE = bdry.end(); it != itE; ++it )
622 Dimension d = K4.uDim( cell );
623 CellMapConstIterator cmIt = complex4.findCell( d, cell );
624 bdry_complex4.insertCell( d, cell, cmIt->second );
627 trace.info() <<
"- [before collapse] K_bdry =" << bdry_complex4 << endl;
628 functions::collapse( bdry_complex4, bdry.begin(), bdry.end(), priority,
true,
true,
true );
629 trace.info() <<
"- [after collapse] K_bdry =" << bdry_complex4 << endl;
630 for ( std::vector<Cell4>::const_iterator it = bdry.begin(), itE = bdry.end(); it != itE; ++it )
633 Dimension d = K4.uDim( cell );
634 CellMapConstIterator cmIt = bdry_complex4.findCell( d, cell );
635 if ( cmIt != bdry_complex4.end( d ) ) {
636 CellMapIterator cmIt2 = complex4.findCell( d, cell );
637 cmIt2->second = sure_data;
643 trace.beginBlock(
"Collapse all. " );
644 std::copy( bdry.begin(), bdry.end(), std::back_inserter( inner ) );
646 functions::collapse( complex4, inner.begin(), inner.end(), priority,
true,
true,
true );
647 trace.info() <<
"- K=" << complex4 << endl;
651 trace.beginBlock(
"Project complex onto surface. " );
652 std::vector<RealPoint3> points;
653 if ( project ==
"Newton" )
654 projectComplex( points, complex4, *shape, dshape, shape3, epsilon, max_iter );
656 doNotProjectComplex( points, complex4, *shape, dshape, shape3 );
660 trace.beginBlock(
"Create Mesh. " );
661 bool highlight = ( view ==
"Singular" );
662 Mesh<RealPoint3> mesh(
true );
663 std::map<Cell4,unsigned int> indices;
665 for ( CellMapConstIterator it = complex4.begin( 0 ), itE = complex4.end( 0 ); it != itE; ++it, ++idx )
667 Cell4 cell = it->first;
668 indices[ cell ] = idx;
669 mesh.addVertex( points[ idx ] );
671 for ( CellMapConstIterator it = complex4.begin( 2 ), itE = complex4.end( 2 ); it != itE; ++it, ++idx )
673 Cell4 cell = it->first;
674 bool fixed = it->second.data & CC4::FIXED;
675 Cells4 bdry = complex4.cellBoundary( cell,
true );
676 std::vector<unsigned int> face_idx;
677 for ( Cells4::const_iterator itC = bdry.begin(), itCE = bdry.end(); itC != itCE; ++itC )
679 if ( complex4.dim( *itC ) == 0 )
680 face_idx.push_back( indices[ *itC ] );
682 Color color = highlight
683 ? ( fixed ? Color::White : Color(128,255,128) )
685 mesh.addQuadFace( face_idx[ 0 ], face_idx[ 1 ], face_idx[ 3 ], face_idx[ 2 ], color );
690 QApplication application(argc,argv);
691 Point4 low4 = K4.lowerBound();
692 Point4 up4 = K4.upperBound();
694 K3.init( Point3( low4[ 0 ], low4[ 1 ], low4[ 2 ] ),
695 Point3( up4 [ 0 ], up4 [ 1 ], up4 [ 2 ] ),
true );
696 Viewer3D<Space3,KSpace3> viewer( K3 );
697 viewer.setWindowTitle(
"Implicit surface viewer by 4d extension");
700 viewer.setLineColor( highlight ? Color::Red : Color( 120, 120, 120 ) );
702 for ( CellMapConstIterator it = complex4.begin( 1 ), itE = complex4.end( 1 ); it != itE; ++it )
704 Cell4 cell = it->first;
705 std::vector<Cell4> dummy;
706 std::back_insert_iterator< std::vector<Cell4> > outIt1( dummy );
707 complex4.directCoFaces( outIt1, cell );
708 if ( ! dummy.empty() )
continue;
709 Cells4 vertices = complex4.cellBoundary( cell );
710 ASSERT( vertices.size() == 2 );
711 RealPoint3 p1 = points[ indices[ vertices.front() ] ];
712 RealPoint3 p2 = points[ indices[ vertices.back() ] ];
713 viewer.addLine( p1, p2, 0.05 );
715 viewer << Viewer3D<Space3,KSpace3>::updateDisplay;
716 return application.exec();