DGtal 2.1.0
Loading...
Searching...
No Matches
AffineBasis.h
1
17#pragma once
18
31#if defined(AffineBasis_RECURSES)
32#error Recursive header files inclusion detected in AffineBasis.h
33#else // defined(AffineBasis_RECURSES)
35#define AffineBasis_RECURSES
36
37#if !defined AffineBasis_h
39#define AffineBasis_h
40
42// Inclusions
43#include <type_traits>
44#include <vector>
45#include "DGtal/base/Common.h"
46#include "DGtal/geometry/tools/AffineGeometry.h"
47
48namespace DGtal
49{
51 // template class AffineBasis
52
87 template < typename TPoint >
89 {
91 typedef TPoint Point;
92 typedef typename Point::Coordinate Scalar;
93 typedef std::vector< Point > Points;
95
96 enum struct Type {
97 INVALID = 0,
101 };
102
103 // ----------------------- standard services --------------------------
104 public:
107
114 AffineBasis( const double tolerance = 1e-12)
115 : epsilon( tolerance )
116 {
117 first = Point::zero;
118 second.resize( Point::dimension );
119 for ( auto k = 0; k < Point::dimension; k++ )
120 second[ k ] = Point::base( k );
122 }
123
140 template <typename TInputPoint>
141 AffineBasis( const std::vector< TInputPoint >& points,
143 const double delta = 0.99,
144 const double tolerance = 1e-12 )
145 : epsilon( tolerance )
146 {
147 if ( points.size() == 0 ) return;
148 first = Affine::transform( points[ 0 ] );
149 std::vector< TInputPoint > basis( points.size() - 1 );
150 for ( std::size_t i = 0; i < basis.size(); i++ )
151 basis[ i ] = ( points[ i+1 ] - first );
152 initBasis( basis );
153 reduce( type, delta );
154 }
155
177 template <typename TInputPoint>
178 AffineBasis( const TInputPoint& origin,
179 const std::vector<TInputPoint>& basis,
181 bool is_reduced = false,
182 const double delta = 0.99,
183 const double tolerance = 1e-12 )
184 : epsilon( tolerance )
185 {
187 initBasis( basis );
188 if ( ! is_reduced ) reduce( type, delta );
189 else _type = type;
190 }
191
206 template <typename TInputPoint>
207 AffineBasis( const TInputPoint& origin,
208 const TInputPoint& normal,
210 const double tolerance = 1e-12 )
211 : epsilon( tolerance )
212 {
214 // basis is shortened is type is LLL.
216 _type = ( type == Type::LLL_REDUCED )
218 }
219
229 void reduce( AffineBasis::Type type, double delta )
230 {
231 if ( type == Type::SHORTEST_ECHELON_REDUCED )
232 sortBasis();
234 reduceAsEchelon( type );
235 else if ( type == Type::LLL_REDUCED )
236 {
237 std::vector< Point > X( second.size()+1 );
238 X[ 0 ] = first;
239 for ( auto i = 0; i < second.size(); i++ )
240 X[ i+1 ] = second[ i ] + first;
242 reduceAsLLL( delta, (Scalar) 0 );
243 }
244 }
245
249 {
250 return second.size();
251 }
252
254 const Point& origin() const
255 {
256 return first;
257 }
258
261 const Points& basis() const
262 {
263 return second;
264 }
266
267 // ----------------------- geometry services --------------------------
268 public:
271
276 bool isParallel( const Self& other ) const
277 {
278 if ( dimension() != other.dimension() ) return false;
279 if ( ( _type != Type::ECHELON_REDUCED )
281 trace.error() << "[AffineBasis::isParallel] Requires type=*_ECHELON_REDUCED\n"
282 << " type=" << reductionTypeName() << "\n" ;
283 for ( const auto& b : other.second )
284 if ( ! isParallel( b ) ) return false;
285 return true;
286 }
287
293 std::pair< Scalar, Point > rationalCoordinates( const Point& p ) const
294 {
295 const auto [d, lambda, remainder] = decompose( p );
296 return std::make_pair( d, lambda );
297 }
298
303 bool isOnAffineSpace( const Point& p ) const
304 {
305 const auto [d, lambda, r] = decompose( p );
306 return ! Affine::ScalarOps::isNonZero( r.normInfinity(), epsilon );
307 }
308
313 bool isParallel( const Point& w ) const
314 {
315 const auto [d, lambda, r] = decomposeVector( w );
316 return ! Affine::ScalarOps::isNonZero( r.normInfinity(), epsilon );
317 }
318
337 Point recompose( Scalar d, const Point& lambda,
338 const Point& r = Point::zero ) const
339 {
340 return first + recomposeVector( d, lambda, r );
341 }
342
362 const Point& r = Point::zero ) const
363 {
364 Point w = r;
365 for ( std::size_t i = 0; i < second.size(); i++ )
366 w += lambda[ i ] * second[ i ];
367 return w / d;
368 }
369
378 std::tuple< Scalar, Point, Point > decompose( const Point& p ) const
379 {
380 return decomposeVector( p - first );
381 }
382
391 std::tuple< Scalar, Point, Point > decomposeVector( Point w ) const
392 {
393 Point r;
394 Scalar alphas = 1;
395 for ( auto i = 0; i < second.size(); i++ )
396 {
397 std::pair< Scalar, Scalar > c
398 = Affine::reduceVector( w, second[ i ], i, epsilon );
399 for ( auto j = 0; j < i; j++ )
400 r[ j ] *= c.first;
401 r[ i ] = c.second;
402 alphas *= c.first;
403 }
404 return alphas >= 0
405 ? std::make_tuple( alphas, r, w )
406 : std::make_tuple( -alphas, -r, -w );
407 }
408
420 template <typename ProjectedPoint>
421 Scalar projectPoints( std::vector< ProjectedPoint >& result,
422 const Points& input )
423 {
424 Scalar lcm = 1;
425 std::vector< Scalar > denoms ( input.size() );
426 std::vector< Point > lambdas( input.size() );
427 Point r;
428 // get points rational coordinates.
429 for ( std::size_t i = 0; i < input.size(); i++ )
430 std::tie( denoms[ i ], lambdas[ i ], r ) = decompose( input[ i ] );
431 // compute ppcm
432 for ( std::size_t i = 0; i < denoms.size(); i++ )
433 {
434 const Scalar d = denoms[ i ];
435 if ( d == 1 ) continue;
436 lcm = Affine::ScalarOps::lcmPositive( lcm, d );
437 }
438 // project points
439 result.resize( input.size() );
440 for ( std::size_t i = 0; i < input.size(); i++ )
441 dilatedTransform( result[ i ], lambdas[ i ], lcm / denoms[ i ] );
442 return lcm;
443 }
444
450 template <typename OtherPoint>
451 static
452 void transform( OtherPoint& pp, const Point& p )
453 {
454 BOOST_STATIC_ASSERT( OtherPoint::dimension <= Point::dimension );
455 typedef typename OtherPoint::Coordinate Scalar;
456 for ( std::size_t i = 0; i < pp.dimension; ++i )
457 pp[ i ] = Scalar( p[ i ] );
458 }
459
467 template <typename OtherPoint>
468 static
469 void dilatedTransform( OtherPoint& pp, const Point& p, Scalar m )
470 {
471 BOOST_STATIC_ASSERT( OtherPoint::dimension <= Point::dimension );
472 for ( std::size_t i = 0; i < pp.dimension; ++i )
473 pp[ i ] = m * Scalar( p[ i ] );
474 }
475
477
478 // ----------------------- debug and I/O services --------------------------
479 public:
482
486 void selfDisplay( std::ostream& out ) const
487 {
488 out << "[ AffineBasis o=" << first
489 << " type=" << reductionTypeName()
490 << " B=";
491 for ( auto b : second ) std::cout << "\n " << b;
492 out << " ]";
493 }
494
497 bool isValid() const
498 {
499 return _type != Type::INVALID;
500 }
501
503 std::string reductionTypeName() const
504 {
505 if ( _type == Type::INVALID ) return "INVALID";
506 else if ( _type == Type::ECHELON_REDUCED ) return "ECHELON_REDUCED";
507 else if ( _type == Type::SHORTEST_ECHELON_REDUCED ) return "SHORTEST_ECHELON_REDUCED";
508 else if ( _type == Type::LLL_REDUCED ) return "LLL_REDUCED";
509 else return "";
510 }
512
513 // ----------------------- public data --------------------------
514 public:
517
520 double epsilon {1e-12};
523
524 // ----------------------- protected services --------------------------
525 protected:
528
532 {
533 for ( auto& v : second )
535 }
536
540 {
541 std::vector< bool > is_independent( second.size(), false );
542 std::vector< std::vector< Scalar > > U( second.size() );
543 Dimension k = 0; // the current column to put in echelon form.
544 for ( std::size_t i = 0; i < second.size(); i++ )
545 {
546 std::size_t row = findIndexWithSmallestNonNullComponent( k, i, second );
547 if ( row != i && row != second.size() )
548 std::swap( second[ i ], second[ row ] );
549 Point& w = second[ i ];
550 // check if this vector is independent from the previous ones
551 is_independent[ i ] = true;
552 for ( std::size_t j = 0; j < i; j++ )
553 if ( is_independent[ j ] )
555 if ( ! Affine::ScalarOps::isNonZero( w.normInfinity(), epsilon ) )
556 is_independent[ i ] = false; // not independent, forget it
557 else
558 { // independent, make sure it is reduced.
560 k++;
561 }
562 }
563 Points new_basis;
564 for ( std::size_t i = 0; i < second.size(); i++ )
565 if ( is_independent[ i ] )
566 new_basis.push_back( second[ i ] );
567 std::swap( second, new_basis );
569 _type = type;
570 }
571
574 {
575 auto compare = [this] ( const Point& v, const Point& w ) -> bool
576 {
577 // Note: curiously std::sort sometimes test v against itself
578 // and must return false in this case.
579 for ( auto k = 0; k < Point::dimension; ++k )
580 {
581 bool v_non_null = Affine::ScalarOps::isNonZero( v[ k ], epsilon );
582 bool w_non_null = Affine::ScalarOps::isNonZero( w[ k ], epsilon );
583 if ( v_non_null && ! w_non_null ) return true;
584 else if ( ! v_non_null && w_non_null ) return false;
585 else if ( v_non_null && w_non_null ) return false; // ==
586 }
587 return false; // 0 == 0
588 };
589 std::sort( second.begin(), second.end(), compare );
590 }
591
599 void reduceAsLLL( double delta, Scalar )
600 {
601 if constexpr( std::is_floating_point< Scalar >::value == true )
602 {
603 trace.error() << "[AffineBasis::reduceAsLLL]"
604 << " It has no meaning to use LLL algorithm on matrix with double coefficients\n";
606 return;
607 }
608 for ( auto& v : second )
610 std::vector< std::vector< Scalar > > B( second.size() );
611 for ( auto i = 0; i < second.size(); i++ )
612 {
613 B[ i ] = std::vector< Scalar >( Point::dimension );
614 for ( auto j = 0; j < Point::dimension; j++ )
615 B[ i ][ j ] = second[ i ][ j ];
616 }
619 second.clear();
620 for ( std::size_t i = 0; i < B.size(); i++ )
621 {
622 Point b;
623 for ( auto j = 0; j < Point::dimension; j++ )
624 b[ j ] = B[ i ][ j ];
625 if ( b != Point::zero )
626 second.push_back( Affine::simplifiedVector( b ) );
627 }
629 }
630
636 template <typename TInputPoint>
637 void initBasis( const std::vector<TInputPoint>& basis )
638 {
639 second.reserve( basis.size() );
640 for ( auto i = 0; i < basis.size(); i++ )
641 {
642 Point b = Affine::transform( basis[ i ] );
643 if ( b != Point::zero ) second.push_back( b );
644 }
645 }
646
650 {
651 // Reduces all vectors
652 normalize();
653 // Purge duplicates
654 std::sort( second.begin(), second.end() );
655 second.erase( std::unique( second.begin(), second.end() ), second.end() );
656 // Sort according to size of components.
657 auto compare = []( const Point& u, const Point& v ) -> bool
658 {
659 const auto n1_u = u.norm1();
660 const auto n1_v = v.norm1();
661 if ( n1_u < n1_v ) return true;
662 else if ( n1_v < n1_u ) return false;
663 const auto noo_u = u.normInfinity();
664 const auto noo_v = v.normInfinity();
665 if ( noo_u < noo_v ) return true;
666 else if ( noo_v < noo_u ) return false;
667 return u < v;
668 };
669 std::sort( second.begin(), second.end(), compare );
670 }
671
684 std::size_t
686 std::size_t i,
687 const std::vector< Point >& basis )
688 {
689 ASSERT( ! basis.empty() );
690 ASSERT( k < Point::dimension );
691 std::size_t index = i;
692 Scalar v = 0;
693 for ( ; index < basis.size(); index++ )
694 {
695 v = abs( basis[ index ][ k ] );
696 if ( Affine::ScalarOps::isNonZero( v, epsilon ) )
697 break;
698 }
699 for ( auto j = index + 1; j < basis.size(); j++ )
700 {
701 Scalar vj = abs( basis[ j ][ k ] );
702 if ( vj != 0 && vj < v )
703 {
704 index = j;
705 v = vj;
706 }
707 }
708 return index;
709 }
710
711 }; // struct AffineBasis
712
713} // namespace DGtal
714
716// Includes inline functions.
717// //
719
720template <typename TPoint>
721std::ostream&
722operator<<( std::ostream& out, const DGtal::AffineBasis<TPoint>& B )
723{
724 B.selfDisplay( out );
725 return out;
726}
727
728
729#endif // !defined AffineBasis_h
730
731#undef AffineBasis_RECURSES
732#endif // else defined(AffineBasis_RECURSES)
std::ostream & error()
void reduceBasisWithLLL(std::vector< std::vector< TComponent > > &B, TDouble delta=0.75)
DGtal is the top-level namespace which contains all DGtal functions and types.
std::ostream & operator<<(std::ostream &out, const ATu0v1< TKSpace, TLinearAlgebra > &object)
DGtal::uint32_t Dimension
Definition Common.h:119
Trace trace
Aim: Utility class to determine the affine geometry of an input set of points. It provides exact resu...
Definition AffineBasis.h:89
AffineBasis(const TInputPoint &origin, const std::vector< TInputPoint > &basis, AffineBasis::Type type, bool is_reduced=false, const double delta=0.99, const double tolerance=1e-12)
bool isParallel(const Self &other) const
Point recomposeVector(Scalar d, const Point &lambda, const Point &r=Point::zero) const
void reduce(AffineBasis::Type type, double delta)
AffineBasis::Type _type
the type of reduction of the basis.
std::tuple< Scalar, Point, Point > decompose(const Point &p) const
Dimension dimension() const
const Point & origin() const
void selfDisplay(std::ostream &out) const
AffineGeometry< Point > Affine
Definition AffineBasis.h:94
Points second
the vector basis
AffineBasis< TPoint > Self
Definition AffineBasis.h:90
Point::Coordinate Scalar
Definition AffineBasis.h:92
std::vector< Point > Points
Definition AffineBasis.h:93
AffineBasis(const std::vector< TInputPoint > &points, AffineBasis::Type type, const double delta=0.99, const double tolerance=1e-12)
std::tuple< Scalar, Point, Point > decomposeVector(Point w) const
bool isParallel(const Point &w) const
bool isValid() const
double epsilon
the accepted value below which a floating-point number is 0.
void orderEchelonBasis()
Guarantees that the basis is in echelon form.
static void dilatedTransform(OtherPoint &pp, const Point &p, Scalar m)
AffineBasis(const TInputPoint &origin, const TInputPoint &normal, AffineBasis::Type type=Type::ECHELON_REDUCED, const double tolerance=1e-12)
Point first
the origin of the affine basis
std::size_t findIndexWithSmallestNonNullComponent(Dimension k, std::size_t i, const std::vector< Point > &basis)
@ SHORTEST_ECHELON_REDUCED
echelon matrix starting from shortest vectors
@ LLL_REDUCED
delta-LLL reduced matrix
@ ECHELON_REDUCED
echelon matrix
@ INVALID
invalid basis
std::string reductionTypeName() const
Scalar projectPoints(std::vector< ProjectedPoint > &result, const Points &input)
static void transform(OtherPoint &pp, const Point &p)
void reduceAsEchelon(Type type)
Point recompose(Scalar d, const Point &lambda, const Point &r=Point::zero) const
const Points & basis() const
AffineBasis(const double tolerance=1e-12)
std::pair< Scalar, Point > rationalCoordinates(const Point &p) const
bool isOnAffineSpace(const Point &p) const
void initBasis(const std::vector< TInputPoint > &basis)
void reduceAsLLL(double delta, Scalar)
Aim: Utility class to determine the affine geometry of an input set of points. It provides exact resu...
static const Point & transform(const Point &w)
static std::pair< Scalar, Scalar > reduceVector(Point &w, const Point &b, const double tolerance)
static std::vector< Point > orthogonalLatticeBasis(const TInputPoint &N, bool shortened=false)
static Point simplifiedVector(Point v)
static std::pair< Point, Points > affineBasis(const std::vector< TInputPoint > &X, const double tolerance=1e-12)
unsigned int index(DGtal::uint32_t n, unsigned int b)
Definition testBits.cpp:44
bool compare(const Range1 &pts, const Range2 &groundTruth)
Definition testFP.cpp:98