171{
172 if ( argc <= 1 )
173 {
174 usage( argc, argv );
175 return 0;
176 }
178 using namespace DGtal;
185 std::string poly = argv[ 1 ];
186 const double B = argc > 2 ? atof( argv[ 2 ] ) : 1.0;
187 const double h = argc > 3 ? atof( argv[ 3 ] ) : 1.0;
188 const double R = argc > 4 ? atof( argv[ 4 ] ) : 2.0;
189 std::string mode = argc > 5 ? argv[ 5 ] : "Const";
190 bool interpolated = mode == "Interp";
191 if ( interpolated )
192 std::cout << "Using vertex-*Interpolated* Corrected Normal Current" << std::endl;
193 else
194 std::cout << "Using face-*Constant* Corrected Normal Current" << std::endl;
196
197 auto params = SH::defaultParameters() | SHG::defaultParameters();
198 params( "t-ring", 3 )( "surfaceTraversal", "Default" );
199 params( "polynomial", poly )( "gridstep", h );
200 params( "minAABB", -B )( "maxAABB", B );
201 params( "offset", 3.0 );
202 auto shape = SH::makeImplicitShape3D( params );
203 auto K = SH::getKSpace( params );
204 auto dshape = SH::makeDigitizedImplicitShape3D( shape, params );
205 auto bimage = SH::makeBinaryImage( dshape, params );
206 if ( bimage == nullptr )
207 {
208 trace.
error() <<
"Unable to read polynomial <"
209 << poly.c_str() << ">" << std::endl;
210 return 1;
211 }
212 auto sembedder = SH::getSCellEmbedder(
K );
213 auto embedder = SH::getCellEmbedder(
K );
214 auto surface = SH::makeDigitalSurface( bimage,
K, params );
215 auto surfels = SH::getSurfelRange( surface, params );
216 trace.
info() <<
"- surface has " << surfels.size()<<
" surfels." << std::endl;
218
220 SM smesh;
221 std::vector< SM::Vertices > faces;
222 SH::Cell2Index c2i;
223 auto pointels = SH::getPointelRange( c2i, surface );
224 auto vertices = SH::RealPoints( pointels.size() );
225 std::transform( pointels.cbegin(), pointels.cend(),
vertices.begin(),
226 [&] (const SH::Cell& c) { return h * embedder( c ); } );
227 for ( auto&& surfel : *surface )
228 {
229 const auto primal_surfel_vtcs = SH::getPointelRange(
K, surfel );
230 SM::Vertices face;
231 for ( auto&& primal_vtx : primal_surfel_vtcs )
232 face.push_back( c2i[ primal_vtx ] );
233 faces.push_back( face );
234 }
236 faces.cbegin(), faces.cend() );
239
241 auto exp_K1 = SHG::getFirstPrincipalCurvatures ( shape,
K, surfels, params );
242 auto exp_K2 = SHG::getSecondPrincipalCurvatures( shape,
K, surfels, params );
243 auto exp_D1 = SHG::getFirstPrincipalDirections ( shape,
K, surfels, params );
244 auto exp_D2 = SHG::getSecondPrincipalDirections( shape,
K, surfels, params );
246
248
249 CNC cnc( smesh );
250
251 auto face_normals = SHG::getCTrivialNormalVectors( surface, surfels, params );
252
253
254 smesh.setFaceNormals( face_normals.cbegin(), face_normals.cend() );
255
256
257 if ( interpolated ) smesh.computeVertexNormalsFromFaceNormals();
258
259 auto mu0 = cnc.computeMu0();
260 auto muXY = cnc.computeMuXY();
262
264
265
266 std::vector< double > K1( smesh.nbFaces() );
267 std::vector< double >
K2( smesh.nbFaces() );
268 std::vector< RealVector > D1( smesh.nbFaces() );
269 std::vector< RealVector > D2( smesh.nbFaces() );
270 for ( auto f = 0; f < smesh.nbFaces(); ++f )
271 {
272 const auto b = smesh.faceCentroid( f );
273 const auto N = smesh.faceNormals()[ f ];
274 const auto area = mu0 .measure( b, R, f );
275 const auto M = muXY.measure( b, R, f );
276 std::tie( K1[ f ], K2[ f ], D1[ f ], D2[ f ] )
277 = cnc.principalCurvatures( area, M, N );
278 }
280
282 auto exp_K1_min_max = std::minmax_element( exp_K1.cbegin(), exp_K1.cend() );
283 auto exp_K2_min_max = std::minmax_element( exp_K2.cbegin(), exp_K2.cend() );
284 auto K1_min_max = std::minmax_element( K1.cbegin(), K1.cend() );
285 auto K2_min_max = std::minmax_element(
K2.cbegin(),
K2.cend() );
286 std::cout << "Expected K1 curvatures:"
287 << " min=" << *exp_K1_min_max.first << " max=" << *exp_K1_min_max.second
288 << std::endl;
289 std::cout << "Computed k1 curvatures:"
290 << " min=" << *K1_min_max.first << " max=" << *K1_min_max.second
291 << std::endl;
292 std::cout << "Expected k2 curvatures:"
293 << " min=" << *exp_K2_min_max.first << " max=" << *exp_K2_min_max.second
294 << std::endl;
295 std::cout << "Computed k2 curvatures:"
296 << " min=" << *K2_min_max.first << " max=" << *K2_min_max.second
297 << std::endl;
298 const auto error_K1 = SHG::getScalarsAbsoluteDifference( K1, exp_K1 );
299 const auto stat_error_K1 = SHG::getStatistic( error_K1 );
300 const auto error_K1_l2 = SHG::getScalarsNormL2( K1, exp_K1 );
301 trace.
info() <<
"|K1-K1_CNC|_oo = " << stat_error_K1.max() << std::endl;
302 trace.
info() <<
"|K1-K1_CNC|_2 = " << error_K1_l2 << std::endl;
303 const auto error_K2 = SHG::getScalarsAbsoluteDifference( K2, exp_K2 );
304 const auto stat_error_K2 = SHG::getStatistic( error_K2 );
305 const auto error_K2_l2 = SHG::getScalarsNormL2( K2, exp_K2 );
306 trace.
info() <<
"|K2-K2_CNC|_oo = " << stat_error_K2.max() << std::endl;
307 trace.
info() <<
"|K2-K2_CNC|_2 = " << error_K2_l2 << std::endl;
309
311
312 smesh.vertexNormals() = SH::RealVectors();
313 smesh.faceNormals() = SH::RealVectors();
315 const double Kmax = std::max( fabs( *exp_K1_min_max.first ),
316 fabs( *exp_K2_min_max.second ) );
319 auto colorsK1 = SMW::Colors( smesh.nbFaces() );
320 auto colorsK2 = SMW::Colors( smesh.nbFaces() );
321 for ( auto i = 0; i < smesh.nbFaces(); i++ )
322 {
323 colorsK1[ i ] = colormapK1( K1[ i ] );
324 colorsK2[ i ] = colormapK2( K2[ i ] );
325 }
326 SMW::writeOBJ( "example-cnc-K1", smesh, colorsK1 );
327 SMW::writeOBJ( "example-cnc-K2", smesh, colorsK2 );
328 const auto avg_e = smesh.averageEdgeLength();
329 SH::RealPoints positions( smesh.nbFaces() );
330 for ( auto f = 0; f < positions.size(); ++f )
331 {
332 D1[ f ] *= smesh.localWindow( f );
333 positions[ f ] = smesh.faceCentroid( f ) - 0.5 * D1[ f ];
334 }
335 SH::saveVectorFieldOBJ( positions, D1, 0.05 * avg_e, SH::Colors(),
336 "example-cnc-D1",
337 SH::Color::Black, SH::Color( 0, 128, 0 ) );
338 for ( auto f = 0; f < positions.size(); ++f )
339 {
340 D2[ f ] *= smesh.localWindow( f );
341 positions[ f ] = smesh.faceCentroid( f ) - 0.5 * D2[ f ];
342 }
343 SH::saveVectorFieldOBJ( positions, D2, 0.05 * avg_e, SH::Colors(),
344 "example-cnc-D2",
345 SH::Color::Black, SH::Color(128, 0,128 ) );
347 return 0;
348}
Aim: This class is used to simplify shape and surface creation. With it, you can create new shapes an...
Aim: This class is used to simplify shape and surface creation. With it, you can create new shapes an...
DGtal::GradientColorMap< double > makeColorMap(double min_value, double max_value)
[curvature-measures-Includes]
Z3i this namespace gathers the standard of types for 3D imagery.
DGtal is the top-level namespace which contains all DGtal functions and types.
QuantifiedColorMap< TColorMap > makeQuantifiedColorMap(TColorMap colormap, int nb=50)
std::pair< typename graph_traits< DGtal::DigitalSurface< TDigitalSurfaceContainer > >::vertex_iterator, typename graph_traits< DGtal::DigitalSurface< TDigitalSurfaceContainer > >::vertex_iterator > vertices(const DGtal::DigitalSurface< TDigitalSurfaceContainer > &digSurf)
Aim: Utility class to compute curvature measures induced by (1) a corrected normal current defined by...
Aim: An helper class for writing mesh file formats (Waverfront OBJ at this point) and creating a Surf...
Aim: Represents an embedded mesh as faces and a list of vertices. Vertices may be shared among faces ...