36#include "DGtal/base/Common.h"
37#include "DGtal/kernel/SpaceND.h"
38#include "DGtal/kernel/domains/HyperRectDomain.h"
40#include "DGtal/topology/KhalimskySpaceND.h"
41#include "DGtal/topology/KhalimskyPreSpaceND.h"
43#include "DGtal/topology/CCellularGridSpaceND.h"
44#include "DGtal/topology/CPreCellularGridSpaceND.h"
46#include "DGtal/topology/SurfelAdjacency.h"
47#include "DGtal/topology/SurfelNeighborhood.h"
48#include "DGtal/shapes/Shapes.h"
49#include "DGtal/topology/helpers/Surfaces.h"
50#include "DGtal/io/boards/Board2D.h"
51#include "DGtal/io/Color.h"
53#include <DGtal/topology/helpers/Surfaces.h>
54#include <DGtal/topology/LightImplicitDigitalSurface.h>
56#include "DGtalCatch.h"
70template <
typename KSpace >
76 INFO(
"Testing uNext & sNext with low = " << low <<
" & high = " << high );
90 refPoint[ i ] = t & (1u << i) ? 1 : 0;
92 INFO(
"Current topology is " << refPoint );
95 const auto refUCell = PK::uCell( refPoint );
96 auto currUCell =
K.uCell( low, refUCell );
97 const auto lowUCell = currUCell;
98 const auto highUCell =
K.uCell( high, refUCell );
99 REQUIRE(
K.uTopology( currUCell ) == PK::uTopology( refUCell ) );
102 const auto refSCell = PK::sCell( refPoint, PK::POS );
103 auto currSCell =
K.sCell( low, refSCell );
104 const auto lowSCell = currSCell;
105 const auto highSCell =
K.sCell( high, refSCell );
106 REQUIRE(
K.sTopology( currSCell ) == PK::sTopology( refSCell ) );
116 REQUIRE( currUCell ==
K.uCell( pt, refUCell ) );
117 REQUIRE( currSCell ==
K.sCell( pt, refSCell ) );
119 uCheck =
K.uNext( currUCell, lowUCell, highUCell );
120 sCheck =
K.sNext( currSCell, lowSCell, highSCell );
138template <
typename KSpace,
typename Cells >
144 REQUIRE( u.size() <= v.size() );
149 for(
auto const & cell : v )
151 if ( !
K.uIsInside( cell ) )
154 REQUIRE( std::find( u.cbegin(), u.cend(),
K.uCell( cell ) ) != u.cend() );
172template <
typename KSpace,
typename Cells >
178 REQUIRE( u.size() <= v.size() );
183 for(
auto const & cell : v )
185 if ( !
K.sIsInside( cell ) )
188 REQUIRE( std::find( u.cbegin(), u.cend(),
K.sCell( cell ) ) != u.cend() );
204template <
typename KSpace >
209 INFO(
"Testing (proper) neighborhood around point " <<
aPoint );
220 refPoint[ i ] = t & (1u << i) ? 1 : 0;
222 INFO(
"Current topology is " << refPoint );
225 const auto refUCell = PK::uCell( refPoint );
227 if ( !
K.uIsInside( PK::uCell(
aPoint, refUCell ) ) )
230 const auto currUCell =
K.uCell(
aPoint, refUCell );
231 REQUIRE(
K.uTopology( currUCell ) == PK::uTopology( refUCell ) );
234 const auto refSCell = PK::sCell( refPoint, PK::NEG );
235 const auto currSCell =
K.sCell(
aPoint, refSCell );
236 REQUIRE(
K.sTopology( currSCell ) == PK::sTopology( refSCell ) );
240 const auto currUCells =
K.uNeighborhood( currUCell );
241 const auto refUCells = PK::uNeighborhood( currUCell );
242 const auto currSCells =
K.sNeighborhood( currSCell );
243 const auto refSCells = PK::sNeighborhood( currSCell );
245 REQUIRE( currUCells.size() == currSCells.size() );
246 REQUIRE( refUCells.size() == refSCells.size() );
247 REQUIRE( refUCells.size() == 2*
K.dimension + 1 );
254 const auto currUCells =
K.uProperNeighborhood( currUCell );
255 const auto refUCells = PK::uProperNeighborhood( currUCell );
256 const auto currSCells =
K.sProperNeighborhood( currSCell );
257 const auto refSCells = PK::sProperNeighborhood( currSCell );
259 REQUIRE( currUCells.size() == currSCells.size() );
260 REQUIRE( refUCells.size() == refSCells.size() );
261 REQUIRE( refUCells.size() == 2*
K.dimension );
275template <
typename KSpace >
280 INFO(
"Testing faces and cofaces around point " <<
aPoint );
291 refPoint[ i ] = t & (1u << i) ? 1 : 0;
293 INFO(
"Current topology is " << refPoint );
296 const auto refUCell = PK::uCell( refPoint );
298 if ( !
K.uIsInside( PK::uCell(
aPoint, refUCell ) ) )
301 const auto currUCell =
K.uCell(
aPoint, refUCell );
302 REQUIRE(
K.uTopology( currUCell ) == PK::uTopology( refUCell ) );
305 const auto refSCell = PK::sCell( refPoint, PK::NEG );
306 const auto currSCell =
K.sCell(
aPoint, refSCell );
307 REQUIRE(
K.sTopology( currSCell ) == PK::sTopology( refSCell ) );
311 const auto currUCells =
K.uFaces( currUCell );
312 const auto refUCells = PK::uFaces( currUCell );
314 REQUIRE( refUCells.size() == floor( std::pow( 3,
K.uDim( currUCell ) ) - 1 ) );
320 const auto currUCells =
K.uCoFaces( currUCell );
321 const auto refUCells = PK::uCoFaces( currUCell );
335template <
typename KSpace >
340 INFO(
"Testing block Incidence in KSpace..." );
347 for ( DirIterator q1 =
K.sDirs( sspel ); q1 != 0; ++q1 )
348 for ( DirIterator q2 =
K.sDirs( sspel ); q2 != 0; ++q2 )
352 SCell s0 =
K.sIncident( sspel, *q1,
true );
353 SCell s1 =
K.sIncident( sspel, *q2,
true );
354 SCell l10 =
K.sIncident( s0, *q2,
true );
355 SCell l01 =
K.sIncident( s1, *q1,
true );
356 INFO(
"D+_" << *q2 <<
"(D+_" << *q1 <<
"(V))=" << l10
357 <<
" D+_" << *q1 <<
"(D+_" << *q2 <<
"(V))=" << l01
371template <
typename KSpace >
376 INFO(
"Testing direct Incidence in KSpace..." );
383 for ( DirIterator q1 =
K.sDirs( sspel ); q1 != 0; ++q1 )
384 for ( DirIterator q2 =
K.sDirs( sspel ); q2 != 0; ++q2 )
388 SCell s0 =
K.sDirectIncident( sspel, *q1 );
389 SCell l10 =
K.sDirectIncident( s0, *q2 );
390 SCell s1 =
K.sDirectIncident( sspel, *q2 );
391 SCell l01 =
K.sDirectIncident( s1, *q1 );
392 INFO(
"Dd_" << *q2 <<
"(Dd_" << *q1 <<
"(V))=" << l10
393 <<
" Dd_" << *q1 <<
"(Dd_" << *q2 <<
"(V))=" << l01
401 REQUIRE( s0 ==
K.sIncident( sspel, *q1,
K.sDirect( sspel, *q1 ) ) );
402 REQUIRE( s1 ==
K.sIncident( sspel, *q2,
K.sDirect( sspel, *q2 ) ) );
403 REQUIRE( l10 ==
K.sIncident( s0, *q2,
K.sDirect( s0, *q2 ) ) );
404 REQUIRE( l01 ==
K.sIncident( s1, *q1,
K.sDirect( s1, *q1 ) ) );
415template <
typename KSpace>
422 INFO(
"Testing surfel adjacency ..." );
425 INFO(
"Testing surfel directness ..." );
426 const SCell sspel =
K.sCell( Point::diagonal(1),
K.POS );
430 SCell surfel =
K.sIncident( sspel, k,
true );
431 SCell innerspel =
K.sDirectIncident( surfel,
K.sOrthDir( surfel ) );
432 INFO(
"spel=" << sspel <<
" surfel=" << surfel <<
" innerspel=" << innerspel );
435 surfel =
K.sIncident( sspel, k,
false );
436 innerspel =
K.sDirectIncident( surfel,
K.sOrthDir( surfel ) );
437 INFO(
"spel=" << sspel <<
" surfel=" << surfel <<
" innerspel=" << innerspel );
441 INFO(
"Testing surfel neighborhood ..." );
443 SCell surfel =
K.sIncident( sspel, 0,
false );
444 SN.
init( &
K, &SAdj, surfel );
446 INFO(
"Testing surface tracking ..." );
451 const Point low = Point::diagonal(-3);
452 const Point high = Point::diagonal(3) + Point::base(0, 2);
459 const Point pcenter = Point::diagonal(0) + Point::base(0);
463 SCell other1, other2;
466 INFO(
"directNext = " << other1 );
469 INFO(
"indirectNext= " << other2 );
471 std::set<SCell> bdry;
473 REQUIRE( bdry.size() == ( 2*
K.dimension*(2*
K.dimension-1) ) );
475 std::set<SCell> bdry_direct;
477 REQUIRE( bdry_direct.size() == ( 2*
K.dimension*(2*
K.dimension-1) ) );
479 if (
K.dimension == 2 )
481 INFO(
"Testing Board2D" );
485 for (
typename std::set<SCell>::const_iterator it = bdry_direct.begin(),
486 it_end = bdry_direct.end(); it != it_end; ++it )
488 board.
saveEPS(
"cells-2.eps" );
489 board.
saveSVG(
"cells-2.svg" );
498template <
typename KSpace>
510 INFO(
"Testing cell draw on digital board ..." );
512 const Point low( -3, -3 );
513 const Point high( 5, 3 );
521 Cell uspel =
K.uCell( Point::diagonal(1) );
524 <<
K.uIncident( uspel, 0,
false )
525 <<
K.uIncident( uspel, 1,
false );
530 Color( 255, 100, 100 ),
534 <<
K.sIncident( sspel2, 0,
K.sDirect( sspel2, 0 ) )
535 <<
K.sIncident( sspel2, 1,
K.sDirect( sspel2, 0 ) );
537 board.
saveEPS(
"cells-1.eps" );
538 board.
saveSVG(
"cells-1.svg" );
542 const SCell slinel0 =
K.sIncident( sspel2, 0,
K.sDirect( sspel2, 0 ) );
543 const SCell spointel01 =
K.sIncident( slinel0, 1,
K.sDirect( slinel0, 1 ) );
547 Color( 255, 100, 100 ) ) )
551 Color( 100, 255, 100 ) ) )
555 Color( 100, 100, 255 ) ) )
558 board.
saveEPS(
"cells-3.eps" );
559 board.
saveSVG(
"cells-3.svg" );
568template <
typename KSpace>
579 INFO(
"Test FindABel" );
581 const Point low = Point::diagonal(-3);
582 const Point high = Point::diagonal(3);
590 const Point p000 = Point::zero;
591 const Point p001 = Point::base(2);
592 const Point p010 = Point::base(1);
593 const Point p011 = p001 + p010;
594 const Point p100 = Point::base(0);
595 const Point p101 = p100 + p001;
596 const Point p110 = p100 + p010;
597 const Point p111 = Point::diagonal(1);
610 REQUIRE( s010 ==
K.sCell( Point::diagonal(1) + Point::base(1),
true ) );
611 REQUIRE( s001 ==
K.sCell( Point::diagonal(1) + Point::base(2),
false ) );
620template <
typename KSpace>
629 const Point low = Point::diagonal(-1);
630 const Point high = Point::diagonal(1);
634 const Cell vox =
K.uSpel( Point::zero );
635 const Cells faces =
K.uFaces( vox );
638 INFO(
"Check CellularGridSpaceND::uFaces" );
645 for (
auto const & face : faces )
646 if (
K.uDim( face ) == k )
665template <
typename KSpace>
674 const Point low = Point::diagonal(-1);
675 const Point high = Point::diagonal(1);
679 const Cell pointel =
K.uPointel( Point::zero );
680 const Cells cofaces =
K.uCoFaces( pointel );
683 INFO(
"Check CellularGridSpaceND::uCoFaces" );
690 for (
auto const & coface : cofaces )
692 if (
K.uDim( coface ) == k )
726 INFO(
"Khalimsky space is " <<
K );
740 INFO(
"Khalimsky space is " <<
K );
754 INFO(
"Khalimsky space is " <<
K );
756 testScan(
K, {-1, -2, -3, -4}, {1, 0, 1, -1} );
765TEST_CASE(
"3D closed Khalimsky space",
"[KSpace][3D][closed]" )
768 const bool spaceOK =
K.init( {-3, -3, -3}, {5, 3, 3},
K.CLOSED );
769 INFO(
"Khalimsky space is " <<
K );
765TEST_CASE(
"3D closed Khalimsky space",
"[KSpace][3D][closed]" ) {
…}
785TEST_CASE(
"2D closed Khalimsky space",
"[KSpace][2D][closed]" )
788 const bool spaceOK =
K.init( {-3, -3}, {5, 3},
K.CLOSED );
789 INFO(
"Khalimsky space is " <<
K );
785TEST_CASE(
"2D closed Khalimsky space",
"[KSpace][2D][closed]" ) {
…}
805TEST_CASE(
"4D closed Khalimsky space",
"[KSpace][4D][closed]" )
808 const bool spaceOK =
K.init( {-3, -3, -3, -3}, {5, 3, 3, 3},
K.CLOSED );
809 INFO(
"Khalimsky space is " <<
K );
812 testScan(
K, {-1, -2, 0, 1}, {1, 2, 1, 2} );
805TEST_CASE(
"4D closed Khalimsky space",
"[KSpace][4D][closed]" ) {
…}
825TEST_CASE(
"2D open Khalimsky space",
"[KSpace][2D][open]" )
828 const bool spaceOK =
K.init( {-3, -3}, {5, 3},
K.OPEN );
829 INFO(
"Khalimsky space is " <<
K );
825TEST_CASE(
"2D open Khalimsky space",
"[KSpace][2D][open]" ) {
…}
845TEST_CASE(
"3D open Khalimsky space",
"[KSpace][3D][open]" )
848 const bool spaceOK =
K.init( {-3, -3, -3}, {5, 3, 3},
K.OPEN );
849 INFO(
"Khalimsky space is " <<
K );
845TEST_CASE(
"3D open Khalimsky space",
"[KSpace][3D][open]" ) {
…}
865TEST_CASE(
"2D periodic Khalimsky space",
"[KSpace][2D][periodic]" )
868 const bool spaceOK =
K.init( {-2, -3}, {2, 3},
K.PERIODIC );
869 INFO(
"Khalimsky space is " <<
K );
865TEST_CASE(
"2D periodic Khalimsky space",
"[KSpace][2D][periodic]" ) {
…}
885TEST_CASE(
"3D periodic Khalimsky space",
"[KSpace][3D][periodic]" )
888 const bool spaceOK =
K.init( {-3, -3, -3}, {2, 2, 3},
K.PERIODIC );
889 INFO(
"Khalimsky space is " <<
K );
885TEST_CASE(
"3D periodic Khalimsky space",
"[KSpace][3D][periodic]" ) {
…}
905TEST_CASE(
"2D mixed Khalimsky space",
"[KSpace][2D][closed][periodic]" )
908 const bool spaceOK =
K.init( {-3, -3}, {5, 2}, {{
K.CLOSED,
K.PERIODIC }} );
909 INFO(
"Khalimsky space is " <<
K );
905TEST_CASE(
"2D mixed Khalimsky space",
"[KSpace][2D][closed][periodic]" ) {
…}
925TEST_CASE(
"3D mixed Khalimsky space",
"[KSpace][3D][closed][periodic][open]" )
928 const bool spaceOK =
K.init( {-3, -3, -3}, {5, 3, 1}, {{
K.CLOSED,
K.OPEN,
K.PERIODIC }} );
929 INFO(
"Khalimsky space is " <<
K );
925TEST_CASE(
"3D mixed Khalimsky space",
"[KSpace][3D][closed][periodic][open]" ) {
…}
946TEST_CASE(
"3D test interior/exterior voxels to a digital surface")
951 for(
auto i=0; i < size*size*size; ++i)
952 set.insertNew(
Point(rand() % size, rand()%size, rand()%size));
953 INFO(
"Inserting " + std::to_string(set.size())+
" voxels") ;
956 Point low( 0, 0, 0 );
957 Point high( 10, 10, 10 );
958 K.init( low, high,
true );
961 for(
const auto &surfel:
surface)
963 auto voxel =
K.interiorVoxel(surfel);
965 auto voxel2 =
K.exteriorVoxel(surfel);
946TEST_CASE(
"3D test interior/exterior voxels to a digital surface") {
…}
Aim: This class specializes a 'Board' class so as to display DGtal objects more naturally (with <<)....
Structure representing an RGB triple with alpha component.
void insert(const Point &p)
Aim: This class is a model of CPreCellularGridSpaceND. It represents the cubical grid as a cell compl...
Aim: This class is a model of CCellularGridSpaceND. It represents the cubical grid as a cell complex,...
Cell uSpel(Point p) const
KhalimskyPreSpaceND< dim, Integer > PreCellularGridSpace
PointVector< dim, Integer > Point
SignedKhalimskyCell< dim, Integer > SCell
SpaceND< dim, Integer > Space
AnyCellCollection< SCell > SCells
static const constexpr Dimension dimension
typename PreCellularGridSpace::DirIterator DirIterator
KhalimskyCell< dim, Integer > Cell
AnyCellCollection< Cell > Cells
Aim: A model of CDigitalSurfaceContainer which defines the digital surface as the boundary of an impl...
static void addNorm1Ball(TDigitalSet &aSet, const Point &aCenter, UnsignedInteger aRadius)
static void trackClosedBoundary(SCellSet &surface, const KSpace &K, const SurfelAdjacency< KSpace::dimension > &surfel_adj, const PointPredicate &pp, const SCell &start_surfel)
static void trackBoundary(SCellSet &surface, const KSpace &K, const SurfelAdjacency< KSpace::dimension > &surfel_adj, const PointPredicate &pp, const SCell &start_surfel)
static SCell findABel(const KSpace &K, const PointPredicate &pp, unsigned int nbtries=1000)
Aim: Represent adjacencies between surfel elements, telling if it follows an interior to exterior ord...
Aim: This helper class is useful to compute the neighboring surfels of a given surfel,...
void init(const KSpace *space, const SurfelAdjacency< KSpace::dimension > *adj, const SCell &aSurfel)
unsigned int getAdjacentOnDigitalSet(SCell &adj_surfel, const DigitalSet &obj, Dimension track_dir, bool pos) const
void clear(const DGtal::Color &color=DGtal::Color::None)
void saveEPS(const char *filename, PageSize size=Board::BoundingBox, double margin=10.0) const
void saveSVG(const char *filename, PageSize size=Board::BoundingBox, double margin=10.0) const
CountedPtr< SH3::DigitalSurface > surface
Point::Coordinate Integer
HyperRectDomain< Space > Domain
KhalimskySpaceND< 3, Integer > KSpace
DigitalSetSelector< Domain, BIG_DS+HIGH_BEL_DS >::Type DigitalSet
DGtal is the top-level namespace which contains all DGtal functions and types.
std::int64_t int64_t
signed 94-bit integer.
DGtal::uint32_t Dimension
Custom style class redefining the pen color and the fill color. You may use Board2D::Color::None for ...
Custom style class redefining the pen attributes. You may use Board2D::Color::None for transparent co...
DigitalSetByAssociativeContainer< Domain, std::unordered_set< typename Domain::Point > > Type
Modifier class in a Board2D stream. Useful to choose your own mode for a given class....
std::string className() const
Return the style name used for drawing this object.
Aim: This concept describes a cellular grid space in nD. In these spaces obtained by cartesian produc...
Aim: This concept describes an unbounded cellular grid space in nD. In these spaces obtained by carte...
void testCellularGridSpaceNDCoFaces(KSpace const &K)
void testCellularGridSpaceNDFaces(KSpace const &K)
void cmpSCellsIfInside(KSpace const &K, typename KSpace::SCells const &u, Cells const &v)
void cmpUCellsIfInside(KSpace const &K, typename KSpace::Cells const &u, Cells const &v)
void testSurfelAdjacency(KSpace const &K)
void testScan(KSpace const &K, typename KSpace::Point const &low, typename KSpace::Point const &high)
void testCellDrawOnBoard(KSpace const &K)
void testDirectIncidence(KSpace const &K, typename KSpace::Point const &aPoint)
void testFaces(KSpace const &K, typename KSpace::Point const &aPoint)
void testNeighborhood(KSpace const &K, typename KSpace::Point const &aPoint)
TEST_CASE("Checking concepts")
void testIncidence(KSpace const &K, typename KSpace::Point const &aPoint)
REQUIRE(domain.isInside(aPoint))
Z2i::DigitalSet DigitalSet