DGtalTools  0.9.4
3dImplicitSurfaceExtractorByThickening.cpp
1 
31 #include <iostream>
33 #include <map>
34 #include <boost/program_options/options_description.hpp>
35 #include <boost/program_options/parsers.hpp>
36 #include <boost/program_options/variables_map.hpp>
37 
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"
54 
56 
57 using namespace std;
58 using namespace DGtal;
59 
60 
61 
131 template <typename CellOutputIterator, typename DigitalSurface>
132 void
133 analyzeSurface( CellOutputIterator itSure, CellOutputIterator itUnsure, DigitalSurface surface )
134 {
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();
145  Surfel s2;
146  for ( ConstIterator it = surface.begin(), itE = surface.end(); it != itE; ++it )
147  {
148  Surfel s = *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 )
153  {
154  CountedPtr<Tracker> tracker( C.newTracker( s ) );
155  if ( tracker->adjacent( s2, t, true ) != 0 )
156  {
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;
161  }
162  }
163  else if ( s_xt == 1 )
164  {
165  CountedPtr<Tracker> tracker( C.newTracker( s ) );
166  if ( tracker->adjacent( s2, t, false ) != 0 )
167  {
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;
172  }
173  }
174  else cout << " " << s_xt;
175  }
176 }
177 
186 template <typename ImplicitSurface, typename RealPoint>
187 RealPoint projectNewton( const ImplicitSurface & is,
188  RealPoint x,
189  typename RealPoint::Coordinate epsilon,
190  unsigned int max_iter )
191 {
192  typedef typename RealPoint::Coordinate Scalar;
193  RealPoint gx;
194  Scalar f, g2;
195  Scalar eps2 = epsilon * epsilon;
196  while ( max_iter-- != 0 )
197  {
198  f = is( x );
199  if ( abs( f ) < epsilon ) return x;
200  gx = is.gradient( x );
201  g2 = gx.dot( gx );
202  if ( g2 < eps2 ) return x;
203  x -= (f/g2) * gx;
204  }
205  return x;
206 }
207 
208 
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,
215  double epsilon,
216  unsigned int max_iter,
217  double max_distance )
218 {
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;
224  points.clear();
225  for ( CellMapConstIterator it = complex3.begin( 0 ), itE = complex3.end( 0 ); it != itE; ++it )
226  {
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 );
234  }
235 }
236 
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 )
244 {
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;
250 
251  Point3 dp = complex3.space().uKCoords( cell ) - Point3::diagonal( 1 );
252  RealPoint3 p = dshape->embed( dp ) / 2.0;
253  Ring v = shape( p );
254  return v;
255 }
256 
257 
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 )
264 {
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;
270  points.clear();
271  for ( CellMapConstIterator it = complex3.begin( 0 ), itE = complex3.end( 0 ); it != itE; ++it )
272  {
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 );
277  }
278 }
279 
280 
281 
282 int main( int argc, char** argv )
283 {
284  typedef int Integer;
285  typedef SpaceND<3,Integer> Space3;
286  typedef KhalimskySpaceND<3,Integer> KSpace3;
287  typedef KSpace3::Cell Cell3;
288  typedef std::map<Cell3, CubicalCellData> Map3;
289  // typedef boost::unordered_map<Cell3, CubicalCellData> Map3;
291  typedef Space3::Point Point3;
292  typedef Space3::RealPoint RealPoint3;
293  typedef Space3::RealVector RealVector3;
294  typedef RealPoint3::Coordinate Ring;
295  typedef Ring Scalar;
296  typedef MPolynomial<3, Ring> Polynomial3;
297  typedef MPolynomialReader<3, Ring> Polynomial3Reader;
298  typedef ImplicitPolynomial3Shape<Space3> ImplicitShape3;
300  ImplicitDigitalShape3;
301  typedef ImplicitDigitalShape3::Domain Domain3;
302  typedef CC3::CellMapIterator CellMapIterator;
303  typedef CC3::CellMapConstIterator CellMapConstIterator;
304  typedef CC3::Cells Cells3;
305 
306  //-------------- parse command line ----------------------------------------------
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)." )
320  ;
321 
322  bool parseOK=true;
323  po::variables_map vm;
324  try {
325  po::store(po::parse_command_line(argc, argv, general_opt), vm);
326  } catch ( const exception& ex ) {
327  parseOK=false;
328  cerr << "Error checking program options: "<< ex.what()<< endl;
329  }
330  po::notify(vm);
331  if( ! parseOK || vm.count("help") || ! vm.count( "polynomial" ) )
332  {
333  if ( ! vm.count( "polynomial" ) )
334  cerr << "Need parameter --polynomial / -p" << endl;
335 
336  cerr << "Usage: " << argv[0] << " -p <polynomial> [options]\n"
337  << "Computes the zero level set of the given polynomial."
338  << endl
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;
351  return 0;
352  }
353 
354  //-------------- read polynomial and creating 3d implicit fct -----------------
355  trace.beginBlock( "Reading polynomial and creating 3D implicit function" );
356  string poly_str = vm[ "polynomial" ].as<string>();
357  Polynomial3 poly;
358  Polynomial3Reader reader;
359  string::const_iterator iter = reader.read( poly, poly_str.begin(), poly_str.end() );
360  if ( iter != poly_str.end() )
361  {
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;
364  return 2;
365  }
366  // Creating implicit shape and storing it with smart pointer for automatic deallocation.
367  CountedPtr<ImplicitShape3> shape( new ImplicitShape3( poly ) );
368 
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 );
374  // Creating digitized shape and storing it with smart pointer for automatic deallocation.
375  CountedPtr<ImplicitDigitalShape3> dshape( new ImplicitDigitalShape3() );
376  dshape->attach( *shape );
377  dshape->init( p1, p2, RealVector3( h, h, h ) );
378  Domain3 domain3 = dshape->getDomain();
379  KSpace3 K3;
380  K3.init( domain3.lowerBound(), domain3.upperBound(), true );
381  trace.info() << "- domain is " << domain3 << std::endl;
382  trace.endBlock();
383 
384  //-------------- read polynomial and creating 3d implicit fct -----------------
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>();
389  CC3 complex3( K3 );
390  for ( Domain3::ConstIterator it = domain3.begin(), itE = domain3.end(); it != itE; ++it )
391  {
392  Cell3 spel = K3.uSpel( *it );
393  // RealPoint3 px = dshape->embed( *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 );
397  }
398  trace.info() << "- K[-t,+t] = " << complex3 << endl;
399  complex3.close();
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 )
408  {
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 );
412  }
413  separating_cells.clear();
414  trace.info() << "- Cl K[-t,+t] + Cl S = " << complex3 << endl;
415  trace.endBlock();
416 
417  //-------------- Get boundary and inner cells --------------------------------
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;
426  trace.endBlock();
427 
428  //-------------- Compute priority function -----------------------------------
429  trace.beginBlock( "Compute priority function. " );
430  Dimension d = complex3.dim();
431  for ( Dimension i = 0; i <= d; ++i )
432  {
433  for ( CellMapIterator it = complex3.begin( i ), itE = complex3.end( i ); it != itE; ++it )
434  {
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 );
441  // std::cout << " " << it->second.data;
442  }
443  }
444  trace.endBlock();
445 
446  //-------------- Collapse boundary -------------------------------------------
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 )
451  {
452  Cell3 cell = *it;
453  Dimension d = K3.uDim( cell );
454  CellMapConstIterator cmIt = complex3.findCell( d, cell );
455  bdry_complex3.insertCell( d, cell, cmIt->second );
456  }
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 )
461  {
462  Cell3 cell = *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;
468  }
469  }
470  trace.endBlock();
471 
472  //-------------- Collapse all -------------------------------------------
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;
477  trace.endBlock();
478 
479  //-------------- Project complex onto surface --------------------------------
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));
487  else
488  doNotProjectComplex( points, complex3, *shape, dshape );
489  trace.endBlock();
490 
491  //-------------- Create Mesh -------------------------------------------
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;
498  int idx = 0;
499  for ( CellMapConstIterator it = complex3.begin( 0 ), itE = complex3.end( 0 ); it != itE; ++it, ++idx )
500  {
501  Cell3 cell = it->first;
502  indices[ cell ] = idx;
503  mesh.addVertex( points[ idx ] );
504  }
505  for ( CellMapConstIterator it = complex3.begin( 2 ), itE = complex3.end( 2 ); it != itE; ++it )
506  {
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 )
512  {
513  if ( complex3.dim( *itC ) == 0 )
514  face_idx.push_back( indices[ *itC ] );
515  }
516  if ( ( ! fixed ) && hide ) continue;
517  Color color = highlight
518  ? ( fixed ? Color::White : Color(128,255,128) )
519  : Color::White;
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 ) )
523  {
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 );
526  }
527  else
528  {
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 );
531  }
532  //mesh.addQuadFace( face_idx[ 0 ], face_idx[ 1 ], face_idx[ 3 ], face_idx[ 2 ], color );
533  }
534  trace.endBlock();
535 
536  //-------------- View surface -------------------------------------------
537  QApplication application(argc,argv);
538  Viewer3D<Space3,KSpace3> viewer( K3 );
539  viewer.setWindowTitle("Implicit surface viewer by thickening");
540  viewer.show();
541  viewer << mesh;
542  // Display lines that are not in the mesh.
543  for ( CellMapConstIterator it = complex3.begin( 1 ), itE = complex3.end( 1 ); it != itE; ++it )
544  {
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;
551 
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) )
558  : Color::White;
559  viewer.setLineColor( color );
560  viewer.addLine( points[ indices[ v0 ] ], points[ indices[ v1 ] ], h/2.0 );
561  }
562  viewer << Viewer3D<Space3,KSpace3>::updateDisplay;
563  return application.exec();
564 }
void beginBlock(const std::string &keyword="")
DGtal::int32_t Integer
boost::uint32_t uint32_t
Component Coordinate
DigitalSurfaceContainer::SurfelConstIterator ConstIterator
DGtal::uint32_t Dimension
STL namespace.
double endBlock()
DigitalSurfaceContainer::DigitalSurfaceTracker DigitalSurfaceTracker
TDigitalSurfaceContainer DigitalSurfaceContainer
bool init(const Point &lower, const Point &upper, bool isClosed)
Cell uIncident(const Cell &c, Dimension k, bool up) const
DigitalSurfaceContainer::KSpace KSpace
Trace trace(traceWriterTerm)
std::ostream & info()
Integer sKCoord(const SCell &c, Dimension k) const
const DigitalSurfaceContainer & container() const
Cell unsigns(const SCell &p) const
T abs(const T &a)
std::ostream & error()
static const constexpr Dimension dimension
Component dot(const Self &v) const
ConstIterator begin() const
DigitalSurfaceContainer::Surfel Surfel
ConstIterator end() const