//-----------------------------------------------------------------------------
// Testing LightImplicitDigitalSurface
//-----------------------------------------------------------------------------
bool testLightImplicitDigitalSurface()
{
  using namespace Z3i;
  typedef ImplicitDigitalEllipse3<Point> ImplicitDigitalEllipse;
  typedef LightImplicitDigitalSurface<KSpace,ImplicitDigitalEllipse> Boundary;
  typedef Boundary::SurfelConstIterator ConstIterator;
  typedef Boundary::Tracker Tracker;
  typedef Boundary::Surfel Surfel;

  unsigned int nbok = 0;
  unsigned int nb = 0;
  trace.beginBlock ( "Testing block ... LightImplicitDigitalSurface" );
  Point p1( -10, -10, -10 );
  Point p2( 10, 10, 10 );
  KSpace K;
  nbok += K.init( p1, p2, true ) ? 1 : 0; 
  nb++;
  trace.info() << "(" << nbok << "/" << nb << ") "
	       << "K.init() is ok" << std::endl;
  ImplicitDigitalEllipse ellipse( 6.0, 4.5, 3.4 );
  Surfel bel = Surfaces<KSpace>::findABel( K, ellipse, 10000 );
  Boundary boundary( K, ellipse, 
                     SurfelAdjacency<KSpace::dimension>( true ), bel );
  unsigned int nbsurfels = 0;
  for ( ConstIterator it = boundary.begin(), it_end = boundary.end();
        it != it_end; ++it )
    {
      ++nbsurfels;
    }
  trace.info() << nbsurfels << " surfels found." << std::endl;
  trace.beginBlock ( "Checks if adjacent surfels are part of the surface." );

  for ( ConstIterator it = boundary.begin(), it_end = boundary.end();
        it != it_end; ++it )
    {
      Tracker* ptrTracker = boundary.newTracker( *it );
      Surfel s = ptrTracker->current();
      Dimension trackDir = * K.sDirs( s );
      Surfel s1, s2;
      // unsigned int m1 = 
      ptrTracker->adjacent( s1, trackDir, true ); 
      // unsigned int m2 = 
      ptrTracker->adjacent( s2, trackDir, false ); 
      // trace.info() << "s = " << s << std::endl;
      // trace.info() << "s1 = " << s1 << " m1 = " << m1 << std::endl;
      // trace.info() << "s2 = " << s2 << " m2 = " << m2 << std::endl;
      nb++, nbok += boundary.isInside( s1 ) ? 1 : 0;
      // trace.info() << "(" << nbok << "/" << nb << ") "
      //              << "boundary.isInside( s1 )" << std::endl;
      nb++, nbok += boundary.isInside( s2 ) ? 1 : 0;
      // trace.info() << "(" << nbok << "/" << nb << ") "
      //              << "boundary.isInside( s2 )" << std::endl;
      delete ptrTracker;
    }
  trace.info() << "(" << nbok << "/" << nb << ") isInside tests." << std::endl;
  trace.endBlock();
  trace.endBlock();
  return nbok == nb;
}
/**
 * Example of a test. To be completed.
 *
 */
bool testDigitalSetBoundary()
{
  unsigned int nbok = 0;
  unsigned int nb = 0;
  
  trace.beginBlock ( "Testing block ... DigitalSetBoundary" );
  using namespace Z2i;
  typedef DigitalSetBoundary<KSpace,DigitalSet> Boundary;
  typedef Boundary::SurfelConstIterator ConstIterator;
  typedef Boundary::Tracker Tracker;
  typedef Boundary::Surfel Surfel;
  Point p1( -10, -10 );
  Point p2( 10, 10 );
  Domain domain( p1, p2 );
  DigitalSet dig_set( domain );
  Shapes<Domain>::addNorm2Ball( dig_set, Point( 0, 0 ), 5 );
  Shapes<Domain>::removeNorm2Ball( dig_set, Point( 0, 0 ), 1 );
  KSpace K;
  nbok += K.init( domain.lowerBound(), domain.upperBound(), true ) ? 1 : 0; 
  nb++;
  trace.info() << "(" << nbok << "/" << nb << ") "
	       << "K.init() is ok" << std::endl;
  Boundary boundary( K, dig_set );
  unsigned int nbsurfels = 0;
  for ( ConstIterator it = boundary.begin(), it_end = boundary.end();
        it != it_end; ++it )
    {
      ++nbsurfels;
    }
  trace.info() << nbsurfels << " surfels found." << std::endl;
  nb++, nbok += nbsurfels == ( 12 + 44 ) ? 1 : 0;
  trace.info() << "(" << nbok << "/" << nb << ") "
	       << "nbsurfels == (12 + 44 )" << std::endl;
  for ( ConstIterator it = boundary.begin(), it_end = boundary.end();
        it != it_end; ++it )
    {
      Tracker* ptrTracker = boundary.newTracker( *it );
      Surfel s = ptrTracker->current();
      Dimension trackDir = * K.sDirs( s );
      Surfel s1, s2;
      unsigned int m1 = ptrTracker->adjacent( s1, trackDir, true ); 
      unsigned int m2 = ptrTracker->adjacent( s2, trackDir, false ); 
      trace.info() << "s = " << s << std::endl;
      trace.info() << "s1 = " << s1 << " m1 = " << m1 << std::endl;
      trace.info() << "s2 = " << s2 << " m2 = " << m2 << std::endl;
      nb++, nbok += boundary.isInside( s1 ) ? 1 : 0;
      trace.info() << "(" << nbok << "/" << nb << ") "
                   << "boundary.isInside( s1 )" << std::endl;
      nb++, nbok += boundary.isInside( s2 ) ? 1 : 0;
      trace.info() << "(" << nbok << "/" << nb << ") "
                   << "boundary.isInside( s2 )" << std::endl;
      delete ptrTracker;
    }
  trace.endBlock();
  return nbok == nb;
}
bool testCellularGridSpaceND()
{
  typedef typename KSpace::Cell Cell;
  typedef typename KSpace::SCell SCell;
  typedef typename KSpace::Point Point;
  typedef typename KSpace::DirIterator DirIterator;
  typedef typename KSpace::Cells Cells;
  typedef typename KSpace::SCells SCells;
  unsigned int nbok = 0;
  unsigned int nb = 0;
  
  trace.beginBlock ( "Testing block KSpace instantiation and scan ..." );
  KSpace K;
  int xlow[ 4 ] = { -3, -2, -2, -1 };
  int xhigh[ 4 ] = { 5, 3, 2, 3 };
  Point low( xlow );
  Point high( xhigh ); 
  bool space_ok = K.init( low, high, true );
  nbok += space_ok ? 1 : 0; 
  nb++;
  trace.info() << "(" << nbok << "/" << nb << ") "
         << "K.init( low, high )" << std::endl;
  trace.info() << "K.dim()=" << K.dimension << endl;
  int spel[ 4 ] = { 1, 1, 1, 1 }; // pixel
  Point kp( spel );
  Cell center = K.uCell( kp );
  Cell c1 = K.uCell( kp );
  Cell clow = K.uCell( low, kp );
  Cell chigh = K.uCell( high, kp );
  trace.info() << c1 << clow << chigh 
         << " topo(c1)=" << K.uTopology( c1 ) << " dirs=";
  for ( DirIterator q = K.uDirs( clow ); q != 0; ++q )
    trace.info() << " " << *q;
  trace.info() << endl;
  Cell f = K.uFirst( c1 );
  Cell l = K.uLast( c1 );
  trace.info() << "Loop in " << clow << chigh << endl;
  c1 = f;
  unsigned int nbelems = 0;
  do {
    ++nbelems;
    // trace.info() << c1;
  } while ( K.uNext( c1, f, l ) );
  trace.info() << " -> " << nbelems << " elements." << endl;
  unsigned int exp_nbelems = 1;
  for ( Dimension i = 0; i < K.dimension; ++i )
    exp_nbelems *= K.size( i );
  nbok += nbelems == exp_nbelems ? 1 : 0; 
  nb++;
  trace.info() << "(" << nbok << "/" << nb << ") "
         << nbelems << " scanned elements == "
         << exp_nbelems << " space size."
         << std::endl;
  trace.endBlock();
  trace.beginBlock ( "Testing neighborhoods in KSpace..." );
  Cells N = K.uNeighborhood( center );
  nbok += N.size() == ( K.dimension*2 + 1 ) ? 1 : 0; 
  nb++;
  trace.info() << "(" << nbok << "/" << nb << ") "
         << N.size() << "(neighborhood size) == "
         << ( K.dimension*2 + 1 ) << "(2*dim()+1)" << endl;
  Cells Np = K.uProperNeighborhood( center );
  nbok += Np.size() == ( K.dimension*2 ) ? 1 : 0; 
  nb++;
  trace.info() << "(" << nbok << "/" << nb << ") "
         << Np.size() << "(proper neighborhood size) == "
         << ( K.dimension*2 ) << "(2*dim())" << endl;
  trace.endBlock();

  trace.beginBlock ( "Testing faces in KSpace..." );
  Cells Nf = K.uFaces( center );
  nbok += Nf.size() == ceil( std::pow( 3.0 ,(int) K.dimension ) - 1 ) ? 1 : 0; 
  nb++;
  trace.info() << "(" << nbok << "/" << nb << ") "
         << Nf.size() << "(faces size) == "
         << floor( std::pow( 3.0, (int)K.dimension ) - 1 ) << "(3^dim()-1)" << endl;
  trace.endBlock();
  
  trace.beginBlock ( "Testing block Incidence in KSpace..." );
  SCell sspel = K.sCell( kp, K.POS );
  for ( DirIterator q1 = K.sDirs( sspel ); q1 != 0; ++q1 )
    for ( DirIterator q2 = K.sDirs( sspel ); q2 != 0; ++q2 )
      {
  if ( *q1 != *q2 )
    {
      SCell s0 = K.sIncident( sspel, *q1, true );
      SCell s1 = K.sIncident( sspel, *q2, true );
      SCell l10 = K.sIncident( s0, *q2, true );
      SCell l01 = K.sIncident( s1, *q1, true );
      trace.info() << "D+_" << *q2 << "(D+_" << *q1 << "(V))=" << l10 
       << " D+_" << *q1 << "(D+_" << *q2 << "(V))=" << l01
       << endl;
      nbok += l10 == K.sOpp( l01 ) ? 1 : 0; 
      nb++;
    }
      }
  trace.info() << "(" << nbok << "/" << nb << ") "
         << "anti-commutativity of incidence operators." << std::endl;
  trace.endBlock();

  trace.beginBlock ( "Testing direct Incidence in KSpace..." );
  for ( DirIterator q1 = K.sDirs( sspel ); q1 != 0; ++q1 )
    for ( DirIterator q2 = K.sDirs( sspel ); q2 != 0; ++q2 )
      {
  if ( *q1 != *q2 )
    {
      SCell s0 = K.sDirectIncident( sspel, *q1 );
      SCell l10 = K.sDirectIncident( s0, *q2 );
      SCell s1 = K.sDirectIncident( sspel, *q2 );
      SCell l01 = K.sDirectIncident( s1, *q1 );
      trace.info() << "Dd_" << *q2 << "(Dd_" << *q1 << "(V))=" << l10 
       << " Dd_" << *q1 << "(Dd_" << *q2 << "(V))=" << l01
       << endl;
      nbok += l10 != l01 ? 1 : 0; 
      nbok += K.sSign( s0 ) == K.POS ? 1 : 0;
      nbok += K.sSign( s1 ) == K.POS ? 1 : 0;
      nbok += K.sSign( l10 ) == K.POS ? 1 : 0;
      nbok += K.sSign( l01 ) == K.POS ? 1 : 0;
      nbok += s0 == K.sIncident( sspel, *q1, K.sDirect( sspel, *q1 ) )
        ? 1 : 0;
      nbok += s1 == K.sIncident( sspel, *q2, K.sDirect( sspel, *q2 ) )
        ? 1 : 0;
      nbok += l10 == K.sIncident( s0, *q2, K.sDirect( s0, *q2 ) )
        ? 1 : 0;
      nbok += l01 == K.sIncident( s1, *q1, K.sDirect( s1, *q1 ) )
        ? 1 : 0;
      nb += 9;
    }
      }
  trace.info() << "(" << nbok << "/" << nb << ") "
         << "correctness of direct and indirect orientations." << std::endl;
  
  trace.endBlock();
  
  
  return nbok == nb;
}
Exemple #4
0
int main( int argc, char** argv )
{
  // for 3D display with Viewer3D
  QApplication application(argc,argv);
  
  //! [digitalSurfaceSlice-readVol]
  trace.beginBlock( "Reading vol file into an image." );
  typedef ImageSelector < Domain, int>::Type Image;
  std::string inputFilename = examplesPath + "samples/Al.100.vol"; 
  Image image = VolReader<Image>::importVol(inputFilename);
  DigitalSet set3d (image.domain());
  SetPredicate<DigitalSet> set3dPredicate( set3d );
  SetFromImage<DigitalSet>::append<Image>(set3d, image, 
                                          0, 1 );
  Viewer3D viewer;  
  viewer.show(); 
  trace.endBlock();
  //! [digitalSurfaceSlice-readVol]
  
  //! [digitalSurfaceSlice-KSpace]
  trace.beginBlock( "Construct the Khalimsky space from the image domain." );
  KSpace ks;
  bool space_ok = ks.init( image.domain().lowerBound(), 
                           image.domain().upperBound(), true );
  if (!space_ok)
    {
      trace.error() << "Error in the Khamisky space construction."<<std::endl;
      return 2;
    }
  trace.endBlock();
  //! [digitalSurfaceSlice-KSpace]

  //! [digitalSurfaceSlice-SurfelAdjacency]
  typedef SurfelAdjacency<KSpace::dimension> MySurfelAdjacency;
  MySurfelAdjacency surfAdj( true ); // interior in all directions.
  //! [digitalSurfaceSlice-SurfelAdjacency]

  //! [digitalSurfaceSlice-ExtractingSurface]
  trace.beginBlock( "Extracting boundary by scanning the space. " );
  typedef KSpace::Surfel Surfel;
  typedef KSpace::SurfelSet SurfelSet;
  typedef SetOfSurfels< KSpace, SurfelSet > MySetOfSurfels;
  typedef DigitalSurface< MySetOfSurfels > MyDigitalSurface;
  MySetOfSurfels theSetOfSurfels( ks, surfAdj );
  Surfaces<KSpace>::sMakeBoundary( theSetOfSurfels.surfelSet(),
                                   ks, set3dPredicate,
                                   image.domain().lowerBound(), 
                                   image.domain().upperBound() );
  MyDigitalSurface digSurf( theSetOfSurfels );
  trace.info() << "Digital surface has " << digSurf.size() << " surfels."
               << std::endl;
  trace.endBlock();
  //! [digitalSurfaceSlice-ExtractingSurface]
  
  //! [digitalSurfaceSlice-ExtractingSlice]
  trace.beginBlock( "Extract slices." );
  typedef MyDigitalSurface::DigitalSurfaceTracker MyTracker;
  typedef DigitalSurface2DSlice< MyTracker > My2DSlice;
  //Extract an initial boundary cell
  Surfel surf = *digSurf.begin();
  MyTracker* tracker1 = digSurf.container().newTracker( surf );
  MyTracker* tracker2 = digSurf.container().newTracker( surf );
  // Extract the bondary contour associated to the initial surfel in
  // its first direction
  My2DSlice slice1( tracker1, *(ks.sDirs( surf )) ); 
  // Extract the bondary contour associated to the initial surfel in
  // its second direction
  My2DSlice slice2( tracker2, *++(ks.sDirs( surf )) ); 
  delete tracker1;
  delete tracker2;
  trace.endBlock();
  //! [digitalSurfaceSlice-ExtractingSlice]

  ASSERT( slice1.start() == slice1.begin() );
  ASSERT( slice1.cstart() == slice1.c() );
  ASSERT( *slice1.begin() == surf );
  ASSERT( *slice1.c() == surf );
  ASSERT( *slice1.start() == surf );
  ASSERT( *slice1.cstart() == surf );
  ASSERT( *slice1.rcstart() == surf );
  ASSERT( slice1.rcstart() == slice1.rc() );
  ASSERT( *slice1.rc() == surf );
  ASSERT( *(slice1.c()+1) == *(slice1.begin()+1) );
  ASSERT( *(slice1.rc()+1) == *(slice1.rbegin()) );

  //! [digitalSurfaceSlice-displayingAll]
  trace.beginBlock( "Display all with QGLViewer." );
  // Displaying all the surfels in transparent mode
  viewer << SetMode3D( surf.className(), "Transparent");
  for( MyDigitalSurface::ConstIterator it = theSetOfSurfels.begin(),
         it_end = theSetOfSurfels.end(); it != it_end; ++it )
    viewer<< *it;
  
  // Displaying First surfels cut with gradient colors.;
  GradientColorMap<int> cmap_grad( 0, slice1.size() );
  cmap_grad.addColor( Color( 50, 50, 255 ) );
  cmap_grad.addColor( Color( 255, 0, 0 ) );
  cmap_grad.addColor( Color( 255, 255, 10 ) );
  
  // Need to avoid surfel superposition (the surfel size in increased)
  viewer << Viewer3D::shiftSurfelVisu; 
  viewer << SetMode3D( surf.className(), "");
  viewer.setFillColor(Color(180, 200, 25, 255));
  
  int d=0;
  for ( My2DSlice::ConstIterator it = slice1.begin(),
          it_end = slice1.end(); it != it_end; ++it )
    {        
      Color col= cmap_grad(d);
      viewer.setFillColor(Color(col.red(),col.green() ,col.blue(), 255));
      viewer<< *it;
      d++;
    }
  
  GradientColorMap<int> cmap_grad2( 0, slice2.size() );
  cmap_grad2.addColor( Color( 50, 50, 255 ) );
  cmap_grad2.addColor( Color( 255, 0, 0 ) );
  cmap_grad2.addColor( Color( 255, 255, 10 ) );
  
  d=0;
  for ( My2DSlice::ConstIterator it = slice2.begin(),
          it_end = slice2.end(); it != it_end; ++it )
    {        
      Color col= cmap_grad2(d);
      viewer.setFillColor(Color(col.red(),col.green() ,col.blue(), 255));
      viewer<< *it;
      d++;
    }
  
  // One need once again to avoid superposition.
  viewer << Viewer3D::shiftSurfelVisu; 
  viewer.setFillColor(Color(18, 200, 25, 255));
  viewer << surf ;
  viewer << Viewer3D::updateDisplay;
  trace.endBlock();
    
  return application.exec();
  //! [digitalSurfaceSlice-displayingAll]
}
int main( int argc, char** argv )
{
  trace.info() <<  "exampleGridCurve3d: the type can be changed in example source code with  <gridcurve>, <inner>, <outer>, <incident> " << std::endl;

  string type = "gridcurve";

  //vol reading and digital set construction
  trace.beginBlock( "Reading vol file into an image." );
  typedef ImageSelector < Domain, int>::Type Image;
  std::string inputFilename = examplesPath + "samples/cat10.vol";
  Image image = VolReader<Image>::importVol(inputFilename);
  DigitalSet set3d (image.domain());
  setFromImage( image, DigitalSetInserter<DigitalSet>(set3d), 1);
  trace.info() << set3d.size() << " voxels." << std::endl;
  trace.endBlock();

  //Khalimsky space construction
  trace.beginBlock( "Construct the Khalimsky space from the image domain." );
  KSpace ks;
/*  bool space_ok = ks.init( image.domain().lowerBound(),
                           image.domain().upperBound(), true );
  if (!space_ok)
    {
      trace.error() << "Error in the Khamisky space construction."<<std::endl;
      return 2;
    }*/
  trace.endBlock();

  //digital surface construction
  typedef SurfelAdjacency<KSpace::dimension> MySurfelAdjacency;
  MySurfelAdjacency surfAdj( true ); // interior in all directions.

  trace.beginBlock( "Extracting boundary by scanning the space. " );
  typedef KSpace::Surfel Surfel;
  typedef KSpace::SurfelSet SurfelSet;
  typedef SetOfSurfels< KSpace, SurfelSet > MySetOfSurfels;
  typedef DigitalSurface< MySetOfSurfels > MyDigitalSurface;
  MySetOfSurfels theSetOfSurfels( ks, surfAdj );
  Surfaces<KSpace>::sMakeBoundary( theSetOfSurfels.surfelSet(),
                                   ks, set3d,
                                   image.domain().lowerBound(),
                                   image.domain().upperBound() );
  MyDigitalSurface digSurf( theSetOfSurfels );
  trace.info() << digSurf.size() << " surfels." << std::endl;
  trace.endBlock();

  //slice retrieving
  trace.beginBlock( "Extracting slice and constructing a grid curve. " );
  typedef MyDigitalSurface::DigitalSurfaceTracker MyTracker;
  typedef DigitalSurface2DSlice< MyTracker > My2DSlice;

  //Extract an initial boundary cell
  Surfel surf = *digSurf.begin();
  MyTracker* tracker = digSurf.container().newTracker( surf );

  // Extract the bondary contour associated to the initial surfel in
  // its first direction
  My2DSlice slice( tracker, *(ks.sDirs( surf )) );
  delete tracker;

  //! [exampleGridCurve3d-Construction]
  GridCurve<KSpace> gc(ks);
  gc.initFromSCellsRange( slice.begin(), slice.end() );
  //! [exampleGridCurve3d-Construction]

  trace.endBlock();


  // for 3D display with Viewer3D
  QApplication application(argc,argv);
  trace.beginBlock( "Display all with QGLViewer." );
  Viewer3D<> viewer;
  viewer.show();
  // Displaying all the surfels in transparent mode
  viewer << SetMode3D( surf.className(), "Transparent");
  for( MyDigitalSurface::ConstIterator it = theSetOfSurfels.begin(),
         it_end = theSetOfSurfels.end(); it != it_end; ++it )
    viewer<< *it;


  // Displaying slice
  viewer << Viewer3D<>::shiftSurfelVisu;
  viewer << SetMode3D( surf.className(), "");
  viewer.setFillColor( Color( 50, 50, 255 ) );

  if (type == "gridcurve")
    {
      viewer  << gc;
    }
  else if (type == "inner")
    {
      viewer << gc.getInnerPointsRange();
    }
  else if (type == "outer")
    {
      viewer << gc.getOuterPointsRange();
    }
  else if (type == "incident")
    {
      viewer << gc.getIncidentPointsRange();
    }
  else
    {
      trace.info() << "Display type not known. Use option -h" << std::endl;
    }

  viewer << Viewer3D<>::updateDisplay;
  trace.endBlock();

  return application.exec();
}
bool testUmbrellaComputer()
{
  using namespace Z3i;
  
  typedef Space::RealPoint RealPoint;
  typedef ImplicitBall<Space> EuclideanShape;
  typedef GaussDigitizer<Space,EuclideanShape> DigitalShape; 
  typedef GaussDigitizer<Space,EuclideanShape>::Domain Domain;
  typedef LightImplicitDigitalSurface<KSpace,DigitalShape> Boundary;
  typedef Boundary::SurfelConstIterator ConstIterator;
  //typedef Boundary::Tracker Tracker;
  typedef Boundary::Surfel Surfel;
  typedef Boundary::DigitalSurfaceTracker DigitalSurfaceTracker;
  typedef DigitalSurface<Boundary> MyDigitalSurface;
  typedef UmbrellaComputer<DigitalSurfaceTracker> MyUmbrellaComputer;

  unsigned int nbok = 0;
  unsigned int nb = 0;
  trace.beginBlock ( "Testing block ... UmbrellaComputer" );
  // Creating shape
  Point c( 0, 0, 0 );
  EuclideanShape ball( c, 2 ); // ball r=4
  DigitalShape shape;
  shape.attach( ball );
  shape.init( RealPoint( -10.0, -10.0, -10.0 ), 
	      RealPoint( 10.0, 10.0, 10.0 ), 1.0 );
  // Creating cellular grid space around.
  Domain domain = shape.getDomain();
  KSpace K;
  nbok += K.init( domain.lowerBound(), domain.upperBound(), true ) ? 1 : 0; 
  nb++;
  trace.info() << "(" << nbok << "/" << nb << ") "
	       << "K.init() is ok" << std::endl;
  // Find start surfel on surface.
  Surfel bel = Surfaces<KSpace>::findABel( K, shape, 10000 );
  // Define surface container then surface itself.
  Boundary boundary( K, // cellular space
		     shape, // point predicate
                     SurfelAdjacency<KSpace::dimension>( true ), // adjacency
		     bel // starting surfel
		     );
  MyDigitalSurface digSurf( boundary ); // boundary is cloned

  // Get tracker on surface.
  DigitalSurfaceTracker* ptrTracker = boundary.newTracker( bel );
  MyUmbrellaComputer umbrella;
  KSpace::DirIterator dirIt = K.sDirs( bel );
  Dimension k = *dirIt;
  Dimension j = *(++dirIt);
  trace.beginBlock ( "Testing block ... forward umbrella" );
  umbrella.init( *ptrTracker, k, true, j );
  unsigned int nb_forward = 0;
  Surfel init_bel = bel;
  do {
    Point x = K.sKCoords( bel );
    trace.info() << x << std::endl;
    umbrella.next();
    ++nb_forward;
    bel = umbrella.surfel();
  } while ( bel != init_bel );
  trace.endBlock();
  trace.beginBlock ( "Testing block ... backward umbrella" );
  unsigned int nb_backward = 0;
  do {
    Point x = K.sKCoords( bel );
    trace.info() << x << std::endl;
    umbrella.previous();
    ++nb_backward;
    bel = umbrella.surfel();
  } while ( bel != init_bel );
  nb++, nbok += nb_forward == nb_backward ? 1 : 0;
  
  trace.info() << "(" << nbok << "/" << nb << ") "
               << " nb_forward(" << nb_forward
	       << ") == nb_backward(" << nb_backward << ")"
	       << std::endl;
  trace.endBlock();
  unsigned int nbsurfels = 0;
  for ( ConstIterator it = boundary.begin(), it_end = boundary.end();
        it != it_end; ++it )
    {
      ++nbsurfels;
    }
  trace.info() << nbsurfels << " surfels found." << std::endl;

  trace.endBlock();

  delete ptrTracker;
  return nbok == nb;
}
int main( int argc, char** argv )
{
  // parse command line ----------------------------------------------
  po::options_description general_opt("Allowed options are");
  general_opt.add_options()
    ("help,h", "display this message")
    ("input,i", po::value< std::string >(), ".vol file")
    ("trivial-radius,t", po::value<double>()->default_value( 3 ), "the parameter t defining the radius for the Trivial estimator, which is used for reorienting II or VCM normal estimations." )
    ("r-radius,r",  po::value< double >(), "Kernel radius r for IntegralInvariant estimator" )
    ("noise,k",  po::value< double >()->default_value(0.5), "Level of Kanungo noise ]0;1[" )
    ("min,l",  po::value<  int >()->default_value(0), "set the minimal image threshold to define the image object (object defined by the voxel with intensity belonging to ]min, max ] )." )
    ("max,u",  po::value<  int >()->default_value(255), "set the minimal image threshold to define the image object (object defined by the voxel with intensity belonging to ]min, max] )." )
    ("lambda,L", po::value<double>()->default_value( 0.05 ), "the parameter lambda of AT functional." )
    ("alpha,a", po::value<double>()->default_value( 0.1 ), "the parameter alpha of AT functional." )
    ("epsilon,e", po::value<double>()->default_value( 4.0 ), "the initial parameter epsilon of AT functional." );

  bool parseOK = true;
  po::variables_map vm;
  try
    {
      po::store( po::parse_command_line( argc, argv, general_opt ), vm );
    }
  catch( const std::exception & ex )
    {
      parseOK = false;
      trace.error() << " Error checking program options: " << ex.what() << std::endl;
    }
  bool neededArgsGiven=true;

  if (parseOK && !(vm.count("input"))){
    missingParam("--input");
    neededArgsGiven=false;
  }
  if (parseOK && !(vm.count("r-radius"))){
    missingParam("--r-radius");
    neededArgsGiven=false;
  }

  double noiseLevel = vm["noise"].as< double >();
  if( noiseLevel < 0.0 || noiseLevel > 1.0 )
    {
      parseOK = false;
      trace.error() << "The noise level should be in the interval: [0, 1]"<< std::endl;
    }

  if(!neededArgsGiven || !parseOK || vm.count("help") || argc <= 1 )
    {
      trace.info()<< "Vol file viewer, with normals regularized by Ambrosio-Tortorelli functionnal" <<std::endl
                  << general_opt << "\n"
                  << "Basic usage: "<<std::endl
                  << "\t at-3d-normals -i file.vol -r 5 --noise 0.1"<<std::endl
                  << std::endl;
      return 0;
    }

  QApplication application(argc,argv);


  int min        =  vm["min"].as<  int >();
  int max        =  vm["max"].as<  int >();
  const double h = 1.0; // not pertinent for now.


  //-----------------------------------------------------------------------------
  // Types.
  typedef Z3i::Space                                 Space;
  typedef Z3i::KSpace                                KSpace;
  typedef Space::RealVector                          RealVector;
  typedef KSpace::SCell                              SCell;
  typedef KSpace::Cell                               Cell;
  typedef KSpace::Surfel                             Surfel;
  typedef Z3i::Domain                                Domain;
  typedef ImageSelector<Domain, unsigned char>::Type Image;
  typedef IntervalForegroundPredicate< Image >       Object;
  typedef KanungoNoise< Object, Domain >             KanungoPredicate;
  typedef BinaryPointPredicate<DomainPredicate<Domain>, KanungoPredicate, AndBoolFct2  > NoisyObject;
  typedef KSpace::SurfelSet                          SurfelSet;
  typedef SetOfSurfels< KSpace, SurfelSet >          MySetOfSurfels;
  typedef DigitalSurface< MySetOfSurfels >           MyDigitalSurface;
  typedef MyDigitalSurface::ConstIterator            ConstIterator;

  //-----------------------------------------------------------------------------
  // Loading vol file.
  trace.beginBlock( "Loading vol file." );
  std::string inputFile = vm[ "input" ].as< std::string >();
  std::string extension = inputFile.substr(inputFile.find_last_of(".") + 1);
  if(extension!="vol" && extension != "p3d" && extension != "pgm3D" && extension != "pgm3d" && extension != "sdp" && extension != "pgm" )
    {
    trace.info() << "File extension not recognized: "<< extension << std::endl;
    return 0;
  }
  Image image = GenericReader<Image>::import (inputFile );
  trace.endBlock();

  //-----------------------------------------------------------------------------
  // Extracting object with possible noise.
  trace.beginBlock( "Extracting object with possible noise." );
  Object                  object( image,  min, max );
  KanungoPredicate        kanungo_pred( object, image.domain(), noiseLevel );
  DomainPredicate<Domain> domain_pred( image.domain() );
  AndBoolFct2             andF;
  NoisyObject             noisy_object(domain_pred, kanungo_pred, andF  );
  Domain                  domain = image.domain();
  KSpace                  K;
  bool space_ok = K.init( domain.lowerBound()-Z3i::Domain::Point::diagonal(),
                          domain.upperBound()+Z3i::Domain::Point::diagonal(), true );
  if (!space_ok)
    {
      trace.error() << "Error in the Khalimsky space construction."<<std::endl;
      return 2;
    }
  CanonicSCellEmbedder< KSpace >       embedder( K );
  SurfelAdjacency< KSpace::dimension > surfAdj( true );
  trace.endBlock();

  //-----------------------------------------------------------------------------
  //! [3dVolBoundaryViewer-ExtractingSurface]
  trace.beginBlock( "Extracting boundary by scanning the space. " );
  MySetOfSurfels theSetOfSurfels( K, surfAdj );
  Surfaces<KSpace>::sMakeBoundary( theSetOfSurfels.surfelSet(),
                                   K, image,
                                   domain.lowerBound(),
                                   domain.upperBound() );
  MyDigitalSurface digSurf( theSetOfSurfels );
  trace.info() << "Digital surface has " << digSurf.size() << " surfels."
               << std::endl;
  trace.endBlock();
  //! [3dVolBoundaryViewer-ExtractingSurface]

  // Map surfel -> estimated normals.
  std::map<SCell,RealVector> n_estimations;

  //-----------------------------------------------------------------------------
  // Estimating orientation of normals
  trace.beginBlock( "Estimating orientation of normals by simple convolutions of trivial surfel normals." );
  double t = vm["trivial-radius"].as<double>();
  typedef RealVector::Component                    Scalar;
  typedef functors::HatFunction<Scalar>            Functor;
  typedef functors::ElementaryConvolutionNormalVectorEstimator< Surfel, CanonicSCellEmbedder<KSpace> > SurfelFunctor;
  typedef ExactPredicateLpSeparableMetric<Space,2> Metric;
  typedef LocalEstimatorFromSurfelFunctorAdapter< MySetOfSurfels, Metric, SurfelFunctor, Functor>      NormalEstimator;
  Functor                      fct( 1.0, t );
  CanonicSCellEmbedder<KSpace> canonic_embedder( K );
  SurfelFunctor                surfelFct( canonic_embedder, 1.0 );
  NormalEstimator              nt_estimator;
  Metric                       aMetric;
  std::vector<RealVector>      nt_estimations;
  nt_estimator.attach( digSurf );
  nt_estimator.setParams( aMetric, surfelFct, fct, t );
  nt_estimator.init( h, digSurf.begin(), digSurf.end());
  nt_estimator.eval( digSurf.begin(), digSurf.end(), std::back_inserter( nt_estimations ) );
  trace.endBlock();

  //-----------------------------------------------------------------------------
  // Estimating normals
  trace.beginBlock( "Estimating normals with II." );
  typedef typename Domain::ConstIterator DomainConstIterator;
  typedef functors::IINormalDirectionFunctor<Space> IINormalFunctor;
  typedef IntegralInvariantCovarianceEstimator<KSpace, NoisyObject, IINormalFunctor> IINormalEstimator;
  std::vector<RealVector> nii_estimations;
  const double            r = vm["r-radius"].as<double>();
  IINormalEstimator       nii_estimator( K, noisy_object );
  trace.info() << " r=" << r << std::endl;
  nii_estimator.setParams( r );
  nii_estimator.init( h, digSurf.begin(), digSurf.end() );
  nii_estimator.eval( digSurf.begin(), digSurf.end(), std::back_inserter( nii_estimations ) );
  // Fix orientations of ii.
  for ( unsigned int i = 0; i < nii_estimations.size(); ++i )
    {
      if ( nii_estimations[ i ].dot( nt_estimations[ i ] ) < 0.0 )
        nii_estimations[ i ] *= -1.0;
    }
  trace.info() << "- nb estimations  = " << nii_estimations.size() << std::endl;
  trace.endBlock();

  // The chosen estimator is II.
  {
    unsigned int i = 0;
    for ( ConstIterator it = digSurf.begin(), itE = digSurf.end(); it != itE; ++it, ++i )
      {
        RealVector nii = nii_estimations[ i ];
        nii /= nii.norm();
        n_estimations[ *it ] = nii;
      }
  }

  //-----------------------------------------------------------------------------
  //! [3dVolBoundaryViewer-ViewingSurface]
  trace.beginBlock( "Displaying everything. " );
  Viewer3D<Space,KSpace> viewer( K );
  viewer.setWindowTitle("Simple boundary of volume Viewer");
  viewer.show();
  viewer << SetMode3D(K.unsigns( *(digSurf.begin()) ).className(), "Basic");
  unsigned int i = 0;
  for ( ConstIterator it = digSurf.begin(), itE = digSurf.end(); it != itE; ++it, ++i )
    {
      viewer.setFillColor( Color( 200, 200, 250 ) );
      Display3DFactory<Space,KSpace>::drawOrientedSurfelWithNormal( viewer, *it, n_estimations[ *it ], false );
    }
  viewer << Viewer3D<>::updateDisplay;
  trace.endBlock();

  //-----------------------------------------------------------------------------
  // Defining Discrete Calculus.
  typedef CubicalComplex< KSpace >                                 CComplex;
  typedef DiscreteExteriorCalculus<2,3, EigenLinearAlgebraBackend> Calculus;
  typedef Calculus::Index                                          Index;
  typedef Calculus::PrimalForm0                                    PrimalForm0;
  typedef Calculus::PrimalForm1                                    PrimalForm1;
  typedef Calculus::PrimalForm2                                    PrimalForm2;
  typedef Calculus::PrimalIdentity0                                PrimalIdentity0;
  typedef Calculus::PrimalIdentity1                                PrimalIdentity1;
  typedef Calculus::PrimalIdentity2                                PrimalIdentity2;
  trace.beginBlock( "Creating Discrete Exterior Calculus. " );
  Calculus calculus;
  calculus.initKSpace<Domain>( domain );
  const KSpace& Kc = calculus.myKSpace; // should not be used.
  // Use a cubical complex to find all lower incident cells easily.
  CComplex complex( K );
  for ( ConstIterator it = digSurf.begin(), itE = digSurf.end(); it != itE; ++it )
    complex.insertCell( 2, K.unsigns( *it ) );
  complex.close();
  for ( CComplex::CellMapIterator it = complex.begin( 0 ), itE = complex.end( 0 ); it != itE; ++it )
    calculus.insertSCell( K.signs( it->first, K.POS ) );
  
  for ( CComplex::CellMapIterator it = complex.begin( 1 ), itE = complex.end( 1 ); it != itE; ++it )
    {
      SCell     linel = K.signs( it->first, K.POS );
      Dimension k     = * K.sDirs( linel );
      bool      pos   = K.sDirect( linel, k );
      calculus.insertSCell( pos ? linel : K.sOpp( linel ) );
      // calculus.insertSCell( K.signs( it->first, K.POS ) );
    }

  // for ( CComplex::CellMapIterator it = complex.begin( 2 ), itE = complex.end( 2 ); it != itE; ++it )
  // calculus.insertSCell( K.signs( it->first, K.POS ) );
  for ( ConstIterator it = digSurf.begin(), itE = digSurf.end(); it != itE; ++it )
    {
      calculus.insertSCell( *it );
      // SCell     surfel = *it;
      // Dimension k      = K.sOrthDir( surfel );
      // bool      pos    = K.sDirect( surfel, k );
      // calculus.insertSCell( pos ? *it : K.sOpp( *it ) );
    }
  calculus.updateIndexes();
  trace.info() << calculus << endl;

  std::vector<PrimalForm2> g;
  g.reserve( 3 );
  g.push_back( PrimalForm2( calculus ) );
  g.push_back( PrimalForm2( calculus ) );
  g.push_back( PrimalForm2( calculus ) );
  Index nb2 = g[ 0 ].myContainer.rows();
  
  for ( Index index = 0; index < nb2; index++)
    {
      const Calculus::SCell& cell = g[ 0 ].getSCell( index );
      if ( theSetOfSurfels.isInside( cell ) ) 
        {
          const RealVector&      n    = n_estimations[ cell ];
          g[ 0 ].myContainer( index ) = n[ 0 ];
          g[ 1 ].myContainer( index ) = n[ 1 ];
          g[ 2 ].myContainer( index ) = n[ 2 ];
        }
      else
        {
          const RealVector&      n    = n_estimations[ K.sOpp( cell ) ];
          g[ 0 ].myContainer( index ) = n[ 0 ];
          g[ 1 ].myContainer( index ) = n[ 1 ];
          g[ 2 ].myContainer( index ) = n[ 2 ];
        }
    }
  cout << endl;
  trace.info() << "primal_D0" << endl;
  const Calculus::PrimalDerivative0 	primal_D0 = calculus.derivative<0,PRIMAL>();
  trace.info() << "primal_D1" << endl;
  const Calculus::PrimalDerivative1 	primal_D1 = calculus.derivative<1,PRIMAL>();
  trace.info() << "dual_D0" << endl;
  const Calculus::DualDerivative0       dual_D0   = calculus.derivative<0,DUAL>();
  trace.info() << "dual_D1" << endl;
  const Calculus::DualDerivative1 	dual_D1   = calculus.derivative<1,DUAL>();
  trace.info() << "primal_h0" << endl;
  const Calculus::PrimalHodge0  	primal_h0 = calculus.hodge<0,PRIMAL>();
  trace.info() << "primal_h1" << endl;
  const Calculus::PrimalHodge1  	primal_h1 = calculus.hodge<1,PRIMAL>();
  trace.info() << "primal_h2" << endl;
  const Calculus::PrimalHodge2     	primal_h2 = calculus.hodge<2,PRIMAL>();
  trace.info() << "dual_h1" << endl;
  const Calculus::DualHodge1         	dual_h1   = calculus.hodge<1,DUAL>();
  trace.info() << "dual_h2" << endl;
  const Calculus::DualHodge2      	dual_h2   = calculus.hodge<2,DUAL>();
  trace.endBlock();

  //-----------------------------------------------------------------------------
  // Building AT functional.
  trace.beginBlock( "Building AT functional. " );
  double a  = vm[ "alpha" ].as<double>();
  double e  = vm[ "epsilon" ].as<double>();
  double l  = vm[ "lambda" ].as<double>();

  // u = g at the beginning
  trace.info() << "u[0,1,2]" << endl;
  std::vector<PrimalForm2> u;
  u.push_back( g[ 0 ] ); u.push_back( g[ 1 ] ); u.push_back( g[ 2 ] );
  // v = 1 at the beginning
  trace.info() << "v" << endl;
  PrimalForm1 v( calculus );
  Index nb1 = v.myContainer.rows();
  for ( Index index = 0; index < nb1; index++)  v.myContainer( index ) = 1.0;
  const PrimalIdentity0 Id0 = calculus.identity<0, PRIMAL>();
  const PrimalIdentity1 Id1 = calculus.identity<1, PRIMAL>();
  const PrimalIdentity2 Id2 = calculus.identity<2, PRIMAL>();
  // Building alpha_
  trace.info() << "alpha_g" << endl;
  const PrimalIdentity2 alpha_Id2 = a * Id2; // a * invG0;
  vector<PrimalForm2> alpha_g;
  alpha_g.push_back( alpha_Id2 * g[ 0 ] );
  alpha_g.push_back( alpha_Id2 * g[ 1 ] );
  alpha_g.push_back( alpha_Id2 * g[ 2 ] );
  trace.info() << "lap_operator_v" << endl;
  const PrimalIdentity1 lap_operator_v = -1.0 * ( primal_D0 * dual_h2 * dual_D1 * primal_h1 
                                                  + dual_h1 * dual_D0 * primal_h2 * primal_D1 );
  // SparseLU is so much faster than SparseQR
  // SimplicialLLT is much faster than SparseLU
  // typedef EigenLinearAlgebraBackend::SolverSparseQR LinearAlgebraSolver;
  // typedef EigenLinearAlgebraBackend::SolverSparseLU LinearAlgebraSolver;
  typedef EigenLinearAlgebraBackend::SolverSimplicialLLT LinearAlgebraSolver;
  typedef DiscreteExteriorCalculusSolver<Calculus, LinearAlgebraSolver, 2, PRIMAL, 2, PRIMAL> SolverU;
  SolverU solver_u;
  typedef DiscreteExteriorCalculusSolver<Calculus, LinearAlgebraSolver, 1, PRIMAL, 1, PRIMAL> SolverV;
  SolverV solver_v;

  trace.info() << "lB'B'" << endl;
  const PrimalIdentity1 lBB = l * lap_operator_v;
  PrimalForm1 l_sur_4( calculus );
  for ( Index index = 0; index < nb1; index++)
    l_sur_4.myContainer( index ) = l / 4.0;
  double coef_eps = 2.0;
  double eps      = 2.0 * e;
  const int n     = 10;
  trace.endBlock();
      
  //-----------------------------------------------------------------------------
  // Solving AT functional.
  trace.beginBlock( "Solving AT functional. " );
  while ( eps / coef_eps >= h )
    {
      eps /= coef_eps;
      trace.info() << "************** epsilon = " << eps << "***************************************" << endl;
      const PrimalIdentity1 BB = eps * lBB + ( l/(4.0*eps) ) * Id1; // tS_S;
      for ( int i = 0; i < n; ++i )
        {
          trace.info() << "---------- Iteration " << i << "/" << n << " ------------------------------" << endl;
          trace.info() << "-------------------------------------------------------------------------------" << endl;
          trace.beginBlock("Solving for u");
          trace.info() << "Building matrix Av2A" << endl;
          PrimalIdentity1 diag_v  = diag( calculus, v );
          PrimalIdentity2 U_Id2 = -1.0 * primal_D1 * diag_v * diag_v * dual_h1 * dual_D0 * primal_h2
            + alpha_Id2;
          trace.info() << "Prefactoring matrix Av2A + alpha_iG0" << endl;
          solver_u.compute( U_Id2 );
          for ( unsigned int d = 0; d < 3; ++d )
            {
              trace.info() << "Solving (Av2A + alpha_iG0) u[" << d << "] = ag[" << d << "]" << endl;
              u[ d ] = solver_u.solve( alpha_g[ d ] );
              trace.info() << "  => " << ( solver_u.isValid() ? "OK" : "ERROR" ) 
                           << " " << solver_u.myLinearAlgebraSolver.info() << endl;
            }
          trace.info() << "-------------------------------------------------------------------------------" << endl;
          trace.endBlock();

          trace.beginBlock("Solving for v");
          const PrimalForm1 former_v = v;
          trace.info() << "Building matrix tu_tA_A_u + BB + Mw2" << endl;
          PrimalIdentity1 V_Id1 = BB;
          for ( unsigned int d = 0; d < 3; ++d )
            {
              const PrimalIdentity1 A_u = diag( calculus, dual_h1 * dual_D0 * primal_h2 * u[ d ] );
              V_Id1.myContainer += square( calculus, A_u ).myContainer;
            }
          trace.info() << "Prefactoring matrix tu_tA_A_u + BB + Mw2" << endl;
          solver_v.compute( V_Id1 );
          trace.info() << "Solving (tu_tA_A_u + BB + Mw2) v = 1/(4eps) * l" << endl;
          v = solver_v.solve( (1.0/eps) * l_sur_4 );
          trace.info() << "  => " << ( solver_v.isValid() ? "OK" : "ERROR" ) 
                       << " " << solver_v.myLinearAlgebraSolver.info() << endl;
          trace.info() << "-------------------------------------------------------------------------------" << endl;
          trace.endBlock();

          for ( Index index = 0; index < nb2; index++)
            {
              double n2 = 0.0;
              for ( unsigned int d = 0; d < 3; ++d )
                n2 += u[ d ].myContainer( index ) * u[ d ].myContainer( index );
              double norm = sqrt( n2 );
              for ( unsigned int d = 0; d < 3; ++d )
                u[ d ].myContainer( index ) /= norm;
            }

          trace.beginBlock("Checking v, computing norms");
          double m1 = 1.0;
          double m2 = 0.0;
          double ma = 0.0;
          for ( Index index = 0; index < nb1; index++)
            {
              double val = v.myContainer( index );
              m1 = std::min( m1, val );
              m2 = std::max( m2, val );
              ma += val;
            }
          trace.info() << "1-form v: min=" << m1 << " avg=" << ( ma / nb1 ) << " max=" << m2 << std::endl;
          for ( Index index = 0; index < nb1; index++)
            v.myContainer( index ) = std::min( std::max(v.myContainer( index ), 0.0) , 1.0 );
          double n_infty = 0.0;
          double n_2 = 0.0;
          double n_1 = 0.0;
          for ( Index index = 0; index < nb1; index++)
            {
              n_infty = std::max( n_infty, fabs( v.myContainer( index ) - former_v.myContainer( index ) ) );
              n_2    += ( v.myContainer( index ) - former_v.myContainer( index ) )
                * ( v.myContainer( index ) - former_v.myContainer( index ) );
              n_1    += fabs( v.myContainer( index ) - former_v.myContainer( index ) );
            }
          n_1 /= v.myContainer.rows();
          n_2 = sqrt( n_2 / v.myContainer.rows() );
          
          trace.info() << "Variation |v^k+1 - v^k|_oo = " << n_infty << endl;
          trace.info() << "Variation |v^k+1 - v^k|_2 = " << n_2 << endl;
          trace.info() << "Variation |v^k+1 - v^k|_1 = " << n_1 << endl;
          trace.endBlock();
          if ( n_infty < 1e-4 ) break;
        } // for ( int i = 0; i < n; ++i )
    }
  trace.endBlock();

  //-----------------------------------------------------------------------------
  // Displaying regularized normals
  trace.beginBlock( "Displaying regularized normals. " );
  Viewer3D<Space,KSpace> viewerR( K );
  viewerR.setWindowTitle("Regularized normals");
  viewerR.show();
  viewerR << SetMode3D(K.unsigns( *(digSurf.begin()) ).className(), "Basic");
  viewerR.setFillColor( Color( 200, 200, 250 ) );
  for ( Index index = 0; index < nb2; index++)
    {
      const SCell& cell    = u[ 0 ].getSCell( index );
      // const RealVector& n  = n_estimations[ cell ];
      RealVector nr        = RealVector( u[ 0 ].myContainer( index ), 
                                         u[ 1 ].myContainer( index ), 
                                         u[ 2 ].myContainer( index ) );
      nr /= nr.norm();
      if ( theSetOfSurfels.isInside( cell ) ) 
        Display3DFactory<Space,KSpace>::drawOrientedSurfelWithNormal( viewerR, cell, nr, false );
      else
        Display3DFactory<Space,KSpace>::drawOrientedSurfelWithNormal( viewerR, K.sOpp( cell ), nr, false );
    }
  viewerR.setLineColor( Color( 255, 0, 0 ) );
  for ( Index index = 0; index < nb1; index++)
    {
      const SCell& cell    = v.getSCell( index );
      Dimension    k       = * K.sDirs( cell ); 
      const SCell  p0      = K.sIncident( cell, k, true );
      const SCell  p1      = K.sIncident( cell, k, false );
      if ( v.myContainer( index ) >= 0.5 ) continue;
      viewerR.addLine( embedder.embed( p0 ), embedder.embed( p1 ), (0.5 - v.myContainer( index ))/ 5.0 );
    }
  viewerR << Viewer3D<>::updateDisplay;
  trace.endBlock();

  return application.exec();

}