From e61c681e415b05d63b2bb6d470e928d46c7afe34 Mon Sep 17 00:00:00 2001 From: skramm Date: Sun, 7 Apr 2024 23:38:18 +0200 Subject: [PATCH] added warning macro, added handling of path SVG command --- docs/homog2d_manual.md | 2 +- homog2d.hpp | 137 +- misc/homog2d_test.cpp | 4227 ++++++++++++++++++++-------------------- 3 files changed, 2219 insertions(+), 2147 deletions(-) diff --git a/docs/homog2d_manual.md b/docs/homog2d_manual.md index d4243bb..35ab133 100644 --- a/docs/homog2d_manual.md +++ b/docs/homog2d_manual.md @@ -2173,7 +2173,7 @@ or [`polygon`](https://www.w3.org/TR/SVG2/shapes.html#PolygonElement)), or by us [`path`](https://www.w3.org/TR/SVG2/paths.html#PathElement) element, that is much more general. This import subsystem handles both the `polyline` , `polygon` and `path` elements. However, for the latter, the "curve" elements (SVG path commands C, S, Q, T) are not handled, -the import code will throw if such a command is encoutered while importing a SVG path object. +the import code will just ignore thoses commands, if encoutered while importing a SVG path object. * When importing a SVG "path", it will be automatically converted to a `CPolyline` or a `OPolyline`, depending on the fact that it holds a `z` at the end of the SVG path "d" string. * If you have trouble with some SVG file, a helper function `printFileAttrib()` is provided that will output all the SVG attributes of a file on `stdout`. diff --git a/homog2d.hpp b/homog2d.hpp index daeb8db..aef19b3 100644 --- a/homog2d.hpp +++ b/homog2d.hpp @@ -145,6 +145,13 @@ See https://github.com/skramm/homog2d static_assert( (std::is_arithmetic::value && !std::is_same::value), "Type of value must be numerical" ) #endif +#ifndef HOMOG2D_NOWARNINGS +#define HOMOG2D_LOG_WARNING( a ) \ + std::cerr << "homog2d warning (" << ++err::warningCount() << "), line " << __LINE__ << "\n msg=" << a << "\n"; +#else +#define HOMOG2D_LOG_WARNING +#endif + /* \todo 20230212 ttmath support: this definition does not work, I don't know why !!! see namespace \ref trait \verbatim @@ -244,6 +251,14 @@ inline size_t& errorCount() static size_t c; return c; } + +/// Used in macro HOMOG2D_LOG_WARNING +inline size_t& warningCount() +{ + static size_t c; + return c; +} + } //namespace err /// Holds the types needed for policy based design @@ -11527,6 +11542,8 @@ numberValues() enum class PathMode { Absolute, Relative }; +/// Holds the current SVG "path" command, and the number of required numerical values +/// \sa SvgValuesBuffer struct SvgPathCommand { PathMode _absRel = PathMode::Absolute; @@ -11541,8 +11558,15 @@ struct SvgPathCommand _command = c; _nbValues = numberValues().at(c); } + bool isNewPolyline() const + { + if( _command == 'm' || _command == 'M' || _command == 'z' || _command == 'Z' ) + return true; + return false; + } }; + /// Generate new point from current mode and previous point, handles absolute/relative coordinates inline Point2d_ @@ -11707,14 +11731,14 @@ m 261.68497,138.79393 2.57,3.15 -0.72,1.27 2.18,1.94 -0.7,4.93 1.88,0.9 The return value holds as 'second' a bool value, will be true if closed polyline */ inline -std::pair,bool> +auto parsePath( const char* s ) { SvgPathCommand mode; - std::vector out; + std::vector> vout(1); SvgValuesBuffer values; -// std::vector values; std::string str(s); + size_t idx = 0; HOMOG2D_LOG( "parsing string -" << str << "- #=" << str.size() ); if( str.size() == 0 ) HOMOG2D_THROW_ERROR_1( "SVG path string is empty" ); @@ -11724,34 +11748,48 @@ parsePath( const char* s ) do { auto e = getNextElem( str, it ); - HOMOG2D_LOG( "parsing element -" << e << "- #=" << e.size() ); +// HOMOG2D_LOG( "parsing element -" << e << "- #=" << e.size() ); if( e.size() == 1 && !isDigit(e[0]) ) // we have a command ! { if( values.size() != 0 ) // if we have some values stored, - values.storeValues( out, mode ); // first process them an add new point + values.storeValues( vout[idx], mode ); // first process them and add new point mode = getCommand( e[0] ); + HOMOG2D_LOG( "command=" << e[0] ); if( !svgPathCommandIsAllowed(mode._command) ) HOMOG2D_THROW_ERROR_1( "SVG path command -" << mode._command << "- not handled" ); + if( mode.isNewPolyline() && !vout[idx].empty() ) + { + vout.push_back( std::vector() ); + idx++; + HOMOG2D_LOG( "NEW vector idx=" << idx ); + } } else // not a command, but a value { - HOMOG2D_LOG( "process value, values size=" << values.size() ); +// HOMOG2D_LOG( "process value, values size=" << values.size() ); if( values.size() == (size_t)mode._nbValues ) // already got enough values - values.storeValues( out, mode ); + values.storeValues( vout[idx], mode ); values.addValue( e ); } } while( it < str.cend() ); - if( values.size() ) - values.storeValues( out, mode ); -//priv::printVector( out, "point set", true ); + if( values.size() ) // process remaining values that have been stored + values.storeValues( vout[idx], mode ); +HOMOG2D_LOG( "Nb vectors=" << vout.size() ); + for( auto& v: vout ) + if( v.size() ) + v = purgeSetDupes( v ); + if( vout.back().empty() ) + vout.pop_back(); + +HOMOG2D_LOG( "RETURN" ); return std::make_pair( - purgeSetDupes( out ), + vout, mode._command == 'Z' ? true : false ); } @@ -11768,8 +11806,7 @@ class Visitor: public tinyxml2::XMLVisitor /// This type is used to provide a type that can be used in a switch (see VisitExit() ), /// as this cannot be done with a string |-( enum SvgType { - T_circle, T_rect, T_line, T_polygon, T_polyline, T_ellipse - ,T_path ///< preliminar + T_circle, T_rect, T_line, T_polygon, T_polyline, T_ellipse ,T_path ,T_other ///< for other elements (\c ) or illegal ones, that will just be ignored }; @@ -11809,6 +11846,7 @@ class Visitor: public tinyxml2::XMLVisitor bool VisitExit( const tinyxml2::XMLElement& ) override; }; +namespace svgp { //------------------------------------------------------------------ /// Fetch attribute from XML element. Tag \c e_name is there just in case of trouble. double @@ -11833,6 +11871,8 @@ getAttribString( const char* attribName, const tinyxml2::XMLElement& e ) return pts; } +} // namespace svgp + /// This is the place where actual SVG data is converted and stored into vector /** (see manual, section "SVG import") @@ -11849,17 +11889,23 @@ bool Visitor::VisitExit( const tinyxml2::XMLElement& e ) { case T_circle: { - std::unique_ptr c( new Circle( getAttribValue( e, "cx", n ), getAttribValue( e, "cy", n ), getAttribValue( e, "r", n ) ) ); + std::unique_ptr c( + new Circle( + svgp::getAttribValue( e, "cx", n ), + svgp::getAttribValue( e, "cy", n ), + svgp::getAttribValue( e, "r", n ) + ) + ); _vec.push_back( std::move(c) ); } break; case T_rect: { - auto x1 = getAttribValue( e, "x", n ); - auto y1 = getAttribValue( e, "y", n ); - auto w = getAttribValue( e, "width", n ); - auto h = getAttribValue( e, "height", n ); + auto x1 = svgp::getAttribValue( e, "x", n ); + auto y1 = svgp::getAttribValue( e, "y", n ); + auto w = svgp::getAttribValue( e, "width", n ); + auto h = svgp::getAttribValue( e, "height", n ); std::unique_ptr r( new FRect( x1, y1, x1+w, y1+h ) ); _vec.push_back( std::move(r) ); } @@ -11868,7 +11914,12 @@ bool Visitor::VisitExit( const tinyxml2::XMLElement& e ) case T_line: { std::unique_ptr s( - new Segment( getAttribValue( e, "x1", n ), getAttribValue( e, "y1", n ), getAttribValue( e, "x2", n ), getAttribValue( e, "y2", n ) ) + new Segment( + svgp::getAttribValue( e, "x1", n ), + svgp::getAttribValue( e, "y1", n ), + svgp::getAttribValue( e, "x2", n ), + svgp::getAttribValue( e, "y2", n ) + ) ); _vec.push_back( std::move(s) ); } @@ -11876,7 +11927,7 @@ bool Visitor::VisitExit( const tinyxml2::XMLElement& e ) case T_polygon: { - auto pts_str = getAttribString( "points", e ); + auto pts_str = svgp::getAttribString( "points", e ); auto vec_pts = svgp::parsePoints( pts_str ); std::unique_ptr p( new CPolyline(vec_pts) ); _vec.push_back( std::move(p) ); @@ -11885,37 +11936,51 @@ bool Visitor::VisitExit( const tinyxml2::XMLElement& e ) case T_polyline: { - auto pts_str = getAttribString( "points", e ); + auto pts_str = svgp::getAttribString( "points", e ); auto vec_pts = svgp::parsePoints( pts_str ); std::unique_ptr p( new OPolyline(vec_pts) ); _vec.push_back( std::move(p) ); } break; - case T_path: + case T_path: // a path can hold multiple polygons (because of the 'L' command) { - auto pts_str = getAttribString( "d", e ); - auto parse_res = svgp::parsePath( pts_str ); - if( parse_res.second == true ) + auto pts_str = svgp::getAttribString( "d", e ); + try { - std::unique_ptr p( new CPolyline(parse_res.first) ); - _vec.push_back( std::move(p) ); - } - else + auto parse_res = svgp::parsePath( pts_str ); + const auto& vec_vec_pts = parse_res.first; // + for( const auto& vec_pts: vec_vec_pts ) + if( parse_res.second == true ) + { +// _vecVar.emplace_back( CPolyline(vec_pts) ); + std::unique_ptr p( new CPolyline(vec_pts) ); + _vec.push_back( std::move(p) ); + + } + else + { +// _vecVar.emplace_back( OPolyline(vec_pts) ); + std::unique_ptr p( new OPolyline(vec_pts) ); + _vec.push_back( std::move(p) ); + } + } + catch( std::exception& err ) // an unhandled path command will just get the whole path command ignored { - std::unique_ptr p( new OPolyline(parse_res.first) ); - _vec.push_back( std::move(p) ); + HOMOG2D_LOG_WARNING( "Unable to import SVG path command\n -msg=" + << err.what() << "\n -input string=" << pts_str + ); } } break; case T_ellipse: { - auto x = getAttribValue( e, "cx", n ); - auto y = getAttribValue( e, "cy", n ); - auto rx = getAttribValue( e, "rx", n ); - auto ry = getAttribValue( e, "ry", n ); - auto rot = svgp::getEllipseRotateAttr( getAttribString( "transform", e ) ); + auto x = svgp::getAttribValue( e, "cx", n ); + auto y = svgp::getAttribValue( e, "cy", n ); + auto rx = svgp::getAttribValue( e, "rx", n ); + auto ry = svgp::getAttribValue( e, "ry", n ); + auto rot = svgp::getEllipseRotateAttr( svgp::getAttribString( "transform", e ) ); Ellipse* ell = new Ellipse( x, y, rx, ry ); auto H = Homogr().addTranslation(-x,-y).addRotation(rot.second).addTranslation(x,y); diff --git a/misc/homog2d_test.cpp b/misc/homog2d_test.cpp index 8d9844a..82fcd1a 100644 --- a/misc/homog2d_test.cpp +++ b/misc/homog2d_test.cpp @@ -3,7 +3,7 @@ This file is part of the C++ library "homog2d", dedicated to handle 2D lines and points, see https://github.com/skramm/homog2d - Author & Copyright 2019-2023 Sebastien Kramm + Author & Copyright 2019-2023 Sebastien Kramm Contact: firstname.lastname@univ-rouen.fr @@ -17,25 +17,25 @@ /** \file homog2d_test.cpp -\brief A test file for homog2d, needs Catch2, v2 (single header file version), -see https://github.com/catchorg/Catch2 +\brief A test file for homog2d, needs Catch2, v2 (single header file version), +see https://github.com/catchorg/Catch2
Run with $ make test - -This file holds mostly "general" tests. - -It also holds some tests that are only related to the OpenCv binding -Thus, they are run only if the symbol \c HOMOG2D_USE_OPENCV is defined.
-This latter part starts around line 3350. -
-Run with $ make test USE_OPENCV=Y - -It also holds some tests that are only related to the SVG import feature, that requires -the \c tinyxml2 library, and that the symbol HOMOG2D_USE_SVG_IMPORT is defined. -
-Run with $ make test USE_TINYXML2=Y + +This file holds mostly "general" tests. + +It also holds some tests that are only related to the OpenCv binding +Thus, they are run only if the symbol \c HOMOG2D_USE_OPENCV is defined.
+This latter part starts around line 3350. +
+Run with $ make test USE_OPENCV=Y + +It also holds some tests that are only related to the SVG import feature, that requires +the \c tinyxml2 library, and that the symbol HOMOG2D_USE_SVG_IMPORT is defined. +
+Run with $ make test USE_TINYXML2=Y */ - + /// see test [gen_bind] #define HOMOG2D_BIND_X xxx @@ -43,23 +43,23 @@ Run with $ make test USE_TINYXML2=Y #define CATCH_CONFIG_RUNNER // alternative: main provided here #include "catch.hpp" -/// Numerical type for object storage for tests. This is usually defined by makefile. +/// Numerical type for object storage for tests. This is usually defined by makefile. /// (The internal numerical type for the library is defined by HOMOG2D_INUMTYPE) #ifndef NUMTYPE #define NUMTYPE double #endif - + // define this if there is a need for bugtracking -//#define HOMOG2D_DEBUGMODE +//#define HOMOG2D_DEBUGMODE #define HOMOG2D_TEST_MODE #include "../homog2d.hpp" - -#if 0 + +#if 0 #define LOCALLOG(a) std::cout << " - line " << __LINE__ << ": " << a << '\n' -#else - #define LOCALLOG(a) -#endif +#else + #define LOCALLOG(a) +#endif double g_epsilon = std::numeric_limits::epsilon()*10000.; @@ -70,41 +70,41 @@ using namespace h2d; int main( int argc, char* argv[] ) { - std::cout << "START TESTS:" - << "\n - homog2d version: " << HOMOG2D_VERSION + std::cout << "START TESTS:" + << "\n - homog2d version: " << HOMOG2D_VERSION << "\n - numerical type: " << XSTR(NUMTYPE) << "\n - internal numerical type=" << XSTR(HOMOG2D_INUMTYPE) << "\n - Catch lib version: " << CATCH_VERSION_MAJOR << '.' << CATCH_VERSION_MINOR << '.' << CATCH_VERSION_PATCH << "\n - build options:" - + << "\n - HOMOG2D_OPTIMIZE_SPEED: " #ifdef HOMOG2D_OPTIMIZE_SPEED << "YES" #else << "NO" #endif - + << "\n - HOMOG2D_USE_OPENCV: " #ifdef HOMOG2D_USE_OPENCV << "YES" #else << "NO" -#endif - - << "\n - HOMOG2D_USE_SVG_IMPORT: " -#ifdef HOMOG2D_USE_SVG_IMPORT - << "YES" -#else - << "NO" -#endif - - << "\n - HOMOG2D_ENABLE_RTP: " -#ifdef HOMOG2D_ENABLE_RTP - << "YES" -#else - << "NO" -#endif - +#endif + + << "\n - HOMOG2D_USE_SVG_IMPORT: " +#ifdef HOMOG2D_USE_SVG_IMPORT + << "YES" +#else + << "NO" +#endif + + << "\n - HOMOG2D_ENABLE_RTP: " +#ifdef HOMOG2D_ENABLE_RTP + << "YES" +#else + << "NO" +#endif + << '\n'; Catch::StringMaker::precision = 25; @@ -112,18 +112,18 @@ int main( int argc, char* argv[] ) // removed until PR merged into next release, see https://github.com/catchorg/Catch2/pull/2245 // Catch::StringMaker::precision = 25; - -// we need to have a bigger threshold value for "float" type, as it has a lot less precision - if( std::string( XSTR(NUMTYPE) ) == "float" ) - thr::nullDistance() = 1E-6; - - thr::printThresholds( std::cout ); - - return Catch::Session().run( argc, argv ); -} - + +// we need to have a bigger threshold value for "float" type, as it has a lot less precision + if( std::string( XSTR(NUMTYPE) ) == "float" ) + thr::nullDistance() = 1E-6; + + thr::printThresholds( std::cout ); + + return Catch::Session().run( argc, argv ); +} + TEST_CASE( "numerical types access", "[types-access]" ) -{ +{ Point2dF ptF; Point2dD ptD; Point2dL ptL; @@ -135,113 +135,113 @@ TEST_CASE( "numerical types access", "[types-access]" ) HomogrF HF; HomogrD HD; HomogrL HL; - - FRectF rF; - FRectD rD; - FRectL rL; - - SegmentF sF; - SegmentD sD; - SegmentL sL; - - CircleF cF; - CircleD cD; - CircleL cL; - - OPolylineF poF; - OPolylineD poD; - OPolylineL poL; - - CPolylineF pcF; - CPolylineD pcD; - CPolylineL pcL; - - EllipseF eF; - EllipseD eD; - EllipseL eL; - - CHECK( ptF.dtype() == dtype(ptF) ); - + + FRectF rF; + FRectD rD; + FRectL rL; + + SegmentF sF; + SegmentD sD; + SegmentL sL; + + CircleF cF; + CircleD cD; + CircleL cL; + + OPolylineF poF; + OPolylineD poD; + OPolylineL poL; + + CPolylineF pcF; + CPolylineD pcD; + CPolylineL pcL; + + EllipseF eF; + EllipseD eD; + EllipseL eL; + + CHECK( ptF.dtype() == dtype(ptF) ); + CHECK( ptF.dtype() == Dtype::Float ); - CHECK( liF.dtype() == Dtype::Float ); - CHECK( HF.dtype() == Dtype::Float ); - CHECK( rF.dtype() == Dtype::Float ); - CHECK( sF.dtype() == Dtype::Float ); - CHECK( cF.dtype() == Dtype::Float ); - CHECK( poF.dtype() == Dtype::Float ); - CHECK( pcF.dtype() == Dtype::Float ); - CHECK( eF.dtype() == Dtype::Float ); - + CHECK( liF.dtype() == Dtype::Float ); + CHECK( HF.dtype() == Dtype::Float ); + CHECK( rF.dtype() == Dtype::Float ); + CHECK( sF.dtype() == Dtype::Float ); + CHECK( cF.dtype() == Dtype::Float ); + CHECK( poF.dtype() == Dtype::Float ); + CHECK( pcF.dtype() == Dtype::Float ); + CHECK( eF.dtype() == Dtype::Float ); + CHECK( ptD.dtype() == Dtype::Double ); - CHECK( liD.dtype() == Dtype::Double ); - CHECK( HD.dtype() == Dtype::Double ); - CHECK( rD.dtype() == Dtype::Double ); - CHECK( sD.dtype() == Dtype::Double ); - CHECK( cD.dtype() == Dtype::Double ); - CHECK( poD.dtype() == Dtype::Double ); - CHECK( pcD.dtype() == Dtype::Double ); - CHECK( eD.dtype() == Dtype::Double ); - - CHECK( dtype(ptD) == Dtype::Double ); - CHECK( dtype(liD) == Dtype::Double ); - CHECK( dtype(HF) == Dtype::Float ); - CHECK( dtype(rF) == Dtype::Float ); - - CHECK( dsize(ptF).first == ptF.dsize().first ); - CHECK( dsize(ptD).first == ptD.dsize().first ); - CHECK( dsize(ptL).first == ptL.dsize().first ); - - CHECK( dsize(ptF).second == ptF.dsize().second ); - CHECK( dsize(ptD).second == ptD.dsize().second ); - CHECK( dsize(ptL).second == ptL.dsize().second ); - - CHECK( dsize(ptF).first == 24 ); - CHECK( dsize(ptF).second == 7 ); - - CHECK( dsize(ptD).first == 53 ); - CHECK( dsize(ptD).second == 10 ); - - CHECK( dsize(ptL).first == 64 ); - CHECK( dsize(ptL).second == 63 ); -} - -TEST_CASE( "comparison testing", "[comparison-test]" ) -{ - { - Segment_ - a1, a2; CHECK( a1 == a2 ); - } - { - Circle_ - a1, a2; CHECK( a1 == a2 ); - } - { - FRect_ - a1, a2; CHECK( a1 == a2 ); - } - { - Line2d_ - a1, a2; CHECK( a1 == a2 ); - } - { - Point2d_ - a1, a2; CHECK( a1 == a2 ); - } - { - Ellipse_ - a1, a2; CHECK( a1 == a2 ); - } - { - CPolyline_ - a1, a2; CHECK( a1 == a2 ); - } - { - OPolyline_ - a1, a2; CHECK( a1 == a2 ); - } -} - - + CHECK( liD.dtype() == Dtype::Double ); + CHECK( HD.dtype() == Dtype::Double ); + CHECK( rD.dtype() == Dtype::Double ); + CHECK( sD.dtype() == Dtype::Double ); + CHECK( cD.dtype() == Dtype::Double ); + CHECK( poD.dtype() == Dtype::Double ); + CHECK( pcD.dtype() == Dtype::Double ); + CHECK( eD.dtype() == Dtype::Double ); + + CHECK( dtype(ptD) == Dtype::Double ); + CHECK( dtype(liD) == Dtype::Double ); + CHECK( dtype(HF) == Dtype::Float ); + CHECK( dtype(rF) == Dtype::Float ); + + CHECK( dsize(ptF).first == ptF.dsize().first ); + CHECK( dsize(ptD).first == ptD.dsize().first ); + CHECK( dsize(ptL).first == ptL.dsize().first ); + + CHECK( dsize(ptF).second == ptF.dsize().second ); + CHECK( dsize(ptD).second == ptD.dsize().second ); + CHECK( dsize(ptL).second == ptL.dsize().second ); + + CHECK( dsize(ptF).first == 24 ); + CHECK( dsize(ptF).second == 7 ); + + CHECK( dsize(ptD).first == 53 ); + CHECK( dsize(ptD).second == 10 ); + + CHECK( dsize(ptL).first == 64 ); + CHECK( dsize(ptL).second == 63 ); +} + +TEST_CASE( "comparison testing", "[comparison-test]" ) +{ + { + Segment_ + a1, a2; CHECK( a1 == a2 ); + } + { + Circle_ + a1, a2; CHECK( a1 == a2 ); + } + { + FRect_ + a1, a2; CHECK( a1 == a2 ); + } + { + Line2d_ + a1, a2; CHECK( a1 == a2 ); + } + { + Point2d_ + a1, a2; CHECK( a1 == a2 ); + } + { + Ellipse_ + a1, a2; CHECK( a1 == a2 ); + } + { + CPolyline_ + a1, a2; CHECK( a1 == a2 ); + } + { + OPolyline_ + a1, a2; CHECK( a1 == a2 ); + } +} + + TEST_CASE( "types testing 1", "[test-types-1]" ) { INFO( "type size" ) @@ -263,7 +263,7 @@ TEST_CASE( "types testing 1", "[test-types-1]" ) Point2d_ pt2F2; Point2d_ pt2F3; pt2F1.set(4.,5); // checking with 2 different types - + CHECK( ptF.type() == Type::Point2d ); CHECK( liF.type() == Type::Line2d ); } @@ -275,14 +275,14 @@ TEST_CASE( "types testing 1", "[test-types-1]" ) Line2dD li_D0(1,1); Line2dF li_F0(2,2); Line2dL li_L0(3,3); -} - -TEST_CASE( "types testing 2", "[test-types-2]" ) -{ - Point2dD ptD0(1,1); - Point2dF ptF0(2,2); - Point2dL ptL0(3,3); - +} + +TEST_CASE( "types testing 2", "[test-types-2]" ) +{ + Point2dD ptD0(1,1); + Point2dF ptF0(2,2); + Point2dL ptL0(3,3); + INFO( "numerical type conversions (assignment)" ) { Point2dD ptD(4,4); @@ -336,90 +336,90 @@ TEST_CASE( "types testing 2", "[test-types-2]" ) OPolylineF pof; OPolylineD pod; - OPolylineL pol; - + OPolylineL pol; + pol = pod; pof = pod; pod = pof; pol = pof; pof = pol; pod = pol; - } -} - -TEST_CASE( "normalization", "[normaliz]" ) -{ - Point2d_ pt1(1,2,3); - CHECK( pt1.get() == std::array{1,2,3} ); - Point2d_ pt2(-1,2,3); - CHECK( pt2.get() == std::array{1,-2,-3} ); -} - -TEST_CASE( "homogeneous coordinates testing", "[homogeneous]" ) -{ - { - Point2d_ pt1(2,2,1); - Point2d_ pt2(4,4,2); - CHECK( pt1 == pt2 ); - pt2.set(8,8,4); - CHECK( pt1 == pt2 ); - - Line2d_ li1(0,1,0); - Line2d_ li2(0,2,0); - CHECK( li1 == li2 ); - li2.set(0,4,0); - CHECK( li1 == li2 ); - - Line2d_ li3(3,4,5); - Line2d_ li4(6,8,10); - CHECK( li3 == li4 ); - } - { - INFO( "constructors using a container of values as single argument" ); - - std::array arr{3,2,1}; - Point2d_ pt1(arr); - CHECK( pt1 == Point2d_(3,2,1) ); - - std::vector vec{3,2,1}; - Point2d_ pt2(vec); - CHECK( pt2 == Point2d_(3,2,1) ); - } -} - -TEST_CASE( "infinity point", "[points-inf]" ) -{ - Line2d liH( LineDir::H, Point2d() ); // horizontal line at y=0 - CHECK_THROWS( Point2d(0,0,0) ); - CHECK_THROWS( Line2d(0,0,0) ); - - Point2d pt1(1,0,0); - Point2d pt2(-1,0,0); - CHECK( pt1.isInf() ); - CHECK( pt2.isInf() ); - - auto li1 = pt1 * Point2d(); - CHECK( li1 == liH ); - auto li2 = pt2 * Point2d(); - CHECK( li2 == liH ); - - Point2d pt3(3,3,0); // infinite points at an angle of 45° - auto li3 = pt3 * Point2d(); - CHECK( std::abs(li3.getAngle( liH ) - M_PI/4.) < g_epsilon ); - - Segment seg( Point2d(), pt1 ); - auto ppts = seg.getPts(); - - auto line = seg.getLine(); - CHECK( line == liH ); -} - -TEST_CASE( "types testing 3", "[test-types-3]" ) -{ - Point2dD ptD0(1,1); // double - Point2dF ptF0(2,2); // float - Point2dL ptL0(3,3); // long - + } +} + +TEST_CASE( "normalization", "[normaliz]" ) +{ + Point2d_ pt1(1,2,3); + CHECK( pt1.get() == std::array{1,2,3} ); + Point2d_ pt2(-1,2,3); + CHECK( pt2.get() == std::array{1,-2,-3} ); +} + +TEST_CASE( "homogeneous coordinates testing", "[homogeneous]" ) +{ + { + Point2d_ pt1(2,2,1); + Point2d_ pt2(4,4,2); + CHECK( pt1 == pt2 ); + pt2.set(8,8,4); + CHECK( pt1 == pt2 ); + + Line2d_ li1(0,1,0); + Line2d_ li2(0,2,0); + CHECK( li1 == li2 ); + li2.set(0,4,0); + CHECK( li1 == li2 ); + + Line2d_ li3(3,4,5); + Line2d_ li4(6,8,10); + CHECK( li3 == li4 ); + } + { + INFO( "constructors using a container of values as single argument" ); + + std::array arr{3,2,1}; + Point2d_ pt1(arr); + CHECK( pt1 == Point2d_(3,2,1) ); + + std::vector vec{3,2,1}; + Point2d_ pt2(vec); + CHECK( pt2 == Point2d_(3,2,1) ); + } +} + +TEST_CASE( "infinity point", "[points-inf]" ) +{ + Line2d liH( LineDir::H, Point2d() ); // horizontal line at y=0 + CHECK_THROWS( Point2d(0,0,0) ); + CHECK_THROWS( Line2d(0,0,0) ); + + Point2d pt1(1,0,0); + Point2d pt2(-1,0,0); + CHECK( pt1.isInf() ); + CHECK( pt2.isInf() ); + + auto li1 = pt1 * Point2d(); + CHECK( li1 == liH ); + auto li2 = pt2 * Point2d(); + CHECK( li2 == liH ); + + Point2d pt3(3,3,0); // infinite points at an angle of 45° + auto li3 = pt3 * Point2d(); + CHECK( std::abs(li3.getAngle( liH ) - M_PI/4.) < g_epsilon ); + + Segment seg( Point2d(), pt1 ); + auto ppts = seg.getPts(); + + auto line = seg.getLine(); + CHECK( line == liH ); +} + +TEST_CASE( "types testing 3", "[test-types-3]" ) +{ + Point2dD ptD0(1,1); // double + Point2dF ptF0(2,2); // float + Point2dL ptL0(3,3); // long + INFO( "numerical type conversions (copy-constructor)" ) { Point2dL ptL1 = ptD0; // double to long @@ -437,112 +437,112 @@ TEST_CASE( "types testing 3", "[test-types-3]" ) CHECK( ptD1.getX() == 2. ); CHECK( ptD2.getX() == 3. ); - CircleL cl; + CircleL cl; CircleF cf; CircleD cd; CircleL cl2 = cd; - CircleF cf2 = cd; - CircleD cd2 = cd; + CircleF cf2 = cd; + CircleD cd2 = cd; CircleL cl3 = cf; - CircleF cf3 = cf; - CircleD cd3 = cf; + CircleF cf3 = cf; + CircleD cd3 = cf; - CircleL cl4 = cl; + CircleL cl4 = cl; CircleF cf4 = cl; CircleD cd4 = cl; - SegmentL sl; + SegmentL sl; SegmentF sf; SegmentD sd; SegmentL sl2 = sd; - SegmentF sf2 = sd; - SegmentD sd2 = sd; + SegmentF sf2 = sd; + SegmentD sd2 = sd; SegmentL sl3 = sf; - SegmentF sf3 = sf; - SegmentD sd3 = sf; + SegmentF sf3 = sf; + SegmentD sd3 = sf; SegmentL sl4 = sl; - SegmentF sf4 = sl; + SegmentF sf4 = sl; SegmentD sd4 = sl; - FRectL rl; + FRectL rl; FRectF rf; FRectD rd; FRectL rl2 = rd; - FRectF rf2 = rd; - FRectD rd2 = rd; + FRectF rf2 = rd; + FRectD rd2 = rd; FRectL rl3 = rf; - FRectF rf3 = rf; - FRectD rd3 = rf; + FRectF rf3 = rf; + FRectD rd3 = rf; - FRectL rl4 = rl; + FRectL rl4 = rl; FRectF rf4 = rl; FRectD rd4 = rl; - OPolylineL pol; + OPolylineL pol; OPolylineF pof; OPolylineD pod; OPolylineL pol2 = pod; - OPolylineF pof2 = pod; - OPolylineD pod2 = pod; + OPolylineF pof2 = pod; + OPolylineD pod2 = pod; OPolylineL pol3 = pof; - OPolylineF pof3 = pof; - OPolylineD pod3 = pof; + OPolylineF pof3 = pof; + OPolylineD pod3 = pof; OPolylineL pol4 = pol; - OPolylineF pof4 = pol; + OPolylineF pof4 = pol; OPolylineD pod4 = pol; - - CPolylineL pcl; - CPolylineF pcf; - CPolylineD pcd; - - CPolylineL pcl2 = pcd; - CPolylineF pcf2 = pcd; - CPolylineD pcd2 = pcd; - - CPolylineL pcl3 = pcf; - CPolylineF pcf3 = pcf; - CPolylineD pcd3 = pcf; - - CPolylineL pcl4 = pcl; - CPolylineF pcf4 = pcl; - CPolylineD pcd4 = pcl; - - EllipseL elll; - EllipseF ellf; - EllipseD elld; - - EllipseL elll2 = elld; - EllipseF ellf2 = elld; - EllipseD elld2 = elld; - - EllipseL elll3 = ellf; - EllipseF ellf3 = ellf; - EllipseD elld3 = ellf; - - EllipseL elll4 = elll; - EllipseF ellf4 = elll; - EllipseD elld4 = elll; - + + CPolylineL pcl; + CPolylineF pcf; + CPolylineD pcd; + + CPolylineL pcl2 = pcd; + CPolylineF pcf2 = pcd; + CPolylineD pcd2 = pcd; + + CPolylineL pcl3 = pcf; + CPolylineF pcf3 = pcf; + CPolylineD pcd3 = pcf; + + CPolylineL pcl4 = pcl; + CPolylineF pcf4 = pcl; + CPolylineD pcd4 = pcl; + + EllipseL elll; + EllipseF ellf; + EllipseD elld; + + EllipseL elll2 = elld; + EllipseF ellf2 = elld; + EllipseD elld2 = elld; + + EllipseL elll3 = ellf; + EllipseF ellf3 = ellf; + EllipseD elld3 = ellf; + + EllipseL elll4 = elll; + EllipseF ellf4 = elll; + EllipseD elld4 = elll; + /** The goal of theses is to make sure that the conversion (float to double, ...) -does not trigger a build failure. +does not trigger a build failure. The checking is only there to avoid a warning about "unused variable". \todo Once we switch to C++17, we can remove the checking and use: https://en.cppreference.com/w/cpp/language/attributes/maybe_unused -*/ - Line2dD li_D0(1,1); - Line2dF li_F0(2,2); - Line2dL li_L0(3,3); +*/ + Line2dD li_D0(1,1); + Line2dF li_F0(2,2); + Line2dL li_L0(3,3); Line2dD li_D1 = li_F0; CHECK( li_D1.get()[2] == 0. ); Line2dD li_L1 = li_F0; CHECK( li_L1.get()[2] == 0. ); @@ -553,89 +553,89 @@ The checking is only there to avoid a warning about "unused variable". Line2dD li_F3 = li_L0; CHECK( li_F3.get()[2] == 0. ); Line2dD li_D3 = li_L0; CHECK( li_D3.get()[2] == 0. ); } -} - -TEST_CASE( "stream operator << test", "[streamingop-test]" ) -{ - Line2d li; - Point2d pt; - Segment seg; - Circle cir; - Ellipse ell; - CPolyline cpol; - OPolyline opol; -// just to make sure that this builds ! - std::ostringstream oss; - oss << li << pt << seg << cir << cpol << opol << ell; - std::ostringstream oss2; - oss2 << cpol; - CHECK( oss2.str() == "CPolyline: empty" ); -} - -TEST_CASE( "line/point distance", "[lp-dist]" ) -{ - size_t n = 1E6; // nb of runs - size_t c1{}; - size_t c2{}; - long double sum1{}; - long double sum2{}; - float k = 1; - std::srand( time(0) ); - for( size_t i=0; i pt1( - 1.0*rand()/RAND_MAX * k, - 1.0*rand()/RAND_MAX * k - ); - Point2d_ pt2( - 1.0*rand()/RAND_MAX * k, - 1.0*rand()/RAND_MAX * k - ); - auto li = pt1 * pt2; - auto d1 = li.distTo(pt1); - auto d2 = li.distTo(pt2); - if( d1 > thr::nullDistance() ) - { -// std::cerr << ": distance check failure 1, value " << d1 << " > thres (" << thr::nullDistance() << ")\n"; - sum1 += d1; - c1++; - } - if( li.distTo(pt2) > thr::nullDistance() ) - { -// std::cerr << "distance check failure 2, value " << d2 << " > thres (" << thr::nullDistance() << ")\n"; - sum2 += d2; - c2++; - } - } - if( c1 != 0 || c2 != 0 ) - { - std::cout << "line/point distance test failures with thres=" << thr::nullDistance() - << "\n nb1=" << c1 << ", nb2=" << c2 << " failures over total nb of tests=" << n - << "\n- mean distance: d1=" << sum1 / c1 - << "\n- mean distance: d2=" << sum2 / c2 - << '\n'; - } - CHECK( c1 == 0 ); - CHECK( c2 == 0 ); -} - -TEST_CASE( "side-pt-line", "[side-pt-line]" ) -{ - Line2d_ li; // vertical line at x=0 - { - Point2d_ pt; - CHECK( side(pt,li) == 0 ); - } - { - Point2d_ pt(1,0); - CHECK( side(pt,li) == 1 ); - } - { - Point2d_ pt(-1,0); - CHECK( side(pt,li) == -1 ); - } -} - +} + +TEST_CASE( "stream operator << test", "[streamingop-test]" ) +{ + Line2d li; + Point2d pt; + Segment seg; + Circle cir; + Ellipse ell; + CPolyline cpol; + OPolyline opol; +// just to make sure that this builds ! + std::ostringstream oss; + oss << li << pt << seg << cir << cpol << opol << ell; + std::ostringstream oss2; + oss2 << cpol; + CHECK( oss2.str() == "CPolyline: empty" ); +} + +TEST_CASE( "line/point distance", "[lp-dist]" ) +{ + size_t n = 1E6; // nb of runs + size_t c1{}; + size_t c2{}; + long double sum1{}; + long double sum2{}; + float k = 1; + std::srand( time(0) ); + for( size_t i=0; i pt1( + 1.0*rand()/RAND_MAX * k, + 1.0*rand()/RAND_MAX * k + ); + Point2d_ pt2( + 1.0*rand()/RAND_MAX * k, + 1.0*rand()/RAND_MAX * k + ); + auto li = pt1 * pt2; + auto d1 = li.distTo(pt1); + auto d2 = li.distTo(pt2); + if( d1 > thr::nullDistance() ) + { +// std::cerr << ": distance check failure 1, value " << d1 << " > thres (" << thr::nullDistance() << ")\n"; + sum1 += d1; + c1++; + } + if( li.distTo(pt2) > thr::nullDistance() ) + { +// std::cerr << "distance check failure 2, value " << d2 << " > thres (" << thr::nullDistance() << ")\n"; + sum2 += d2; + c2++; + } + } + if( c1 != 0 || c2 != 0 ) + { + std::cout << "line/point distance test failures with thres=" << thr::nullDistance() + << "\n nb1=" << c1 << ", nb2=" << c2 << " failures over total nb of tests=" << n + << "\n- mean distance: d1=" << sum1 / c1 + << "\n- mean distance: d2=" << sum2 / c2 + << '\n'; + } + CHECK( c1 == 0 ); + CHECK( c2 == 0 ); +} + +TEST_CASE( "side-pt-line", "[side-pt-line]" ) +{ + Line2d_ li; // vertical line at x=0 + { + Point2d_ pt; + CHECK( side(pt,li) == 0 ); + } + { + Point2d_ pt(1,0); + CHECK( side(pt,li) == 1 ); + } + { + Point2d_ pt(-1,0); + CHECK( side(pt,li) == -1 ); + } +} + TEST_CASE( "test1", "[test1]" ) { Point2d_ ptA1; // 0,0 @@ -736,13 +736,13 @@ TEST_CASE( "test1", "[test1]" ) CHECK( lih1 == lih2 ); } } - -TEST_CASE( "build point line from container", "[build-cont]" ) -{ - std::array arr{3,2,1}; - Point2d_ pt( arr ); - CHECK( pt == Point2d(3,2,1) ); -} + +TEST_CASE( "build point line from container", "[build-cont]" ) +{ + std::array arr{3,2,1}; + Point2d_ pt( arr ); + CHECK( pt == Point2d(3,2,1) ); +} TEST_CASE( "test throw", "[test_thr]" ) { @@ -856,11 +856,11 @@ TEST_CASE( "test parallel", "[test_para]" ) CHECK( getParallelDistance( l1, l2 ) == 1. ); Line2d_ l3( Point2d(-3,0), Point2d(-3,-10) ); // vertical line at x=1 CHECK( getParallelDistance( l1, l3 ) == 3. ); - CHECK( getParallelDistance( l2, l3 ) == 4. ); - - Line2d_ l4( LineDir::H, 1 ); // horizontal line + CHECK( getParallelDistance( l2, l3 ) == 4. ); + + Line2d_ l4( LineDir::H, 1 ); // horizontal line CHECK_THROWS( getParallelDistance( l1, l4 ) ); - } + } } TEST_CASE( "dist2points", "[t_d2p]" ) @@ -879,7 +879,7 @@ TEST_CASE( "dist2points", "[t_d2p]" ) CHECK( li.getCoord( GivenCoord::Y, 1. ) == 2. ); Point2d_ p1( 3,3); - Point2d_ p2( 4,4); + Point2d_ p2( 4,4); auto r = std::abs( p1.distTo( p2 ) - std::sqrt(2.) ); CHECK( r < thr::nullDistance() ); } @@ -978,15 +978,15 @@ TEST_CASE( "test Homogr", "[testH]" ) std::vector v_pt(3); // 3 points at (0,0) H.applyTo( v_pt ); // translate them all to (+5,+6) - CHECK( v_pt[2].getX() == 5 ); + CHECK( v_pt[2].getX() == 5 ); CHECK( v_pt[2].getY() == 6 ); - CHECK( v_pt[0].getX() == 5 ); + CHECK( v_pt[0].getX() == 5 ); CHECK( v_pt[0].getY() == 6 ); auto v_pt2 = H * v_pt; CHECK( v_pt2.size() == 3 ); - CHECK( v_pt2[2].getX() == 10 ); + CHECK( v_pt2[2].getX() == 10 ); CHECK( v_pt2[2].getY() == 12 ); - CHECK( v_pt2[0].getX() == 10 ); + CHECK( v_pt2[0].getX() == 10 ); CHECK( v_pt2[0].getY() == 12 ); std::array a_pt; @@ -1228,7 +1228,7 @@ TEST_CASE( "getCorrectPoints", "[gcpts]" ) CHECK( p1.second == Point2d(5,5) ); } } - + ////////////////////////////////////////////////////////////// ///// ISINSIDE TESTS ///// ////////////////////////////////////////////////////////////// @@ -1236,218 +1236,218 @@ TEST_CASE( "getCorrectPoints", "[gcpts]" ) // this test only makes sure that all theses situations compile TEST_CASE( "IsInside - manual", "[IsInside_man]" ) { - Point2d pt1(10,10),pt2(10,10); - Line2d li1, li2; + Point2d pt1(10,10),pt2(10,10); + Line2d li1, li2; FRect rect1, rect2; Circle cir1, cir2; - Segment seg1, seg2; + Segment seg1, seg2; Ellipse ell1(5.,5.), ell2(3,4); - CPolyline cpol1,cpol2; - OPolyline opol1,opol2; - - CHECK( !pt2.isInside( pt1 ) ); - CHECK( !pt2.isInside( li1 ) ); - CHECK( !pt2.isInside( rect1 ) ); - CHECK( !pt2.isInside( cir1 ) ); - CHECK( !pt2.isInside( ell1 ) ); - CHECK( !pt2.isInside( cpol1 ) ); - CHECK( !pt2.isInside( opol1 ) ); - - CHECK( !li2.isInside( pt1 ) ); - CHECK( !li2.isInside( li1 ) ); - CHECK( !li2.isInside( rect1 ) ); - CHECK( !li2.isInside( cir1 ) ); - CHECK( !li2.isInside( ell1 ) ); - CHECK( !li2.isInside( cpol1 ) ); - CHECK( !li2.isInside( opol1 ) ); - - CHECK( !rect2.isInside( pt1 ) ); - CHECK( !rect2.isInside( li1 ) ); - CHECK( !rect2.isInside( rect1 ) ); - CHECK( !rect2.isInside( cir1 ) ); - CHECK( !rect2.isInside( ell1 ) ); - CHECK( !rect2.isInside( cpol1 ) ); - CHECK( !rect2.isInside( opol1 ) ); - - CHECK( !cir2.isInside( pt1 ) ); - CHECK( !cir2.isInside( li1 ) ); - CHECK( !cir2.isInside( rect1 ) ); - CHECK( !cir2.isInside( cir1 ) ); - CHECK( !cir2.isInside( ell1 ) ); - CHECK( !cir2.isInside( cpol1 ) ); - CHECK( !cir2.isInside( opol1 ) ); - - CHECK( !seg2.isInside( pt1 ) ); - CHECK( !seg2.isInside( li1 ) ); - CHECK( !seg2.isInside( rect1 ) ); - CHECK( !seg2.isInside( cir1 ) ); - CHECK( !seg2.isInside( ell1 ) ); - CHECK( !seg2.isInside( cpol1 ) ); - CHECK( !seg2.isInside( opol1 ) ); - - CHECK( !ell2.isInside( pt1 ) ); - CHECK( !ell2.isInside( li1 ) ); - CHECK( !ell2.isInside( rect1 ) ); - CHECK( !ell2.isInside( cir1 ) ); - CHECK( !ell2.isInside( ell1 ) ); - CHECK( !ell2.isInside( cpol1 ) ); - CHECK( !ell2.isInside( opol1 ) ); - - CHECK( !cpol2.isInside( pt1 ) ); - CHECK( !cpol2.isInside( li1 ) ); - CHECK( !cpol2.isInside( rect1 ) ); - CHECK( !cpol2.isInside( cir1 ) ); - CHECK( !cpol2.isInside( ell1 ) ); - CHECK( !cpol2.isInside( cpol1 ) ); - CHECK( !cpol2.isInside( opol1 ) ); - - CHECK( !opol2.isInside( pt1 ) ); - CHECK( !opol2.isInside( li1 ) ); - CHECK( !opol2.isInside( rect1 ) ); - CHECK( !opol2.isInside( cir1 ) ); - CHECK( !opol2.isInside( ell1 ) ); - CHECK( !opol2.isInside( cpol1 ) ); - CHECK( !opol2.isInside( opol1 ) ); -} - -TEST_CASE( "Polyline IsInside Polyline", "[tpolipol]" ) -{ - { // checking https://github.com/skramm/homog2d/issues/10 - CPolyline_ p1(std::vector{ - Point2d(-2, 2), - Point2d(-2,-2), - Point2d( 2,-2), - Point2d( 2, 2), - }); - CPolyline_ p2(std::vector{ - Point2d(-5, 5), - Point2d(-5,-5), - Point2d( 5,-5), - Point2d( 5, 5) - }); - CHECK( p2.isInside( p1 ) == false ); - CHECK( p1.isInside( p2 ) == true ); - } - { // one inside the other - #include "figures_test/polyline_inside_a1.code" - CHECK( pl2.isInside( pl ) == false ); - CHECK( pl.isInside( pl2 ) == true ); - } - { // simple intersection, one point outside - #include "figures_test/polyline_inside_a2.code" - CHECK( pl2.isInside( pl ) == false ); - CHECK( pl.isInside( pl2 ) == false ); - } - { // nothing common - #include "figures_test/polyline_inside_a3.code" - CHECK( pl2.isInside( pl ) == false ); - CHECK( pl.isInside( pl2 ) == false ); - } - { // all points inside, but some intersections - #include "figures_test/polyline_inside_a4.code" - CHECK( pl2.isInside( pl ) == false ); - CHECK( pl.isInside( pl2 ) == false ); - } - - { // Open polyline inside a closed polyline - #include "figures_test/polyline_inside_b1.code" - CHECK( pl2.isInside( pl ) == true ); - CHECK( pl.isInside( pl2 ) == false ); - } - { // Open polyline inside a closed polyline, but one point outside - #include "figures_test/polyline_inside_b2.code" - CHECK( pl2.isInside( pl ) == false ); - CHECK( pl.isInside( pl2 ) == false ); - } - { // Open polyline with no common with closed polyline - #include "figures_test/polyline_inside_b3.code" - CHECK( pl2.isInside( pl ) == false ); - CHECK( pl.isInside( pl2 ) == false ); - } - - { // two Open polylines - OPolyline_ pl(std::vector>{ - {1,2},{1,1},{2,2} - }); - OPolyline_ pl2(std::vector>{ - {3,3},{3,2},{4,2} - }); - CHECK( pl2.isInside( pl ) == false ); - CHECK( pl.isInside( pl2 ) == false ); - } - { // two Open polylines - OPolyline_ pl(std::vector>{ - {1,1},{5,1},{5,5},{1,5} - }); - OPolyline_ pl2(std::vector>{ - {3,3},{3,2},{4,2} - }); - CHECK( pl2.isInside( pl ) == false ); - CHECK( pl.isInside( pl2 ) == false ); - } -} - -TEST_CASE( "Point2d IsInside Polyline", "[tptipol]" ) -{ - Point2d_ pt; - - OPolyline_ opol{ std::vector{ {0,0}, {3,4}, {1,8} } }; - CHECK( !pt.isInside( opol ) ); - - { - CPolyline_ cpol{ FRect_() }; // polygon (0,0)-(1,0)-(1,1)-(0,1) - - pt.set( 0, 0 ); - CHECK( !pt.isInside( cpol ) ); // equal to one of the points - pt.set( .2, .3 ); - CHECK( pt.isInside( cpol ) ); - pt.set( 1,0 ); - CHECK( !pt.isInside( cpol ) ); - pt.set( .5, 0. ); - CHECK( !pt.isInside( cpol ) ); - } - { -// CPolyline_ cpol{ {1,0},{2,1},{1,2},{0,1} }; - CPolyline_ cpol{ std::vector{ {1,0},{2,1},{1,2},{0,1} } }; - - pt.set( 1,1 ); - CHECK( pt.isInside( cpol ) ); - } -} - -// Whatever the primitive, a line is NEVER inside anything -TEST_CASE( "Line2d IsInside something", "[tlir]" ) -{ - Point2d_ pt1(2,10); - Point2d_ pt2(10,2); - auto li = pt1*pt2; - CHECK( li.isInside( pt1, pt2 ) == false ); - - Circle_ c( 4, 5, 2 ); - CHECK( li.isInside( c ) == false ); - - Ellipse_ ell(pt1); - CHECK( li.isInside( ell ) == false ); -} - -TEST_CASE( "Point2d IsInside Circle", "[tptic]" ) -{ - Circle_ c1(10.); - Circle_ c2(2.); - { - Point2d p1( 3,3 ); // point inside circle - CHECK( p1.isInside(c1) ); - CHECK( !p1.isInside(c2) ); - CHECK( p1.isInside( Point2d(0,0), 8 ) ); - CHECK( !p1.isInside( Point2d(0,0), 2 ) ); - } - { - Point2d p1( 10, 0 ); // point on the edge of circle - CHECK( !p1.isInside(c1) ); - Point2d p2( 0, 10 ); // point on the edge of circle - CHECK( !p2.isInside(c1) ); - } -} + CPolyline cpol1,cpol2; + OPolyline opol1,opol2; + + CHECK( !pt2.isInside( pt1 ) ); + CHECK( !pt2.isInside( li1 ) ); + CHECK( !pt2.isInside( rect1 ) ); + CHECK( !pt2.isInside( cir1 ) ); + CHECK( !pt2.isInside( ell1 ) ); + CHECK( !pt2.isInside( cpol1 ) ); + CHECK( !pt2.isInside( opol1 ) ); + + CHECK( !li2.isInside( pt1 ) ); + CHECK( !li2.isInside( li1 ) ); + CHECK( !li2.isInside( rect1 ) ); + CHECK( !li2.isInside( cir1 ) ); + CHECK( !li2.isInside( ell1 ) ); + CHECK( !li2.isInside( cpol1 ) ); + CHECK( !li2.isInside( opol1 ) ); + + CHECK( !rect2.isInside( pt1 ) ); + CHECK( !rect2.isInside( li1 ) ); + CHECK( !rect2.isInside( rect1 ) ); + CHECK( !rect2.isInside( cir1 ) ); + CHECK( !rect2.isInside( ell1 ) ); + CHECK( !rect2.isInside( cpol1 ) ); + CHECK( !rect2.isInside( opol1 ) ); + + CHECK( !cir2.isInside( pt1 ) ); + CHECK( !cir2.isInside( li1 ) ); + CHECK( !cir2.isInside( rect1 ) ); + CHECK( !cir2.isInside( cir1 ) ); + CHECK( !cir2.isInside( ell1 ) ); + CHECK( !cir2.isInside( cpol1 ) ); + CHECK( !cir2.isInside( opol1 ) ); + + CHECK( !seg2.isInside( pt1 ) ); + CHECK( !seg2.isInside( li1 ) ); + CHECK( !seg2.isInside( rect1 ) ); + CHECK( !seg2.isInside( cir1 ) ); + CHECK( !seg2.isInside( ell1 ) ); + CHECK( !seg2.isInside( cpol1 ) ); + CHECK( !seg2.isInside( opol1 ) ); + + CHECK( !ell2.isInside( pt1 ) ); + CHECK( !ell2.isInside( li1 ) ); + CHECK( !ell2.isInside( rect1 ) ); + CHECK( !ell2.isInside( cir1 ) ); + CHECK( !ell2.isInside( ell1 ) ); + CHECK( !ell2.isInside( cpol1 ) ); + CHECK( !ell2.isInside( opol1 ) ); + + CHECK( !cpol2.isInside( pt1 ) ); + CHECK( !cpol2.isInside( li1 ) ); + CHECK( !cpol2.isInside( rect1 ) ); + CHECK( !cpol2.isInside( cir1 ) ); + CHECK( !cpol2.isInside( ell1 ) ); + CHECK( !cpol2.isInside( cpol1 ) ); + CHECK( !cpol2.isInside( opol1 ) ); + + CHECK( !opol2.isInside( pt1 ) ); + CHECK( !opol2.isInside( li1 ) ); + CHECK( !opol2.isInside( rect1 ) ); + CHECK( !opol2.isInside( cir1 ) ); + CHECK( !opol2.isInside( ell1 ) ); + CHECK( !opol2.isInside( cpol1 ) ); + CHECK( !opol2.isInside( opol1 ) ); +} + +TEST_CASE( "Polyline IsInside Polyline", "[tpolipol]" ) +{ + { // checking https://github.com/skramm/homog2d/issues/10 + CPolyline_ p1(std::vector{ + Point2d(-2, 2), + Point2d(-2,-2), + Point2d( 2,-2), + Point2d( 2, 2), + }); + CPolyline_ p2(std::vector{ + Point2d(-5, 5), + Point2d(-5,-5), + Point2d( 5,-5), + Point2d( 5, 5) + }); + CHECK( p2.isInside( p1 ) == false ); + CHECK( p1.isInside( p2 ) == true ); + } + { // one inside the other + #include "figures_test/polyline_inside_a1.code" + CHECK( pl2.isInside( pl ) == false ); + CHECK( pl.isInside( pl2 ) == true ); + } + { // simple intersection, one point outside + #include "figures_test/polyline_inside_a2.code" + CHECK( pl2.isInside( pl ) == false ); + CHECK( pl.isInside( pl2 ) == false ); + } + { // nothing common + #include "figures_test/polyline_inside_a3.code" + CHECK( pl2.isInside( pl ) == false ); + CHECK( pl.isInside( pl2 ) == false ); + } + { // all points inside, but some intersections + #include "figures_test/polyline_inside_a4.code" + CHECK( pl2.isInside( pl ) == false ); + CHECK( pl.isInside( pl2 ) == false ); + } + + { // Open polyline inside a closed polyline + #include "figures_test/polyline_inside_b1.code" + CHECK( pl2.isInside( pl ) == true ); + CHECK( pl.isInside( pl2 ) == false ); + } + { // Open polyline inside a closed polyline, but one point outside + #include "figures_test/polyline_inside_b2.code" + CHECK( pl2.isInside( pl ) == false ); + CHECK( pl.isInside( pl2 ) == false ); + } + { // Open polyline with no common with closed polyline + #include "figures_test/polyline_inside_b3.code" + CHECK( pl2.isInside( pl ) == false ); + CHECK( pl.isInside( pl2 ) == false ); + } + + { // two Open polylines + OPolyline_ pl(std::vector>{ + {1,2},{1,1},{2,2} + }); + OPolyline_ pl2(std::vector>{ + {3,3},{3,2},{4,2} + }); + CHECK( pl2.isInside( pl ) == false ); + CHECK( pl.isInside( pl2 ) == false ); + } + { // two Open polylines + OPolyline_ pl(std::vector>{ + {1,1},{5,1},{5,5},{1,5} + }); + OPolyline_ pl2(std::vector>{ + {3,3},{3,2},{4,2} + }); + CHECK( pl2.isInside( pl ) == false ); + CHECK( pl.isInside( pl2 ) == false ); + } +} + +TEST_CASE( "Point2d IsInside Polyline", "[tptipol]" ) +{ + Point2d_ pt; + + OPolyline_ opol{ std::vector{ {0,0}, {3,4}, {1,8} } }; + CHECK( !pt.isInside( opol ) ); + + { + CPolyline_ cpol{ FRect_() }; // polygon (0,0)-(1,0)-(1,1)-(0,1) + + pt.set( 0, 0 ); + CHECK( !pt.isInside( cpol ) ); // equal to one of the points + pt.set( .2, .3 ); + CHECK( pt.isInside( cpol ) ); + pt.set( 1,0 ); + CHECK( !pt.isInside( cpol ) ); + pt.set( .5, 0. ); + CHECK( !pt.isInside( cpol ) ); + } + { +// CPolyline_ cpol{ {1,0},{2,1},{1,2},{0,1} }; + CPolyline_ cpol{ std::vector{ {1,0},{2,1},{1,2},{0,1} } }; + + pt.set( 1,1 ); + CHECK( pt.isInside( cpol ) ); + } +} + +// Whatever the primitive, a line is NEVER inside anything +TEST_CASE( "Line2d IsInside something", "[tlir]" ) +{ + Point2d_ pt1(2,10); + Point2d_ pt2(10,2); + auto li = pt1*pt2; + CHECK( li.isInside( pt1, pt2 ) == false ); + + Circle_ c( 4, 5, 2 ); + CHECK( li.isInside( c ) == false ); + + Ellipse_ ell(pt1); + CHECK( li.isInside( ell ) == false ); +} + +TEST_CASE( "Point2d IsInside Circle", "[tptic]" ) +{ + Circle_ c1(10.); + Circle_ c2(2.); + { + Point2d p1( 3,3 ); // point inside circle + CHECK( p1.isInside(c1) ); + CHECK( !p1.isInside(c2) ); + CHECK( p1.isInside( Point2d(0,0), 8 ) ); + CHECK( !p1.isInside( Point2d(0,0), 2 ) ); + } + { + Point2d p1( 10, 0 ); // point on the edge of circle + CHECK( !p1.isInside(c1) ); + Point2d p2( 0, 10 ); // point on the edge of circle + CHECK( !p2.isInside(c1) ); + } +} TEST_CASE( "Point2d IsInside FRect", "[tptir]" ) { @@ -1474,25 +1474,25 @@ TEST_CASE( "Point2d IsInside FRect", "[tptir]" ) CHECK( Point2d_( 2,10).isInside( pt1, pt2 ) == false ); CHECK( Point2d_(10, 2).isInside( pt1, pt2 ) == false ); CHECK( Point2d_(10,10).isInside( pt1, pt2 ) == false ); -} - -TEST_CASE( "Segment IsInside FRect", "[tsir]" ) -{ +} + +TEST_CASE( "Segment IsInside FRect", "[tsir]" ) +{ FRect_ r(2,3, 10,10); CHECK( r.length() == 30 ); CHECK( r.area() == 56 ); CHECK( r.width() == 8 ); CHECK( r.height() == 7 ); - - CHECK( Segment_( 0,0, 5,0 ).isInside( r ) == false ); // outside + + CHECK( Segment_( 0,0, 5,0 ).isInside( r ) == false ); // outside CHECK( Segment_( 2,5, 4,5 ).isInside( r ) == false ); // on the contour CHECK( Segment_( 2.00001, 5., 4.,5. ).isInside( r ) == true ); CHECK( Segment_( 3,5, 4,5 ).isInside( r ) == true ); -} - -TEST_CASE( "Circle IsInside FRect", "[tcir]" ) -{ - FRect_ r(2,3, 10,10); +} + +TEST_CASE( "Circle IsInside FRect", "[tcir]" ) +{ + FRect_ r(2,3, 10,10); CHECK( Circle_( 5,5, 2 ).isInside( r ) == false ); // touches rectangle at (5,3) CHECK( Circle_( 5,5, 1 ).isInside( r ) == true ); CHECK( Circle_( 6,6, 2 ).isInside( r ) == true ); @@ -1500,30 +1500,30 @@ TEST_CASE( "Circle IsInside FRect", "[tcir]" ) CHECK( Circle( 6,6, 22 ).isInside( r ) == false); CHECK( r.isInside( Circle( 6,6, 22 ) ) == true ); - + std::vector vecpt{ {3,3},{4,4} }; CPolyline pl(vecpt); CHECK( pl.isInside( r ) == false ); // on contour -} - +} + // TODO !!! -#if 0 -TEST_CASE( "Ellipse IsInside FRect", "[tellir]" ) -{ - FRect_ r(2,3, 10,10); - { - Ellipse_ ell; - CHECK( ell.isInside( r ) == false ); - } - { - Ellipse_ ell( Point2d(5,5), 2, 1 ); - CHECK( ell.isInside( r ) == true ); - } - { - Ellipse_ ell( Point2d(5,5), 6, 2 ); - CHECK( ell.isInside( r ) == false ); - } -} +#if 0 +TEST_CASE( "Ellipse IsInside FRect", "[tellir]" ) +{ + FRect_ r(2,3, 10,10); + { + Ellipse_ ell; + CHECK( ell.isInside( r ) == false ); + } + { + Ellipse_ ell( Point2d(5,5), 2, 1 ); + CHECK( ell.isInside( r ) == true ); + } + { + Ellipse_ ell( Point2d(5,5), 6, 2 ); + CHECK( ell.isInside( r ) == false ); + } +} #endif TEST_CASE( "Circle IsInside Circle", "[tcic]" ) @@ -1550,13 +1550,13 @@ TEST_CASE( "Circle IsInside Circle", "[tcic]" ) CHECK( seg.getPts().second == Point2d(5,0) ); CHECK( seg.length() == 5 ); } -} +} ////////////////////////////////////////////////////////////// ///// INTERSECTION TESTS ///// ////////////////////////////////////////////////////////////// - + // This test is there just to make sure that every combination does build TEST_CASE( "Intersection", "[int_all]" ) { @@ -1564,52 +1564,52 @@ TEST_CASE( "Intersection", "[int_all]" ) Circle c1,c2; Segment s1,s2; Line2d l1,l2; - OPolyline po1,po2; - CPolyline pc1,pc2; + OPolyline po1,po2; + CPolyline pc1,pc2; CHECK( !r1.intersects( r2 )() ); // intersection with object of same type CHECK( !c1.intersects( c2 )() ); CHECK( !s1.intersects( s2 )() ); - CHECK( !l1.intersects( l2 )() ); - CHECK( !po1.intersects( po2 )() ); + CHECK( !l1.intersects( l2 )() ); + CHECK( !po1.intersects( po2 )() ); CHECK( !pc1.intersects( pc2 )() ); CHECK( r1.intersects( c2 )() ); CHECK( r1.intersects( s2 )() ); - CHECK( r1.intersects( l2 )() ); - CHECK( !r1.intersects( po2 )() ); // polyline is empty, so no intersection + CHECK( r1.intersects( l2 )() ); + CHECK( !r1.intersects( po2 )() ); // polyline is empty, so no intersection CHECK( !r1.intersects( pc2 )() ); // polyline is empty, so no intersection CHECK( c1.intersects( r2 )() ); CHECK( c1.intersects( s2 )() ); - CHECK( c1.intersects( l2 )() ); - CHECK( !c1.intersects( po2 )() ); // polyline is empty, so no intersection + CHECK( c1.intersects( l2 )() ); + CHECK( !c1.intersects( po2 )() ); // polyline is empty, so no intersection CHECK( !c1.intersects( pc2 )() ); // polyline is empty, so no intersection CHECK( s1.intersects( r2 )() ); CHECK( s1.intersects( c2 )() ); - CHECK( s1.intersects( l2 )() ); - CHECK( !s1.intersects( po2 )() ); // polyline is empty, so no intersection + CHECK( s1.intersects( l2 )() ); + CHECK( !s1.intersects( po2 )() ); // polyline is empty, so no intersection CHECK( !s1.intersects( pc2 )() ); // polyline is empty, so no intersection CHECK( l1.intersects( r2 )() ); CHECK( l1.intersects( c2 )() ); - CHECK( l1.intersects( s2 )() ); - CHECK( !l1.intersects( po2 )() ); // polyline is empty, so no intersection - CHECK( !l1.intersects( pc2 )() ); // polyline is empty, so no intersection - - CHECK( !po1.intersects( r2 )() ); - CHECK( !po1.intersects( c2 )() ); - CHECK( !po1.intersects( s2 )() ); - CHECK( !po1.intersects( l2 )() ); - CHECK( !po1.intersects( pc2 )() ); - - CHECK( !pc1.intersects( r2 )() ); - CHECK( !pc1.intersects( c2 )() ); - CHECK( !pc1.intersects( s2 )() ); - CHECK( !pc1.intersects( l2 )() ); - CHECK( !pc1.intersects( po2 )() ); -} + CHECK( l1.intersects( s2 )() ); + CHECK( !l1.intersects( po2 )() ); // polyline is empty, so no intersection + CHECK( !l1.intersects( pc2 )() ); // polyline is empty, so no intersection + + CHECK( !po1.intersects( r2 )() ); + CHECK( !po1.intersects( c2 )() ); + CHECK( !po1.intersects( s2 )() ); + CHECK( !po1.intersects( l2 )() ); + CHECK( !po1.intersects( pc2 )() ); + + CHECK( !pc1.intersects( r2 )() ); + CHECK( !pc1.intersects( c2 )() ); + CHECK( !pc1.intersects( s2 )() ); + CHECK( !pc1.intersects( l2 )() ); + CHECK( !pc1.intersects( po2 )() ); +} TEST_CASE( "Line/Line intersection", "[int_LL]" ) @@ -1698,20 +1698,20 @@ TEST_CASE( "Segment/Segment intersection", "[int_SS]" ) CHECK( si.size() == 1 ); CHECK( si.get() == Point2d(0,1) ); } - } - { - s1.set( 0., 0., 1., 0. ); - s2.set( 1., 1., 1., -1E-05 ); - { - auto si = s1.intersects(s2); - CHECK( si() ); - CHECK( si.size() == 1 ); - CHECK( si.get() == Point2d(1,0) ); - } - s2.set( 1., 1., 1., +1E-05 ); - { - CHECK( !s1.intersects(s2)() ); - } + } + { + s1.set( 0., 0., 1., 0. ); + s2.set( 1., 1., 1., -1E-05 ); + { + auto si = s1.intersects(s2); + CHECK( si() ); + CHECK( si.size() == 1 ); + CHECK( si.get() == Point2d(1,0) ); + } + s2.set( 1., 1., 1., +1E-05 ); + { + CHECK( !s1.intersects(s2)() ); + } } } @@ -1719,8 +1719,8 @@ TEST_CASE( "Circle/Circle intersection", "[int_CC]" ) { { Circle_ cA, cB; - CHECK( cA == cB ); // identical circles do - CHECK( !cA.intersects(cB)() ); // not intersect + CHECK( cA == cB ); // identical circles do + CHECK( !cA.intersects(cB)() ); // not intersect } { Circle_ cA; @@ -1732,9 +1732,9 @@ TEST_CASE( "Circle/Circle intersection", "[int_CC]" ) Circle_ cA( Point2d(0,0), 2 ); Circle_ cB( Point2d(3,0), 2 ); CHECK( cA != cB ); - CHECK( cA.intersects(cB)() ); - CHECK( cA.intersects(cB).size() == 2 ); - } + CHECK( cA.intersects(cB)() ); + CHECK( cA.intersects(cB).size() == 2 ); + } { Circle_ cA( Point2d(0,0), 1 ); Circle_ cB( Point2d(2,0), 1 ); @@ -1742,17 +1742,17 @@ TEST_CASE( "Circle/Circle intersection", "[int_CC]" ) CHECK( cA.intersects(cB)() ); auto inter = cA.intersects(cB); CHECK( inter() == true ); - CHECK( inter.size() == 1 ); -// CHECK( inter.get().first == Point2d(1,0) ); -// CHECK( inter.get().second == Point2d(1,0) ); - CHECK( inter.get()[0] == Point2d(1,0) ); + CHECK( inter.size() == 1 ); +// CHECK( inter.get().first == Point2d(1,0) ); +// CHECK( inter.get().second == Point2d(1,0) ); + CHECK( inter.get()[0] == Point2d(1,0) ); } } - -// This part uses externally defined rectangles, check the files -// figures_test/frect_intersect_*.code -// this is done so we can, for a single defined rectangle pair, -// have both the test code here, and a graphical representation of the situation. + +// This part uses externally defined rectangles, check the files +// figures_test/frect_intersect_*.code +// this is done so we can, for a single defined rectangle pair, +// have both the test code here, and a graphical representation of the situation. // The corresponding svg images are built with `$ make test_fig` (no external dependency) TEST_CASE( "FRect/FRect intersection", "[int_FF]" ) { @@ -1762,14 +1762,14 @@ TEST_CASE( "FRect/FRect intersection", "[int_FF]" ) CHECK( !r1.intersects(r2)() ); // no intersection ! auto inters = r1.intersects(r2); CHECK( inters.size() == 0 ); - + auto i1 = intersectArea(r1,r2); CHECK( i1() == true ); - CHECK( i1.get() == r1 ); - - auto u1 = unionArea(r1,r2); - CHECK( u1.size() == 4 ); // 4 points - CHECK( u1 == CPolyline(r1) ); // same + CHECK( i1.get() == r1 ); + + auto u1 = unionArea(r1,r2); + CHECK( u1.size() == 4 ); // 4 points + CHECK( u1 == CPolyline(r1) ); // same } { #include "figures_test/frect_intersect_1.code" @@ -1783,9 +1783,9 @@ TEST_CASE( "FRect/FRect intersection", "[int_FF]" ) CHECK( a() == false ); // no intersection ! auto b = r1&r2; CHECK( b() == false ); // no intersection ! - - auto u1 = unionArea(r1,r2); - CHECK( u1.size() == 0 ); // empty + + auto u1 = unionArea(r1,r2); + CHECK( u1.size() == 0 ); // empty } { #include "figures_test/frect_intersect_2.code" @@ -1805,10 +1805,10 @@ TEST_CASE( "FRect/FRect intersection", "[int_FF]" ) auto rect_inter2 = r1&r2; CHECK( rect_inter2() == true ); CHECK( rect_inter2.get() == FRect(2,1, 3,2) ); - - auto u1 = unionArea(r1,r2); -// Polyline p{ [0,0],[1,1] }; - CHECK( u1.size() == 8 ); + + auto u1 = unionArea(r1,r2); +// Polyline p{ [0,0],[1,1] }; + CHECK( u1.size() == 8 ); } { // 4 intersection points @@ -1826,10 +1826,10 @@ TEST_CASE( "FRect/FRect intersection", "[int_FF]" ) auto rect_inter = r1.intersectArea(r2); CHECK( rect_inter() == true ); CHECK( rect_inter.get() == FRect(2,2, 4,4) ); - - auto u1 = unionArea(r1,r2); -// Polyline p{ [0,0],[1,1] }; - CHECK( u1.size() == 12 ); + + auto u1 = unionArea(r1,r2); +// Polyline p{ [0,0],[1,1] }; + CHECK( u1.size() == 12 ); } { // horizontal segment overlap @@ -1848,10 +1848,10 @@ TEST_CASE( "FRect/FRect intersection", "[int_FF]" ) auto rect_inter = r1.intersectArea(r2); CHECK( rect_inter() == true ); - CHECK( rect_inter.get() == FRect(3,1, 4,3) ); - - auto u1 = unionArea(r1,r2); -// Polyline p{ [0,0],[1,1] }; + CHECK( rect_inter.get() == FRect(3,1, 4,3) ); + + auto u1 = unionArea(r1,r2); +// Polyline p{ [0,0],[1,1] }; CHECK( u1.size() == 4 ); } { // common vertical segment @@ -1873,9 +1873,9 @@ TEST_CASE( "FRect/FRect intersection", "[int_FF]" ) CHECK( inters() == false ); CHECK( inters.size() == 0 ); CHECK( r1.intersectArea(r2)() == false ); // still no intersection - - auto u1 = unionArea(r1,r2); - CHECK( u1.size() == 0 ); + + auto u1 = unionArea(r1,r2); + CHECK( u1.size() == 0 ); } { // two rectangles joined by corner at 3,2 #include "figures_test/frect_intersect_6.code" @@ -1884,9 +1884,9 @@ TEST_CASE( "FRect/FRect intersection", "[int_FF]" ) CHECK( inters.size() == 1 ); auto vpts = inters.get(); CHECK( vpts[0] == Point2d( 3,2 ) ); - CHECK( r1.intersectArea(r2)() == false ); // only one point ! - - auto u1 = unionArea(r1,r2); + CHECK( r1.intersectArea(r2)() == false ); // only one point ! + + auto u1 = unionArea(r1,r2); CHECK( u1.size() == 8 ); } { // two rectangles @@ -1926,71 +1926,71 @@ TEST_CASE( "FRect/FRect intersection", "[int_FF]" ) // auto rect_inter = r1.intersectArea(r2); // CHECK( rect_inter == FRect(3,2, 4,3) ); } - + { // one rectangle inside the other #include "figures_test/frect_intersect_10.code" CHECK( r1.intersects(r2)() == false ); auto inters = r1.intersects(r2); - CHECK( inters.size() == 0 ); - auto inters2 = r1.intersectArea(r2); - CHECK( inters2() == true ); + CHECK( inters.size() == 0 ); + auto inters2 = r1.intersectArea(r2); + CHECK( inters2() == true ); CHECK( inters2.get() == r2 ); } { // one rectangle inside the other, with a common segment #include "figures_test/frect_intersect_11.code" CHECK( r1.intersects(r2)() == true ); auto inters = r1.intersects(r2); - CHECK( inters.size() == 2 ); + CHECK( inters.size() == 2 ); auto vpts = inters.get(); CHECK( vpts[0] == Point2d( 2,3 ) ); - CHECK( vpts[1] == Point2d( 3,3 ) ); - CHECK( r1.intersectArea(r2)() == true ); - auto inter=r1.intersectArea(r2); + CHECK( vpts[1] == Point2d( 3,3 ) ); + CHECK( r1.intersectArea(r2)() == true ); + auto inter=r1.intersectArea(r2); CHECK( inter.get() == r2 ); - } - -// this below needs to be expanded + } + +// this below needs to be expanded { #include "figures_test/frect_intersect_12.code" CHECK( r1.intersects(r2)() == true ); auto inters = r1.intersects(r2); - CHECK( inters.size() == 3 ); - auto vpts = inters.get(); + CHECK( inters.size() == 3 ); + auto vpts = inters.get(); // priv::printVector( vpts, "intersection points demo12" ); - CHECK( vpts[0] == Point2d( 2,0 ) ); + CHECK( vpts[0] == Point2d( 2,0 ) ); CHECK( vpts[1] == Point2d( 2,2 ) ); - CHECK( vpts[2] == Point2d( 3,0 ) ); - CHECK( r1.intersectArea(r2)() == true ); -// auto inter=r1.intersectArea(r2); + CHECK( vpts[2] == Point2d( 3,0 ) ); + CHECK( r1.intersectArea(r2)() == true ); +// auto inter=r1.intersectArea(r2); // CHECK( inter.get() == r2 ); - } - -// this below needs to be expanded + } + +// this below needs to be expanded { #include "figures_test/frect_intersect_13.code" CHECK( r1.intersects(r2)() == true ); auto inters = r1.intersects(r2); - CHECK( inters.size() == 2 ); + CHECK( inters.size() == 2 ); auto vpts = inters.get(); CHECK( vpts[0] == Point2d( 3,0 ) ); - CHECK( vpts[1] == Point2d( 3,2 ) ); - CHECK( r1.intersectArea(r2)() == false ); -// auto inter=r1.intersectArea(r2); + CHECK( vpts[1] == Point2d( 3,2 ) ); + CHECK( r1.intersectArea(r2)() == false ); +// auto inter=r1.intersectArea(r2); // CHECK( inter.get() == r2 ); - } -} - -TEST_CASE( "IoU of 2 rectangles", "[IOU_RECT]" ) -{ - FRect_ r1( 0,0,1,1 ); - FRect_ r2( 2,2,3,3 ); - CHECK( IoU(r1,r2) == 0. ); - r1.set(0,0,2,2); - CHECK( IoU(r1,r2) == 0. ); - - r2.set(1,0,3,2); - CHECK( IoU(r1,r2) == Approx(1./3.) ); -} + } +} + +TEST_CASE( "IoU of 2 rectangles", "[IOU_RECT]" ) +{ + FRect_ r1( 0,0,1,1 ); + FRect_ r2( 2,2,3,3 ); + CHECK( IoU(r1,r2) == 0. ); + r1.set(0,0,2,2); + CHECK( IoU(r1,r2) == 0. ); + + r2.set(1,0,3,2); + CHECK( IoU(r1,r2) == Approx(1./3.) ); +} TEST_CASE( "Circle/Segment intersection", "[int_CS]" ) { @@ -2208,7 +2208,7 @@ TEST_CASE( "Line/FRect intersection", "[int_LF]" ) auto ri = li.intersects( FRect(pt1, pt2) ); CHECK( ri() == true ); CHECK( ri.size() == 2 ); - auto pts = ri.get(); + auto pts = ri.get(); CHECK( pts[0] == pt1 ); CHECK( pts[1] == pt2 ); @@ -2227,12 +2227,12 @@ TEST_CASE( "Line/FRect intersection", "[int_LF]" ) CHECK( ri1() == true ); CHECK( ri2() == true ); - CHECK( ri1.size() == 2 ); - CHECK( ri2.size() == 2 ); - auto pts1 = ri1.get(); - auto pts2 = ri2.get(); + CHECK( ri1.size() == 2 ); + CHECK( ri2.size() == 2 ); + auto pts1 = ri1.get(); + auto pts2 = ri2.get(); CHECK( pts1[0] == pt1 ); - CHECK( pts1[1] == Point2d(0,1) ); + CHECK( pts1[1] == Point2d(0,1) ); CHECK( pts2[0] == pt1 ); CHECK( pts2[1] == Point2d(0,1) ); @@ -2241,10 +2241,10 @@ TEST_CASE( "Line/FRect intersection", "[int_LF]" ) ri1 = li.intersects( r1 ); ri2 = r1.intersects( li ); CHECK( ri1() == true ); - CHECK( ri2() == true ); - CHECK( ri1.size() == 2 ); - CHECK( ri2.size() == 2 ); - + CHECK( ri2() == true ); + CHECK( ri1.size() == 2 ); + CHECK( ri2.size() == 2 ); + pts1 = ri1.get(); CHECK( pts1[0] == Point2d(1,0) ); CHECK( pts1[1] == Point2d(1,1) ); @@ -2254,10 +2254,10 @@ TEST_CASE( "Line/FRect intersection", "[int_LF]" ) ri2 = r1.intersects( li ); CHECK( ri1() == true ); CHECK( ri2() == true ); - CHECK( ri1.size() == 2 ); - CHECK( ri2.size() == 2 ); - - pts1 = ri1.get(); + CHECK( ri1.size() == 2 ); + CHECK( ri2.size() == 2 ); + + pts1 = ri1.get(); CHECK( pts1[0] == pt1 ); CHECK( pts1[1] == Point2d(1,0) ); @@ -2266,10 +2266,10 @@ TEST_CASE( "Line/FRect intersection", "[int_LF]" ) ri2 = r1.intersects( li ); CHECK( ri1() == true ); CHECK( ri2() == true ); - CHECK( ri1.size() == 2 ); - CHECK( ri2.size() == 2 ); - - pts1 = ri1.get(); + CHECK( ri1.size() == 2 ); + CHECK( ri2.size() == 2 ); + + pts1 = ri1.get(); CHECK( pts1[0] == Point2d(0,1) ); CHECK( pts1[1] == Point2d(1,1) ); @@ -2277,233 +2277,233 @@ TEST_CASE( "Line/FRect intersection", "[int_LF]" ) ri1 = li.intersects( r1 ); ri2 = r1.intersects( li ); CHECK( ri1() == true ); - CHECK( ri2() == true ); - CHECK( ri1.size() == 2 ); - CHECK( ri2.size() == 2 ); - - pts1 = ri1.get(); + CHECK( ri2() == true ); + CHECK( ri1.size() == 2 ); + CHECK( ri2.size() == 2 ); + + pts1 = ri1.get(); CHECK( pts1[0] == Point2d(0.,.5) ); CHECK( pts1[1] == Point2d(1.,.5) ); - } - INFO( "single point intersection" ) - { - FRect_ r1( 0,0,1,1 ); - Line2d_ li( -1,+1, +1,-1 ); - auto inter = r1.intersects( li ); - CHECK( inter.size() == 1 ); - auto pts = inter.get(); - CHECK( pts[0] == Point2d(0,0) ); - } + } + INFO( "single point intersection" ) + { + FRect_ r1( 0,0,1,1 ); + Line2d_ li( -1,+1, +1,-1 ); + auto inter = r1.intersects( li ); + CHECK( inter.size() == 1 ); + auto pts = inter.get(); + CHECK( pts[0] == Point2d(0,0) ); + } } ////////////////////////////////////////////////////////////// ///// MISC. TESTS ///// ////////////////////////////////////////////////////////////// - - -TEST_CASE( "Colinearity", "[colinearity]" ) -{ - Point2d p00(0,0), p01(0,1), p30(3,0); - Point2d pt1, pt2, pt3; - - { - pt1 = p01; pt2 = p00; pt3 = p30; // case A - auto arr = priv::getLargestDistancePoints( pt1, pt2, pt3 ); - CHECK( arr[0]==pt3 ); - CHECK( arr[1]==pt1 ); - CHECK( arr[2]==pt2 ); - } - { - pt1 = p01; pt2 = p30; pt3 = p00; // case B - auto arr = priv::getLargestDistancePoints( pt1, pt2, pt3 ); - CHECK( arr[0]==pt2 ); - CHECK( arr[1]==pt1 ); - CHECK( arr[2]==pt3 ); - } - { - pt1 = p00; pt2 = p01; pt3 = p30; // case C - auto arr = priv::getLargestDistancePoints( pt1, pt2, pt3 ); - CHECK( arr[0]==pt3 ); - CHECK( arr[1]==pt2 ); - CHECK( arr[2]==pt1 ); - } - { - pt1 = p30; pt2 = p01; pt3 = p00; // case D - auto arr = priv::getLargestDistancePoints( pt1, pt2, pt3 ); - CHECK( arr[0]==pt1 ); - CHECK( arr[1]==pt2 ); - CHECK( arr[2]==pt3 ); - } - { - pt1 = p00; pt2 = p30; pt3 = p01; // case E - auto arr = priv::getLargestDistancePoints( pt1, pt2, pt3 ); - CHECK( arr[0]==pt2 ); - CHECK( arr[1]==pt3 ); - CHECK( arr[2]==pt1 ); - } - { - pt1 = p30; pt2 = p00; pt3 = p01; // case F - auto arr = priv::getLargestDistancePoints( pt1, pt2, pt3 ); - CHECK( arr[0]==pt1 ); - CHECK( arr[1]==pt3 ); - CHECK( arr[2]==pt2 ); - } - { - Point2d p1(0,0), p2(1,0), p3(4,0); - CHECK( areCollinear( p1, p2, p3 ) ); - } - { - Point2d p1(1,0), p2(0,0), p3(4,0); - CHECK( areCollinear( p1, p2, p3 ) ); - } - { - Point2d p1(1,0), p2(0,0), p3(4,1E-3); - CHECK( !areCollinear( p1, p2, p3 ) ); - } -} - -TEST_CASE( "Line2d", "[line1]" ) -{ - Line2d_ li; - CHECK( li.area() == 0. ); - CHECK_THROWS( li.length() ); -} - -TEST_CASE( "Circle", "[cir1]" ) + + +TEST_CASE( "Colinearity", "[colinearity]" ) { + Point2d p00(0,0), p01(0,1), p30(3,0); + Point2d pt1, pt2, pt3; + { - Circle_ c1; // Default constructor - CHECK( c1.center() == Point2d(0,0) ); - CHECK( center(c1) == Point2d(0,0) ); - CHECK( c1.radius() == 1. ); - CHECK( radius(c1) == 1. ); - CHECK( c1.getBB() == FRect(-1,-1,1,1 ) ); - CHECK( getBB(c1) == FRect(-1,-1,1,1 ) ); - - CHECK( c1.area() == area(c1) ); - CHECK( c1.length() == length(c1) ); + pt1 = p01; pt2 = p00; pt3 = p30; // case A + auto arr = priv::getLargestDistancePoints( pt1, pt2, pt3 ); + CHECK( arr[0]==pt3 ); + CHECK( arr[1]==pt1 ); + CHECK( arr[2]==pt2 ); } - { - int i = 44; + { + pt1 = p01; pt2 = p30; pt3 = p00; // case B + auto arr = priv::getLargestDistancePoints( pt1, pt2, pt3 ); + CHECK( arr[0]==pt2 ); + CHECK( arr[1]==pt1 ); + CHECK( arr[2]==pt3 ); + } + { + pt1 = p00; pt2 = p01; pt3 = p30; // case C + auto arr = priv::getLargestDistancePoints( pt1, pt2, pt3 ); + CHECK( arr[0]==pt3 ); + CHECK( arr[1]==pt2 ); + CHECK( arr[2]==pt1 ); + } + { + pt1 = p30; pt2 = p01; pt3 = p00; // case D + auto arr = priv::getLargestDistancePoints( pt1, pt2, pt3 ); + CHECK( arr[0]==pt1 ); + CHECK( arr[1]==pt2 ); + CHECK( arr[2]==pt3 ); + } + { + pt1 = p00; pt2 = p30; pt3 = p01; // case E + auto arr = priv::getLargestDistancePoints( pt1, pt2, pt3 ); + CHECK( arr[0]==pt2 ); + CHECK( arr[1]==pt3 ); + CHECK( arr[2]==pt1 ); + } + { + pt1 = p30; pt2 = p00; pt3 = p01; // case F + auto arr = priv::getLargestDistancePoints( pt1, pt2, pt3 ); + CHECK( arr[0]==pt1 ); + CHECK( arr[1]==pt3 ); + CHECK( arr[2]==pt2 ); + } + { + Point2d p1(0,0), p2(1,0), p3(4,0); + CHECK( areCollinear( p1, p2, p3 ) ); + } + { + Point2d p1(1,0), p2(0,0), p3(4,0); + CHECK( areCollinear( p1, p2, p3 ) ); + } + { + Point2d p1(1,0), p2(0,0), p3(4,1E-3); + CHECK( !areCollinear( p1, p2, p3 ) ); + } +} + +TEST_CASE( "Line2d", "[line1]" ) +{ + Line2d_ li; + CHECK( li.area() == 0. ); + CHECK_THROWS( li.length() ); +} + +TEST_CASE( "Circle", "[cir1]" ) +{ + { + Circle_ c1; // Default constructor + CHECK( c1.center() == Point2d(0,0) ); + CHECK( center(c1) == Point2d(0,0) ); + CHECK( c1.radius() == 1. ); + CHECK( radius(c1) == 1. ); + CHECK( c1.getBB() == FRect(-1,-1,1,1 ) ); + CHECK( getBB(c1) == FRect(-1,-1,1,1 ) ); + + CHECK( c1.area() == area(c1) ); + CHECK( c1.length() == length(c1) ); + } + { + int i = 44; Circle_ c1(i); // 1-arg Constructor - radius CHECK( c1.center() == Point2d(0,0) ); CHECK( c1.radius() == i ); - CHECK( c1.getBB() == FRect(-i,-i,i,i) ); - CHECK( getBB(c1) == FRect(-i,-i,i,i) ); - c1.set( 454 ); - CHECK( c1.center() == Point2d(0,0) ); - CHECK( c1.radius() == 454 ); + CHECK( c1.getBB() == FRect(-i,-i,i,i) ); + CHECK( getBB(c1) == FRect(-i,-i,i,i) ); + c1.set( 454 ); + CHECK( c1.center() == Point2d(0,0) ); + CHECK( c1.radius() == 454 ); } { Point2d_ pt( 4,5); - Circle_ c2(pt); // 1-arg Constructor - center - CHECK( c2.center() == Point2d(4,5) ); - CHECK( c2.radius() == 1 ); - - CHECK( center(c2) == Point2d(4,5) ); // free functions call - CHECK( radius(c2) == 1 ); - - c2.set( Point2d_(18,22) ); - CHECK( c2.center() == Point2d(18,22) ); - CHECK( c2.radius() == 1 ); - } - { - Point2d_ pt1(0,0), pt2(4,0); - Circle_ c1(pt1,pt2); // 2-args constructor: 2 points - CHECK( c1.center() == Point2d(2,0) ); - CHECK( c1.radius() == 2. ); - c1.set( Point2d(1,1), Point2d(1,5) ); - CHECK( c1.center() == Point2d(1,3) ); - CHECK( c1.radius() == 2. ); - } - { - Point2d_ pt1(4,5); - Circle_ c2(pt1,3); // 2-args constructor - center and radius - CHECK( c2.center() == pt1 ); - CHECK( c2.radius() == 3 ); - - c2.set( Point2d_(42,24), 18 ); - CHECK( c2.center() == Point2d_(42,24) ); - CHECK( c2.radius() == 18 ); - } + Circle_ c2(pt); // 1-arg Constructor - center + CHECK( c2.center() == Point2d(4,5) ); + CHECK( c2.radius() == 1 ); + + CHECK( center(c2) == Point2d(4,5) ); // free functions call + CHECK( radius(c2) == 1 ); + + c2.set( Point2d_(18,22) ); + CHECK( c2.center() == Point2d(18,22) ); + CHECK( c2.radius() == 1 ); + } + { + Point2d_ pt1(0,0), pt2(4,0); + Circle_ c1(pt1,pt2); // 2-args constructor: 2 points + CHECK( c1.center() == Point2d(2,0) ); + CHECK( c1.radius() == 2. ); + c1.set( Point2d(1,1), Point2d(1,5) ); + CHECK( c1.center() == Point2d(1,3) ); + CHECK( c1.radius() == 2. ); + } + { + Point2d_ pt1(4,5); + Circle_ c2(pt1,3); // 2-args constructor - center and radius + CHECK( c2.center() == pt1 ); + CHECK( c2.radius() == 3 ); + + c2.set( Point2d_(42,24), 18 ); + CHECK( c2.center() == Point2d_(42,24) ); + CHECK( c2.radius() == 18 ); + } { Circle_ c1(1,2,3); // 3-args constructor: x0, y0, radius CHECK( c1.center() == Point2d(1,2) ); - CHECK( c1.radius() == 3 ); - - c1.set( 11, 22, 33 ); - CHECK( c1.center() == Point2d(11,22) ); + CHECK( c1.radius() == 3 ); + + c1.set( 11, 22, 33 ); + CHECK( c1.center() == Point2d(11,22) ); CHECK( c1.radius() == 33 ); } { - CHECK_THROWS( Circle_(1,2,0.) ); /// ? + CHECK_THROWS( Circle_(1,2,0.) ); /// ? CHECK_THROWS( Circle_(1,2,-1.) ); - } - { - Point2d_ pt1(4,5), pt2(6,7), pt3; // 3-args constructor: 3 points - Circle_ c1(pt1,pt2,pt3); // 2-args constructor: 2 points - - c1.set( Point2d(4,5), Point2d(6,7), Point2d(2,2) ); - - CHECK_THROWS( c1.set( Point2d(4,5), Point2d(4,5) ) ); // points must be different ! - CHECK_THROWS( c1.set( Point2d(4,5), Point2d(4,5), Point2d(2,2) ) ); - CHECK_THROWS( Circle( Point2d(4,5), Point2d(4,5), Point2d(2,2) ) ); - CHECK_THROWS( Circle( Point2d(4,5), Point2d(4,5) ) ); - } - { - Circle_ c1; - - CHECK( c1.radius() == 1 ); - c1.radius() = 2; // member function call - CHECK( c1.radius() == 2 ); - radius(c1) = 3; // free function call - CHECK( radius(c1) == 3 ); - - CHECK( center(c1) == Point2d() ); - c1.center() = Point2d(3,4); // member function call - CHECK( center(c1) == Point2d(3,4) ); - center(c1) = Point2d(5,6); // free function call - CHECK( center(c1) == Point2d(5,6) ); - } -} - -TEST_CASE( "Circle from points", "[cir2]" ) -{ - { - INFO( "circle from 2 pts"); - std::vector> pts1{ {0,0}, {2,0} }; - std::vector> pts2{ {0,3}, {4,3} }; -// check using constructor - Circle_ c1(pts1); - CHECK( c1.center() == Point2d(1,0) ); - CHECK( c1.radius() == 1. ); - Circle_ c2(pts2); - CHECK( c2.center() == Point2d(2,3) ); - CHECK( c2.radius() == 2. ); -// check using "set" function - c1.set(pts2); - CHECK( c1.center() == Point2d(2,3) ); - CHECK( c1.radius() == 2. ); - c2.set(pts1); - CHECK( c2.center() == Point2d(1,0) ); - CHECK( c2.radius() == 1. ); - } - { - INFO( "circle from 4 pts"); - std::vector> pts1{ {0,4}, {4,0}, {0,-4}, {0,0} }; - Circle_ c1(pts1); - CHECK( c1.center() == Point2d(0,0) ); - CHECK( c1.radius() == 4. ); - c1.set(pts1); - CHECK( c1.center() == Point2d(0,0) ); - CHECK( c1.radius() == 4. ); - - std::vector> pts2{ {0,4} }; - CHECK_THROWS( Circle_(pts2) ); // 1 point not allowed - std::vector> pts3; - CHECK_THROWS( Circle_(pts3) ); // 0 points not allowed - } -} + } + { + Point2d_ pt1(4,5), pt2(6,7), pt3; // 3-args constructor: 3 points + Circle_ c1(pt1,pt2,pt3); // 2-args constructor: 2 points + + c1.set( Point2d(4,5), Point2d(6,7), Point2d(2,2) ); + + CHECK_THROWS( c1.set( Point2d(4,5), Point2d(4,5) ) ); // points must be different ! + CHECK_THROWS( c1.set( Point2d(4,5), Point2d(4,5), Point2d(2,2) ) ); + CHECK_THROWS( Circle( Point2d(4,5), Point2d(4,5), Point2d(2,2) ) ); + CHECK_THROWS( Circle( Point2d(4,5), Point2d(4,5) ) ); + } + { + Circle_ c1; + + CHECK( c1.radius() == 1 ); + c1.radius() = 2; // member function call + CHECK( c1.radius() == 2 ); + radius(c1) = 3; // free function call + CHECK( radius(c1) == 3 ); + + CHECK( center(c1) == Point2d() ); + c1.center() = Point2d(3,4); // member function call + CHECK( center(c1) == Point2d(3,4) ); + center(c1) = Point2d(5,6); // free function call + CHECK( center(c1) == Point2d(5,6) ); + } +} + +TEST_CASE( "Circle from points", "[cir2]" ) +{ + { + INFO( "circle from 2 pts"); + std::vector> pts1{ {0,0}, {2,0} }; + std::vector> pts2{ {0,3}, {4,3} }; +// check using constructor + Circle_ c1(pts1); + CHECK( c1.center() == Point2d(1,0) ); + CHECK( c1.radius() == 1. ); + Circle_ c2(pts2); + CHECK( c2.center() == Point2d(2,3) ); + CHECK( c2.radius() == 2. ); +// check using "set" function + c1.set(pts2); + CHECK( c1.center() == Point2d(2,3) ); + CHECK( c1.radius() == 2. ); + c2.set(pts1); + CHECK( c2.center() == Point2d(1,0) ); + CHECK( c2.radius() == 1. ); + } + { + INFO( "circle from 4 pts"); + std::vector> pts1{ {0,4}, {4,0}, {0,-4}, {0,0} }; + Circle_ c1(pts1); + CHECK( c1.center() == Point2d(0,0) ); + CHECK( c1.radius() == 4. ); + c1.set(pts1); + CHECK( c1.center() == Point2d(0,0) ); + CHECK( c1.radius() == 4. ); + + std::vector> pts2{ {0,4} }; + CHECK_THROWS( Circle_(pts2) ); // 1 point not allowed + std::vector> pts3; + CHECK_THROWS( Circle_(pts3) ); // 0 points not allowed + } +} TEST_CASE( "Ellipse", "[ell1]" ) { @@ -2569,15 +2569,15 @@ TEST_CASE( "Segment", "[seg1]" ) { Segment_ s( p1, p2 ); // same x value CHECK( s.getPts().first == p1 ); - CHECK( s.getPts().second == p2 ); - - s.translate( 2, 3 ); + CHECK( s.getPts().second == p2 ); + + s.translate( 2, 3 ); CHECK( s.getPts().first == Point2d(45,11) ); - CHECK( s.getPts().second == Point2d(45,21) ); - + CHECK( s.getPts().second == Point2d(45,21) ); + translate( s, -2, -3 ); - CHECK( s.getPts().first == Point2d(43,8) ); - CHECK( s.getPts().second == Point2d(43,18) ); + CHECK( s.getPts().first == Point2d(43,8) ); + CHECK( s.getPts().second == Point2d(43,18) ); } { Segment_ s( p2, p1 ); // same x value @@ -2594,15 +2594,15 @@ TEST_CASE( "Segment", "[seg1]" ) CHECK( s.getPts().first == p3 ); CHECK( s.getPts().second == p1 ); } - } - { - Point2d_ p1( 4,5 ); - Point2d_ p2( 1,2 ); - auto ppts = std::make_pair( p1, p2 ); - Segment_ s( ppts ); - CHECK( s.getPts().first == p2 ); - CHECK( s.getPts().second == p1 ); - s.set( ppts ); + } + { + Point2d_ p1( 4,5 ); + Point2d_ p2( 1,2 ); + auto ppts = std::make_pair( p1, p2 ); + Segment_ s( ppts ); + CHECK( s.getPts().first == p2 ); + CHECK( s.getPts().second == p1 ); + s.set( ppts ); } { Line2d_ li( Point2d(0,0), Point2d(2,2) ); @@ -2622,7 +2622,7 @@ TEST_CASE( "Segment", "[seg1]" ) } { Segment_ s1( Point2d(0,0), Point2d(3,4) ); - CHECK( s1.length() == 5 ); + CHECK( s1.length() == 5 ); CHECK( s1.area() == 0 ); Segment_ s2( Point2d(9,9), Point2d(8,8) ); @@ -2639,13 +2639,13 @@ TEST_CASE( "Segment", "[seg1]" ) auto pt = s1.getCenter(); CHECK( pt == Point2d_(1,1) ); - } - { - Segment_ s1( Point2d(0,0), Point2d(2,2) ); - Segment_ s2( Point2d(2,0), Point2d(0,2) ); - auto bis = s1.getBisector(); - CHECK( bis == s2.getLine() ); - } + } + { + Segment_ s1( Point2d(0,0), Point2d(2,2) ); + Segment_ s2( Point2d(2,0), Point2d(0,2) ); + auto bis = s1.getBisector(); + CHECK( bis == s2.getLine() ); + } { // test that points on a line at equal distance from a point, when // transformed in a segment, we get back as middle point the same one. Line2d li(9,9); // diagonal line (0,0) - (9,9) @@ -2654,287 +2654,287 @@ TEST_CASE( "Segment", "[seg1]" ) CHECK( s1.getCenter() == Point2d_(5,5) ); } } - -TEST_CASE( "Translate", "[trans]") -{ - INFO( "Translate Circle" ); - Circle_ c(5,5,10); - c.translate(-1,+1); - CHECK( c.center() == Point2d(4,6) ); - translate( c, +1, -1 ); - CHECK( c.center() == Point2d(5,5) ); - - translate( c, std::make_pair(2,3) ); - CHECK( c.center() == Point2d(7,8) ); - c.translate( std::make_pair(-2,-3) ); - CHECK( c.center() == Point2d(5,5) ); - - INFO( "Translate Point" ); - Point2d_ pt(4,5); - pt.translate(-1,+1); - CHECK( pt == Point2d(3,6) ); - translate(pt,+1,-1); - CHECK( pt == Point2d(4,5) ); - - translate( pt, std::make_pair(2,3) ); - CHECK( pt == Point2d(6,8) ); - pt.translate( std::make_pair(-2,-3) ); - CHECK( pt == Point2d(4,5) ); -} - -TEST_CASE( "MoveTo", "[moveto]") -{ - INFO( "MoveTo Circle" ); - Circle_ c(5,5,10); - c.moveTo( 9,8 ); - CHECK( c.center() == Point2d(9,8) ); - moveTo(c,8,9); - CHECK( c.center() == Point2d(8,9) ); - - Point2d_ pt1(1,1); - moveTo(c,pt1); - CHECK( c.center() == pt1 ); - Point2d_ pt2(3,3); - c.moveTo(pt2); - CHECK( c.center() == pt2 ); - -// INFO( "MoveTo Segment" ); - - -} - -TEST_CASE( "Segment-split", "[seg-split]" ) -{ - Segment_ s1( Point2d(0,0), Point2d(2,0) ); - auto psegs1 = s1.split(); - auto psegs2 = split(s1); - CHECK( psegs1 == psegs2 ); - - CHECK( psegs1.first == Segment( Point2d(0,0), Point2d(1,0) ) ); - CHECK( psegs1.second == Segment( Point2d(1,0), Point2d(2,0) ) ); -} - -TEST_CASE( "Segment 2", "[seg2]" ) -{ - { - Segment_ seg (0,0,1,0); // horizontal segment - auto psegs = seg.getParallelSegs(1.); - auto psegs2 = seg.getParallelSegs(1.); - CHECK( psegs == psegs2 ); - CHECK( psegs.first == Segment(0,-1,1,-1) ); - CHECK( psegs.second == Segment(0,+1,1,+1) ); - } - { - Segment_ seg (0,0,0,1); // vertical segment - auto psegs = seg.getParallelSegs(1.); - CHECK( psegs.first == Segment(-1,0,-1,+1) ); - CHECK( psegs.second == Segment(+1,0,+1,+1) ); - } -} - -TEST_CASE( "Segment extended", "[seg_extended]" ) -{ - Segment_ seg(0,0,1,0); - auto s2 = seg.getExtended(); - CHECK( s2.length() == 3. ); - CHECK( s2.getPts().first == Point2d(-1,0) ); - CHECK( s2.getPts().second == Point2d(2,0) ); - - auto s3 = getExtended(seg); - CHECK( s3.length() == 3. ); - CHECK( s3.getPts().first == Point2d(-1,0) ); - CHECK( s3.getPts().second == Point2d(2,0) ); -} - -TEST_CASE( "Segment orthogonal", "[seg_orthog]" ) -{ - Segment_ seg(0,0,1,0); - auto pts = seg.getOrthogPts(); - auto pts2 = getOrthogPts(seg); - CHECK( pts2 == pts ); - auto pol = CPolyline(pts); - CHECK( pol == CPolyline( std::vector{ {0,-1},{1,-1},{1,1},{0,1} } ) ); - - auto osegs = seg.getOrthogSegs(); - auto osegs2 = getOrthogSegs(seg); - CHECK( osegs2 == osegs ); - std::array,4> gt; - gt[0] = Segment( 0,0,0,+1 ); - gt[1] = Segment( 0,0,0,-1 ); - gt[2] = Segment( 1,0,1,+1 ); - gt[3] = Segment( 1,0,1,-1 ); - std::sort( gt.begin(), gt.end() ); - std::sort( osegs.begin(), osegs.end() ); - CHECK( gt == osegs ); -} - -TEST_CASE( "generalized bounding box of two objects", "[gen-BB]" ) -{ - Point2d_ pt,pt2(1,1); - FRect_ re,re2; - Segment_ se,se2(1,0,0,1); - CPolyline_ pc,pc2; - OPolyline_ po,po2; - Ellipse_ el,el2; - - CHECK( getBB(pt,pt2) == FRect_() ); // (0,0) - (1,1) - CHECK( getBB(se,se2) == FRect_() ); // [(0,0) - (1,1)] - [(1,0) - (0,1)] - CHECK( getBB(se,se) == FRect_() ); // identical segments, but they cover some area - CHECK( getBB(re,re2) == FRect_() ); - CHECK( getBB(se,se2) == FRect_() ); - CHECK( getBB(el,el2) == getBB(el) ); // identical - - CHECK_THROWS( getBB(pc,pc2) ); // empty - CHECK_THROWS( getBB(pt,pt) ); // identical points => no BB - - pt2.set(0,1); - CHECK_THROWS( getBB(pt,pt2) ); // one of the coordinates is identical - - se.set( 0,0,1,0 ); - se2.set( 10,0,11,0 ); - CHECK_THROWS( getBB(se,se) ); // segments are colinear - CHECK_THROWS( getBB(se,se2) ); // segments are colinear - - pt2.set(0,0); - CHECK_THROWS( getBB(se,pt2) ); // no area - - pc.set( std::vector>{ {0,0},{1,0} } ); - CHECK_THROWS( getBB(pc) ); // no area - CHECK_THROWS( getBB(pc, pc2) ); // no area, and second is empty - - pc2.set( std::vector>{ {5,0},{6,0} } ); - CHECK_THROWS( getBB(pc,pc2) ); // colinear - - pc2.set( std::vector>{ {5,1},{6,1} } ); - CHECK( getBB(pc,pc2) == FRect_(0,0,6,1) ); -} - - -TEST_CASE( "common bounding box with points ", "[point2d-BB]" ) -{ - Point2d_ pt; - FRect_ r1; - CHECK( getBB( pt, r1 ) == r1 ); - CHECK( getBB( r1, pt ) == r1 ); - Segment_ seg( 0,0,1,1); - CHECK( getBB( seg, pt ) == r1 ); - CHECK( getBB( pt, seg ) == r1 ); - - Circle_ cir; - pt.set( 10,0); - CHECK( getBB( pt, cir ) == FRect_(-1.,-1.,+10.,+1. ) ); -} - -TEST_CASE( "FRect pair bounding box", "[frect-BB]" ) -{ - { // two identical rectangles - FRect_ r1, r2; - CHECK( getBB(r1,r2) == r1 ); - } - { // one bottom left, the other top right - FRect_ r1(0,0, 1,1); - FRect_ r2(2,2, 4,4); - CHECK( getBB(r1,r2) == FRect_(0,0,4,4) ); - } - { // one top left, the other bottom right - FRect_ r1(0,2, 1,3); - FRect_ r2(2,0, 4,1); - CHECK( getBB(r1,r2) == FRect_(0,0,4,3) ); - } - { // one inside the other - FRect_ r1(0,0, 5,5); - FRect_ r2(1,1, 3,3); - CHECK( getBB(r1,r2) == FRect_(0,0,5,5) ); - } - { // one inside the other, with a common edge - FRect_ r1(0,0, 5,5); - FRect_ r2(1,0, 3,3); - CHECK( getBB(r1,r2) == FRect_(0,0,5,5) ); - } - -// test for rectangle of different types - { // one inside the other, with a common edge - FRect_ r1(0,0, 5,5); - FRect_ r2(1,0, 3,3); - auto bb = getBB(r1,r2); - CHECK( bb == FRect_(0,0,5,5) ); - CHECK( bb.dtype() == Dtype::Float ); - } -} - -TEST_CASE( "bounding box of two objects", "[getBB-pair]" ) -{ - FRect_ r1(0,3, 2,0); - FRect_ r2(4,5, 6,8); - FRect_ bbr(0,0, 6,8); - CHECK( bbr == getBB( r1,r2 ) ); -/* { // distant segments - Segment_ s1(0,3, 2,0); - Segment_ s2(4,5, 6,8); - FRect_ bbs(0,0, 6,8); - CHECK( bbs == getBB( s1,s2 ) ); - } - { // crossing segments - Segment_ s1(0,0, 5,5); - Segment_ s2(0,6, 4,0); - FRect_ bbs(0,0, 5,6); - CHECK( bbs == getBB( s1,s2 ) ); - } -*/ - { // one circle inside the other - Circle_ c1(0,0, 2); - Circle_ c2(0,1, 4); - FRect_ bbc(-4,-3, +4,5); - CHECK( bbc == getBB( c1,c2 ) ); - } - { // two distant circles - Circle_ c1(5,5, 2); - Circle_ c2(0,0, 1); - FRect_ bbc(-1,-1, 7,7); - CHECK( bbc == getBB( c1,c2 ) ); - } - { // CPolyline / Frect - CPolyline_ po( std::vector{ {0,0}, {1,1}, {3,2} } ); - FRect_ rect; - CHECK( getBB(po,rect) == FRect_(0,0,3,2) ); - } - { // OPolyline / Frect - OPolyline_ po( std::vector{ {0,0}, {1,1}, {3,2} } ); - FRect_ rect; - CHECK( getBB(po,rect) == FRect_(0,0,3,2) ); - } -/* { // OPolyline / Segment - OPolyline_ po( std::vector{ {0,0}, {1,1}, {3,2} } ); - Segment_ seg; - CHECK( getBB(seg,po) == FRect_(0,0,3,2) ); - }*/ - - { // Circle / Frect - Circle_ cir( 5,5,3 ); // center at 5,5, radius=3 - FRect_ rect; // (0,0)--(1,1) - CHECK( getBB(cir,rect) == FRect_(0,0,8,8) ); - } -/* { // Circle / Segment - Circle_ cir( 5,5,3 ); // center at 5,5, radius=3 - Segment_ seg(10,20,30,40); - CHECK( getBB(cir,seg) == FRect_(2,2,30,40) ); - }*/ - { // Circle / Circle (one inside the other) - Circle_ cir1( 5,5,3 ); // center at 5,5, radius=3 - Circle_ cir2( 5,5,1 ); // center at 5,5, radius=1 - CHECK( getBB(cir1,cir2) == FRect_(2,2,8,8) ); - } - { // two identical Circles - Circle_ cir1,cir2; - CHECK( getBB(cir1,cir2) == FRect_(-1,-1,1,1) ); - } - { // two identical Ellipses - Ellipse_ e1,e2; - CHECK( getBB(e1,e2) == FRect_(-2,-1,2,1) ); - } -} - - + +TEST_CASE( "Translate", "[trans]") +{ + INFO( "Translate Circle" ); + Circle_ c(5,5,10); + c.translate(-1,+1); + CHECK( c.center() == Point2d(4,6) ); + translate( c, +1, -1 ); + CHECK( c.center() == Point2d(5,5) ); + + translate( c, std::make_pair(2,3) ); + CHECK( c.center() == Point2d(7,8) ); + c.translate( std::make_pair(-2,-3) ); + CHECK( c.center() == Point2d(5,5) ); + + INFO( "Translate Point" ); + Point2d_ pt(4,5); + pt.translate(-1,+1); + CHECK( pt == Point2d(3,6) ); + translate(pt,+1,-1); + CHECK( pt == Point2d(4,5) ); + + translate( pt, std::make_pair(2,3) ); + CHECK( pt == Point2d(6,8) ); + pt.translate( std::make_pair(-2,-3) ); + CHECK( pt == Point2d(4,5) ); +} + +TEST_CASE( "MoveTo", "[moveto]") +{ + INFO( "MoveTo Circle" ); + Circle_ c(5,5,10); + c.moveTo( 9,8 ); + CHECK( c.center() == Point2d(9,8) ); + moveTo(c,8,9); + CHECK( c.center() == Point2d(8,9) ); + + Point2d_ pt1(1,1); + moveTo(c,pt1); + CHECK( c.center() == pt1 ); + Point2d_ pt2(3,3); + c.moveTo(pt2); + CHECK( c.center() == pt2 ); + +// INFO( "MoveTo Segment" ); + + +} + +TEST_CASE( "Segment-split", "[seg-split]" ) +{ + Segment_ s1( Point2d(0,0), Point2d(2,0) ); + auto psegs1 = s1.split(); + auto psegs2 = split(s1); + CHECK( psegs1 == psegs2 ); + + CHECK( psegs1.first == Segment( Point2d(0,0), Point2d(1,0) ) ); + CHECK( psegs1.second == Segment( Point2d(1,0), Point2d(2,0) ) ); +} + +TEST_CASE( "Segment 2", "[seg2]" ) +{ + { + Segment_ seg (0,0,1,0); // horizontal segment + auto psegs = seg.getParallelSegs(1.); + auto psegs2 = seg.getParallelSegs(1.); + CHECK( psegs == psegs2 ); + CHECK( psegs.first == Segment(0,-1,1,-1) ); + CHECK( psegs.second == Segment(0,+1,1,+1) ); + } + { + Segment_ seg (0,0,0,1); // vertical segment + auto psegs = seg.getParallelSegs(1.); + CHECK( psegs.first == Segment(-1,0,-1,+1) ); + CHECK( psegs.second == Segment(+1,0,+1,+1) ); + } +} + +TEST_CASE( "Segment extended", "[seg_extended]" ) +{ + Segment_ seg(0,0,1,0); + auto s2 = seg.getExtended(); + CHECK( s2.length() == 3. ); + CHECK( s2.getPts().first == Point2d(-1,0) ); + CHECK( s2.getPts().second == Point2d(2,0) ); + + auto s3 = getExtended(seg); + CHECK( s3.length() == 3. ); + CHECK( s3.getPts().first == Point2d(-1,0) ); + CHECK( s3.getPts().second == Point2d(2,0) ); +} + +TEST_CASE( "Segment orthogonal", "[seg_orthog]" ) +{ + Segment_ seg(0,0,1,0); + auto pts = seg.getOrthogPts(); + auto pts2 = getOrthogPts(seg); + CHECK( pts2 == pts ); + auto pol = CPolyline(pts); + CHECK( pol == CPolyline( std::vector{ {0,-1},{1,-1},{1,1},{0,1} } ) ); + + auto osegs = seg.getOrthogSegs(); + auto osegs2 = getOrthogSegs(seg); + CHECK( osegs2 == osegs ); + std::array,4> gt; + gt[0] = Segment( 0,0,0,+1 ); + gt[1] = Segment( 0,0,0,-1 ); + gt[2] = Segment( 1,0,1,+1 ); + gt[3] = Segment( 1,0,1,-1 ); + std::sort( gt.begin(), gt.end() ); + std::sort( osegs.begin(), osegs.end() ); + CHECK( gt == osegs ); +} + +TEST_CASE( "generalized bounding box of two objects", "[gen-BB]" ) +{ + Point2d_ pt,pt2(1,1); + FRect_ re,re2; + Segment_ se,se2(1,0,0,1); + CPolyline_ pc,pc2; + OPolyline_ po,po2; + Ellipse_ el,el2; + + CHECK( getBB(pt,pt2) == FRect_() ); // (0,0) - (1,1) + CHECK( getBB(se,se2) == FRect_() ); // [(0,0) - (1,1)] - [(1,0) - (0,1)] + CHECK( getBB(se,se) == FRect_() ); // identical segments, but they cover some area + CHECK( getBB(re,re2) == FRect_() ); + CHECK( getBB(se,se2) == FRect_() ); + CHECK( getBB(el,el2) == getBB(el) ); // identical + + CHECK_THROWS( getBB(pc,pc2) ); // empty + CHECK_THROWS( getBB(pt,pt) ); // identical points => no BB + + pt2.set(0,1); + CHECK_THROWS( getBB(pt,pt2) ); // one of the coordinates is identical + + se.set( 0,0,1,0 ); + se2.set( 10,0,11,0 ); + CHECK_THROWS( getBB(se,se) ); // segments are colinear + CHECK_THROWS( getBB(se,se2) ); // segments are colinear + + pt2.set(0,0); + CHECK_THROWS( getBB(se,pt2) ); // no area + + pc.set( std::vector>{ {0,0},{1,0} } ); + CHECK_THROWS( getBB(pc) ); // no area + CHECK_THROWS( getBB(pc, pc2) ); // no area, and second is empty + + pc2.set( std::vector>{ {5,0},{6,0} } ); + CHECK_THROWS( getBB(pc,pc2) ); // colinear + + pc2.set( std::vector>{ {5,1},{6,1} } ); + CHECK( getBB(pc,pc2) == FRect_(0,0,6,1) ); +} + + +TEST_CASE( "common bounding box with points ", "[point2d-BB]" ) +{ + Point2d_ pt; + FRect_ r1; + CHECK( getBB( pt, r1 ) == r1 ); + CHECK( getBB( r1, pt ) == r1 ); + Segment_ seg( 0,0,1,1); + CHECK( getBB( seg, pt ) == r1 ); + CHECK( getBB( pt, seg ) == r1 ); + + Circle_ cir; + pt.set( 10,0); + CHECK( getBB( pt, cir ) == FRect_(-1.,-1.,+10.,+1. ) ); +} + +TEST_CASE( "FRect pair bounding box", "[frect-BB]" ) +{ + { // two identical rectangles + FRect_ r1, r2; + CHECK( getBB(r1,r2) == r1 ); + } + { // one bottom left, the other top right + FRect_ r1(0,0, 1,1); + FRect_ r2(2,2, 4,4); + CHECK( getBB(r1,r2) == FRect_(0,0,4,4) ); + } + { // one top left, the other bottom right + FRect_ r1(0,2, 1,3); + FRect_ r2(2,0, 4,1); + CHECK( getBB(r1,r2) == FRect_(0,0,4,3) ); + } + { // one inside the other + FRect_ r1(0,0, 5,5); + FRect_ r2(1,1, 3,3); + CHECK( getBB(r1,r2) == FRect_(0,0,5,5) ); + } + { // one inside the other, with a common edge + FRect_ r1(0,0, 5,5); + FRect_ r2(1,0, 3,3); + CHECK( getBB(r1,r2) == FRect_(0,0,5,5) ); + } + +// test for rectangle of different types + { // one inside the other, with a common edge + FRect_ r1(0,0, 5,5); + FRect_ r2(1,0, 3,3); + auto bb = getBB(r1,r2); + CHECK( bb == FRect_(0,0,5,5) ); + CHECK( bb.dtype() == Dtype::Float ); + } +} + +TEST_CASE( "bounding box of two objects", "[getBB-pair]" ) +{ + FRect_ r1(0,3, 2,0); + FRect_ r2(4,5, 6,8); + FRect_ bbr(0,0, 6,8); + CHECK( bbr == getBB( r1,r2 ) ); +/* { // distant segments + Segment_ s1(0,3, 2,0); + Segment_ s2(4,5, 6,8); + FRect_ bbs(0,0, 6,8); + CHECK( bbs == getBB( s1,s2 ) ); + } + { // crossing segments + Segment_ s1(0,0, 5,5); + Segment_ s2(0,6, 4,0); + FRect_ bbs(0,0, 5,6); + CHECK( bbs == getBB( s1,s2 ) ); + } +*/ + { // one circle inside the other + Circle_ c1(0,0, 2); + Circle_ c2(0,1, 4); + FRect_ bbc(-4,-3, +4,5); + CHECK( bbc == getBB( c1,c2 ) ); + } + { // two distant circles + Circle_ c1(5,5, 2); + Circle_ c2(0,0, 1); + FRect_ bbc(-1,-1, 7,7); + CHECK( bbc == getBB( c1,c2 ) ); + } + { // CPolyline / Frect + CPolyline_ po( std::vector{ {0,0}, {1,1}, {3,2} } ); + FRect_ rect; + CHECK( getBB(po,rect) == FRect_(0,0,3,2) ); + } + { // OPolyline / Frect + OPolyline_ po( std::vector{ {0,0}, {1,1}, {3,2} } ); + FRect_ rect; + CHECK( getBB(po,rect) == FRect_(0,0,3,2) ); + } +/* { // OPolyline / Segment + OPolyline_ po( std::vector{ {0,0}, {1,1}, {3,2} } ); + Segment_ seg; + CHECK( getBB(seg,po) == FRect_(0,0,3,2) ); + }*/ + + { // Circle / Frect + Circle_ cir( 5,5,3 ); // center at 5,5, radius=3 + FRect_ rect; // (0,0)--(1,1) + CHECK( getBB(cir,rect) == FRect_(0,0,8,8) ); + } +/* { // Circle / Segment + Circle_ cir( 5,5,3 ); // center at 5,5, radius=3 + Segment_ seg(10,20,30,40); + CHECK( getBB(cir,seg) == FRect_(2,2,30,40) ); + }*/ + { // Circle / Circle (one inside the other) + Circle_ cir1( 5,5,3 ); // center at 5,5, radius=3 + Circle_ cir2( 5,5,1 ); // center at 5,5, radius=1 + CHECK( getBB(cir1,cir2) == FRect_(2,2,8,8) ); + } + { // two identical Circles + Circle_ cir1,cir2; + CHECK( getBB(cir1,cir2) == FRect_(-1,-1,1,1) ); + } + { // two identical Ellipses + Ellipse_ e1,e2; + CHECK( getBB(e1,e2) == FRect_(-2,-1,2,1) ); + } +} + + TEST_CASE( "FRect", "[frect]" ) { { @@ -2944,13 +2944,13 @@ TEST_CASE( "FRect", "[frect]" ) CHECK( r1.length() == 4 ); CHECK( r1.area() == 1 ); CHECK( r1.getCenter() == Point2d(0.5,0.5) ); - -// free functions - CHECK( width(r1) == 1. ); - CHECK( height(r1) == 1. ); - CHECK( length(r1) == 4 ); - CHECK( area(r1) == 1 ); - CHECK( getCenter(r1) == Point2d(0.5,0.5) ); + +// free functions + CHECK( width(r1) == 1. ); + CHECK( height(r1) == 1. ); + CHECK( length(r1) == 4 ); + CHECK( area(r1) == 1 ); + CHECK( getCenter(r1) == Point2d(0.5,0.5) ); auto p_pts = r1.getPts(); CHECK( p_pts.first == Point2d() ); @@ -2997,565 +2997,565 @@ TEST_CASE( "FRect", "[frect]" ) CHECK( r.length() == 300 ); CHECK( r.area() == 5000 ); CHECK( r.getCenter() == Point2d(0,0) ); - } - { - FRect_ r1( 0,0, 5, 3 ); - FRect_ r2( 2,3, 7, 6 ); - CHECK( r1.size() == r2.size() ); - CHECK( size(r1) == size(r2) ); - } -} - -TEST_CASE( "FRect extended", "[frect-ext]" ) -{ - FRect_ rect; - auto r1a = rect.getExtended(); - auto r1b = getExtended(rect); - CHECK( r1a == r1b ); - CHECK( r1a == FRect(-1,-1,2,2 ) ); - CHECK( r1a.area() == 9 * rect.area() ); - - FRect_ rect2(4,5,6,6); - CHECK( rect2.getExtended() == FRect(2,4,8,7) ); -} - -TEST_CASE( "FRect diagonals", "[frect-diags]" ) -{ - FRect_ r1; - auto psegs1 = r1.getDiagonals(); - auto psegs2 = getDiagonals(r1); - CHECK( psegs1 == psegs2 ); - - CHECK( psegs1.first == Segment(0,0,1,1) ); - CHECK( psegs1.second == Segment(0,1,1,0) ); -} - + } + { + FRect_ r1( 0,0, 5, 3 ); + FRect_ r2( 2,3, 7, 6 ); + CHECK( r1.size() == r2.size() ); + CHECK( size(r1) == size(r2) ); + } +} + +TEST_CASE( "FRect extended", "[frect-ext]" ) +{ + FRect_ rect; + auto r1a = rect.getExtended(); + auto r1b = getExtended(rect); + CHECK( r1a == r1b ); + CHECK( r1a == FRect(-1,-1,2,2 ) ); + CHECK( r1a.area() == 9 * rect.area() ); + + FRect_ rect2(4,5,6,6); + CHECK( rect2.getExtended() == FRect(2,4,8,7) ); +} + +TEST_CASE( "FRect diagonals", "[frect-diags]" ) +{ + FRect_ r1; + auto psegs1 = r1.getDiagonals(); + auto psegs2 = getDiagonals(r1); + CHECK( psegs1 == psegs2 ); + + CHECK( psegs1.first == Segment(0,0,1,1) ); + CHECK( psegs1.second == Segment(0,1,1,0) ); +} + TEST_CASE( "Polyline comparison 1", "[polyline-comparison-1]" ) -{ - std::vector> vpt1{ {0,0}, {1,0}, {1,1} }; - std::vector> vpt2{ {1,0}, {1,1}, {0,0} }; - { - OPolyline opl1, opl2; - opl1.set( vpt1 ); - opl2.set( vpt2 ); - CHECK( opl1 != opl2 ); - - CPolyline cpl1, cpl2; - cpl1.set( vpt1 ); - cpl2.set( vpt2 ); - CHECK( cpl1 == cpl2 ); - - CHECK( opl1 != cpl1 ); // comparing polygons of different types is allowed - CHECK( opl2 != cpl1 ); // (but will always return false) - } -} - - -TEST_CASE( "Polyline minimization", "[polyline-min]" ) -{ - INFO( "Polyline pl2( IsClosed::No" ); - { -#include "figures_test/polyline_min_1O.code" // open - - CHECK( pl.size() == 3 ); - CHECK( pl.nbSegs() == 2 ); - pl.minimize(); - CHECK( pl.size() == 2 ); - CHECK( pl.nbSegs() == 1 ); - } - { -#include "figures_test/polyline_min_1C.code" // closed - CHECK( pl.size() == 3 ); - CHECK( pl.nbSegs() == 3 ); - pl.minimize(); - CHECK( pl.size() == 2 ); - CHECK( pl.nbSegs() == 2 ); - } - { -#include "figures_test/polyline_min_2O.code"// open - CHECK( pl.size() == 3 ); - CHECK( pl.nbSegs() == 2 ); - pl.minimize(); -// std::cout << "pl:" << pl << '\n'; - CHECK( pl.size() == 3 ); // no change - CHECK( pl.nbSegs() == 2 ); - } - { -#include "figures_test/polyline_min_2C.code" // closed - CHECK( pl.size() == 3 ); - CHECK( pl.nbSegs() == 3 ); - pl.minimize(); - CHECK( pl.size() == 3 ); // no change ! - CHECK( pl.nbSegs() == 3 ); - } - { -#include "figures_test/polyline_min_3O.code" // open - CHECK( pl.size() == 4 ); - CHECK( pl.nbSegs() == 3 ); - pl.minimize(); -// std::cout << "pl:" << pl << '\n'; - CHECK( pl.size() == 4 ); // no change - CHECK( pl.nbSegs() == 3 ); - } - { -#include "figures_test/polyline_min_3C.code" // closed - CHECK( pl.size() == 4 ); - CHECK( pl.nbSegs() == 4 ); - pl.minimize(); -// std::cout << "pl:" << pl << '\n'; - CHECK( pl.size() == 3 ); // no change - CHECK( pl.nbSegs() == 3 ); - } -} - -template -void polytest_1( const base::PolylineBase& pl1 ) -{ - CHECK( pl1.isPolygon() == false ); - CHECK( isPolygon(pl1) == false ); - - CHECK( pl1.size() == 0 ); - CHECK( size(pl1) == 0 ); - - CHECK( pl1.nbSegs() == 0 ); - CHECK( nbSegs(pl1) == 0 ); - - CHECK( pl1.length() == 0 ); - CHECK( length(pl1) == 0 ); - - CHECK( pl1.area() == 0 ); - CHECK( area(pl1) == 0 ); - - CHECK_THROWS( centroid(pl1) ); // unable - CHECK_THROWS( pl1.centroid() ); // unable -} - +{ + std::vector> vpt1{ {0,0}, {1,0}, {1,1} }; + std::vector> vpt2{ {1,0}, {1,1}, {0,0} }; + { + OPolyline opl1, opl2; + opl1.set( vpt1 ); + opl2.set( vpt2 ); + CHECK( opl1 != opl2 ); + + CPolyline cpl1, cpl2; + cpl1.set( vpt1 ); + cpl2.set( vpt2 ); + CHECK( cpl1 == cpl2 ); + + CHECK( opl1 != cpl1 ); // comparing polygons of different types is allowed + CHECK( opl2 != cpl1 ); // (but will always return false) + } +} + + +TEST_CASE( "Polyline minimization", "[polyline-min]" ) +{ + INFO( "Polyline pl2( IsClosed::No" ); + { +#include "figures_test/polyline_min_1O.code" // open + + CHECK( pl.size() == 3 ); + CHECK( pl.nbSegs() == 2 ); + pl.minimize(); + CHECK( pl.size() == 2 ); + CHECK( pl.nbSegs() == 1 ); + } + { +#include "figures_test/polyline_min_1C.code" // closed + CHECK( pl.size() == 3 ); + CHECK( pl.nbSegs() == 3 ); + pl.minimize(); + CHECK( pl.size() == 2 ); + CHECK( pl.nbSegs() == 2 ); + } + { +#include "figures_test/polyline_min_2O.code"// open + CHECK( pl.size() == 3 ); + CHECK( pl.nbSegs() == 2 ); + pl.minimize(); +// std::cout << "pl:" << pl << '\n'; + CHECK( pl.size() == 3 ); // no change + CHECK( pl.nbSegs() == 2 ); + } + { +#include "figures_test/polyline_min_2C.code" // closed + CHECK( pl.size() == 3 ); + CHECK( pl.nbSegs() == 3 ); + pl.minimize(); + CHECK( pl.size() == 3 ); // no change ! + CHECK( pl.nbSegs() == 3 ); + } + { +#include "figures_test/polyline_min_3O.code" // open + CHECK( pl.size() == 4 ); + CHECK( pl.nbSegs() == 3 ); + pl.minimize(); +// std::cout << "pl:" << pl << '\n'; + CHECK( pl.size() == 4 ); // no change + CHECK( pl.nbSegs() == 3 ); + } + { +#include "figures_test/polyline_min_3C.code" // closed + CHECK( pl.size() == 4 ); + CHECK( pl.nbSegs() == 4 ); + pl.minimize(); +// std::cout << "pl:" << pl << '\n'; + CHECK( pl.size() == 3 ); // no change + CHECK( pl.nbSegs() == 3 ); + } +} + +template +void polytest_1( const base::PolylineBase& pl1 ) +{ + CHECK( pl1.isPolygon() == false ); + CHECK( isPolygon(pl1) == false ); + + CHECK( pl1.size() == 0 ); + CHECK( size(pl1) == 0 ); + + CHECK( pl1.nbSegs() == 0 ); + CHECK( nbSegs(pl1) == 0 ); + + CHECK( pl1.length() == 0 ); + CHECK( length(pl1) == 0 ); + + CHECK( pl1.area() == 0 ); + CHECK( area(pl1) == 0 ); + + CHECK_THROWS( centroid(pl1) ); // unable + CHECK_THROWS( pl1.centroid() ); // unable +} + TEST_CASE( "Polyline", "[polyline]" ) { { // default constructor - OPolyline_ pl1; + OPolyline_ pl1; polytest_1( pl1 ); - CPolyline_ pl2; - polytest_1( pl2 ); - } - { - CPolyline_ pc1; - OPolyline_ po1; - std::vector vpt{ {0,0} }; - CHECK_THROWS( pc1.set( vpt ) ); // can't hold only 1 point - CHECK_THROWS( po1.set( vpt ) ); - } - { - std::vector vpt{ {0,0} }; - CHECK_THROWS( CPolyline_( vpt ) ); // can(t build a polyline with a vector of size=1 - CHECK_THROWS( OPolyline_( vpt ) ); - } + CPolyline_ pl2; + polytest_1( pl2 ); + } + { + CPolyline_ pc1; + OPolyline_ po1; + std::vector vpt{ {0,0} }; + CHECK_THROWS( pc1.set( vpt ) ); // can't hold only 1 point + CHECK_THROWS( po1.set( vpt ) ); + } + { + std::vector vpt{ {0,0} }; + CHECK_THROWS( CPolyline_( vpt ) ); // can(t build a polyline with a vector of size=1 + CHECK_THROWS( OPolyline_( vpt ) ); + } { // build from Rectangle FRect r( 5,6, 7,8 ); - CPolyline_ pl1( r ); + CPolyline_ pl1( r ); - CHECK( pl1.isPolygon() == true ); + CHECK( pl1.isPolygon() == true ); CHECK( isPolygon(pl1) == true ); - CHECK( pl1.size() == 4 ); + CHECK( pl1.size() == 4 ); CHECK( size(pl1) == 4 ); - CHECK( pl1.nbSegs() == 4 ); + CHECK( pl1.nbSegs() == 4 ); CHECK( nbSegs(pl1) == 4 ); - CHECK( pl1.length() == 8 ); + CHECK( pl1.length() == 8 ); CHECK( length(pl1) == 8 ); - CHECK( pl1.area() == 4 ); - CHECK( area(pl1) == 4 ); - CHECK( pl1.centroid() == Point2d(6,7) ); - CHECK( centroid(pl1) == Point2d(6,7) ); - - auto vpts = pl1.getPts(); - CHECK( vpts.size() == 4 ); - CHECK( vpts[0] == Point2d(5,6) ); - - pl1.translate(1,2); + CHECK( pl1.area() == 4 ); + CHECK( area(pl1) == 4 ); + CHECK( pl1.centroid() == Point2d(6,7) ); + CHECK( centroid(pl1) == Point2d(6,7) ); + + auto vpts = pl1.getPts(); + CHECK( vpts.size() == 4 ); + CHECK( vpts[0] == Point2d(5,6) ); + + pl1.translate(1,2); CHECK( pl1 == CPolyline_( FRect( 6,8, 8,10 ) ) ); - } - { // set from Rectangle - FRect r( 5,6, 7,8 ); - CPolyline_ pl1; - pl1.set(r); - - CHECK( pl1.isPolygon() == true ); - CHECK( pl1.size() == 4 ); - CHECK( pl1.getPoint(0) == Point2d(5,6) ); - } - { - OPolyline_ po1; + } + { // set from Rectangle + FRect r( 5,6, 7,8 ); + CPolyline_ pl1; + pl1.set(r); + + CHECK( pl1.isPolygon() == true ); + CHECK( pl1.size() == 4 ); + CHECK( pl1.getPoint(0) == Point2d(5,6) ); + } + { + OPolyline_ po1; CPolyline_ pc1; - std::vector vpt{ {0,0},{2,2}, {2,2}, {4,3} }; - - CHECK_THROWS( po1.set( vpt ) ); // cant have two contiguous identical points - CHECK_THROWS( pc1.set( vpt ) ); // cant have two contiguous identical points + std::vector vpt{ {0,0},{2,2}, {2,2}, {4,3} }; + + CHECK_THROWS( po1.set( vpt ) ); // cant have two contiguous identical points + CHECK_THROWS( pc1.set( vpt ) ); // cant have two contiguous identical points } - { + { std::vector vpt{ {0,0}, {1,1.5}, {3,5}, {1,4} }; - OPolyline_ po1(vpt); - CPolyline_ pc1(vpt); - - CHECK( po1.isClosed() == false ); - CHECK( pc1.isClosed() == true ); - - CHECK( po1.size() == 4 ); + OPolyline_ po1(vpt); + CPolyline_ pc1(vpt); + + CHECK( po1.isClosed() == false ); + CHECK( pc1.isClosed() == true ); + + CHECK( po1.size() == 4 ); CHECK( pc1.size() == 4 ); - - CHECK( po1.nbSegs() == 3 ); - CHECK( pc1.nbSegs() == 4 ); - CHECK( po1.isPolygon() == false ); - CHECK( pc1.isPolygon() == true ); - + CHECK( po1.nbSegs() == 3 ); + CHECK( pc1.nbSegs() == 4 ); + + CHECK( po1.isPolygon() == false ); + CHECK( pc1.isPolygon() == true ); + CPolyline_ pc2(po1); CHECK( pc2.nbSegs() == 4 ); CHECK( pc2.isPolygon() == true ); FRect bb1( 0,0, 3,5); - CHECK( getBB(po1) == bb1 ); + CHECK( getBB(po1) == bb1 ); CHECK( getBB(pc1) == bb1 ); - CHECK( po1.getBB() == bb1 ); + CHECK( po1.getBB() == bb1 ); CHECK( pc1.getBB() == bb1 ); - } - { - std::vector vpt{ {0,0}, {1,1}, {0,1}, {1,0} }; - CPolyline_ pc1(vpt); - CHECK( pc1.isClosed() == true ); - CHECK( pc1.size() == 4 ); - CHECK( pc1.nbSegs() == 4 ); - CHECK( pc1.isPolygon() == false ); // crossing segments - - pc1.translate(2,1.); - CHECK( pc1.getPoint(0) == Point2d(2,1.) ); // (0,0) translated to (2,1) - } - { // build from std::array - std::array pts{{ {0,0}, {1,1}, {0,1} }}; - CPolyline_ pc(pts); - OPolyline_ po(pts); - CHECK( pc.isClosed() == true ); - CHECK( po.isClosed() == false ); - CHECK( pc.size() == 3 ); - CHECK( po.size() == 3 ); - CHECK( pc.nbSegs() == 3 ); - CHECK( po.nbSegs() == 2 ); - } - { // build from std::list - std::list pts{ {0,0}, {1,1}, {0,1} }; - CPolyline_ pc(pts); - OPolyline_ po(pts); - CHECK( pc.isClosed() == true ); - CHECK( po.isClosed() == false ); - CHECK( pc.size() == 3 ); - CHECK( po.size() == 3 ); - CHECK( pc.nbSegs() == 3 ); - CHECK( po.nbSegs() == 2 ); - } - { // build from segment - Segment_ seg; - CPolyline_ pc( seg ); - OPolyline_ po( seg ); - CHECK( pc.size() == 2 ); - CHECK( po.size() == 2 ); - CHECK( pc.getPoint(0) == Point2d(0,0) ); - CHECK( po.getPoint(0) == Point2d(0,0) ); - CHECK( pc.nbSegs() == 2 ); - CHECK( po.nbSegs() == 1 ); - } - { // build from segment - 2 - Segment_ seg; // (0,0) - (1,1) - CPolyline_ pc1( seg ); - OPolyline_ po1( seg ); - std::list pts{ {1,1},{0,0} }; - CPolyline_ pc2(pts); - OPolyline_ po2(pts); - CHECK( pc1 == pc2 ); - CHECK( po1 == po2 ); - } -} - -TEST_CASE( "Polyline RCP (Regular Convex Polygon)", "[polyline-RCP]" ) -{ - CHECK_THROWS( CPolyline( 5, 0u ) ); - CHECK_THROWS( CPolyline( -5, 5u ) ); - CHECK_THROWS( CPolyline( 1, 2u ) ); - CHECK_THROWS( CPolyline( 0, 5u ) ); - - CPolyline pol( 5, 5u ); - CHECK( pol.size() == 5 ); - CHECK( pol.nbSegs() == 5 ); - pol.set( 8, 4u ); - CHECK( pol.size() == 4 ); - CHECK( pol.nbSegs() == 4 ); - - CHECK_THROWS( pol.set( 1, 2u ) ); - CHECK_THROWS( pol.set( 0, 5u ) ); - CHECK_THROWS( pol.set( -5, 5u ) ); - CHECK_THROWS( pol.set( 5, 0u ) ); -} - -TEST_CASE( "Polyline setParallelogram", "[polyline-setParallelogram]" ) -{ - { - Point2d_ pt1(0,0); - Point2d_ pt2(0,2); - Point2d_ pt3(4,5); - CPolyline_ pol; - pol.setParallelogram( pt1, pt2, pt3 ); - - std::vector> vpts{ {0,0}, {0,2}, {4,5}, {4,3} }; - CPolyline_ pol2(vpts); - CHECK( pol == pol2 ); - } -} - -TEST_CASE( "Polyline get extreme point", "[polyline-getExtremePoint]" ) -{ - { - CPolyline cp_empty; - CHECK_THROWS( cp_empty.getLmPoint() ); - } - { - Point2d_ pt( 3, 4 ); - std::vector> vpt{ pt }; - - CHECK( getLmPoint(vpt) == pt ); - CHECK( getRmPoint(vpt) == pt ); - CHECK( getTmPoint(vpt) == pt ); - CHECK( getBmPoint(vpt) == pt ); - } - { - std::vector> vpt{ {1,0}, {0,1}, {-1,0}, {0,-1} }; - CPolyline cp( vpt ); - CHECK( cp.size() == 4 ); - CHECK( cp.getLmPoint() == Point2d_(-1,0) ); - CHECK( cp.getRmPoint() == Point2d_(1,0) ); - CHECK( cp.getTmPoint() == Point2d_(0,1) ); - CHECK( cp.getBmPoint() == Point2d_(0,-1) ); - - CHECK( getLmPoint(vpt) == Point2d_(-1,0) ); - CHECK( getRmPoint(vpt) == Point2d_(1,0) ); - CHECK( getTmPoint(vpt) == Point2d_(0,1) ); - CHECK( getBmPoint(vpt) == Point2d_(0,-1) ); - } - { - std::vector> vpt{ {0,0}, {1,0}, {2,0}, {2,1}, {2,2}, {1,2}, {0,2}, {0,1} }; - CPolyline cp( vpt ); - CHECK( cp.size() == 8 ); - CHECK( cp.getLmPoint() == Point2d_(0,0) ); - CHECK( cp.getRmPoint() == Point2d_(2,0) ); - CHECK( cp.getTmPoint() == Point2d_(0,2) ); - CHECK( cp.getBmPoint() == Point2d_(0,0) ); - - CHECK( getLmPoint(vpt) == Point2d_(0,0) ); - CHECK( getRmPoint(vpt) == Point2d_(2,0) ); - CHECK( getTmPoint(vpt) == Point2d_(0,2) ); - CHECK( getBmPoint(vpt) == Point2d_(0,0) ); - } -} - -TEST_CASE( "Bounding Box of set of objects", "[BB-cont]" ) -{ - std::vector vpts1{ {0,0}, {1,1.5}, {3,5}, {1,4} }; - FRect bb1( 0,0, 3,5 ); - { - CHECK( getBB(vpts1) == bb1 ); - - CPolyline cpol( vpts1 ); - OPolyline opol( vpts1 ); - std::vector vec_c{ cpol }; - std::vector vec_o{ opol }; - CHECK( getBB(vec_c) == bb1 ); - CHECK( getBB(vec_o) == bb1 ); - } - { - std::vector vseg; - CHECK_THROWS( getBB(vseg) ); - vseg.emplace_back( Segment(0,0,0,1) ); - CHECK_THROWS( getBB(vseg) ); // no area ! - - vseg.emplace_back( Segment(4,5,6,7) ); - FRect bb2( 0,0, 6,7 ); - CHECK( getBB(vseg) == bb2 ); - } - { - std::list vcir; - CHECK_THROWS( getBB(vcir) ); // empty ! - vcir.emplace_back( Circle(1,1,1) ); - FRect bb3( 0,0, 2,2 ); - CHECK( getBB(vcir) == bb3 ); - } - { - std::vector vpt{ {0,11} }; - CHECK_THROWS( getBB(vpt) ); // need at least 2 points ! - - std::list lpt{ {2,11} }; - CHECK_THROWS( getBB(lpt) ); // need at least 2 points ! - - std::array apt; - apt[0] = Point2d{2,11}; - CHECK_THROWS( getBB(apt) ); // need at least 2 points ! - } -} - -TEST_CASE( "getCenters() and getLines()", "[getCenters]" ) -{ - { - std::vector vec; - auto out1 = getCenters( vec ); - CHECK( out1.empty() ); - - vec.emplace_back( Segment( 0,0,2,0) ); - vec.emplace_back( Segment( 0,1,2,1) ); - auto out2 = getCenters( vec ); - CHECK( out2.size() == 2 ); - CHECK( out2[0] == Point2d(1,0) ); - CHECK( out2[1] == Point2d(1,1) ); - - auto out3 = getLines( vec ); - CHECK( out3.size() == 2 ); - CHECK( out3[0] == Line2d( Point2d(0,0), Point2d(5,0) ) ); - CHECK( out3[1] == Line2d( Point2d(0,1), Point2d(5,1) ) ); - } - { - std::list vec; - auto out1 = getCenters( vec ); - CHECK( out1.empty() ); - - vec.emplace_back( Circle( 0,0, 2) ); - vec.emplace_back( Circle( 1,1, 3) ); - auto out2 = getCenters( vec ); - CHECK( out2.size() == 2 ); - CHECK( out2[0] == Point2d(0,0) ); - CHECK( out2[1] == Point2d(1,1) ); - } - { - std::array vec; - auto out1 = getCenters( vec ); - CHECK( out1.size() == 2 ); // because input array has size=2 - - vec[0] = Ellipse( 0,0, 2, 3 ); - vec[1] = Ellipse( 1,1, 3, 8 ); - auto out2 = getCenters( vec ); - CHECK( out2.size() == 2 ); - CHECK( out2[0] == Point2d(0,0) ); - CHECK( out2[1] == Point2d(1,1) ); - } -} - -TEST_CASE( "Polygon convexity", "[polyline-convex]" ) -{ - OPolyline_ plo; - CPolyline_ plc; - CHECK( !plo.isConvex() ); // empty !! - CHECK( !plc.isConvex() ); - - CHECK( !isConvex(plc) ); // free function call - CHECK( !isConvex(plo) ); - - plo.set( std::vector{ {0,0}, {2,0} } ); - plc.set( std::vector{ {0,0}, {2,0} } ); - CHECK( !plo.isConvex() ); // 2 pts are fine, but not convex - CHECK( !plc.isConvex() ); - - plo.set( std::vector{ {0,0}, {2,0}, {2,1} } ); - plc.set( std::vector{ {0,0}, {2,0}, {2,1} } ); - CHECK( !plo.isConvex() ); // 3 pts ok, but open Polyline never convex - CHECK( plc.isConvex() ); - - plc.set( std::vector{ {0,0}, {2,0}, {2,2}, {0,2} } ); - CHECK( plc.isConvex() ); - plc.set( std::vector{ {0,0}, {2,0}, {1,1}, {2,2}, {0,2} } ); - CHECK( !plc.isConvex() ); - - plc.set( std::vector{ {2,2}, {2,-2}, {-2,-2}, {-2,2} } ); - CHECK( plc.isConvex() ); - plc.set( std::vector{ {2,2}, {2,-2}, {1,1}, {-2,2} } ); - CHECK( !plc.isConvex() ); -} - + } + { + std::vector vpt{ {0,0}, {1,1}, {0,1}, {1,0} }; + CPolyline_ pc1(vpt); + CHECK( pc1.isClosed() == true ); + CHECK( pc1.size() == 4 ); + CHECK( pc1.nbSegs() == 4 ); + CHECK( pc1.isPolygon() == false ); // crossing segments + + pc1.translate(2,1.); + CHECK( pc1.getPoint(0) == Point2d(2,1.) ); // (0,0) translated to (2,1) + } + { // build from std::array + std::array pts{{ {0,0}, {1,1}, {0,1} }}; + CPolyline_ pc(pts); + OPolyline_ po(pts); + CHECK( pc.isClosed() == true ); + CHECK( po.isClosed() == false ); + CHECK( pc.size() == 3 ); + CHECK( po.size() == 3 ); + CHECK( pc.nbSegs() == 3 ); + CHECK( po.nbSegs() == 2 ); + } + { // build from std::list + std::list pts{ {0,0}, {1,1}, {0,1} }; + CPolyline_ pc(pts); + OPolyline_ po(pts); + CHECK( pc.isClosed() == true ); + CHECK( po.isClosed() == false ); + CHECK( pc.size() == 3 ); + CHECK( po.size() == 3 ); + CHECK( pc.nbSegs() == 3 ); + CHECK( po.nbSegs() == 2 ); + } + { // build from segment + Segment_ seg; + CPolyline_ pc( seg ); + OPolyline_ po( seg ); + CHECK( pc.size() == 2 ); + CHECK( po.size() == 2 ); + CHECK( pc.getPoint(0) == Point2d(0,0) ); + CHECK( po.getPoint(0) == Point2d(0,0) ); + CHECK( pc.nbSegs() == 2 ); + CHECK( po.nbSegs() == 1 ); + } + { // build from segment - 2 + Segment_ seg; // (0,0) - (1,1) + CPolyline_ pc1( seg ); + OPolyline_ po1( seg ); + std::list pts{ {1,1},{0,0} }; + CPolyline_ pc2(pts); + OPolyline_ po2(pts); + CHECK( pc1 == pc2 ); + CHECK( po1 == po2 ); + } +} + +TEST_CASE( "Polyline RCP (Regular Convex Polygon)", "[polyline-RCP]" ) +{ + CHECK_THROWS( CPolyline( 5, 0u ) ); + CHECK_THROWS( CPolyline( -5, 5u ) ); + CHECK_THROWS( CPolyline( 1, 2u ) ); + CHECK_THROWS( CPolyline( 0, 5u ) ); + + CPolyline pol( 5, 5u ); + CHECK( pol.size() == 5 ); + CHECK( pol.nbSegs() == 5 ); + pol.set( 8, 4u ); + CHECK( pol.size() == 4 ); + CHECK( pol.nbSegs() == 4 ); + + CHECK_THROWS( pol.set( 1, 2u ) ); + CHECK_THROWS( pol.set( 0, 5u ) ); + CHECK_THROWS( pol.set( -5, 5u ) ); + CHECK_THROWS( pol.set( 5, 0u ) ); +} + +TEST_CASE( "Polyline setParallelogram", "[polyline-setParallelogram]" ) +{ + { + Point2d_ pt1(0,0); + Point2d_ pt2(0,2); + Point2d_ pt3(4,5); + CPolyline_ pol; + pol.setParallelogram( pt1, pt2, pt3 ); + + std::vector> vpts{ {0,0}, {0,2}, {4,5}, {4,3} }; + CPolyline_ pol2(vpts); + CHECK( pol == pol2 ); + } +} + +TEST_CASE( "Polyline get extreme point", "[polyline-getExtremePoint]" ) +{ + { + CPolyline cp_empty; + CHECK_THROWS( cp_empty.getLmPoint() ); + } + { + Point2d_ pt( 3, 4 ); + std::vector> vpt{ pt }; + + CHECK( getLmPoint(vpt) == pt ); + CHECK( getRmPoint(vpt) == pt ); + CHECK( getTmPoint(vpt) == pt ); + CHECK( getBmPoint(vpt) == pt ); + } + { + std::vector> vpt{ {1,0}, {0,1}, {-1,0}, {0,-1} }; + CPolyline cp( vpt ); + CHECK( cp.size() == 4 ); + CHECK( cp.getLmPoint() == Point2d_(-1,0) ); + CHECK( cp.getRmPoint() == Point2d_(1,0) ); + CHECK( cp.getTmPoint() == Point2d_(0,1) ); + CHECK( cp.getBmPoint() == Point2d_(0,-1) ); + + CHECK( getLmPoint(vpt) == Point2d_(-1,0) ); + CHECK( getRmPoint(vpt) == Point2d_(1,0) ); + CHECK( getTmPoint(vpt) == Point2d_(0,1) ); + CHECK( getBmPoint(vpt) == Point2d_(0,-1) ); + } + { + std::vector> vpt{ {0,0}, {1,0}, {2,0}, {2,1}, {2,2}, {1,2}, {0,2}, {0,1} }; + CPolyline cp( vpt ); + CHECK( cp.size() == 8 ); + CHECK( cp.getLmPoint() == Point2d_(0,0) ); + CHECK( cp.getRmPoint() == Point2d_(2,0) ); + CHECK( cp.getTmPoint() == Point2d_(0,2) ); + CHECK( cp.getBmPoint() == Point2d_(0,0) ); + + CHECK( getLmPoint(vpt) == Point2d_(0,0) ); + CHECK( getRmPoint(vpt) == Point2d_(2,0) ); + CHECK( getTmPoint(vpt) == Point2d_(0,2) ); + CHECK( getBmPoint(vpt) == Point2d_(0,0) ); + } +} + +TEST_CASE( "Bounding Box of set of objects", "[BB-cont]" ) +{ + std::vector vpts1{ {0,0}, {1,1.5}, {3,5}, {1,4} }; + FRect bb1( 0,0, 3,5 ); + { + CHECK( getBB(vpts1) == bb1 ); + + CPolyline cpol( vpts1 ); + OPolyline opol( vpts1 ); + std::vector vec_c{ cpol }; + std::vector vec_o{ opol }; + CHECK( getBB(vec_c) == bb1 ); + CHECK( getBB(vec_o) == bb1 ); + } + { + std::vector vseg; + CHECK_THROWS( getBB(vseg) ); + vseg.emplace_back( Segment(0,0,0,1) ); + CHECK_THROWS( getBB(vseg) ); // no area ! + + vseg.emplace_back( Segment(4,5,6,7) ); + FRect bb2( 0,0, 6,7 ); + CHECK( getBB(vseg) == bb2 ); + } + { + std::list vcir; + CHECK_THROWS( getBB(vcir) ); // empty ! + vcir.emplace_back( Circle(1,1,1) ); + FRect bb3( 0,0, 2,2 ); + CHECK( getBB(vcir) == bb3 ); + } + { + std::vector vpt{ {0,11} }; + CHECK_THROWS( getBB(vpt) ); // need at least 2 points ! + + std::list lpt{ {2,11} }; + CHECK_THROWS( getBB(lpt) ); // need at least 2 points ! + + std::array apt; + apt[0] = Point2d{2,11}; + CHECK_THROWS( getBB(apt) ); // need at least 2 points ! + } +} + +TEST_CASE( "getCenters() and getLines()", "[getCenters]" ) +{ + { + std::vector vec; + auto out1 = getCenters( vec ); + CHECK( out1.empty() ); + + vec.emplace_back( Segment( 0,0,2,0) ); + vec.emplace_back( Segment( 0,1,2,1) ); + auto out2 = getCenters( vec ); + CHECK( out2.size() == 2 ); + CHECK( out2[0] == Point2d(1,0) ); + CHECK( out2[1] == Point2d(1,1) ); + + auto out3 = getLines( vec ); + CHECK( out3.size() == 2 ); + CHECK( out3[0] == Line2d( Point2d(0,0), Point2d(5,0) ) ); + CHECK( out3[1] == Line2d( Point2d(0,1), Point2d(5,1) ) ); + } + { + std::list vec; + auto out1 = getCenters( vec ); + CHECK( out1.empty() ); + + vec.emplace_back( Circle( 0,0, 2) ); + vec.emplace_back( Circle( 1,1, 3) ); + auto out2 = getCenters( vec ); + CHECK( out2.size() == 2 ); + CHECK( out2[0] == Point2d(0,0) ); + CHECK( out2[1] == Point2d(1,1) ); + } + { + std::array vec; + auto out1 = getCenters( vec ); + CHECK( out1.size() == 2 ); // because input array has size=2 + + vec[0] = Ellipse( 0,0, 2, 3 ); + vec[1] = Ellipse( 1,1, 3, 8 ); + auto out2 = getCenters( vec ); + CHECK( out2.size() == 2 ); + CHECK( out2[0] == Point2d(0,0) ); + CHECK( out2[1] == Point2d(1,1) ); + } +} + +TEST_CASE( "Polygon convexity", "[polyline-convex]" ) +{ + OPolyline_ plo; + CPolyline_ plc; + CHECK( !plo.isConvex() ); // empty !! + CHECK( !plc.isConvex() ); + + CHECK( !isConvex(plc) ); // free function call + CHECK( !isConvex(plo) ); + + plo.set( std::vector{ {0,0}, {2,0} } ); + plc.set( std::vector{ {0,0}, {2,0} } ); + CHECK( !plo.isConvex() ); // 2 pts are fine, but not convex + CHECK( !plc.isConvex() ); + + plo.set( std::vector{ {0,0}, {2,0}, {2,1} } ); + plc.set( std::vector{ {0,0}, {2,0}, {2,1} } ); + CHECK( !plo.isConvex() ); // 3 pts ok, but open Polyline never convex + CHECK( plc.isConvex() ); + + plc.set( std::vector{ {0,0}, {2,0}, {2,2}, {0,2} } ); + CHECK( plc.isConvex() ); + plc.set( std::vector{ {0,0}, {2,0}, {1,1}, {2,2}, {0,2} } ); + CHECK( !plc.isConvex() ); + + plc.set( std::vector{ {2,2}, {2,-2}, {-2,-2}, {-2,2} } ); + CHECK( plc.isConvex() ); + plc.set( std::vector{ {2,2}, {2,-2}, {1,1}, {-2,2} } ); + CHECK( !plc.isConvex() ); +} + TEST_CASE( "Polygon orientation", "[polyline-orient]" ) { - { - CPolyline_ pl1; + { + CPolyline_ pl1; CPolyline_ pl2; // +-----+ - pl1.set( std::vector{ {0,0}, {2,0}, {2,1}, {0,1} } ); // | | - pl2.set( std::vector{ {0,0}, {0,1}, {2,1}, {2,0} } ); // +-----+ - CHECK( pl1 == pl2 ); // is closed, so the points describe the same thing - } - { - OPolyline_ pl1; + pl1.set( std::vector{ {0,0}, {2,0}, {2,1}, {0,1} } ); // | | + pl2.set( std::vector{ {0,0}, {0,1}, {2,1}, {2,0} } ); // +-----+ + CHECK( pl1 == pl2 ); // is closed, so the points describe the same thing + } + { + OPolyline_ pl1; OPolyline_ pl2; // +-----+ +-----+ - pl1.set( std::vector{ {0,0}, {2,0}, {2,1}, {0,1} } ); // | and | | - pl2.set( std::vector{ {0,0}, {0,1}, {2,1}, {2,0} } ); // +-----+ + + - CHECK( pl1 != pl2 ); // is open, so they are different - - CPolyline_ plc1(pl1); - CPolyline_ plc2(pl2); - CHECK( plc1 == plc2 ); - } - { - std::vector v1{ {0,0}, {1,0}, {1,1} }; - std::vector v2{ {1,1}, {1,0}, {0,0} }; - - CPolyline_ pl1(v1); + pl1.set( std::vector{ {0,0}, {2,0}, {2,1}, {0,1} } ); // | and | | + pl2.set( std::vector{ {0,0}, {0,1}, {2,1}, {2,0} } ); // +-----+ + + + CHECK( pl1 != pl2 ); // is open, so they are different + + CPolyline_ plc1(pl1); + CPolyline_ plc2(pl2); + CHECK( plc1 == plc2 ); + } + { + std::vector v1{ {0,0}, {1,0}, {1,1} }; + std::vector v2{ {1,1}, {1,0}, {0,0} }; + + CPolyline_ pl1(v1); CPolyline_ pl2(v2); - CHECK( pl1 == pl2 ); - OPolyline_ po1(v1); - OPolyline_ po2(v2); + CHECK( pl1 == pl2 ); + OPolyline_ po1(v1); + OPolyline_ po2(v2); CHECK( po1 == po2 ); - } -} - -TEST_CASE( "Polyline fullstep rotation", "[polyline-rot]" ) -{ - OPolyline_ plo; - CPolyline_ plc; - OPolyline_ plo_ff; - CPolyline_ plc_ff; - std::vector vpts{ {0,0}, {2,0}, {1,1} }; - { - plo.set( vpts ); - plc.set( vpts ); - plo.rotate( Rotate::CCW ); - plc.rotate( Rotate::CCW ); - std::vector vpts2{ {0,0},{0,2},{-1,1} }; - OPolyline_ plo2( vpts2 ); - CPolyline_ plc2( vpts2 ); - CHECK( plo == plo2 ); - CHECK( plc == plc2 ); - - plo_ff.set( vpts ); - plc_ff.set( vpts ); - rotate( plo_ff, Rotate::CCW ); - rotate( plc_ff, Rotate::CCW ); - CHECK( plo == plo_ff ); - CHECK( plc == plc_ff ); - } - { - plo.set( vpts ); - plc.set( vpts ); - plo.rotate( Rotate::VMirror ); - plc.rotate( Rotate::VMirror ); - std::vector vpts2{ {0,0},{-2,0},{-1,1} }; - OPolyline_ plo2( vpts2 ); - CPolyline_ plc2( vpts2 ); - CHECK( plo == plo2 ); - CHECK( plc == plc2 ); - } - { - plo.set( vpts ); - plc.set( vpts ); - plo.rotate( Rotate::HMirror ); - plc.rotate( Rotate::HMirror ); - std::vector vpts2{ {0,0},{2,0},{1,-1} }; - OPolyline_ plo2( vpts2 ); - CPolyline_ plc2( vpts2 ); - CHECK( plo == plo2 ); - CHECK( plc == plc2 ); - } -} + } +} + +TEST_CASE( "Polyline fullstep rotation", "[polyline-rot]" ) +{ + OPolyline_ plo; + CPolyline_ plc; + OPolyline_ plo_ff; + CPolyline_ plc_ff; + std::vector vpts{ {0,0}, {2,0}, {1,1} }; + { + plo.set( vpts ); + plc.set( vpts ); + plo.rotate( Rotate::CCW ); + plc.rotate( Rotate::CCW ); + std::vector vpts2{ {0,0},{0,2},{-1,1} }; + OPolyline_ plo2( vpts2 ); + CPolyline_ plc2( vpts2 ); + CHECK( plo == plo2 ); + CHECK( plc == plc2 ); + + plo_ff.set( vpts ); + plc_ff.set( vpts ); + rotate( plo_ff, Rotate::CCW ); + rotate( plc_ff, Rotate::CCW ); + CHECK( plo == plo_ff ); + CHECK( plc == plc_ff ); + } + { + plo.set( vpts ); + plc.set( vpts ); + plo.rotate( Rotate::VMirror ); + plc.rotate( Rotate::VMirror ); + std::vector vpts2{ {0,0},{-2,0},{-1,1} }; + OPolyline_ plo2( vpts2 ); + CPolyline_ plc2( vpts2 ); + CHECK( plo == plo2 ); + CHECK( plc == plc2 ); + } + { + plo.set( vpts ); + plc.set( vpts ); + plo.rotate( Rotate::HMirror ); + plc.rotate( Rotate::HMirror ); + std::vector vpts2{ {0,0},{2,0},{1,-1} }; + OPolyline_ plo2( vpts2 ); + CPolyline_ plc2( vpts2 ); + CHECK( plo == plo2 ); + CHECK( plc == plc2 ); + } +} TEST_CASE( "Polygon area", "[polyline-area]" ) { @@ -3573,24 +3573,24 @@ TEST_CASE( "Polygon area", "[polyline-area]" ) CHECK( pl1.nbSegs() == 6 ); CHECK( pl1.isPolygon() == true ); CHECK( pl1.area() == 3. ); - } - OPolyline_ plo; - plo.set( std::vector{ {0,0}, {2,0}, {2,1}, {0,1} } ); - CHECK( plo.isPolygon() == false ); - CHECK( plo.area() == 0. ); + } + OPolyline_ plo; + plo.set( std::vector{ {0,0}, {2,0}, {2,1}, {0,1} } ); + CHECK( plo.isPolygon() == false ); + CHECK( plo.area() == 0. ); } - + TEST_CASE( "Polyline comparison 2", "[polyline-comp-2]" ) -{ - { - OPolyline_ pa,pb; - CHECK( pa == pb ); - } - { - CPolyline_ pa,pb; - CHECK( pa == pb ); - } - +{ + { + OPolyline_ pa,pb; + CHECK( pa == pb ); + } + { + CPolyline_ pa,pb; + CHECK( pa == pb ); + } + OPolyline_ pl2( std::vector{ {7,8},{3,4},{5,6} } ); OPolyline_ pl1( std::vector{ {3,4},{5,6},{7,8} } ); @@ -3598,7 +3598,7 @@ TEST_CASE( "Polyline comparison 2", "[polyline-comp-2]" ) OPolyline_ p1 = pl1; OPolyline_ p2 = pl2; - CHECK( p1.isClosed() == false ); + CHECK( p1.isClosed() == false ); CHECK( p1.isNormalized() == false ); CHECK( p2.isNormalized() == false ); CHECK( p1!=p2 ); @@ -3606,40 +3606,40 @@ TEST_CASE( "Polyline comparison 2", "[polyline-comp-2]" ) { CPolyline_ p1 = pl1; // build a closed one from an open one CPolyline_ p2 = pl2; - + CHECK( p1.isClosed() == true ); CHECK( p1.isNormalized() == false ); CHECK( p2.isNormalized() == false ); CHECK( (p1==p2) == true ); CHECK( p1.isNormalized() == true ); CHECK( p2.isNormalized() == true ); - } - { - CPolyline_ p2( std::vector{ {1,2},{1,1},{2,1} } ); - CPolyline_ p1( std::vector{ {1,1},{2,1},{1,2} } ); - - CHECK( p1.getPoint(0) == Point2d_(1,1) ); - CHECK( p2.getPoint(0) == Point2d_(1,2) ); - CHECK( p1 == p2 ); // normalizing - CHECK( p1.getPoint(0) == Point2d_(1,1) ); - CHECK( p2.getPoint(0) == Point2d_(1,1) ); - } - { - CPolyline pa, pb; - { -#include "figures_test/polyline_comp_1a.code" - pa = pl; - } - { -#include "figures_test/polyline_comp_1b.code" - pb = pl; - } - CHECK( pa.size() == pb.size() ); - CHECK( pa != pb ); - } -} - - + } + { + CPolyline_ p2( std::vector{ {1,2},{1,1},{2,1} } ); + CPolyline_ p1( std::vector{ {1,1},{2,1},{1,2} } ); + + CHECK( p1.getPoint(0) == Point2d_(1,1) ); + CHECK( p2.getPoint(0) == Point2d_(1,2) ); + CHECK( p1 == p2 ); // normalizing + CHECK( p1.getPoint(0) == Point2d_(1,1) ); + CHECK( p2.getPoint(0) == Point2d_(1,1) ); + } + { + CPolyline pa, pb; + { +#include "figures_test/polyline_comp_1a.code" + pa = pl; + } + { +#include "figures_test/polyline_comp_1b.code" + pb = pl; + } + CHECK( pa.size() == pb.size() ); + CHECK( pa != pb ); + } +} + + TEST_CASE( "general binding", "[gen_bind]" ) { struct MyType @@ -3651,238 +3651,245 @@ TEST_CASE( "general binding", "[gen_bind]" ) CHECK( pt.getX() == 3 ); Line2d li (mtpt); // ??? } - -TEST_CASE( "SVG drawing default", "[svg_draw_default]" ) -{ - FRect r; - img::Image im; - r.draw( im ); - im.write( "BUILD/dummy.svg" ); -} - -TEST_CASE( "convex hull", "[conv_hull]" ) -{ - { - CPolyline_ pl; - - pl.set( FRect_(1,1,3,3) ); - CHECK( priv::chull::getPivotPoint(pl.getPts() ) == 0 ); - - std::vector v1{ {0,0}, {2,0}, {2,2}, {1,2}, {1,1}, {0,1} }; - pl.set( v1 ); - CHECK( priv::chull::getPivotPoint(pl.getPts() ) == 0 ); - - std::rotate( v1.begin(), v1.begin()+1, v1.end() ); - pl.set( v1 ); - CHECK( priv::chull::getPivotPoint(pl.getPts() ) == 5 ); - - std::rotate( v1.begin(), v1.begin()+1, v1.end() ); - pl.set( v1 ); - CHECK( priv::chull::getPivotPoint(pl.getPts() ) == 4 ); - } - { -#include "figures_test/polyline_chull_1.code" - auto vp = pl.getPts(); - CHECK( priv::chull::getPivotPoint(pl.getPts() ) == 0 ); - auto vout = priv::chull::sortPoints( vp, 0 ); - CHECK( vout == std::vector{ 0,1,2,3 } ); - auto ch = convexHull( pl ); - CHECK( ch == CPolyline( - std::vector{ - {0,0},{3,0},{0,3} - } - ) - ); - } - { - CPolyline pl1; - auto ch1 = convexHull( pl1 ); - CHECK( ch1.size() == 0 ); - auto ch2 = pl1.convexHull(); - CHECK( ch2.size() == 0 ); - } - { - CPolyline pl1( std::vector{ {1,1}, {2,2} }); - auto ch1 = convexHull( pl1 ); - CHECK( ch1.size() == 2 ); - auto ch2 = pl1.convexHull(); - CHECK( ch2.size() == 2 ); - } -} - - -////////////////////////////////////////////////////////////// -///// SVG IMPORT TESTS ///// -////////////////////////////////////////////////////////////// -// to run these, call make with USE_TINYXML2=Y - -#ifdef HOMOG2D_USE_SVG_IMPORT -TEST_CASE( "SVG_Import_1", "[svg_import_1]" ) -{ -/// \note Here, we use only the "double" type, because that is the one used on import - { - Circle c( 50,50,20); - img::Image im(200,200); - c.draw( im ); - im.write( "BUILD/test_svg_11.svg" ); - - tinyxml2::XMLDocument doc; // load the file that we just created - doc.LoadFile( "BUILD/test_svg_11.svg" ); - - h2d::svg::Visitor visitor; - doc.Accept( &visitor ); - const auto& data = visitor.get(); - CHECK( data.size() == 1 ); - const auto& elem = data.at(0); - CHECK( elem->type() == Type::Circle ); - - const Circle* pc2 = static_cast( elem.get() ); - CHECK( pc2->radius() == 20 ); - } - { // this test makes sure the element is ignored - tinyxml2::XMLDocument doc; - doc.LoadFile( "misc/other/test_svg_import_1.svg" ); - h2d::svg::Visitor visitor; - doc.Accept( &visitor ); - const auto& data = visitor.get(); - CHECK( data.size() == 3 ); - for( const auto& elem: data ) - CHECK( elem->type() == Type::Circle ); - } - { // read a file with 3 circles, one rect, one segment and a polygon - tinyxml2::XMLDocument doc; - doc.LoadFile( "misc/other/test_svg_import_2.svg" ); - h2d::svg::Visitor visitor; - doc.Accept( &visitor ); - const auto& data = visitor.get(); - CHECK( data.size() == 6 ); - CHECK( data.at(3)->type() == Type::Segment ); - CHECK( data.at(4)->type() == Type::FRect ); - CHECK( data.at(5)->type() == Type::CPolyline ); - } -} - -TEST_CASE( "SVG Import Ellipse", "[svg_import_ell]" ) -{ - tinyxml2::XMLDocument doc; - doc.LoadFile( "misc/other/test_svg_import_3.svg" ); - h2d::svg::Visitor visitor; - doc.Accept( &visitor ); - const auto& data = visitor.get(); - CHECK( data.size() == 3 ); - for( const auto& p: data ) - { - std::cout << *p << '\n'; - if( p->type() == Type::Ellipse ) - { - Ellipse ell( 150, 100, 60, 15, 20*M_PI/180. ); - const Ellipse* pell = static_cast( p.get() ); - CHECK( ell == *pell ); - } - } -} - -TEST_CASE( "SVG Import path 1", "[svg_import_path_1]" ) -{ - { // empty string - const char* s1 = ""; - CHECK_THROWS( svg::svgp::parsePath( s1 ) ); - } - { - const char* s1 ="10 20 30 40"; - auto res = svg::svgp::parsePath( s1 ); - CHECK( res.first.size() == 2 ); - CHECK( res.first[0] == Point2d(10,20) ); - CHECK( res.first[1] == Point2d(30,40) ); - CHECK( res.second == false ); - } - { - const char* s1 ="10 20 30 40z"; - auto res = svg::svgp::parsePath( s1 ); - CHECK( res.first.size() == 2 ); - CHECK( res.second == true ); - } - { - const char* s1 ="10 20 m 1 2 3 4z"; //relative - auto res = svg::svgp::parsePath( s1 ); - CHECK( res.first.size() == 3 ); - CHECK( res.first[0] == Point2d(10,20) ); - CHECK( res.first[1] == Point2d(11,22) ); - CHECK( res.first[2] == Point2d(14,26) ); - CHECK( res.second == true ); - } - { - const char* s1 ="10 20 H 30 40"; //horizontal line - auto res = svg::svgp::parsePath( s1 ); - CHECK( res.first.size() == 3 ); - CHECK( res.first[0] == Point2d(10,20) ); - CHECK( res.first[1] == Point2d(30,20) ); - CHECK( res.first[2] == Point2d(40,20) ); - CHECK( res.second == false); - } - { - const char* s1 ="10"; // missing second value - CHECK_THROWS( svg::svgp::parsePath( s1 ) ); - } - { - const char* s1 ="10 20 30"; // missing second value - CHECK_THROWS( svg::svgp::parsePath( s1 ) ); - } - { - const char* s1 ="M10 20 C 30"; // C command not handled - CHECK_THROWS( svg::svgp::parsePath( s1 ) ); - } -} -#endif // HOMOG2D_USE_SVG_IMPORT - -////////////////////////////////////////////////////////////// -///// BOOST GEOMETRY BINDING TESTS ///// -////////////////////////////////////////////////////////////// - -#ifdef HOMOG2D_USE_BOOSTGEOM -TEST_CASE( "boost geometry point import", "[bg-pt-import]" ) -{ - namespace bg = boost::geometry; - bg::model::point ptb1(3,4); - bg::model::d2::point_xy ptb2(5,6); - Point2d_ pt1(ptb1); - Point2d_ pt2(ptb2); - CHECK( pt1 == Point2d_(3,4) ); - CHECK( pt2 == Point2d_(5,6) ); - - pt1 = ptb1; // using assignment operator - pt2 = ptb2; - CHECK( pt1 == Point2d_(3,4) ); - CHECK( pt2 == Point2d_(5,6) ); -} - -TEST_CASE( "boost geometry point export", "[bg-pt-export]" ) -{ - Point2d_ ref1(3,4); - Point2d_ ref2(5,6); - namespace bg = boost::geometry; - using point_t1 = bg::model::point; - using point_t2 = bg::model::d2::point_xy; - - auto pt1 = getPt( ref1 ); - auto pt2 = getPt( ref2 ); - CHECK( bg::get<0>(pt1) == ref1.getX() ); - CHECK( bg::get<0>(pt2) == ref2.getX() ); -} - -TEST_CASE( "boost geometry vector point export", "[bg-vpt-export]" ) -{ - namespace bg = boost::geometry; - std::vector vin; - vin.push_back( Point2d(0.0, 0.0) ); - vin.push_back( Point2d(0.0, 5.0)); - vin.push_back( Point2d(5.0, 5.0)); - auto vout = getPts>(vin); // convert to a vector of boost geometry points - CHECK( vout.size() == 3 ); -} - -#endif // HOMOG2D_USE_BOOSTGEOM - + +TEST_CASE( "SVG drawing default", "[svg_draw_default]" ) +{ + FRect r; + img::Image im; + r.draw( im ); + im.write( "BUILD/dummy.svg" ); +} + +TEST_CASE( "convex hull", "[conv_hull]" ) +{ + { + CPolyline_ pl; + + pl.set( FRect_(1,1,3,3) ); + CHECK( priv::chull::getPivotPoint(pl.getPts() ) == 0 ); + + std::vector v1{ {0,0}, {2,0}, {2,2}, {1,2}, {1,1}, {0,1} }; + pl.set( v1 ); + CHECK( priv::chull::getPivotPoint(pl.getPts() ) == 0 ); + + std::rotate( v1.begin(), v1.begin()+1, v1.end() ); + pl.set( v1 ); + CHECK( priv::chull::getPivotPoint(pl.getPts() ) == 5 ); + + std::rotate( v1.begin(), v1.begin()+1, v1.end() ); + pl.set( v1 ); + CHECK( priv::chull::getPivotPoint(pl.getPts() ) == 4 ); + } + { +#include "figures_test/polyline_chull_1.code" + auto vp = pl.getPts(); + CHECK( priv::chull::getPivotPoint(pl.getPts() ) == 0 ); + auto vout = priv::chull::sortPoints( vp, 0 ); + CHECK( vout == std::vector{ 0,1,2,3 } ); + auto ch = convexHull( pl ); + CHECK( ch == CPolyline( + std::vector{ + {0,0},{3,0},{0,3} + } + ) + ); + } + { + CPolyline pl1; + auto ch1 = convexHull( pl1 ); + CHECK( ch1.size() == 0 ); + auto ch2 = pl1.convexHull(); + CHECK( ch2.size() == 0 ); + } + { + CPolyline pl1( std::vector{ {1,1}, {2,2} }); + auto ch1 = convexHull( pl1 ); + CHECK( ch1.size() == 2 ); + auto ch2 = pl1.convexHull(); + CHECK( ch2.size() == 2 ); + } +} + + +////////////////////////////////////////////////////////////// +///// SVG IMPORT TESTS ///// +////////////////////////////////////////////////////////////// +// to run these, call make with USE_TINYXML2=Y + +#ifdef HOMOG2D_USE_SVG_IMPORT +TEST_CASE( "SVG_Import_1", "[svg_import_1]" ) +{ +/// \note Here, we use only the "double" type, because that is the one used on import + { + Circle c( 50,50,20); + img::Image im(200,200); + c.draw( im ); + im.write( "BUILD/test_svg_11.svg" ); + + tinyxml2::XMLDocument doc; // load the file that we just created + doc.LoadFile( "BUILD/test_svg_11.svg" ); + + h2d::svg::Visitor visitor; + doc.Accept( &visitor ); + const auto& data = visitor.get(); + CHECK( data.size() == 1 ); + const auto& elem = data.at(0); + CHECK( elem->type() == Type::Circle ); + + const Circle* pc2 = static_cast( elem.get() ); + CHECK( pc2->radius() == 20 ); + } + { // this test makes sure the element is ignored + tinyxml2::XMLDocument doc; + doc.LoadFile( "misc/other/test_svg_import_1.svg" ); + h2d::svg::Visitor visitor; + doc.Accept( &visitor ); + const auto& data = visitor.get(); + CHECK( data.size() == 3 ); + for( const auto& elem: data ) + CHECK( elem->type() == Type::Circle ); + } + { // read a file with 3 circles, one rect, one segment and a polygon + tinyxml2::XMLDocument doc; + doc.LoadFile( "misc/other/test_svg_import_2.svg" ); + h2d::svg::Visitor visitor; + doc.Accept( &visitor ); + const auto& data = visitor.get(); + CHECK( data.size() == 6 ); + CHECK( data.at(3)->type() == Type::Segment ); + CHECK( data.at(4)->type() == Type::FRect ); + CHECK( data.at(5)->type() == Type::CPolyline ); + } +} + +TEST_CASE( "SVG Import Ellipse", "[svg_import_ell]" ) +{ + tinyxml2::XMLDocument doc; + doc.LoadFile( "misc/other/test_svg_import_3.svg" ); + h2d::svg::Visitor visitor; + doc.Accept( &visitor ); + const auto& data = visitor.get(); + CHECK( data.size() == 3 ); + for( const auto& p: data ) + { + std::cout << *p << '\n'; + if( p->type() == Type::Ellipse ) + { + Ellipse ell( 150, 100, 60, 15, 20*M_PI/180. ); + const Ellipse* pell = static_cast( p.get() ); + CHECK( ell == *pell ); + } + } +} + +TEST_CASE( "SVG Import path 1", "[svg_import_path_1]" ) +{ + { // empty string + const char* s1 = ""; + CHECK_THROWS( svg::svgp::parsePath( s1 ) ); + } + { + const char* s1 ="10 20 30 40"; + const auto& res = svg::svgp::parsePath( s1 ); + CHECK( res.first.size() == 1 ); // one vector + CHECK( res.first[0].size() == 2 ); // holding two points + CHECK( res.first[0][0] == Point2d(10,20) ); + CHECK( res.first[0][1] == Point2d(30,40) ); + CHECK( res.second == false ); + } + + { + const char* s1 ="10 20 30 40z"; + auto res = svg::svgp::parsePath( s1 ); + CHECK( res.first.size() == 1 ); + CHECK( res.first[0].size() == 2 ); + CHECK( res.second == true ); + } + { + const char* s1 ="10 20 m 1 2 3 4z"; //relative and "Move To" (=>so two vectors in output) + auto res = svg::svgp::parsePath( s1 ); + CHECK( res.first.size() == 2 ); + CHECK( res.first[0].size() == 1 ); + CHECK( res.first[1].size() == 2 ); + CHECK( res.first[0][0] == Point2d(10,20) ); + CHECK( res.first[1][0] == Point2d(11,22) ); + CHECK( res.first[1][1] == Point2d(14,26) ); + CHECK( res.second == true ); + } + { + const char* s1 ="10 20 H 30 40"; //horizontal line + auto res = svg::svgp::parsePath( s1 ); + CHECK( res.first.size() == 1 ); + CHECK( res.first[0].size() == 3 ); + CHECK( res.first[0][0] == Point2d(10,20) ); + CHECK( res.first[0][1] == Point2d(30,20) ); + CHECK( res.first[0][2] == Point2d(40,20) ); + CHECK( res.second == false); + } + { + const char* s1 ="10"; // missing second value + CHECK_THROWS( svg::svgp::parsePath( s1 ) ); + } + { + const char* s1 ="10 20 30"; // missing second value + CHECK_THROWS( svg::svgp::parsePath( s1 ) ); + } + { + const char* s1 ="M10 20 C 30"; // C command not handled + CHECK_THROWS( svg::svgp::parsePath( s1 ) ); + } +} + +#endif // HOMOG2D_USE_SVG_IMPORT + +////////////////////////////////////////////////////////////// +///// BOOST GEOMETRY BINDING TESTS ///// +////////////////////////////////////////////////////////////// + +#ifdef HOMOG2D_USE_BOOSTGEOM +TEST_CASE( "boost geometry point import", "[bg-pt-import]" ) +{ + namespace bg = boost::geometry; + bg::model::point ptb1(3,4); + bg::model::d2::point_xy ptb2(5,6); + Point2d_ pt1(ptb1); + Point2d_ pt2(ptb2); + CHECK( pt1 == Point2d_(3,4) ); + CHECK( pt2 == Point2d_(5,6) ); + + pt1 = ptb1; // using assignment operator + pt2 = ptb2; + CHECK( pt1 == Point2d_(3,4) ); + CHECK( pt2 == Point2d_(5,6) ); +} + +TEST_CASE( "boost geometry point export", "[bg-pt-export]" ) +{ + Point2d_ ref1(3,4); + Point2d_ ref2(5,6); + namespace bg = boost::geometry; + using point_t1 = bg::model::point; + using point_t2 = bg::model::d2::point_xy; + + auto pt1 = getPt( ref1 ); + auto pt2 = getPt( ref2 ); + CHECK( bg::get<0>(pt1) == ref1.getX() ); + CHECK( bg::get<0>(pt2) == ref2.getX() ); +} + +TEST_CASE( "boost geometry vector point export", "[bg-vpt-export]" ) +{ + namespace bg = boost::geometry; + std::vector vin; + vin.push_back( Point2d(0.0, 0.0) ); + vin.push_back( Point2d(0.0, 5.0)); + vin.push_back( Point2d(5.0, 5.0)); + auto vout = getPts>(vin); // convert to a vector of boost geometry points + CHECK( vout.size() == 3 ); +} + +#endif // HOMOG2D_USE_BOOSTGEOM + ////////////////////////////////////////////////////////////// ///// OPENCV BINDING TESTS ///// ////////////////////////////////////////////////////////////// @@ -4036,6 +4043,6 @@ TEST_CASE( "Opencv binding", "[test_opencv]" ) // Line2d_ lib( Point2d(100,200) ); // CHECK( lia == lib ); } -} +} #endif // HOMOG2D_USE_OPENCV