34 #include <boost/program_options/options_description.hpp>    35 #include <boost/program_options/parsers.hpp>    36 #include <boost/program_options/variables_map.hpp>    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;
   131 template <
typename CellOutputIterator, 
typename DigitalSurface>
   133 analyzeSurface( CellOutputIterator itSure, CellOutputIterator itUnsure, DigitalSurface surface )
   135   typedef typename DigitalSurface::KSpace        KSpace;
   136   typedef typename DigitalSurface::Surfel        Surfel;
   137   typedef typename DigitalSurface::ConstIterator ConstIterator;
   138   typedef typename DigitalSurface::DigitalSurfaceContainer Container;
   139   typedef typename DigitalSurface::DigitalSurfaceTracker   Tracker;
   140   typedef typename KSpace::Integer               Integer;
   141   typedef typename KSpace::Cell                  Cell;
   142   const Dimension t  = KSpace::dimension - 1;
   143   const Container& C = surface.container();
   144   const KSpace& K    = surface.container().space();
   146   for ( ConstIterator it = surface.begin(), itE = surface.end(); it != itE; ++it )
   149       Cell   is  = K.unsigns( s );
   150       Integer s_xt = K.sKCoord( s, t );
   151       if ( s_xt == 0 )         *itUnsure++ = is;
   152       else if ( s_xt == -1 ) 
   154           CountedPtr<Tracker> tracker( C.newTracker( s ) );
   155           if ( tracker->adjacent( s2, t, 
true ) != 0 )
   157               Integer s2_xt = K.sKCoord( s2, t );
   158               Cell ic = K.uIncident( is, t, 
true ); 
   159               if ( s2_xt > 0 ) *itSure++   = ic;
   160               else             *itUnsure++ = ic;
   163       else if ( s_xt == 1 )
   165           CountedPtr<Tracker> tracker( C.newTracker( s ) );
   166           if ( tracker->adjacent( s2, t, 
false ) != 0 )
   168               Integer s2_xt = K.sKCoord( s2, t );
   169               Cell ic = K.uIncident( is, t, 
false ); 
   170               if ( s2_xt < 0 ) *itSure++   = ic;
   171               else             *itUnsure++ = ic;
   174       else cout << 
" " << s_xt;
   186 template <
typename ImplicitSurface, 
typename RealPo
int>
   187 RealPoint projectNewton( 
const ImplicitSurface & is, 
   189                          typename RealPoint::Coordinate epsilon,
   190                          unsigned int max_iter )
   192   typedef typename RealPoint::Coordinate Scalar;
   195   Scalar eps2 = epsilon * epsilon;
   196   while ( max_iter-- != 0 )
   199       if ( abs( f ) < epsilon ) 
return x;
   200       gx = is.gradient( x );
   202       if ( g2 < eps2 ) 
return x;
   209 template <
typename CubicalComplex3, 
typename ImplicitShape3, 
   210           typename ImplicitDigitalShape3>
   211 void projectComplex( std::vector< typename ImplicitShape3::RealPoint >& points,
   212                      const CubicalComplex3& complex3,
   213                      const ImplicitShape3& shape,
   214                      const ImplicitDigitalShape3& dshape,
   216                      unsigned int max_iter, 
   217                      double max_distance )
   219   typedef typename CubicalComplex3::Cell     Cell3;
   220   typedef typename CubicalComplex3::Point    Point3;
   221   typedef typename CubicalComplex3::CellMapConstIterator CellMapConstIterator;
   222   typedef typename ImplicitShape3::RealPoint RealPoint3;
   223   typedef typename ImplicitShape3::Ring      Ring;
   225   for ( CellMapConstIterator it = complex3.begin( 0 ), itE = complex3.end( 0 ); it != itE; ++it )
   227       Cell3 cell    = it->first;
   228       Point3 dp     = complex3.space().uKCoords( cell ) - Point3::diagonal( 1 );
   229       RealPoint3 p  = dshape->embed( dp ) / 2.0;
   230       RealPoint3 q  = projectNewton( shape, p, epsilon, max_iter );
   231       double     d  = (p-q).norm();
   232       if ( d > max_distance ) q = p + (q-p)*( max_distance / d );
   233       points.push_back( q );
   237 template <
typename CubicalComplex3, 
typename ImplicitShape3, 
   238           typename ImplicitDigitalShape3>
   239 typename ImplicitShape3::Ring
   240 getValue( 
const CubicalComplex3& complex3,
   241           const typename CubicalComplex3::Cell& cell,
   242           const ImplicitShape3& shape,
   243           const ImplicitDigitalShape3& dshape )
   245   typedef typename CubicalComplex3::Cell     Cell3;
   246   typedef typename CubicalComplex3::Cells    Cells3;
   247   typedef typename CubicalComplex3::Point    Point3;
   248   typedef typename ImplicitShape3::RealPoint RealPoint3;
   249   typedef typename ImplicitShape3::Ring      Ring;
   251   Point3 dp    = complex3.space().uKCoords( cell ) - Point3::diagonal( 1 );
   252   RealPoint3 p = dshape->embed( dp ) / 2.0;
   258 template <
typename CubicalComplex3, 
typename ImplicitShape3, 
   259           typename ImplicitDigitalShape3>
   260 void doNotProjectComplex( std::vector< typename ImplicitShape3::RealPoint >& points,
   261                           const CubicalComplex3& complex3,
   262                           const ImplicitShape3& shape,
   263                           const ImplicitDigitalShape3& dshape )
   265   typedef typename CubicalComplex3::Cell     Cell3;
   266   typedef typename CubicalComplex3::Point    Point3;
   267   typedef typename CubicalComplex3::CellMapConstIterator CellMapConstIterator;
   268   typedef typename ImplicitShape3::RealPoint RealPoint3;
   269   typedef typename ImplicitShape3::Ring      Ring;
   271   for ( CellMapConstIterator it = complex3.begin( 0 ), itE = complex3.end( 0 ); it != itE; ++it )
   273       Cell3 cell    = it->first;
   274       Point3 dp     = complex3.space().uKCoords( cell );
   275       RealPoint3 p  = dshape->embed( dp ) / 2.0;
   276       points.push_back( p );
   282 int main( 
int argc, 
char** argv )
   285   typedef SpaceND<3,Integer>                Space3;
   286   typedef KhalimskySpaceND<3,Integer>       KSpace3;
   287   typedef KSpace3::Cell                     Cell3;
   288   typedef std::map<Cell3, CubicalCellData>  Map3;
   290   typedef CubicalComplex< KSpace3, Map3 >   CC3;
   291   typedef Space3::Point                     Point3;
   292   typedef Space3::RealPoint                 RealPoint3;
   293   typedef Space3::RealVector                RealVector3;
   294   typedef RealPoint3::Coordinate            Ring;
   296   typedef MPolynomial<3, Ring>              Polynomial3;
   297   typedef MPolynomialReader<3, Ring>        Polynomial3Reader;
   298   typedef ImplicitPolynomial3Shape<Space3>  ImplicitShape3;
   299   typedef GaussDigitizer< Space3, ImplicitShape3 > 
   300                                             ImplicitDigitalShape3;
   301   typedef ImplicitDigitalShape3::Domain     Domain3;
   302   typedef CC3::CellMapIterator              CellMapIterator;
   303   typedef CC3::CellMapConstIterator         CellMapConstIterator;
   304   typedef CC3::Cells                        Cells3;
   307   namespace po = boost::program_options;
   308   po::options_description general_opt(
"Allowed options are: ");
   309   general_opt.add_options()
   310     (
"help,h", 
"display this message")
   311     (
"polynomial,p", po::value<string>(), 
"the implicit polynomial whose zero-level defines the shape of interest." )
   312     (
"minAABB,a",  po::value<double>()->default_value( -10.0 ), 
"the min value of the AABB bounding box (domain)" )
   313     (
"maxAABB,A",  po::value<double>()->default_value( 10.0 ), 
"the max value of the AABB bounding box (domain)" )
   314     (
"gridstep,g", po::value< double >()->default_value( 1.0 ), 
"the gridstep that defines the digitization (often called h). " )
   315     (
"thickness,t", po::value< double >()->default_value( 1e-2 ), 
"the thickening parameter for the implicit surface." )
   316     (
"project,P", po::value< std::string >()->default_value( 
"Newton" ), 
"defines the projection: either No or Newton." )
   317     (
"epsilon,e", po::value< double >()->default_value( 1e-6 ), 
"the maximum precision relative to the implicit surface in the Newton approximation of F=0." )
   318     (
"max_iter,n", po::value< unsigned int >()->default_value( 500 ), 
"the maximum number of iteration in the Newton approximation of F=0." )
   319     (
"view,v", po::value< std::string >()->default_value( 
"Normal" ), 
"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)." )
   323   po::variables_map vm;
   325     po::store(po::parse_command_line(argc, argv, general_opt), vm);  
   326   } 
catch ( 
const exception& ex ) {
   328     cerr << 
"Error checking program options: "<< ex.what()<< endl;
   331   if( ! parseOK || vm.count(
"help") || ! vm.count( 
"polynomial" ) )
   333       if ( ! vm.count( 
"polynomial" ) ) 
   334         cerr << 
"Need parameter --polynomial / -p" << endl;
   336       cerr << 
"Usage: " << argv[0] << 
" -p <polynomial> [options]\n"   337                 << 
"Computes the zero level set of the given polynomial."    339                 << general_opt << 
"\n";
   340       cerr << 
"Example:\n" << endl
   341            << argv[0] << 
" -p \"x^2-y*z^2\" -g 0.1 -a -2 -A 2 -v Singular" << endl
   342            << 
" - whitney  : x^2-y*z^2" << endl
   343            << 
" - 4lines   : x*y*(y-x)*(y-z*x)" << endl
   344            << 
" - cone     : z^2-x^2-y^2" << endl
   345            << 
" - simonU   : x^2-z*y^2+x^4+y^4" << endl
   346            << 
" - cayley3  : 4*(x^2 + y^2 + z^2) + 16*x*y*z - 1" << endl
   347            << 
" - crixxi   : -0.9*(y^2+z^2-1)^2-(x^2+y^2-1)^3" << endl << endl;
   348       cerr << 
"Some other examples (more difficult):" << endl
   349            << argv[0] << 
" -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" << endl
   350            << argv[0] << 
" -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" << endl;
   355   trace.beginBlock( 
"Reading polynomial and creating 3D implicit function" );
   356   string poly_str = vm[ 
"polynomial" ].as<
string>();
   358   Polynomial3Reader reader;
   359   string::const_iterator iter = reader.read( poly, poly_str.begin(), poly_str.end() );
   360   if ( iter != poly_str.end() )
   362       trace.error() << 
"ERROR reading polynomial: I read only <" << poly_str.substr( 0, iter - poly_str.begin() )
   363                     << 
">, and I built P=" << poly << std::endl;
   367   CountedPtr<ImplicitShape3> shape( 
new ImplicitShape3( poly ) );
   369   Ring min_x = vm[ 
"minAABB" ].as<
double>();
   370   Ring max_x = vm[ 
"maxAABB" ].as<
double>();
   371   Ring h     = vm[ 
"gridstep" ].as<
double>();
   372   RealPoint3 p1( min_x, min_x, min_x );
   373   RealPoint3 p2( max_x, max_x, max_x );
   375   CountedPtr<ImplicitDigitalShape3> dshape( 
new ImplicitDigitalShape3() );
   376   dshape->attach( *shape );
   377   dshape->init( p1, p2, RealVector3( h, h, h ) );
   378   Domain3 domain3 = dshape->getDomain();
   380   K3.init( domain3.lowerBound(), domain3.upperBound(), true );
   381   trace.info() << 
"- domain is " << domain3 << std::endl;
   385   trace.beginBlock( 
"Extracting thickened isosurface [-t,+t] of 3D polynomial. " );
   386   CubicalCellData unsure_data( 0 );
   387   CubicalCellData sure_data( CC3::FIXED );
   388   Ring t     = vm[ 
"thickness" ].as<
double>();
   390   for ( Domain3::ConstIterator it = domain3.begin(), itE = domain3.end(); it != itE; ++it )
   392       Cell3 spel    = K3.uSpel( *it );
   394       RealPoint3 px = dshape->embed( K3.uKCoords( spel ) - Point3::diagonal( 1 ) ) / 2.0;
   395       Ring s        = (*shape)( px );
   396       if ( (-t <= s ) && ( s <= t ) ) complex3.insertCell( spel, unsure_data );
   398   trace.info() << 
"-    K[-t,+t]        = " << complex3 << endl;
   400   trace.info() << 
"- Cl K[-t,+t]        = " << complex3 << endl;
   401   std::vector<Cell3> separating_cells;
   402   std::back_insert_iterator< std::vector<Cell3> > outItSurface( separating_cells );
   403   Surfaces<KSpace3>::uWriteBoundary( outItSurface,
   404                                      K3, *dshape, domain3.lowerBound(), domain3.upperBound() );
   405   trace.info() << 
"- separating S       = " << separating_cells.size() << 
" 2-cells." << endl;
   406   complex3.insertCells( separating_cells.begin(), separating_cells.end(), sure_data );
   407   for ( std::vector<Cell3>::const_iterator it = separating_cells.begin(), itE = separating_cells.end(); it != itE; ++it )
   409       Cells3 bdry = K3.uFaces( *it );
   410       for ( Cells3::const_iterator itBdry = bdry.begin(), itBdryE = bdry.end(); itBdry != itBdryE; ++itBdry )
   411         complex3.insertCell( *itBdry, sure_data );
   413   separating_cells.clear();
   414   trace.info() << 
"- Cl K[-t,+t] + Cl S = " << complex3 << endl;
   418   trace.beginBlock( 
"Get boundary and inner cells. " );
   419   std::vector<Cell3> inner;
   420   std::vector<Cell3> bdry;
   421   functions::filterCellsWithinBounds
   422     ( complex3, K3.uKCoords( K3.lowerCell() ), K3.uKCoords( K3.upperCell() ),
   423        std::back_inserter( bdry ), std::back_inserter( inner ) );
   424   trace.info() << 
"- there are " << inner.size() << 
" inner cells." << endl;
   425   trace.info() << 
"- there are " << bdry.size() << 
" boundary cells." << endl;
   429   trace.beginBlock( 
"Compute priority function. " );
   430   Dimension d = complex3.dim();
   431   for ( Dimension i = 0; i <= d; ++i )
   433       for ( CellMapIterator it = complex3.begin( i ), itE = complex3.end( i ); it != itE; ++it )
   435           Cell3 cell   = it->first;
   436           Ring v       = getValue( complex3, cell, *shape, dshape );
   437           v = abs( 10000.0*v );
   438           if ( v > 10000000.0 ) v = 10000000.0;
   439           it->second.data &= ~CC3::VALUE;
   440           it->second.data |= (DGtal::uint32_t) floor( v );
   447   trace.beginBlock( 
"Collapse boundary. " );
   448   typename CC3::DefaultCellMapIteratorPriority priority;
   449   CC3 bdry_complex3( K3 );
   450   for ( std::vector<Cell3>::const_iterator it = bdry.begin(), itE = bdry.end(); it != itE; ++it )
   453       Dimension d = K3.uDim( cell );
   454       CellMapConstIterator cmIt = complex3.findCell( d, cell );
   455       bdry_complex3.insertCell( d, cell, cmIt->second );
   457   trace.info() << 
"- [before collapse] K_bdry =" << bdry_complex3 << endl;
   458   functions::collapse( bdry_complex3, bdry.begin(), bdry.end(), priority, 
true, 
true, false );
   459   trace.info() << 
"- [after collapse]  K_bdry =" << bdry_complex3 << endl;
   460   for ( std::vector<Cell3>::const_iterator it = bdry.begin(), itE = bdry.end(); it != itE; ++it )
   463       Dimension d = K3.uDim( cell );
   464       CellMapConstIterator cmIt = bdry_complex3.findCell( d, cell );
   465       if ( cmIt != bdry_complex3.end( d ) ) {
   466         CellMapIterator cmIt2 = complex3.findCell( d, cell );
   467         cmIt2->second = sure_data;
   473   trace.beginBlock( 
"Collapse all. " );
   474   std::copy( bdry.begin(), bdry.end(), std::back_inserter( inner ) );
   475   functions::collapse( complex3, inner.begin(), inner.end(), priority, 
true, 
true, true );
   476   trace.info() << 
"- K = " << complex3 << endl;
   480   trace.beginBlock( 
"Project complex onto surface. " );
   481   std::string project   = vm[ 
"project" ].as<std::string>();
   482   double epsilon        = vm[ 
"epsilon" ].as<
double>();
   483   unsigned int max_iter = vm[ 
"max_iter" ].as<
unsigned int>();
   484   std::vector<RealPoint3> points;
   485   if ( project == 
"Newton" )
   486     projectComplex( points, complex3, *shape, dshape, epsilon, max_iter, h * sqrt(3.0));
   488     doNotProjectComplex( points, complex3, *shape, dshape );
   492   trace.beginBlock( 
"Create Mesh. " );
   493   std::string view = vm[ 
"view" ].as<std::string>();
   494   bool highlight = ( view == 
"Singular" );
   495   bool hide      = ( view == 
"Hide" );
   496   Mesh<RealPoint3> mesh( 
true );
   497   std::map<Cell3,unsigned int> indices;
   499   for ( CellMapConstIterator it = complex3.begin( 0 ), itE = complex3.end( 0 ); it != itE; ++it, ++idx )
   501       Cell3 cell = it->first;
   502       indices[ cell ] = idx;
   503       mesh.addVertex( points[ idx ] );
   505   for ( CellMapConstIterator it = complex3.begin( 2 ), itE = complex3.end( 2 ); it != itE; ++it )
   507       Cell3 cell = it->first;
   508       bool fixed = it->second.data & CC3::FIXED;
   509       Cells3 bdry = complex3.cellBoundary( cell, 
true );
   510       std::vector<unsigned int> face_idx;
   511       for ( Cells3::const_iterator itC = bdry.begin(), itCE = bdry.end(); itC != itCE; ++itC )
   513           if ( complex3.dim( *itC ) == 0 )
   514             face_idx.push_back( indices[ *itC ] );
   516       if ( ( ! fixed ) && hide ) 
continue;
   517       Color color = highlight
   518         ? ( fixed ? Color::White : Color(128,255,128) )
   520       RealVector3 diag03 = points[ face_idx[ 0 ] ] - points[ face_idx[ 3 ] ];
   521       RealVector3 diag12 = points[ face_idx[ 1 ] ] - points[ face_idx[ 2 ] ];
   522       if ( diag03.dot( diag03 ) <= diag12.dot( diag12 ) )
   524           mesh.addTriangularFace( face_idx[ 0 ], face_idx[ 1 ], face_idx[ 3 ], color );
   525           mesh.addTriangularFace( face_idx[ 0 ], face_idx[ 3 ], face_idx[ 2 ], color );
   529           mesh.addTriangularFace( face_idx[ 0 ], face_idx[ 1 ], face_idx[ 2 ], color );
   530           mesh.addTriangularFace( face_idx[ 1 ], face_idx[ 3 ], face_idx[ 2 ], color );
   537   QApplication application(argc,argv);
   538   Viewer3D<Space3,KSpace3> viewer( K3 );
   539   viewer.setWindowTitle(
"Implicit surface viewer by thickening");
   543   for ( CellMapConstIterator it = complex3.begin( 1 ), itE = complex3.end( 1 ); it != itE; ++it )
   545       Cell3 cell  = it->first;
   546       bool fixed  = it->second.data & CC3::FIXED;
   547       std::vector<Cell3> dummy;
   548       std::back_insert_iterator< std::vector<Cell3> > outIt( dummy );
   549       complex3.directCoFaces( outIt, cell );
   550       if ( ! dummy.empty() )     
continue;
   552       Cells3 bdry = complex3.cellBoundary( cell, 
true );
   553       Cell3 v0    = *(bdry.begin() );
   554       Cell3 v1    = *(bdry.begin() + 1);
   555       if ( ( ! fixed ) && hide ) 
continue;
   556       Color color = highlight
   557         ? ( fixed ? Color::White : Color(128,255,128) )
   559       viewer.setLineColor( color );
   560       viewer.addLine( points[ indices[ v0 ] ], points[ indices[ v1 ] ], h/2.0 );
   562   viewer << Viewer3D<Space3,KSpace3>::updateDisplay;
   563   return application.exec();