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
110 template < typename TPoint >
112 {
114 typedef TPoint Point;
115 typedef typename Point::Coordinate Scalar;
116 typedef std::vector< Point > Points;
118
119 enum struct Type {
120 INVALID = 0,
124 };
125
126 // ----------------------- standard services --------------------------
127 public:
130
137 AffineBasis( const double tolerance = 1e-12)
138 : epsilon( tolerance )
139 {
140 first = Point::zero;
141 second.resize( Point::dimension );
142 for ( auto k = 0; k < Point::dimension; k++ )
143 second[ k ] = Point::base( k );
145 }
146
163 template <typename TInputPoint>
164 AffineBasis( const std::vector< TInputPoint >& points,
166 const double delta = 0.99,
167 const double tolerance = 1e-12 )
168 : epsilon( tolerance )
169 {
170 if ( points.size() == 0 ) return;
171 first = Affine::transform( points[ 0 ] );
172 std::vector< TInputPoint > basis( points.size() - 1 );
173 for ( std::size_t i = 0; i < basis.size(); i++ )
174 basis[ i ] = ( points[ i+1 ] - first );
175 initBasis( basis );
176 reduce( type, delta );
177 }
178
200 template <typename TInputPoint>
201 AffineBasis( const TInputPoint& origin,
202 const std::vector<TInputPoint>& basis,
204 bool is_reduced = false,
205 const double delta = 0.99,
206 const double tolerance = 1e-12 )
207 : epsilon( tolerance )
208 {
210 initBasis( basis );
211 if ( ! is_reduced ) reduce( type, delta );
212 else _type = type;
213 }
214
229 template <typename TInputPoint>
230 AffineBasis( const TInputPoint& origin,
231 const TInputPoint& normal,
233 const double tolerance = 1e-12 )
234 : epsilon( tolerance )
235 {
237 // basis is shortened is type is LLL.
239 _type = ( type == Type::LLL_REDUCED )
241 }
242
252 void reduce( AffineBasis::Type type, double delta )
253 {
254 if ( type == Type::SHORTEST_ECHELON_REDUCED )
255 sortBasis();
257 reduceAsEchelon( type );
258 else if ( type == Type::LLL_REDUCED )
259 {
260 std::vector< Point > X( second.size()+1 );
261 X[ 0 ] = first;
262 for ( auto i = 0; i < second.size(); i++ )
263 X[ i+1 ] = second[ i ] + first;
265 reduceAsLLL( delta, (Scalar) 0 );
266 }
267 }
268
272 {
273 return second.size();
274 }
275
277 const Point& origin() const
278 {
279 return first;
280 }
281
284 const Points& basis() const
285 {
286 return second;
287 }
289
290 // ----------------------- geometry services --------------------------
291 public:
294
299 bool isParallel( const Self& other ) const
300 {
301 if ( dimension() != other.dimension() ) return false;
302 if ( ( _type != Type::ECHELON_REDUCED )
304 trace.error() << "[AffineBasis::isParallel] Requires type=*_ECHELON_REDUCED\n"
305 << " type=" << reductionTypeName() << "\n" ;
306 for ( const auto& b : other.second )
307 if ( ! isParallel( b ) ) return false;
308 return true;
309 }
310
316 std::pair< Scalar, Point > rationalCoordinates( const Point& p ) const
317 {
318 const auto [d, lambda, remainder] = decompose( p );
319 return std::make_pair( d, lambda );
320 }
321
326 bool isOnAffineSpace( const Point& p ) const
327 {
328 const auto [d, lambda, r] = decompose( p );
329 return ! Affine::ScalarOps::isNonZero( r.normInfinity(), epsilon );
330 }
331
336 bool isParallel( const Point& w ) const
337 {
338 const auto [d, lambda, r] = decomposeVector( w );
339 return ! Affine::ScalarOps::isNonZero( r.normInfinity(), epsilon );
340 }
341
360 Point recompose( Scalar d, const Point& lambda,
361 const Point& r = Point::zero ) const
362 {
363 return first + recomposeVector( d, lambda, r );
364 }
365
385 const Point& r = Point::zero ) const
386 {
387 Point w = r;
388 for ( std::size_t i = 0; i < second.size(); i++ )
389 w += lambda[ i ] * second[ i ];
390 return w / d;
391 }
392
401 std::tuple< Scalar, Point, Point > decompose( const Point& p ) const
402 {
403 return decomposeVector( p - first );
404 }
405
414 std::tuple< Scalar, Point, Point > decomposeVector( Point w ) const
415 {
416 Point r;
417 Scalar alphas = 1;
418 for ( auto i = 0; i < second.size(); i++ )
419 {
420 std::pair< Scalar, Scalar > c
421 = Affine::reduceVector( w, second[ i ], i, epsilon );
422 for ( auto j = 0; j < i; j++ )
423 r[ j ] *= c.first;
424 r[ i ] = c.second;
425 alphas *= c.first;
426 }
427 return alphas >= 0
428 ? std::make_tuple( alphas, r, w )
429 : std::make_tuple( -alphas, -r, -w );
430 }
431
443 template <typename ProjectedPoint>
444 Scalar projectPoints( std::vector< ProjectedPoint >& result,
445 const Points& input )
446 {
447 Scalar lcm = 1;
448 std::vector< Scalar > denoms ( input.size() );
449 std::vector< Point > lambdas( input.size() );
450 Point r;
451 // get points rational coordinates.
452 for ( std::size_t i = 0; i < input.size(); i++ )
453 std::tie( denoms[ i ], lambdas[ i ], r ) = decompose( input[ i ] );
454 // compute ppcm
455 for ( std::size_t i = 0; i < denoms.size(); i++ )
456 {
457 const Scalar d = denoms[ i ];
458 if ( d == 1 ) continue;
459 lcm = Affine::ScalarOps::lcmPositive( lcm, d );
460 }
461 // project points
462 result.resize( input.size() );
463 for ( std::size_t i = 0; i < input.size(); i++ )
464 dilatedTransform( result[ i ], lambdas[ i ], lcm / denoms[ i ] );
465 return lcm;
466 }
467
473 template <typename OtherPoint>
474 static
475 void transform( OtherPoint& pp, const Point& p )
476 {
477 BOOST_STATIC_ASSERT( OtherPoint::dimension <= Point::dimension );
478 typedef typename OtherPoint::Coordinate Scalar;
479 for ( std::size_t i = 0; i < pp.dimension; ++i )
480 pp[ i ] = Scalar( p[ i ] );
481 }
482
490 template <typename OtherPoint>
491 static
492 void dilatedTransform( OtherPoint& pp, const Point& p, Scalar m )
493 {
494 BOOST_STATIC_ASSERT( OtherPoint::dimension <= Point::dimension );
495 for ( std::size_t i = 0; i < pp.dimension; ++i )
496 pp[ i ] = m * Scalar( p[ i ] );
497 }
498
500
501 // ----------------------- debug and I/O services --------------------------
502 public:
505
509 void selfDisplay( std::ostream& out ) const
510 {
511 out << "[ AffineBasis o=" << first
512 << " type=" << reductionTypeName()
513 << " B=";
514 for ( auto b : second ) std::cout << "\n " << b;
515 out << " ]";
516 }
517
520 bool isValid() const
521 {
522 return _type != Type::INVALID;
523 }
524
526 std::string reductionTypeName() const
527 {
528 if ( _type == Type::INVALID ) return "INVALID";
529 else if ( _type == Type::ECHELON_REDUCED ) return "ECHELON_REDUCED";
530 else if ( _type == Type::SHORTEST_ECHELON_REDUCED ) return "SHORTEST_ECHELON_REDUCED";
531 else if ( _type == Type::LLL_REDUCED ) return "LLL_REDUCED";
532 else return "";
533 }
535
536 // ----------------------- public data --------------------------
537 public:
540
543 double epsilon {1e-12};
546
547 // ----------------------- protected services --------------------------
548 protected:
551
555 {
556 for ( auto& v : second )
558 }
559
563 {
564 std::vector< bool > is_independent( second.size(), false );
565 std::vector< std::vector< Scalar > > U( second.size() );
566 Dimension k = 0; // the current column to put in echelon form.
567 for ( std::size_t i = 0; i < second.size(); i++ )
568 {
569 std::size_t row = findIndexWithSmallestNonNullComponent( k, i, second );
570 if ( row != i && row != second.size() )
571 std::swap( second[ i ], second[ row ] );
572 Point& w = second[ i ];
573 // check if this vector is independent from the previous ones
574 is_independent[ i ] = true;
575 for ( std::size_t j = 0; j < i; j++ )
576 if ( is_independent[ j ] )
578 if ( ! Affine::ScalarOps::isNonZero( w.normInfinity(), epsilon ) )
579 is_independent[ i ] = false; // not independent, forget it
580 else
581 { // independent, make sure it is reduced.
583 k++;
584 }
585 }
586 Points new_basis;
587 for ( std::size_t i = 0; i < second.size(); i++ )
588 if ( is_independent[ i ] )
589 new_basis.push_back( second[ i ] );
590 std::swap( second, new_basis );
592 _type = type;
593 }
594
597 {
598 auto compare = [this] ( const Point& v, const Point& w ) -> bool
599 {
600 // Note: curiously std::sort sometimes test v against itself
601 // and must return false in this case.
602 for ( auto k = 0; k < Point::dimension; ++k )
603 {
604 bool v_non_null = Affine::ScalarOps::isNonZero( v[ k ], epsilon );
605 bool w_non_null = Affine::ScalarOps::isNonZero( w[ k ], epsilon );
606 if ( v_non_null && ! w_non_null ) return true;
607 else if ( ! v_non_null && w_non_null ) return false;
608 else if ( v_non_null && w_non_null ) return false; // ==
609 }
610 return false; // 0 == 0
611 };
612 std::sort( second.begin(), second.end(), compare );
613 }
614
622 void reduceAsLLL( double delta, Scalar )
623 {
624 if constexpr( std::is_floating_point< Scalar >::value == true )
625 {
626 trace.error() << "[AffineBasis::reduceAsLLL]"
627 << " It has no meaning to use LLL algorithm on matrix with double coefficients\n";
629 return;
630 }
631 for ( auto& v : second )
633 std::vector< std::vector< Scalar > > B( second.size() );
634 for ( auto i = 0; i < second.size(); i++ )
635 {
636 B[ i ] = std::vector< Scalar >( Point::dimension );
637 for ( auto j = 0; j < Point::dimension; j++ )
638 B[ i ][ j ] = second[ i ][ j ];
639 }
642 second.clear();
643 for ( std::size_t i = 0; i < B.size(); i++ )
644 {
645 Point b;
646 for ( auto j = 0; j < Point::dimension; j++ )
647 b[ j ] = B[ i ][ j ];
648 if ( b != Point::zero )
649 second.push_back( Affine::simplifiedVector( b ) );
650 }
652 }
653
659 template <typename TInputPoint>
660 void initBasis( const std::vector<TInputPoint>& basis )
661 {
662 second.reserve( basis.size() );
663 for ( auto i = 0; i < basis.size(); i++ )
664 {
665 Point b = Affine::transform( basis[ i ] );
666 if ( b != Point::zero ) second.push_back( b );
667 }
668 }
669
673 {
674 // Reduces all vectors
675 normalize();
676 // Purge duplicates
677 std::sort( second.begin(), second.end() );
678 second.erase( std::unique( second.begin(), second.end() ), second.end() );
679 // Sort according to size of components.
680 auto compare = []( const Point& u, const Point& v ) -> bool
681 {
682 const auto n1_u = u.norm1();
683 const auto n1_v = v.norm1();
684 if ( n1_u < n1_v ) return true;
685 else if ( n1_v < n1_u ) return false;
686 const auto noo_u = u.normInfinity();
687 const auto noo_v = v.normInfinity();
688 if ( noo_u < noo_v ) return true;
689 else if ( noo_v < noo_u ) return false;
690 return u < v;
691 };
692 std::sort( second.begin(), second.end(), compare );
693 }
694
707 std::size_t
709 std::size_t i,
710 const std::vector< Point >& basis )
711 {
712 ASSERT( ! basis.empty() );
713 ASSERT( k < Point::dimension );
714 std::size_t index = i;
715 Scalar v = 0;
716 for ( ; index < basis.size(); index++ )
717 {
718 v = abs( basis[ index ][ k ] );
719 if ( Affine::ScalarOps::isNonZero( v, epsilon ) )
720 break;
721 }
722 for ( auto j = index + 1; j < basis.size(); j++ )
723 {
724 Scalar vj = abs( basis[ j ][ k ] );
725 if ( vj != 0 && vj < v )
726 {
727 index = j;
728 v = vj;
729 }
730 }
731 return index;
732 }
733
734 }; // struct AffineBasis
735
736} // namespace DGtal
737
739// Includes inline functions.
740// //
742
743template <typename TPoint>
744std::ostream&
745operator<<( std::ostream& out, const DGtal::AffineBasis<TPoint>& B )
746{
747 B.selfDisplay( out );
748 return out;
749}
750
751
752#endif // !defined AffineBasis_h
753
754#undef AffineBasis_RECURSES
755#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 ClosedIntegerHalfPlane< TSpace > &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...
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
Points second
the vector basis
AffineBasis< TPoint > Self
Point::Coordinate Scalar
std::vector< Point > Points
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