This example shows how to use tangency to compute exact or approximate geodesic shortest paths on 3D digital objects, here a unit sphere digitized at your chosen gridstep.
For instance, you may call it with a digitization gridstep 0.0625 and parameter 1.8 (greater than sqrt(3), so guarantees exact shortest paths).
It computes all shortest paths to the lowest point of the digitized sphere, outputs the maximal distance (so less than pi), and checks that the furthest point is indeed antipodal to the source point. Two OBJ files named "sphere1-geodesics.obj "and "sphere1-geodesics-iso.obj" are also outputed, and you may use them to render the result like below.
#include <iostream>
#include <queue>
#include "DGtal/base/Common.h"
#include "DGtal/shapes/Shapes.h"
#include "DGtal/shapes/SurfaceMesh.h"
#include "DGtal/helpers/StdDefs.h"
#include "DGtal/helpers/Shortcuts.h"
#include "DGtal/images/ImageContainerBySTLVector.h"
#include "DGtal/io/writers/SurfaceMeshWriter.h"
#include "DGtal/geometry/volumes/TangencyComputer.h"
using namespace std;
typedef Shortcuts< KSpace >
SH3;
typedef SurfaceMesh< RealPoint, RealVector >
SMesh;
typedef SurfaceMeshWriter< RealPoint, RealVector >
SMeshWriter;
const std::vector< double >& vvalues,
int nb_isolines_per_unit = 10,
const double thickness = 0.1 )
{
std::string cobjname = output;
std::string cisoname = output + "-iso";
auto quantify = [] ( double v, double m, double nb )
{ return round( v/m*nb )*m/nb; };
const auto fvalues =
surfmesh.computeFaceValuesFromVertexValues( vvalues );
double maxValue = * ( std::max_element( vvalues.cbegin(), vvalues.cend() ) );
double minValue = * ( std::min_element( vvalues.cbegin(), vvalues.cend() ) );
double maxDist = maxValue - minValue;
trace.
info() <<
"Max distance is " << maxDist << std::endl;
auto cmap = SH3::getColorMap( 0.0, maxDist );
std::vector< Color > fcolors(
surfmesh.nbFaces() );
for (
Index f = 0; f < fvalues.size(); ++f )
fcolors[ f ] = cmap( quantify( fvalues[ f ] - minValue, maxDist, 50 ) );
SMeshWriter::writeOBJ( cobjname,
surfmesh, fcolors );
double unit = pow( 10.0, floor( log( maxDist ) / log( 10.0 ) ) - 1.0 );
const int N = 10 * nb_isolines_per_unit;
std::vector< double > isolines( N );
std::vector< Color > isocolors( N );
for ( int i = 0; i < N; i++ )
{
isolines [ i ] = (double) i * 10.0 * unit / (double) nb_isolines_per_unit
+ minValue;
isocolors[ i ] = ( i % nb_isolines_per_unit == 0 )
? Color::Red : Color::Black;
}
SMeshWriter::writeIsoLinesOBJ( cisoname,
surfmesh, fvalues, vvalues,
isolines, thickness, isocolors );
}
int main(
int argc,
char** argv )
{
trace.
info() <<
"Usage: " << argv[ 0 ] <<
" <h> <opt>" << std::endl;
trace.
info() <<
"\tComputes shortest paths to a source point on a sphere digitized with gridstep <h>." << std::endl;
trace.
info() <<
"\t- h [==1.0]: digitization gridstep" << std::endl;
trace.
info() <<
"\t- opt [==sqrt(3)]: >= sqrt(3): secure shortest paths, 0: fast" << std::endl;
double h = argc > 1 ? atof( argv[ 1 ] ) : 0.0625;
double opt = argc > 2 ? atof( argv[ 2 ] ) : sqrt(3.0);
auto params = SH3::defaultParameters();
params( "polynomial", "sphere1" )( "gridstep", h );
params( "minAABB", -2)( "maxAABB", 2)( "offset", 1.0 )( "closed", 1 );
auto implicit_shape = SH3::makeImplicitShape3D ( params );
auto digitized_shape = SH3::makeDigitizedImplicitShape3D( implicit_shape, params );
auto K = SH3::getKSpace( params );
params );
auto embedder = SH3::getCellEmbedder(
K );
std::vector< Point > lattice_points;
std::vector< Vertices > faces;
SH3::Cell2Index c2i;
auto pointels = SH3::getPointelRange( c2i,
surface );
for (
auto p : pointels ) lattice_points.push_back(
K.uCoords( p ) );
trace.
info() <<
"#pointels=" << pointels.size() << std::endl;
vertices = SH3::RealPoints( pointels.size() );
std::transform( pointels.cbegin(), pointels.cend(),
vertices.begin(),
[&] (
const SH3::Cell& c) { return h * embedder( c ); } );
{
const auto primal_surfel_vtcs = SH3::getPointelRange(
K, surfel );
std::vector< Index > face;
for ( auto&& primal_vtx : primal_surfel_vtcs )
face.push_back( c2i[ primal_vtx ] );
faces.push_back( face );
}
faces.cbegin(), faces.cend() );
const Index nb = lattice_points.size();
for (
Index i = 1; i < nb; i++ )
{
if ( lattice_points[ i ] < lattice_points[ lowest ] ) lowest = i;
if ( lattice_points[ uppest ] < lattice_points[ i ] ) uppest = i;
}
TangencyComputer< KSpace > TC(
K );
TC.init( lattice_points.cbegin(), lattice_points.cend() );
auto SP = TC.makeShortestPaths( opt );
SP.init( lowest );
double last_distance = 0.0;
while ( ! SP.finished() )
{
last = std::get<0>( SP.current() );
last_distance = std::get<2>( SP.current() );
SP.expand();
}
std::cout << "Max distance is " << last_distance*h << std::endl;
std::cout << "Comput. time is " << time << std::endl;
std::cout << "Last index is " << last << std::endl;
std::cout << "Uppest index is " << uppest << std::endl;
std::vector<double> distances = SP.distances();
for (
Index i = 0; i < distances.size(); i++ )
distances[ i ] *= h;
saveToObj(
"sphere1-geodesics", smesh, distances, 10, 0.1 );
return 0;
}
void beginBlock(const std::string &keyword="")
CountedPtr< SH3::DigitalSurface > surface
CountedPtr< SH3::BinaryImage > binary_image
Space::RealVector RealVector
DigitalPlane::Point Vector
SurfaceMesh< RealPoint, RealVector > SMesh
void saveToObj(const std::string &output, const SMesh &surfmesh, const std::vector< double > &vvalues, int nb_isolines_per_unit=10, const double thickness=0.1)
SurfaceMeshWriter< RealPoint, RealVector > SMeshWriter
DGtal is the top-level namespace which contains all DGtal functions and types.
std::pair< typename graph_traits< DGtal::DigitalSurface< TDigitalSurfaceContainer > >::vertex_iterator, typename graph_traits< DGtal::DigitalSurface< TDigitalSurfaceContainer > >::vertex_iterator > vertices(const DGtal::DigitalSurface< TDigitalSurfaceContainer > &digSurf)
int main(int argc, char **argv)
HyperRectDomain< Space > Domain
PointVector< 3, double > RealPoint