DGtal 1.3.0
Loading...
Searching...
No Matches
testKhalimskySpaceND.cpp
Go to the documentation of this file.
1
33#include <cstddef>
34#include <algorithm>
35
36#include "DGtal/base/Common.h"
37#include "DGtal/kernel/SpaceND.h"
38#include "DGtal/kernel/domains/HyperRectDomain.h"
39
40#include "DGtal/topology/KhalimskySpaceND.h"
41#include "DGtal/topology/KhalimskyPreSpaceND.h"
42
43#include "DGtal/topology/CCellularGridSpaceND.h"
44#include "DGtal/topology/CPreCellularGridSpaceND.h"
45
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"
52
53#include <DGtal/topology/helpers/Surfaces.h>
54#include <DGtal/topology/LightImplicitDigitalSurface.h>
55
56#include "DGtalCatch.h"
58
59using namespace DGtal;
60using namespace std;
61
63
70template < typename KSpace >
71void testScan( KSpace const & K,
72 typename KSpace::Point const & low,
73 typename KSpace::Point const & high
74 )
75{
76 INFO( "Testing uNext & sNext with low = " << low << " & high = " << high );
77
78 using Point = typename KSpace::Point;
79 using Space = typename KSpace::Space;
80 using PK = typename KSpace::PreCellularGridSpace;
81
82 const HyperRectDomain< Space > domain( low, high );
83
84 // Looping through cell topology
85 for ( unsigned int t = 0; t < 1u << KSpace::dimension; ++t )
86 {
87 // Constructing the Khalimsky coordinates
88 Point refPoint;
89 for ( DGtal::Dimension i = 0; i < KSpace::dimension; ++i )
90 refPoint[ i ] = t & (1u << i) ? 1 : 0;
91
92 INFO( "Current topology is " << refPoint );
93
94 // Initializing unsigned cells
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 ) );
100
101 // Initializing signed cells
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 ) );
107
108 // Spanning the domain
109 bool uCheck = true;
110 bool sCheck = true;
111 for ( Point const & pt : domain )
112 {
113 REQUIRE( uCheck == true );
114 REQUIRE( sCheck == true );
115
116 REQUIRE( currUCell == K.uCell( pt, refUCell ) );
117 REQUIRE( currSCell == K.sCell( pt, refSCell ) );
118
119 uCheck = K.uNext( currUCell, lowUCell, highUCell );
120 sCheck = K.sNext( currSCell, lowSCell, highSCell );
121 }
122
123 // Checking scan end conditions.
124 REQUIRE( uCheck == false );
125 REQUIRE( sCheck == false );
126 }
127}
128
130
138template < typename KSpace, typename Cells >
140 typename KSpace::Cells const & u,
141 Cells const & v
142 )
143{
144 REQUIRE( u.size() <= v.size() );
145
146 std::size_t cnt = 0;
147
148 // Scanning the lists
149 for( auto const & cell : v )
150 {
151 if ( ! K.uIsInside( cell ) )
152 continue;
153
154 REQUIRE( std::find( u.cbegin(), u.cend(), K.uCell( cell ) ) != u.cend() );
155
156 ++cnt;
157 }
158
159 // Checking counter
160 REQUIRE( u.size() == cnt );
161}
162
164
172template < typename KSpace, typename Cells >
174 typename KSpace::SCells const & u,
175 Cells const & v
176 )
177{
178 REQUIRE( u.size() <= v.size() );
179
180 std::size_t cnt = 0;
181
182 // Scanning the lists
183 for( auto const & cell : v )
184 {
185 if ( ! K.sIsInside( cell ) )
186 continue;
187
188 REQUIRE( std::find( u.cbegin(), u.cend(), K.sCell( cell ) ) != u.cend() );
189
190 ++cnt;
191 }
192
193 // Checking counter
194 REQUIRE( u.size() == cnt );
195}
196
198
204template < typename KSpace >
206 typename KSpace::Point const & aPoint
207 )
208{
209 INFO( "Testing (proper) neighborhood around point " << aPoint );
210
211 using Point = typename KSpace::Point;
212 using PK = typename KSpace::PreCellularGridSpace;
213
214 // Looping through cell topology
215 for ( unsigned int t = 0; t < 1u << KSpace::dimension; ++t )
216 {
217 // Constructing the Khalimsky coordinates
218 Point refPoint;
219 for ( DGtal::Dimension i = 0; i < KSpace::dimension; ++i )
220 refPoint[ i ] = t & (1u << i) ? 1 : 0;
221
222 INFO( "Current topology is " << refPoint );
223
224 // Constructing the unsigned cell
225 const auto refUCell = PK::uCell( refPoint );
226
227 if ( ! K.uIsInside( PK::uCell( aPoint, refUCell ) ) )
228 continue; // Do not continue if current point is outside space.
229
230 const auto currUCell = K.uCell( aPoint, refUCell );
231 REQUIRE( K.uTopology( currUCell ) == PK::uTopology( refUCell ) );
232
233 // Constructing the signed cell
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 ) );
237
238 // Testing neighbordhoods
239 {
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 );
244
245 REQUIRE( currUCells.size() == currSCells.size() );
246 REQUIRE( refUCells.size() == refSCells.size() );
247 REQUIRE( refUCells.size() == 2*K.dimension + 1 );
248 cmpUCellsIfInside( K, currUCells, refUCells );
249 cmpSCellsIfInside( K, currSCells, refSCells );
250 }
251
252 // Testing proper neighbordhoods
253 {
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 );
258
259 REQUIRE( currUCells.size() == currSCells.size() );
260 REQUIRE( refUCells.size() == refSCells.size() );
261 REQUIRE( refUCells.size() == 2*K.dimension );
262 cmpUCellsIfInside( K, currUCells, refUCells );
263 cmpSCellsIfInside( K, currSCells, refSCells );
264 }
265 }
266}
267
269
275template < typename KSpace >
276void testFaces( KSpace const & K,
277 typename KSpace::Point const & aPoint
278 )
279{
280 INFO( "Testing faces and cofaces around point " << aPoint );
281
282 using Point = typename KSpace::Point;
283 using PK = typename KSpace::PreCellularGridSpace;
284
285 // Looping through cell topology
286 for ( unsigned int t = 0; t < 1u << KSpace::dimension; ++t )
287 {
288 // Constructing the Khalimsky coordinates
289 Point refPoint;
290 for ( DGtal::Dimension i = 0; i < KSpace::dimension; ++i )
291 refPoint[ i ] = t & (1u << i) ? 1 : 0;
292
293 INFO( "Current topology is " << refPoint );
294
295 // Constructing the unsigned cell
296 const auto refUCell = PK::uCell( refPoint );
297
298 if ( ! K.uIsInside( PK::uCell( aPoint, refUCell ) ) )
299 continue; // Do not test if current point is outside space.
300
301 const auto currUCell = K.uCell( aPoint, refUCell );
302 REQUIRE( K.uTopology( currUCell ) == PK::uTopology( refUCell ) );
303
304 // Constructing the signed cell
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 ) );
308
309 // Testing faces
310 {
311 const auto currUCells = K.uFaces( currUCell );
312 const auto refUCells = PK::uFaces( currUCell );
313
314 REQUIRE( refUCells.size() == floor( std::pow( 3, K.uDim( currUCell ) ) - 1 ) );
315 cmpUCellsIfInside( K, currUCells, refUCells );
316 }
317
318 // Testing proper neighbordhoods
319 {
320 const auto currUCells = K.uCoFaces( currUCell );
321 const auto refUCells = PK::uCoFaces( currUCell );
322
323 cmpUCellsIfInside( K, currUCells, refUCells );
324 }
325 }
326}
327
329
335template < typename KSpace >
336void testIncidence( KSpace const & K,
337 typename KSpace::Point const & aPoint
338 )
339{
340 INFO( "Testing block Incidence in KSpace..." );
341
342 using SCell = typename KSpace::SCell;
343 using DirIterator = typename KSpace::DirIterator;
344
345 SCell sspel = K.sSpel( aPoint, K.POS );
346
347 for ( DirIterator q1 = K.sDirs( sspel ); q1 != 0; ++q1 )
348 for ( DirIterator q2 = K.sDirs( sspel ); q2 != 0; ++q2 )
349 {
350 if ( *q1 != *q2 )
351 {
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
358 );
359 REQUIRE( l10 == K.sOpp( l01 ) );
360 }
361 }
362}
363
365
371template < typename KSpace >
373 typename KSpace::Point const & aPoint
374 )
375{
376 INFO( "Testing direct Incidence in KSpace..." );
377
378 using SCell = typename KSpace::SCell;
379 using DirIterator = typename KSpace::DirIterator;
380
381 SCell sspel = K.sSpel( aPoint, K.POS );
382
383 for ( DirIterator q1 = K.sDirs( sspel ); q1 != 0; ++q1 )
384 for ( DirIterator q2 = K.sDirs( sspel ); q2 != 0; ++q2 )
385 {
386 if ( *q1 != *q2 )
387 {
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
394 );
395
396 REQUIRE( l10 != l01 );
397 REQUIRE( K.sSign( s0 ) == K.POS );
398 REQUIRE( K.sSign( s1 ) == K.POS );
399 REQUIRE( K.sSign( l10 ) == K.POS );
400 REQUIRE( K.sSign( l01 ) == K.POS );
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 ) ) );
405 }
406 }
407}
408
410
415template <typename KSpace>
417{
418 using SCell = typename KSpace::SCell;
419 using Point = typename KSpace::Point;
420 using Integer = typename KSpace::Integer;
421
422 INFO( "Testing surfel adjacency ..." );
424
425 INFO( "Testing surfel directness ..." );
426 const SCell sspel = K.sCell( Point::diagonal(1), K.POS );
427
428 for ( Dimension k = 0; k < K.dimension; ++k )
429 {
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 );
433 REQUIRE( sspel == innerspel );
434
435 surfel = K.sIncident( sspel, k, false );
436 innerspel = K.sDirectIncident( surfel, K.sOrthDir( surfel ) );
437 INFO( "spel=" << sspel << " surfel=" << surfel << " innerspel=" << innerspel );
438 REQUIRE( sspel == innerspel );
439 }
440
441 INFO( "Testing surfel neighborhood ..." );
443 SCell surfel = K.sIncident( sspel, 0, false );
444 SN.init( &K, &SAdj, surfel );
445
446 INFO( "Testing surface tracking ..." );
450
451 const Point low = Point::diagonal(-3);
452 const Point high = Point::diagonal(3) + Point::base(0, 2);
455
456 const Domain domain( low, high );
457 DigitalSet shape_set( domain );
458
459 const Point pcenter = Point::diagonal(0) + Point::base(0);
460 Shapes<Domain>::addNorm1Ball( shape_set, pcenter, 1 );
461
462 CAPTURE( surfel );
463 SCell other1, other2;
464
465 SN.getAdjacentOnDigitalSet( other1, shape_set, 1, K.sDirect( surfel, 1 ) );
466 INFO( "directNext = " << other1 );
467
468 SN.getAdjacentOnDigitalSet( other2, shape_set, 1, !K.sDirect( surfel, 1 ) );
469 INFO( "indirectNext= " << other2 );
470
471 std::set<SCell> bdry;
472 Surfaces<KSpace>::trackBoundary( bdry, K, SAdj, shape_set, surfel );
473 REQUIRE( bdry.size() == ( 2*K.dimension*(2*K.dimension-1) ) );
474
475 std::set<SCell> bdry_direct;
476 Surfaces<KSpace>::trackClosedBoundary( bdry_direct, K, SAdj, shape_set, surfel );
477 REQUIRE( bdry_direct.size() == ( 2*K.dimension*(2*K.dimension-1) ) );
478
479 if ( K.dimension == 2 )
480 {
481 INFO( "Testing Board2D" );
482 Board2D board;
484 board << SetMode( domain.className(), "Paving" ) << domain;
485 for ( typename std::set<SCell>::const_iterator it = bdry_direct.begin(),
486 it_end = bdry_direct.end(); it != it_end; ++it )
487 board << *it;
488 board.saveEPS( "cells-2.eps" );
489 board.saveSVG( "cells-2.svg" );
490 }
491}
492
494
498template <typename KSpace>
500{
501 REQUIRE(( K.dimension == 2 ));
502
503 typedef typename KSpace::Integer Integer;
504 typedef typename KSpace::Cell Cell;
505 typedef typename KSpace::SCell SCell;
506 typedef typename KSpace::Point Point;
507 typedef SpaceND<2, Integer> Z2;
509
510 INFO( "Testing cell draw on digital board ..." );
511
512 const Point low( -3, -3 );
513 const Point high( 5, 3 );
514
515 const Domain domain( low, high );
516 Board2D board;
518 board << SetMode( domain.className(), "Paving" )
519 << domain;
520
521 Cell uspel = K.uCell( Point::diagonal(1) ); // pixel 0,0
522 board << uspel
523 << low << high
524 << K.uIncident( uspel, 0, false )
525 << K.uIncident( uspel, 1, false );
526
527 const SCell sspel2 = K.sCell( Point( 5, 1 ), K.POS ); // pixel 2,0
528 board << CustomStyle( sspel2.className(),
529 new CustomPen( Color( 200, 0, 0 ),
530 Color( 255, 100, 100 ),
531 2.0,
533 << sspel2
534 << K.sIncident( sspel2, 0, K.sDirect( sspel2, 0 ) )
535 << K.sIncident( sspel2, 1, K.sDirect( sspel2, 0 ) );
536
537 board.saveEPS( "cells-1.eps" );
538 board.saveSVG( "cells-1.svg" );
539 board.clear();
540
541 board << domain;
542 const SCell slinel0 = K.sIncident( sspel2, 0, K.sDirect( sspel2, 0 ) );
543 const SCell spointel01 = K.sIncident( slinel0, 1, K.sDirect( slinel0, 1 ) );
544
545 board << CustomStyle( sspel2.className(),
546 new CustomColors( Color( 200, 0, 0 ),
547 Color( 255, 100, 100 ) ) )
548 << sspel2
549 << CustomStyle( slinel0.className(),
550 new CustomColors( Color( 0, 200, 0 ),
551 Color( 100, 255, 100 ) ) )
552 << slinel0
553 << CustomStyle( spointel01.className(),
554 new CustomColors( Color( 0, 0, 200 ),
555 Color( 100, 100, 255 ) ) )
556 << spointel01;
557
558 board.saveEPS( "cells-3.eps" );
559 board.saveSVG( "cells-3.svg" );
560}
561
562
564
568template <typename KSpace>
569void testFindABel( KSpace const & K )
570{
571 REQUIRE(( K.dimension >= 3 ));
572
573 typedef typename KSpace::Point Point;
577 typedef typename KSpace::SCell SCell;
578
579 INFO( "Test FindABel" );
580
581 const Point low = Point::diagonal(-3);
582 const Point high = Point::diagonal(3);
583
586
587 const Domain domain( low, high );
588 DigitalSet shape_set( domain );
589
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);
598
599 shape_set.insert( p000 );
600 shape_set.insert( p100 );
601
602 Surfaces<KSpace>::findABel( K, shape_set , p000 , p011 );
603 Surfaces<KSpace>::findABel( K, shape_set , p000 , p110 );
604 Surfaces<KSpace>::findABel( K, shape_set , p000 , p111 );
605 Surfaces<KSpace>::findABel( K, shape_set , p000 , p101 );
606
607 SCell s010 = Surfaces<KSpace>::findABel( K, shape_set , p000 , p010 );
608 SCell s001 = Surfaces<KSpace>::findABel( K, shape_set , p000 , p001 );
609
610 REQUIRE( s010 == K.sCell( Point::diagonal(1) + Point::base(1), true ) );
611 REQUIRE( s001 == K.sCell( Point::diagonal(1) + Point::base(2), false ) );
612}
613
614
616
620template <typename KSpace>
622{
623 typedef typename KSpace::Cell Cell;
624 typedef typename KSpace::Point Point;
625 typedef typename KSpace::Cells Cells;
626
628
629 const Point low = Point::diagonal(-1);
630 const Point high = Point::diagonal(1);
633
634 const Cell vox = K.uSpel( Point::zero );
635 const Cells faces = K.uFaces( vox );
636
637 // Check that there is no duplicates.
638 INFO( "Check CellularGridSpaceND::uFaces" );
639 for ( Dimension k = 0; k < N; ++k )
640 {
641 CAPTURE( k );
642
643 DGtal::int64_t nf = 0;
644
645 for ( auto const & face : faces )
646 if ( K.uDim( face ) == k )
647 {
648 INFO( face );
649 ++nf;
650 }
651
652 // Number of k-faces of N-cube is binom(n,k)*2^(n-k)
653 DGtal::int64_t exp_nf = (DGtal::int64_t) round( boost::math::binomial_coefficient<double>(N, k) );
654 exp_nf <<= N-k;
655
656 REQUIRE( nf == exp_nf );
657 }
658}
659
661
665template <typename KSpace>
667{
668 typedef typename KSpace::Cell Cell;
669 typedef typename KSpace::Point Point;
670 typedef typename KSpace::Cells Cells;
671
673
674 const Point low = Point::diagonal(-1);
675 const Point high = Point::diagonal(1);
678
679 const Cell pointel = K.uPointel( Point::zero );
680 const Cells cofaces = K.uCoFaces( pointel );
681
682 // Check that there is no duplicates.
683 INFO( "Check CellularGridSpaceND::uCoFaces" );
684 for ( Dimension k = 1; k <= N; ++k )
685 {
686 CAPTURE( k );
687
688 DGtal::int64_t nf = 0;
689
690 for ( auto const & coface : cofaces )
691 {
692 if ( K.uDim( coface ) == k )
693 {
694 CAPTURE( coface );
695 ++nf;
696 }
697 }
698
699 CAPTURE( nf );
700
701 // Number of k-faces of N-cube is binom(n,k)*2^(n-k)
702 DGtal::int64_t exp_nf = (DGtal::int64_t) round( boost::math::binomial_coefficient<double>(N, N-k) );
703 exp_nf <<= k;
704
705 REQUIRE( nf == exp_nf );
706 }
707}
708
710// Test cases
711
712TEST_CASE( "Checking concepts" )
713{
717
718 BOOST_CONCEPT_ASSERT(( concepts::CCellularGridSpaceND< KhalimskySpaceND<2> > ));
719 BOOST_CONCEPT_ASSERT(( concepts::CCellularGridSpaceND< KhalimskySpaceND<3> > ));
720 BOOST_CONCEPT_ASSERT(( concepts::CCellularGridSpaceND< KhalimskySpaceND<4> > ));
721}
722
723TEST_CASE( "2D Khalimsky pre-space", "[KPreSpace][2D]" )
724{
726 INFO( "Khalimsky space is " << K );
727
728 testScan( K, {-1, -2}, {1, 2} );
729 testIncidence( K, {0, 0} );
730 testDirectIncidence( K, {0, 0} );
735}
736
737TEST_CASE( "3D Khalimsky pre-space", "[KPreSpace][3D]" )
738{
740 INFO( "Khalimsky space is " << K );
741
742 testScan( K, {-2, -3, -4}, {1, 2, 4} );
743 testIncidence( K, {0, 0, 0} );
744 testDirectIncidence( K, {0, 0, 0} );
746 testFindABel( K );
749}
750
751TEST_CASE( "4D Khalimsky pre-space", "[KPreSpace][4D]" )
752{
754 INFO( "Khalimsky space is " << K );
755
756 testScan( K, {-1, -2, -3, -4}, {1, 0, 1, -1} );
757 testIncidence( K, {0, 0, 0, 0} );
758 testDirectIncidence( K, {0, 0, 0, 0} );
760 testFindABel( K );
763}
764
765TEST_CASE( "3D closed Khalimsky space", "[KSpace][3D][closed]" )
766{
768 const bool spaceOK = K.init( {-3, -3, -3}, {5, 3, 3}, K.CLOSED );
769 INFO( "Khalimsky space is " << K );
770 REQUIRE( spaceOK == true );
771
772 testScan( K, {-1, -2, -1}, {1, 2, 2} );
773 testNeighborhood( K, {0, 0, 0} );
774 testNeighborhood( K, {-2, 3, 2} );
775 testFaces( K, {0, 0, 0} );
776 testFaces( K, {-2, 3, -3} );
777 testIncidence( K, {0, 0, 0} );
778 testDirectIncidence( K, {0, 0, 0} );
780 testFindABel( K );
783}
784
785TEST_CASE( "2D closed Khalimsky space", "[KSpace][2D][closed]" )
786{
788 const bool spaceOK = K.init( {-3, -3}, {5, 3}, K.CLOSED );
789 INFO( "Khalimsky space is " << K );
790 REQUIRE( spaceOK == true );
791
792 testScan( K, {-1, -2}, {1, 2} );
793 testNeighborhood( K, {0, 0} );
794 testNeighborhood( K, {-2, 3} );
795 testFaces( K, {0, 0} );
796 testFaces( K, {-2, 3} );
797 testIncidence( K, {0, 0} );
798 testDirectIncidence( K, {0, 0} );
803}
804
805TEST_CASE( "4D closed Khalimsky space", "[KSpace][4D][closed]" )
806{
808 const bool spaceOK = K.init( {-3, -3, -3, -3}, {5, 3, 3, 3}, K.CLOSED );
809 INFO( "Khalimsky space is " << K );
810 REQUIRE( spaceOK == true );
811
812 testScan( K, {-1, -2, 0, 1}, {1, 2, 1, 2} );
813 testNeighborhood( K, {0, 0, 0, 0} );
814 testNeighborhood( K, {-2, 3, -1, 3} );
815 testFaces( K, {0, 0, 0, 0} );
816 testFaces( K, {-2, 3, -1, 3} );
817 testIncidence( K, {0, 0, 0, 0} );
818 testDirectIncidence( K, {0, 0, 0, 0} );
820 testFindABel( K );
823}
824
825TEST_CASE( "2D open Khalimsky space", "[KSpace][2D][open]" )
826{
828 const bool spaceOK = K.init( {-3, -3}, {5, 3}, K.OPEN );
829 INFO( "Khalimsky space is " << K );
830 REQUIRE( spaceOK == true );
831
832 testScan( K, {-1, -2}, {1, 2} );
833 testNeighborhood( K, {0, 0} );
834 testNeighborhood( K, {-2, 3} );
835 testFaces( K, {0, 0} );
836 testFaces( K, {-2, 3} );
837 testIncidence( K, {0, 0} );
838 testDirectIncidence( K, {0, 0} );
843}
844
845TEST_CASE( "3D open Khalimsky space", "[KSpace][3D][open]" )
846{
848 const bool spaceOK = K.init( {-3, -3, -3}, {5, 3, 3}, K.OPEN );
849 INFO( "Khalimsky space is " << K );
850 REQUIRE( spaceOK == true );
851
852 testScan( K, {-1, -2, -1}, {1, 2, 2} );
853 testNeighborhood( K, {0, 0, 0} );
854 testNeighborhood( K, {-2, 3, 2} );
855 testFaces( K, {0, 0, 0} );
856 testFaces( K, {-2, 3, -3} );
857 testIncidence( K, {0, 0, 0} );
858 testDirectIncidence( K, {0, 0, 0} );
860 testFindABel( K );
863}
864
865TEST_CASE( "2D periodic Khalimsky space", "[KSpace][2D][periodic]" )
866{
868 const bool spaceOK = K.init( {-2, -3}, {2, 3}, K.PERIODIC );
869 INFO( "Khalimsky space is " << K );
870 REQUIRE( spaceOK == true );
871
872 testScan( K, {-1, -2}, {1, 2} );
873 testNeighborhood( K, {0, 0} );
874 testNeighborhood( K, {-2, 3} );
875 testFaces( K, {0, 0} );
876 testFaces( K, {-2, 3} );
877 testIncidence( K, {0, 3} );
878 testDirectIncidence( K, {0, 3} );
883}
884
885TEST_CASE( "3D periodic Khalimsky space", "[KSpace][3D][periodic]" )
886{
888 const bool spaceOK = K.init( {-3, -3, -3}, {2, 2, 3}, K.PERIODIC );
889 INFO( "Khalimsky space is " << K );
890 REQUIRE( spaceOK == true );
891
892 testScan( K, {-1, -2, -1}, {1, 2, 2} );
893 testNeighborhood( K, {0, 0, 0} );
894 testNeighborhood( K, {-2, 3, 2} );
895 testFaces( K, {0, 0, 0} );
896 testFaces( K, {-2, 3, -3} );
897 testIncidence( K, {0, 0, 0} );
898 testDirectIncidence( K, {0, 0, 0} );
900 testFindABel( K );
903}
904
905TEST_CASE( "2D mixed Khalimsky space", "[KSpace][2D][closed][periodic]" )
906{
908 const bool spaceOK = K.init( {-3, -3}, {5, 2}, {{ K.CLOSED, K.PERIODIC }} );
909 INFO( "Khalimsky space is " << K );
910 REQUIRE( spaceOK == true );
911
912 testScan( K, {-1, 2}, {1, 5} );
913 testNeighborhood( K, {0, 0} );
914 testNeighborhood( K, {-2, 4} );
915 testFaces( K, {0, 0} );
916 testFaces( K, {-2, 4} );
917 testIncidence( K, {0, 3} );
918 testDirectIncidence( K, {0, 3} );
923}
924
925TEST_CASE( "3D mixed Khalimsky space", "[KSpace][3D][closed][periodic][open]" )
926{
928 const bool spaceOK = K.init( {-3, -3, -3}, {5, 3, 1}, {{ K.CLOSED, K.OPEN, K.PERIODIC }} );
929 INFO( "Khalimsky space is " << K );
930 REQUIRE( spaceOK == true );
931
932 testScan( K, {-1, -2, -1}, {1, 2, 2} );
933 testNeighborhood( K, {0, 0, 0} );
934 testNeighborhood( K, {-2, 3, 2} );
935 testFaces( K, {0, 0, 0} );
936 testFaces( K, {-2, 3, -3} );
937 testIncidence( K, {0, 0, 0} );
938 testDirectIncidence( K, {0, 0, 0} );
940 testFindABel( K );
943}
944
945
946TEST_CASE("3D test interior/exterior voxels to a digital surface")
947{
948 using Point = Z3i::Point;
949 const auto size=10;
950 Z3i::DigitalSet set(Z3i::Domain(Point(0,0,0),Point(size,size,size)));
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") ;
954 REQUIRE(set.isValid());
956 Point low( 0, 0, 0 );
957 Point high( 10, 10, 10 );
958 K.init( low, high, true );
959 auto bel = Surfaces<Z3i::KSpace>::findABel(K, set);
961 for(const auto &surfel: surface)
962 {
963 auto voxel = K.interiorVoxel(surfel);
964 REQUIRE(set(voxel));
965 auto voxel2 = K.exteriorVoxel(surfel);
966 REQUIRE(!set(voxel2));
967 }
968
969
970}
Aim: This class specializes a 'Board' class so as to display DGtal objects more naturally (with <<)....
Definition: Board2D.h:71
Structure representing an RGB triple with alpha component.
Definition: Color.h:68
Aim: A wrapper class around a STL associative container for storing sets of digital points within som...
std::string className() const
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,...
Point interiorVoxel(const SCell &c) const
For a given surfel ((n-1)-signed cell), returns its interior voxel (point in Z^d given by the direct ...
bool sNext(SCell &p, const SCell &lower, const SCell &upper) const
Increment the cell [p] to its next position (as classically done in a scanning).
Cell uSpel(Point p) const
From the digital coordinates of a point in Zn, builds the corresponding spel (cell of maximal dimensi...
bool uIsInside(const PreCell &p, Dimension k) const
Useful to check if you are going out of the space.
Cell uIncident(const Cell &c, Dimension k, bool up) const
Return the forward or backward unsigned cell incident to [c] along axis [k], depending on [up].
Cells uCoFaces(const Cell &c) const
Return the proper cofaces of [c] (chain of upper incidence) that belong to the space.
TInteger Integer
Arithmetic ring induced by (+,-,*) and Integer numbers.
bool init(const Point &lower, const Point &upper, bool isClosed)
Specifies the upper and lower bounds for the maximal cells in this space.
Dimension sOrthDir(const SCell &s) const
Given a signed surfel [s], returns its orthogonal direction (ie, the coordinate where the surfel is c...
DirIterator sDirs(const SCell &p) const
Given a signed cell [p], returns an iterator to iterate over each coordinate the cell spans.
Cells uFaces(const Cell &c) const
Return the proper faces of [c] (chain of lower incidence) that belong to the space.
Cells uNeighborhood(const Cell &cell) const
Computes the 1-neighborhood of the cell [c] and returns it.
SCell sOpp(const SCell &p) const
Creates the signed cell with the inverse sign of [p].
bool uNext(Cell &p, const Cell &lower, const Cell &upper) const
Increment the cell [p] to its next position (as classically done in a scanning).
Cell uPointel(Point p) const
From the digital coordinates of a point in Zn, builds the corresponding pointel (cell of dimension 0)...
KhalimskyPreSpaceND< dim, Integer > PreCellularGridSpace
bool sDirect(const SCell &p, Dimension k) const
Return 'true' if the direct orientation of [p] along [k] is in the positive coordinate direction.
Integer sTopology(const SCell &p) const
Return the topology word of [p].
SCell sCell(const SPreCell &c) const
From a signed cell, returns a signed cell lying into this Khalismky space.
PointVector< dim, Integer > Point
SCells sProperNeighborhood(const SCell &cell) const
Computes the proper 1-neighborhood of the cell [c] and returns it.
Integer uTopology(const Cell &p) const
Return the topology word of [p].
Cells uProperNeighborhood(const Cell &cell) const
Computes the proper 1-neighborhood of the cell [c] and returns it.
SignedKhalimskyCell< dim, Integer > SCell
SCell sSpel(Point p, Sign sign=POS) const
From the digital coordinates of a point in Zn, builds the corresponding spel (cell of maximal dimensi...
Cell uCell(const PreCell &c) const
From an unsigned cell, returns an unsigned cell lying into this Khalismky space.
static const constexpr Sign POS
SCell sDirectIncident(const SCell &p, Dimension k) const
Return the direct incident cell of [p] along [k] (the incident cell along [k])
@ CLOSED
The dimension is closed and non-periodic.
@ OPEN
The dimension is open.
@ PERIODIC
The dimension is periodic.
SpaceND< dim, Integer > Space
SCells sNeighborhood(const SCell &cell) const
Computes the 1-neighborhood of the cell [c] and returns it.
bool sIsInside(const SPreCell &p, Dimension k) const
Useful to check if you are going out of the space.
AnyCellCollection< SCell > SCells
Sign sSign(const SCell &c) const
Return its sign.
Dimension uDim(const Cell &p) const
Return the dimension of the cell [p].
SCell sIncident(const SCell &c, Dimension k, bool up) const
Return the forward or backward signed cell incident to [c] along axis [k], depending on [up].
static const constexpr Dimension dimension
typename PreCellularGridSpace::DirIterator DirIterator
Point exteriorVoxel(const SCell &c) const
For a given surfel ((n-1)-signed cell), returns its exterior voxel (point in Z^d given by the indirec...
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)
Definition: Board.cpp:152
void saveEPS(const char *filename, PageSize size=Board::BoundingBox, double margin=10.0) const
Definition: Board.cpp:805
void saveSVG(const char *filename, PageSize size=Board::BoundingBox, double margin=10.0) const
Definition: Board.cpp:1012
void setUnit(Unit unit)
Definition: Board.cpp:240
Z3i::SCell SCell
Space::Point Point
Definition: StdDefs.h:168
DGtal is the top-level namespace which contains all DGtal functions and types.
boost::int64_t int64_t
signed 94-bit integer.
Definition: BasicTypes.h:74
DGtal::uint32_t Dimension
Definition: Common.h:137
STL namespace.
Custom style class redefining the pen color and the fill color. You may use Board2D::Color::None for ...
Definition: Board2D.h:279
Custom style class redefining the pen attributes. You may use Board2D::Color::None for transparent co...
Definition: Board2D.h:374
Modifier class in a Board2D stream. Useful to choose your own mode for a given class....
Definition: Board2D.h:247
Represents a signed cell in a cellular grid space by its Khalimsky coordinates and a boolean value.
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...
MyPointD Point
Definition: testClone2.cpp:383
CAPTURE(thicknessHV)
KSpace K
KSpace::Cell Cell
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)
Domain domain
const Point aPoint(3, 4)
HyperRectDomain< Space > Domain
REQUIRE(domain.isInside(aPoint))
bool testFindABel()
Z2i::DigitalSet DigitalSet