From 1c05d1fb0d9169ee19d7d8c3203abb3da5e9f14d Mon Sep 17 00:00:00 2001 From: David Gunderman Date: Thu, 20 Jun 2019 12:58:18 -0700 Subject: [PATCH 01/44] Added BezierCurve class to primal geom, changed CMake file --- src/axom/primal/CMakeLists.txt | 1 + src/axom/primal/geometry/BezierCurve.hpp | 140 +++++++++++++++++++++++ 2 files changed, 141 insertions(+) create mode 100644 src/axom/primal/geometry/BezierCurve.hpp diff --git a/src/axom/primal/CMakeLists.txt b/src/axom/primal/CMakeLists.txt index 6e188f8698..9ecc42267c 100644 --- a/src/axom/primal/CMakeLists.txt +++ b/src/axom/primal/CMakeLists.txt @@ -28,6 +28,7 @@ set( primal_headers geometry/Tetrahedron.hpp geometry/Triangle.hpp geometry/Vector.hpp + geometry/BezierCurve.hpp ## operators operators/clip.hpp diff --git a/src/axom/primal/geometry/BezierCurve.hpp b/src/axom/primal/geometry/BezierCurve.hpp new file mode 100644 index 0000000000..f243885d2a --- /dev/null +++ b/src/axom/primal/geometry/BezierCurve.hpp @@ -0,0 +1,140 @@ +/*! + * \file BezierCurve.hpp + * + * \brief A BezierCurve primitive for primal + */ + +#ifndef PRIMAL_BEZIERCURVE_HPP_ +#define PRIMAL_BEZIERCURVE_HPP_ + +#include "axom/primal/geometry/Point.hpp" +#include "axom/primal/geometry/Vector.hpp" +#include "axom/primal/geometry/NumericArray.hpp" + +#include +#include // for std::ostream + +namespace axom +{ +namespace primal +{ + +// Forward declare the templated classes and operator functions +template < typename T, int NDIMS > +class BezierCurve; + +/*! \brief Overloaded output operator for Bezier Curves*/ +template < typename T,int NDIMS > +std::ostream& operator<<(std::ostream & os, const BezierCurve< T,NDIMS > & bCurve); + +/*! + * \class BezierCurve + * + * \brief Represents a Bezier Curve defined by an array of points + * \tparam T the coordinate type, e.g., double, float, etc. + * \tparam NDIMS the number of dimensions + * \note The control points should be ordered from t=0 to t=1 + */ + +template < typename T,int NDIMS > +class BezierCurve +{ +public: + typedef Point< T,NDIMS > PointType; + typedef Vector< T,NDIMS > VectorType; + typedef NumericArray< T,NDIMS > NumArrayType; + +private: + typedef std::vector< PointType > Coords; + +public: + /*! Default constructor for an empty Bezier Curve*/ + BezierCurve() {} + + /*! + * \brief Constructor for an empty Bezier Curve that reserves space for + * the given order of the curve + * + * \param [in] order number of control points minus 1 for which to reserve space + * \pre order is not negative + * + */ + BezierCurve(int order) + { + SLIC_ASSERT(order >= 0); + m_controlpoints.reserve(order + 1); + } + + /*! Return the order of the Bezier Curve*/ + int order() const { return m_controlpoints.size()-1; } + + /*! Appends a control point to the list of control points*/ + void addControlpoint(const PointType& pt) + { + m_controlpoints.push_back(pt); + } + + /*! Clears the list of control points*/ + void clear() + { + m_controlpoints.clear(); + } + + /*! Retrieves the vertex at index idx */ + PointType& operator[](int idx) { return m_controlpoints[idx]; } + /*! Retrieves the vertex at index idx */ + const PointType& operator[](int idx) const { return m_controlpoints[idx]; } + + /*! + * \brief Simple formatted print of a Bezier Curve instance + * + * \param os The output stream to write to + * \return A reference to the modified ostream + */ + std::ostream& print(std::ostream& os) const + { + const int sz = order(); + + os <<"{" << sz <<"-order Bezier Curve:"; + for (int i=0 ; i< sz; ++i) + { + os << m_controlpoints[i] << ","; + } + if (sz >= 1) + { + os<= 2; + } + +private: + Coords m_controlpoints; +}; // class BezierCurve + +//------------------------------------------------------------------------------ +/// Free functions implementing BezierCurve's operators +//------------------------------------------------------------------------------ +template < typename T, int NDIMS > +std::ostream& operator<<(std::ostream & os, const BezierCurve< T,NDIMS > & bCurve) +{ + bCurve.print(os); + return os; +} // std::ostream& operator <<... + +} // namespace primal +} // namespace axom + +#endif // PRIMAL_BEZIERCURVE_HPP_ From d6cd28d1f5a1cd39cac43480fe13eb34ebe618eb Mon Sep 17 00:00:00 2001 From: David Gunderman Date: Thu, 20 Jun 2019 16:14:37 -0700 Subject: [PATCH 02/44] Fixed bugs in definition of order, added test to primal_intro --- .../primal/examples/primal_introduction.cpp | 14 +++++++++++++ src/axom/primal/geometry/BezierCurve.hpp | 20 ++++++++++--------- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/src/axom/primal/examples/primal_introduction.cpp b/src/axom/primal/examples/primal_introduction.cpp index aa8cfe8658..8b51040c96 100644 --- a/src/axom/primal/examples/primal_introduction.cpp +++ b/src/axom/primal/examples/primal_introduction.cpp @@ -36,6 +36,7 @@ #include "axom/primal/geometry/Segment.hpp" #include "axom/primal/geometry/Triangle.hpp" #include "axom/primal/geometry/Vector.hpp" +#include "axom/primal/geometry/BezierCurve.hpp" // _prims_header_end // Axom operations @@ -84,6 +85,7 @@ typedef Polygon PolygonType; typedef Ray RayType; typedef Segment SegmentType; typedef Vector VectorType; +typedef BezierCurve BezierCurveType; // _using_end std::string asyheader = @@ -123,6 +125,17 @@ void writeToFile(std::string fname, std::string contents) } } +BezierCurveType showOrderBezier() +{ + //_ctrlpts_start + BezierCurveType bCurve(4); + std::cout << "----------------------Checking Bezier Functions-----------------------" << std::endl; + std::cout << bCurve.getOrder() << std::endl; + //_ctrlpts_end + + return bCurve; +} + PolygonType showClip() { // _clip_start @@ -724,6 +737,7 @@ int main(int argc, char** argv) showIntersect(); showOrientation(); showDistance(); + showOrderBezier(); return 0; } diff --git a/src/axom/primal/geometry/BezierCurve.hpp b/src/axom/primal/geometry/BezierCurve.hpp index f243885d2a..66b7396c16 100644 --- a/src/axom/primal/geometry/BezierCurve.hpp +++ b/src/axom/primal/geometry/BezierCurve.hpp @@ -55,18 +55,20 @@ class BezierCurve * \brief Constructor for an empty Bezier Curve that reserves space for * the given order of the curve * - * \param [in] order number of control points minus 1 for which to reserve space + * \param [in] order number of control points minus 1 for which to reserve control point space * \pre order is not negative * */ - BezierCurve(int order) + BezierCurve(int ord) { - SLIC_ASSERT(order >= 0); - m_controlpoints.reserve(order + 1); + SLIC_ASSERT(ord >= 0); + m_controlpoints.reserve(ord+1); + m_order=ord; } /*! Return the order of the Bezier Curve*/ - int order() const { return m_controlpoints.size()-1; } + int getOrder() const + { return m_order; } /*! Appends a control point to the list of control points*/ void addControlpoint(const PointType& pt) @@ -93,14 +95,14 @@ class BezierCurve */ std::ostream& print(std::ostream& os) const { - const int sz = order(); + const int sz = m_order; - os <<"{" << sz <<"-order Bezier Curve:"; + os <<"{" << sz <<"-degree Bezier Curve:"; for (int i=0 ; i< sz; ++i) { os << m_controlpoints[i] << ","; } - if (sz >= 1) + if (sz >= 2) { os<= 2; } - private: Coords m_controlpoints; + int m_order; }; // class BezierCurve //------------------------------------------------------------------------------ From 4d0e1827e411252450a4ae5d3c9deb9b9be62cd0 Mon Sep 17 00:00:00 2001 From: David Gunderman Date: Mon, 24 Jun 2019 14:27:14 -0700 Subject: [PATCH 03/44] moved bezier tests from /examples/primal_introduction_ex to /tests/primal_bezier.cpp, added eval_bezier.hpp to operators --- src/axom/primal/operators/eval_bezier.hpp | 68 ++++++++++++++++++ src/axom/primal/tests/primal_bezier.cpp | 86 +++++++++++++++++++++++ 2 files changed, 154 insertions(+) create mode 100644 src/axom/primal/operators/eval_bezier.hpp create mode 100644 src/axom/primal/tests/primal_bezier.cpp diff --git a/src/axom/primal/operators/eval_bezier.hpp b/src/axom/primal/operators/eval_bezier.hpp new file mode 100644 index 0000000000..4d4fb4dbc6 --- /dev/null +++ b/src/axom/primal/operators/eval_bezier.hpp @@ -0,0 +1,68 @@ +// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +/*! + * \file + * + * \brief Evaluates a Bezier Curve using deCasteljau's algorithm + * + */ + +#ifndef EVAL_BEZIER_HPP_ +#define EVAL_BEZIER_HPP_ + +#include "axom/slic/interface/slic.hpp" + +#include "axom/primal/geometry/NumericArray.hpp" +#include "axom/core/numerics/Matrix.hpp" +#include "axom/primal/geometry/Point.hpp" +#include "axom/primal/geometry/Triangle.hpp" +#include "axom/primal/geometry/OrientedBoundingBox.hpp" +#include "axom/primal/geometry/BezierCurve.hpp" + +namespace axom +{ +namespace primal +{ + +/*! + * \brief Evaluates a Bezier Curve at particular parameter value between 0 and 1 + * + * \param [in] bCurve BezierCurve to be evaluated + * \param [in] t parameter value between 0 and 1 at which to evaluate + * \return p the value of the Bezier Curve at t + * + */ + +template < typename T, int NDIMS > +Point< T, NDIMS > eval_bezier(const BezierCurve< T, NDIMS > bcurve, T t) +{ + Point< T, NDIMS > ptval(0.0,NDIMS); + int ord = bcurve.getOrder(); + T dCarray[ord+1]; + + for ( int i=0; i < NDIMS; i++) + { + for ( int j=0 ; j <= ord ; j++) + { + dCarray[j] = bcurve[j][i]; + } + for ( int j=1 ; j <= ord ; j++) + { + for ( int k=0 ; k <= ord-j ; k++) + { + dCarray[k]=(1-t)*dCarray[k]+t*dCarray[k+1]; + } + } + ptval[i]=dCarray[0]; + } + + return ptval; +} + +} /* namespace primal */ +} /* namespace axom */ + +#endif /* EVAL_BEZIER_HPP_ */ diff --git a/src/axom/primal/tests/primal_bezier.cpp b/src/axom/primal/tests/primal_bezier.cpp new file mode 100644 index 0000000000..b91ba22679 --- /dev/null +++ b/src/axom/primal/tests/primal_bezier.cpp @@ -0,0 +1,86 @@ +// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +/* /file bezier_test.cpp + * /brief This file tests the BezierCurve.hpp and eval_bezier.hpp files +*/ + +// _prims_header_start +// Axom primitives +#include "axom/primal/geometry/Point.hpp" +#include "axom/primal/geometry/BezierCurve.hpp" +// _prims_header_end + +// _eval_bez_start +#include "axom/primal/operators/eval_bezier.hpp" +// _eval_bez_end + +// C++ headers +#include +#include + +#include "fmt/fmt.hpp" + +// _using_start +// "using" directives to simplify code +using namespace axom; +using namespace primal; + +// almost all our examples are in 3D +constexpr int in3D = 3; + +// primitives represented by doubles in 3D +typedef Point PointType; +typedef BezierCurve BezierCurveType; +// _using_end + +BezierCurveType testBezier() +{ + //_ctrlpts_start + BezierCurveType bCurve(1); + std::cout << "----------------------Checking Bezier Functions-----------------------" << std::endl; + std::cout << "Checking the order constructor:" << std::endl; + std::cout << "Expected order: " << 1 << ". Order obtained from getOrder: " << bCurve.getOrder() << "." << std::endl; + std::cout << "Adding points (.6, 1.2, 1.0) and (0.0 , 1.6, 1.8)" << std::endl; + + bCurve.addControlpoint( PointType::make_point( 0.6, 1.2, 1.0 ) ); + bCurve.addControlpoint( PointType::make_point( 0.0, 1.6, 1.8 ) ); + + + std::cout << bCurve << std::endl; + + const int nbr_points = 4; + PointType data[nbr_points]; + data[0] = PointType::make_point(0.6, 1.2, 1.0); + data[1] = PointType::make_point(1.3, 1.6, 1.8); + data[2] = PointType::make_point(2.9, 2.4, 2.3); + data[3] = PointType::make_point(3.2, 3.5, 3.0); + BezierCurveType b2Curve(data, 4); + std::cout << "Checking the control point constructor:" << std::endl; + std::cout << b2Curve << std::endl; + + std::cout << "Checking indexing operator: " << std::endl; + std::cout << "The final control points of the above two bezier curves are " << bCurve[1] << " and " << b2Curve[3] << "." << std::endl; + + std::cout << "Checking the evaluation of bezier curves above: " << std::endl; + std::cout << "Curve 1 at t=0 is " << eval_bezier(bCurve,0.0) << " and Curve 2 at t=.5 is " << eval_bezier(b2Curve,.5) << std::endl; + std::cout << "------------------End checking Bezier Functions---------------------" << std::endl; + //_ctrlpts_end + + return bCurve; +} + +int main(int argc, char** argv) +{ + + // Deal with unused variables + AXOM_DEBUG_VAR(argc); + AXOM_DEBUG_VAR(argv); + + testBezier(); + + return 0; +} + From 62b9e5409a5c2fedab07bdd849c9f07d2e6ffe05 Mon Sep 17 00:00:00 2001 From: David Gunderman Date: Mon, 24 Jun 2019 14:32:42 -0700 Subject: [PATCH 04/44] Forgot to add some files --- src/axom/primal/CMakeLists.txt | 1 + .../primal/examples/primal_introduction.cpp | 32 ++++++++++++++- src/axom/primal/geometry/BezierCurve.hpp | 41 +++++++++++++++---- src/axom/primal/tests/CMakeLists.txt | 1 + 4 files changed, 66 insertions(+), 9 deletions(-) diff --git a/src/axom/primal/CMakeLists.txt b/src/axom/primal/CMakeLists.txt index 9ecc42267c..16d62e0d09 100644 --- a/src/axom/primal/CMakeLists.txt +++ b/src/axom/primal/CMakeLists.txt @@ -38,6 +38,7 @@ set( primal_headers operators/squared_distance.hpp operators/compute_bounding_box.hpp operators/in_sphere.hpp + operators/eval_bezier.hpp operators/detail/clip_impl.hpp operators/detail/intersect_impl.hpp diff --git a/src/axom/primal/examples/primal_introduction.cpp b/src/axom/primal/examples/primal_introduction.cpp index 8b51040c96..0abb95cfed 100644 --- a/src/axom/primal/examples/primal_introduction.cpp +++ b/src/axom/primal/examples/primal_introduction.cpp @@ -60,6 +60,9 @@ // _sqdist_header_start #include "axom/primal/operators/squared_distance.hpp" // _sqdist_header_end +// _eval_bez_start +#include "axom/primal/operators/eval_bezier.hpp" +// _eval_bez_end // C++ headers #include // do we need this? @@ -128,9 +131,34 @@ void writeToFile(std::string fname, std::string contents) BezierCurveType showOrderBezier() { //_ctrlpts_start - BezierCurveType bCurve(4); + BezierCurveType bCurve(1); std::cout << "----------------------Checking Bezier Functions-----------------------" << std::endl; - std::cout << bCurve.getOrder() << std::endl; + std::cout << "Checking the order constructor:" << std::endl; + std::cout << "Expected order: " << 1 << ". Order obtained from getOrder: " << bCurve.getOrder() << "." << std::endl; + std::cout << "Adding points (.6, 1.2, 1.0) and (0.0 , 1.6, 1.8)" << std::endl; + + bCurve.addControlpoint( PointType::make_point( 0.6, 1.2, 1.0 ) ); + bCurve.addControlpoint( PointType::make_point( 0.0, 1.6, 1.8 ) ); + + + std::cout << bCurve << std::endl; + + const int nbr_points = 4; + PointType data[nbr_points]; + data[0] = PointType::make_point(0.6, 1.2, 1.0); + data[1] = PointType::make_point(1.3, 1.6, 1.8); + data[2] = PointType::make_point(2.9, 2.4, 2.3); + data[3] = PointType::make_point(3.2, 3.5, 3.0); + BezierCurveType b2Curve(data, 4); + std::cout << "Checking the control point constructor:" << std::endl; + std::cout << b2Curve << std::endl; + + std::cout << "Checking indexing operator: " << std::endl; + std::cout << "The final control points of the above two bezier curves are " << bCurve[1] << " and " << b2Curve[3] << "." << std::endl; + + std::cout << "Checking the evaluation of bezier curves above: " << std::endl; + std::cout << "Curve 1 at t=0 is " << eval_bezier(bCurve,0.0) << " and Curve 2 at t=.5 is " << eval_bezier(b2Curve,.5) << std::endl; + std::cout << "------------------End checking Bezier Functions---------------------" << std::endl; //_ctrlpts_end return bCurve; diff --git a/src/axom/primal/geometry/BezierCurve.hpp b/src/axom/primal/geometry/BezierCurve.hpp index 66b7396c16..4f05c6551a 100644 --- a/src/axom/primal/geometry/BezierCurve.hpp +++ b/src/axom/primal/geometry/BezierCurve.hpp @@ -66,7 +66,25 @@ class BezierCurve m_order=ord; } - /*! Return the order of the Bezier Curve*/ + BezierCurve(PointType* pts, int n) + { + if (n <= 0) + { + clear(); + } + // sanity check + SLIC_ASSERT(pts != nullptr); + + m_controlpoints.reserve(n); + m_order=n-1; + + for (int i = 0 ; i <= n ; i++) + { + this->addControlpoint(pts[i]); + } + } + + /*! Returns the order of the Bezier Curve*/ int getOrder() const { return m_order; } @@ -82,10 +100,22 @@ class BezierCurve m_controlpoints.clear(); } - /*! Retrieves the vertex at index idx */ + /*! Retrieves the control point at index idx */ PointType& operator[](int idx) { return m_controlpoints[idx]; } - /*! Retrieves the vertex at index idx */ + /*! Retrieves the control point at index idx */ const PointType& operator[](int idx) const { return m_controlpoints[idx]; } + + std::vector< Point< T, NDIMS > > getControlPoints() const + { + std::vector< Point< T, NDIMS > > cpts; + cpts.reserve(m_order+1); + + for (int i=0 ; i < m_order-1 ; i++) + { + cpts[i]=m_controlpoints[i]; + } + return cpts; + } /*! * \brief Simple formatted print of a Bezier Curve instance @@ -102,10 +132,7 @@ class BezierCurve { os << m_controlpoints[i] << ","; } - if (sz >= 2) - { - os< Date: Mon, 24 Jun 2019 17:02:45 -0700 Subject: [PATCH 05/44] Added a de CastelJau Bezier splitting operator, which is not complete yet. Added a test for splitting to the primal_bezier.cpp file --- src/axom/primal/geometry/BezierCurve.hpp | 23 +++++++++++++++++++ src/axom/primal/operators/eval_bezier.hpp | 27 +++++++++++++++++++++++ src/axom/primal/tests/primal_bezier.cpp | 7 ++++++ 3 files changed, 57 insertions(+) diff --git a/src/axom/primal/geometry/BezierCurve.hpp b/src/axom/primal/geometry/BezierCurve.hpp index 4f05c6551a..9fac1e4364 100644 --- a/src/axom/primal/geometry/BezierCurve.hpp +++ b/src/axom/primal/geometry/BezierCurve.hpp @@ -66,6 +66,29 @@ class BezierCurve m_order=ord; } + BezierCurve(T* pts, int n) + { + if (n<=0) + { + clear(); + } + // sanity check + SLIC_ASSERT(pts != nullptr); + + m_controlpoints.reserve(n); + m_order=n-1; + + T tempar[NDIMS]; + for ( int i = 0 ; i<=n ; i++) + { + for ( int j = 0 ; j< NDIMS ; j++) + { + tempar[j]=pts[j*n+i]; + } + this->addControlpoint(tempar); + } + } + BezierCurve(PointType* pts, int n) { if (n <= 0) diff --git a/src/axom/primal/operators/eval_bezier.hpp b/src/axom/primal/operators/eval_bezier.hpp index 4d4fb4dbc6..84fd6b46e8 100644 --- a/src/axom/primal/operators/eval_bezier.hpp +++ b/src/axom/primal/operators/eval_bezier.hpp @@ -62,6 +62,33 @@ Point< T, NDIMS > eval_bezier(const BezierCurve< T, NDIMS > bcurve, T t) return ptval; } +template < typename T, int NDIMS > +void split_bezier(const BezierCurve< T, NDIMS > bcurve, T t, BezierCurve< T, NDIMS >& c1, BezierCurve< T, NDIMS>& c2) +{ + int ord = bcurve.getOrder(); + //SLIC_ASSERT( (ord == c1.getOrder()) && (ord == c2.getOrder()) ); + T* dCarray = new T[NDIMS*2*(ord+1)]; + for ( int i=0; i < NDIMS; i++) + { + for ( int j=0 ; j <= ord ; j++) + { + dCarray[i*(2*(ord)+1)+j] = bcurve[j][i]; + } + for ( int j=1 ; j <= ord ; j++) + { + dCarray[i*(2*(ord)+1)+(ord)+j]=dCarray[0]; + for ( int k=0 ; k <= ord-j ; k++) + { + dCarray[i*(2*(ord)+1)+k]=(1-t)*dCarray[i*(2*(ord)+1)+k]+t*dCarray[i*(2*(ord)+1)+k+1]; + } + } + } + c1=BezierCurve< T, NDIMS> (dCarray,ord+1); + c2=BezierCurve< T, NDIMS> (dCarray+NDIMS*(ord),ord+1); + delete [] dCarray; + return; +} + } /* namespace primal */ } /* namespace axom */ diff --git a/src/axom/primal/tests/primal_bezier.cpp b/src/axom/primal/tests/primal_bezier.cpp index b91ba22679..29b9fdd6c4 100644 --- a/src/axom/primal/tests/primal_bezier.cpp +++ b/src/axom/primal/tests/primal_bezier.cpp @@ -66,6 +66,13 @@ BezierCurveType testBezier() std::cout << "Checking the evaluation of bezier curves above: " << std::endl; std::cout << "Curve 1 at t=0 is " << eval_bezier(bCurve,0.0) << " and Curve 2 at t=.5 is " << eval_bezier(b2Curve,.5) << std::endl; + + BezierCurveType b3Curve(3); + BezierCurveType b4Curve(3); + split_bezier(b2Curve,.5,b3Curve,b4Curve); + + std::cout << "Checking the splitting of bezier curve 2: " << std::endl; + std::cout << "The two resulting curves are: " << b3Curve << " and " << b4Curve << "." << std::endl; std::cout << "------------------End checking Bezier Functions---------------------" << std::endl; //_ctrlpts_end From 9da1d89e4a97bd0e0a2b0b3e111f1f463aeaa24e Mon Sep 17 00:00:00 2001 From: David Gunderman Date: Tue, 25 Jun 2019 09:03:14 -0700 Subject: [PATCH 06/44] deleted bezier tests from /examples/primal_introduction.cpp --- .../primal/examples/primal_introduction.cpp | 42 ------------------- 1 file changed, 42 deletions(-) diff --git a/src/axom/primal/examples/primal_introduction.cpp b/src/axom/primal/examples/primal_introduction.cpp index 0abb95cfed..aa8cfe8658 100644 --- a/src/axom/primal/examples/primal_introduction.cpp +++ b/src/axom/primal/examples/primal_introduction.cpp @@ -36,7 +36,6 @@ #include "axom/primal/geometry/Segment.hpp" #include "axom/primal/geometry/Triangle.hpp" #include "axom/primal/geometry/Vector.hpp" -#include "axom/primal/geometry/BezierCurve.hpp" // _prims_header_end // Axom operations @@ -60,9 +59,6 @@ // _sqdist_header_start #include "axom/primal/operators/squared_distance.hpp" // _sqdist_header_end -// _eval_bez_start -#include "axom/primal/operators/eval_bezier.hpp" -// _eval_bez_end // C++ headers #include // do we need this? @@ -88,7 +84,6 @@ typedef Polygon PolygonType; typedef Ray RayType; typedef Segment SegmentType; typedef Vector VectorType; -typedef BezierCurve BezierCurveType; // _using_end std::string asyheader = @@ -128,42 +123,6 @@ void writeToFile(std::string fname, std::string contents) } } -BezierCurveType showOrderBezier() -{ - //_ctrlpts_start - BezierCurveType bCurve(1); - std::cout << "----------------------Checking Bezier Functions-----------------------" << std::endl; - std::cout << "Checking the order constructor:" << std::endl; - std::cout << "Expected order: " << 1 << ". Order obtained from getOrder: " << bCurve.getOrder() << "." << std::endl; - std::cout << "Adding points (.6, 1.2, 1.0) and (0.0 , 1.6, 1.8)" << std::endl; - - bCurve.addControlpoint( PointType::make_point( 0.6, 1.2, 1.0 ) ); - bCurve.addControlpoint( PointType::make_point( 0.0, 1.6, 1.8 ) ); - - - std::cout << bCurve << std::endl; - - const int nbr_points = 4; - PointType data[nbr_points]; - data[0] = PointType::make_point(0.6, 1.2, 1.0); - data[1] = PointType::make_point(1.3, 1.6, 1.8); - data[2] = PointType::make_point(2.9, 2.4, 2.3); - data[3] = PointType::make_point(3.2, 3.5, 3.0); - BezierCurveType b2Curve(data, 4); - std::cout << "Checking the control point constructor:" << std::endl; - std::cout << b2Curve << std::endl; - - std::cout << "Checking indexing operator: " << std::endl; - std::cout << "The final control points of the above two bezier curves are " << bCurve[1] << " and " << b2Curve[3] << "." << std::endl; - - std::cout << "Checking the evaluation of bezier curves above: " << std::endl; - std::cout << "Curve 1 at t=0 is " << eval_bezier(bCurve,0.0) << " and Curve 2 at t=.5 is " << eval_bezier(b2Curve,.5) << std::endl; - std::cout << "------------------End checking Bezier Functions---------------------" << std::endl; - //_ctrlpts_end - - return bCurve; -} - PolygonType showClip() { // _clip_start @@ -765,7 +724,6 @@ int main(int argc, char** argv) showIntersect(); showOrientation(); showDistance(); - showOrderBezier(); return 0; } From 29262599618e7d8219e83a033fc07e0594d0cf41 Mon Sep 17 00:00:00 2001 From: David Gunderman Date: Wed, 26 Jun 2019 16:55:50 -0700 Subject: [PATCH 07/44] Converted more of the primal_bezier to gtest and slic_INFO formats --- src/axom/primal/CMakeLists.txt | 1 - src/axom/primal/geometry/BezierCurve.hpp | 120 ++++++++++++---- src/axom/primal/operators/eval_bezier.hpp | 95 ------------- src/axom/primal/tests/primal_bezier.cpp | 166 ++++++++++++++++------ 4 files changed, 218 insertions(+), 164 deletions(-) delete mode 100644 src/axom/primal/operators/eval_bezier.hpp diff --git a/src/axom/primal/CMakeLists.txt b/src/axom/primal/CMakeLists.txt index 16d62e0d09..9ecc42267c 100644 --- a/src/axom/primal/CMakeLists.txt +++ b/src/axom/primal/CMakeLists.txt @@ -38,7 +38,6 @@ set( primal_headers operators/squared_distance.hpp operators/compute_bounding_box.hpp operators/in_sphere.hpp - operators/eval_bezier.hpp operators/detail/clip_impl.hpp operators/detail/intersect_impl.hpp diff --git a/src/axom/primal/geometry/BezierCurve.hpp b/src/axom/primal/geometry/BezierCurve.hpp index 9fac1e4364..fecdc0ea27 100644 --- a/src/axom/primal/geometry/BezierCurve.hpp +++ b/src/axom/primal/geometry/BezierCurve.hpp @@ -7,12 +7,16 @@ #ifndef PRIMAL_BEZIERCURVE_HPP_ #define PRIMAL_BEZIERCURVE_HPP_ +#include "axom/slic.hpp" #include "axom/primal/geometry/Point.hpp" #include "axom/primal/geometry/Vector.hpp" #include "axom/primal/geometry/NumericArray.hpp" +#include "fmt/fmt.hpp" #include #include // for std::ostream +#include +#include namespace axom { @@ -45,7 +49,7 @@ class BezierCurve typedef NumericArray< T,NDIMS > NumArrayType; private: - typedef std::vector< PointType > Coords; + typedef std::vector< PointType > CoordsVec; public: /*! Default constructor for an empty Bezier Curve*/ @@ -55,7 +59,7 @@ class BezierCurve * \brief Constructor for an empty Bezier Curve that reserves space for * the given order of the curve * - * \param [in] order number of control points minus 1 for which to reserve control point space + * \param [in] order the order of the resulting Bezier curve * \pre order is not negative * */ @@ -63,45 +67,62 @@ class BezierCurve { SLIC_ASSERT(ord >= 0); m_controlpoints.reserve(ord+1); - m_order=ord; + m_controlpoints.resize(ord+1); } + + /*! + * \brief Constructor for a Bezier Curve from a list of Points + * \verbatim {x_0, x_1, x_2, x_3, y_0, y_1, y_2, y_3, z_0, z_1, z_2, z_3} + * + * \param [in] pts an array with (n+1)*NDIMS entries, ordered by coordinate then by control point order + * \param [in] ord number of control points minus 1 for which to reserve control point space + * \pre order is not negative + * + */ - BezierCurve(T* pts, int n) + BezierCurve(T* pts, int ord) { - if (n<=0) + if ( ord <= 0 ) { clear(); } // sanity check SLIC_ASSERT(pts != nullptr); - m_controlpoints.reserve(n); - m_order=n-1; + m_controlpoints.reserve(ord+1); T tempar[NDIMS]; - for ( int i = 0 ; i<=n ; i++) + for ( int i = 0 ; i <= ord ; i++) { - for ( int j = 0 ; j< NDIMS ; j++) + for ( int j = 0 ; j < NDIMS ; j++) { - tempar[j]=pts[j*n+i]; + tempar[j]=pts[j*(ord+1)+i]; } this->addControlpoint(tempar); } } - BezierCurve(PointType* pts, int n) + /*! + * \brief Constructor for a Bezier Curve from an array of coordinates + * + * \param [in] pts a vector with ord+1 points in it + * \param [in] ord number of control points minus 1 for which to reserve control point space + * \pre order is not negative + * + */ + + BezierCurve(PointType* pts, int ord) { - if (n <= 0) + if (ord <= 0) { clear(); } // sanity check SLIC_ASSERT(pts != nullptr); - m_controlpoints.reserve(n); - m_order=n-1; + m_controlpoints.reserve(ord+1); - for (int i = 0 ; i <= n ; i++) + for (int i = 0 ; i <= ord ; i++) { this->addControlpoint(pts[i]); } @@ -109,7 +130,7 @@ class BezierCurve /*! Returns the order of the Bezier Curve*/ int getOrder() const - { return m_order; } + { return m_controlpoints.size()-1; } /*! Appends a control point to the list of control points*/ void addControlpoint(const PointType& pt) @@ -130,14 +151,66 @@ class BezierCurve std::vector< Point< T, NDIMS > > getControlPoints() const { - std::vector< Point< T, NDIMS > > cpts; - cpts.reserve(m_order+1); + return m_controlpoints; + } + + /*! + * \brief Evaluates a Bezier Curve at particular parameter value between 0 and 1 + * + * \param [in] t parameter value between 0 and 1 at which to evaluate + * \return p the value of the Bezier Curve at t + * + */ - for (int i=0 ; i < m_order-1 ; i++) + Point< T, NDIMS > eval_bezier(T t) + { + Point< T, NDIMS > ptval(0.0,NDIMS); + int ord = m_controlpoints.size()-1; + T dCarray[ord+1]; + + for ( int i=0; i < NDIMS; i++) + { + for ( int j=0 ; j <= ord ; j++) + { + dCarray[j] = m_controlpoints[j][i]; + } + for ( int j=1 ; j <= ord ; j++) + { + for ( int k=0 ; k <= ord-j ; k++) + { + dCarray[k]=(1-t)*dCarray[k]+t*dCarray[k+1]; + } + } + ptval[i]=dCarray[0]; + } + + return ptval; + } + + void split_bezier(T t, BezierCurve< T, NDIMS >& c1, BezierCurve< T, NDIMS>& c2) + { + int ord = m_controlpoints.size()-1; + T* dCarray = new T[NDIMS*2*(ord+1)]; + for ( int i=0; i < NDIMS; i++) { - cpts[i]=m_controlpoints[i]; + for ( int j=0 ; j <= ord ; j++) + { + dCarray[i*((ord+1))+j] = m_controlpoints[j][i]; + } + dCarray[NDIMS*(ord+1)+(i)*((ord+1))]= dCarray[i*((ord+1))]; + for ( int j=1 ; j <= ord ; j++) + { + for ( int k=0 ; k <= ord-j ; k++) + { + dCarray[i*((ord+1))+k]=(1-t)*dCarray[i*((ord+1))+k]+t*dCarray[i*((ord+1))+k+1]; + } + dCarray[NDIMS*(ord+1)+(i)*((ord+1))+j]=dCarray[i*((ord+1))]; + } } - return cpts; + c2=BezierCurve< T, NDIMS> (dCarray,ord); + c1=BezierCurve< T, NDIMS> (dCarray+NDIMS*(ord+1),ord); + delete [] dCarray; + return; } /*! @@ -148,7 +221,7 @@ class BezierCurve */ std::ostream& print(std::ostream& os) const { - const int sz = m_order; + const int sz = m_controlpoints.size()-1; os <<"{" << sz <<"-degree Bezier Curve:"; for (int i=0 ; i< sz; ++i) @@ -172,8 +245,7 @@ class BezierCurve return m_controlpoints.size() >= 2; } private: - Coords m_controlpoints; - int m_order; + CoordsVec m_controlpoints; }; // class BezierCurve //------------------------------------------------------------------------------ diff --git a/src/axom/primal/operators/eval_bezier.hpp b/src/axom/primal/operators/eval_bezier.hpp deleted file mode 100644 index 84fd6b46e8..0000000000 --- a/src/axom/primal/operators/eval_bezier.hpp +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and -// other Axom Project Developers. See the top-level COPYRIGHT file for details. -// -// SPDX-License-Identifier: (BSD-3-Clause) - -/*! - * \file - * - * \brief Evaluates a Bezier Curve using deCasteljau's algorithm - * - */ - -#ifndef EVAL_BEZIER_HPP_ -#define EVAL_BEZIER_HPP_ - -#include "axom/slic/interface/slic.hpp" - -#include "axom/primal/geometry/NumericArray.hpp" -#include "axom/core/numerics/Matrix.hpp" -#include "axom/primal/geometry/Point.hpp" -#include "axom/primal/geometry/Triangle.hpp" -#include "axom/primal/geometry/OrientedBoundingBox.hpp" -#include "axom/primal/geometry/BezierCurve.hpp" - -namespace axom -{ -namespace primal -{ - -/*! - * \brief Evaluates a Bezier Curve at particular parameter value between 0 and 1 - * - * \param [in] bCurve BezierCurve to be evaluated - * \param [in] t parameter value between 0 and 1 at which to evaluate - * \return p the value of the Bezier Curve at t - * - */ - -template < typename T, int NDIMS > -Point< T, NDIMS > eval_bezier(const BezierCurve< T, NDIMS > bcurve, T t) -{ - Point< T, NDIMS > ptval(0.0,NDIMS); - int ord = bcurve.getOrder(); - T dCarray[ord+1]; - - for ( int i=0; i < NDIMS; i++) - { - for ( int j=0 ; j <= ord ; j++) - { - dCarray[j] = bcurve[j][i]; - } - for ( int j=1 ; j <= ord ; j++) - { - for ( int k=0 ; k <= ord-j ; k++) - { - dCarray[k]=(1-t)*dCarray[k]+t*dCarray[k+1]; - } - } - ptval[i]=dCarray[0]; - } - - return ptval; -} - -template < typename T, int NDIMS > -void split_bezier(const BezierCurve< T, NDIMS > bcurve, T t, BezierCurve< T, NDIMS >& c1, BezierCurve< T, NDIMS>& c2) -{ - int ord = bcurve.getOrder(); - //SLIC_ASSERT( (ord == c1.getOrder()) && (ord == c2.getOrder()) ); - T* dCarray = new T[NDIMS*2*(ord+1)]; - for ( int i=0; i < NDIMS; i++) - { - for ( int j=0 ; j <= ord ; j++) - { - dCarray[i*(2*(ord)+1)+j] = bcurve[j][i]; - } - for ( int j=1 ; j <= ord ; j++) - { - dCarray[i*(2*(ord)+1)+(ord)+j]=dCarray[0]; - for ( int k=0 ; k <= ord-j ; k++) - { - dCarray[i*(2*(ord)+1)+k]=(1-t)*dCarray[i*(2*(ord)+1)+k]+t*dCarray[i*(2*(ord)+1)+k+1]; - } - } - } - c1=BezierCurve< T, NDIMS> (dCarray,ord+1); - c2=BezierCurve< T, NDIMS> (dCarray+NDIMS*(ord),ord+1); - delete [] dCarray; - return; -} - -} /* namespace primal */ -} /* namespace axom */ - -#endif /* EVAL_BEZIER_HPP_ */ diff --git a/src/axom/primal/tests/primal_bezier.cpp b/src/axom/primal/tests/primal_bezier.cpp index 29b9fdd6c4..612e16a1aa 100644 --- a/src/axom/primal/tests/primal_bezier.cpp +++ b/src/axom/primal/tests/primal_bezier.cpp @@ -7,69 +7,141 @@ * /brief This file tests the BezierCurve.hpp and eval_bezier.hpp files */ -// _prims_header_start -// Axom primitives -#include "axom/primal/geometry/Point.hpp" -#include "axom/primal/geometry/BezierCurve.hpp" -// _prims_header_end - -// _eval_bez_start -#include "axom/primal/operators/eval_bezier.hpp" -// _eval_bez_end +#include "gtest/gtest.h" -// C++ headers -#include -#include - -#include "fmt/fmt.hpp" +#include "axom/primal/geometry/BezierCurve.hpp" // _using_start -// "using" directives to simplify code using namespace axom; using namespace primal; - -// almost all our examples are in 3D -constexpr int in3D = 3; - -// primitives represented by doubles in 3D -typedef Point PointType; -typedef BezierCurve BezierCurveType; // _using_end + constexpr int DIM = 3; + typedef double CoordType; + typedef primal::Point< CoordType, DIM > PointType; + typedef primal::BezierCurve< CoordType, DIM > BezierCurveType; +//---------------------------------------------------------------------------------- +TEST( primal_beziercurve, bezier_constructor ) +{ + static const int DIM = 3; + typedef double CoordType; + typedef primal::BezierCurve< CoordType, DIM > BezierCurveType; + SLIC_INFO("\nprimal: testing default bezier constructor ") ; + BezierCurveType bCurve; -BezierCurveType testBezier() + EXPECT_EQ(bCurve.getControlPoints() ,(std::vector< Point< CoordType, DIM > >())); +} + +//---------------------------------------------------------------------------------- +TEST( primal_beziercurve, bezier_order_constructor ) { - //_ctrlpts_start + static const int DIM = 3; + typedef double CoordType; + typedef primal::BezierCurve< CoordType, DIM > BezierCurveType; + SLIC_INFO("\nprimal: testing bezier order constructor ") ; + BezierCurveType bCurve(1); - std::cout << "----------------------Checking Bezier Functions-----------------------" << std::endl; - std::cout << "Checking the order constructor:" << std::endl; - std::cout << "Expected order: " << 1 << ". Order obtained from getOrder: " << bCurve.getOrder() << "." << std::endl; - std::cout << "Adding points (.6, 1.2, 1.0) and (0.0 , 1.6, 1.8)" << std::endl; - bCurve.addControlpoint( PointType::make_point( 0.6, 1.2, 1.0 ) ); - bCurve.addControlpoint( PointType::make_point( 0.0, 1.6, 1.8 ) ); + EXPECT_EQ(bCurve.getOrder(), 1); +} + +//---------------------------------------------------------------------------------- +TEST( primal_beziercurve, bezier_add_controlpoints ) +{ + static const int DIM =3; + typedef double CoordType; + typedef primal::Point< CoordType, DIM > PointType; + typedef primal::BezierCurve< CoordType, DIM > BezierCurveType; + SLIC_INFO("\nprimal: testing adding control points to empty beziercurve"); + + BezierCurveType bCurve; + + CoordType coords[6] = {.6,1.2,1.0,0.0,1.6,1.8}; + bCurve.addControlpoint( PointType::make_point( coords[0], coords[1], coords[2] ) ); + bCurve.addControlpoint( PointType::make_point( coords[3], coords[4], coords[5] ) ); + + EXPECT_EQ(bCurve.getOrder(), 1); + for (int i=0; i PointType; + typedef primal::BezierCurve< CoordType, DIM > BezierCurveType; + SLIC_INFO("\nprimal: testing point vector constructor"); + + CoordType coords[6] = {.6,1.2,1.0,0.0,1.6,1.8}; + PointType pts[2] = {PointType::make_point(coords[0], coords[1], coords[2]),PointType::make_point(coords[3], coords[4], coords[5])}; + + BezierCurveType bCurve(pts,1); + EXPECT_EQ(bCurve.getOrder(), 1); + for (int i=0; i PointType; + typedef primal::BezierCurve< CoordType, DIM > BezierCurveType; + SLIC_INFO("\nprimal: testing point vector constructor"); + + CoordType coords[6] = {.6,1.2,1.0,0.0,1.6,1.8}; + PointType pts[2] = {PointType::make_point(coords[0], coords[1], coords[2]),PointType::make_point(coords[3], coords[4], coords[5])}; + + BezierCurveType bCurve(pts,1); + EXPECT_EQ(bCurve.getOrder(), 1); + for (int i=0; i Date: Thu, 27 Jun 2019 16:47:00 -0700 Subject: [PATCH 08/44] Added files from last commit: --- src/axom/primal/geometry/BezierCurve.hpp | 6 ++ src/axom/primal/tests/primal_bezier.cpp | 75 +++++++++++++++++++++--- 2 files changed, 73 insertions(+), 8 deletions(-) diff --git a/src/axom/primal/geometry/BezierCurve.hpp b/src/axom/primal/geometry/BezierCurve.hpp index fecdc0ea27..6872563d68 100644 --- a/src/axom/primal/geometry/BezierCurve.hpp +++ b/src/axom/primal/geometry/BezierCurve.hpp @@ -128,6 +128,10 @@ class BezierCurve } } + /*! Sets the order of the Bezier Curve*/ + void setOrder( int ord) + { m_controlpoints.resize(ord+1); } + /*! Returns the order of the Bezier Curve*/ int getOrder() const { return m_controlpoints.size()-1; } @@ -190,6 +194,8 @@ class BezierCurve void split_bezier(T t, BezierCurve< T, NDIMS >& c1, BezierCurve< T, NDIMS>& c2) { int ord = m_controlpoints.size()-1; + c1.setOrder(ord); + c2.setOrder(ord); T* dCarray = new T[NDIMS*2*(ord+1)]; for ( int i=0; i < NDIMS; i++) { diff --git a/src/axom/primal/tests/primal_bezier.cpp b/src/axom/primal/tests/primal_bezier.cpp index 612e16a1aa..38ec80f948 100644 --- a/src/axom/primal/tests/primal_bezier.cpp +++ b/src/axom/primal/tests/primal_bezier.cpp @@ -70,13 +70,13 @@ TEST( primal_beziercurve, bezier_add_controlpoints ) } //---------------------------------------------------------------------------------- -TEST( primal_beziercurve, bezier_point_vector_constructor ) +TEST( primal_beziercurve, bezier_point_array_constructor ) { static const int DIM =3; typedef double CoordType; typedef primal::Point< CoordType, DIM > PointType; typedef primal::BezierCurve< CoordType, DIM > BezierCurveType; - SLIC_INFO("\nprimal: testing point vector constructor"); + SLIC_INFO("\nprimal: testing point array constructor"); CoordType coords[6] = {.6,1.2,1.0,0.0,1.6,1.8}; PointType pts[2] = {PointType::make_point(coords[0], coords[1], coords[2]),PointType::make_point(coords[3], coords[4], coords[5])}; @@ -93,18 +93,17 @@ TEST( primal_beziercurve, bezier_point_vector_constructor ) } //---------------------------------------------------------------------------------- -TEST( primal_beziercurve, bezier_double_array_constructor ) +TEST( primal_beziercurve, bezier_coordinate_array_constructor ) { static const int DIM =3; typedef double CoordType; - typedef primal::Point< CoordType, DIM > PointType; typedef primal::BezierCurve< CoordType, DIM > BezierCurveType; - SLIC_INFO("\nprimal: testing point vector constructor"); + SLIC_INFO("\nprimal: testing coordinate array constructor"); CoordType coords[6] = {.6,1.2,1.0,0.0,1.6,1.8}; - PointType pts[2] = {PointType::make_point(coords[0], coords[1], coords[2]),PointType::make_point(coords[3], coords[4], coords[5])}; + CoordType coordsToPass[6] = {coords[0], coords[3], coords[1],coords[4], coords[2], coords[5]}; - BezierCurveType bCurve(pts,1); + BezierCurveType bCurve(coordsToPass,1); EXPECT_EQ(bCurve.getOrder(), 1); for (int i=0; i PointType; + typedef primal::BezierCurve< CoordType, DIM > BezierCurveType; + SLIC_INFO("\nprimal: testing bezier evaluation"); + + const int nbr_points = 4; + PointType data[nbr_points]; + data[0] = PointType::make_point(0.6, 1.2, 1.0); + data[1] = PointType::make_point(1.3, 1.6, 1.8); + data[2] = PointType::make_point(2.9, 2.4, 2.3); + data[3] = PointType::make_point(3.2, 3.5, 3.0); + BezierCurveType b2Curve(data, nbr_points-1); + + PointType midtval = PointType::make_point(2.05,2.0875,2.0375); + for ( int i=0; i PointType; + typedef primal::BezierCurve< CoordType, DIM > BezierCurveType; + SLIC_INFO("\nprimal: testing coordinate array constructor"); + + const int nbr_points = 4; + PointType data[nbr_points]; + data[0] = PointType::make_point(0.6, 1.2, 1.0); + data[1] = PointType::make_point(1.3, 1.6, 1.8); + data[2] = PointType::make_point(2.9, 2.4, 2.3); + data[3] = PointType::make_point(3.2, 3.5, 3.0); + BezierCurveType b2Curve(data, nbr_points-1); + + BezierCurveType b3Curve(3);// Checks that split works with order constructor input + BezierCurveType b4Curve;// Checks that split works with default constructor input + b2Curve.split_bezier(.5,b3Curve,b4Curve); + + CoordType b3Coords[12] = {.6, .95, 1.525, 2.05, 1.2, 1.4, 1.7, 2.0875, 1.0, 1.4, 1.725, 2.0375}; + CoordType b4Coords[12] = {2.05, 2.575, 3.05, 3.2, 2.0875, 2.475, 2.95, 3.5, 2.0375, 2.35, 2.64, 3.0}; + for ( int i=0; i Date: Fri, 28 Jun 2019 09:09:58 -0700 Subject: [PATCH 09/44] Added equality/inequality test for BezierCurve class, finished unit testing for BezierCurve class --- src/axom/primal/geometry/BezierCurve.hpp | 23 ++++++++++- src/axom/primal/tests/primal_bezier.cpp | 50 +++--------------------- 2 files changed, 27 insertions(+), 46 deletions(-) diff --git a/src/axom/primal/geometry/BezierCurve.hpp b/src/axom/primal/geometry/BezierCurve.hpp index 6872563d68..669235fb22 100644 --- a/src/axom/primal/geometry/BezierCurve.hpp +++ b/src/axom/primal/geometry/BezierCurve.hpp @@ -153,6 +153,17 @@ class BezierCurve /*! Retrieves the control point at index idx */ const PointType& operator[](int idx) const { return m_controlpoints[idx]; } + /* Checks equality of two Bezier Curve */ + friend inline bool operator==(const BezierCurve< T, NDIMS>& lhs, const BezierCurve< T, NDIMS>& rhs) + { + return lhs.m_controlpoints == rhs.m_controlpoints; + } + + friend inline bool operator!=(const BezierCurve< T, NDIMS>& lhs, const BezierCurve< T, NDIMS>& rhs) + { + return !(lhs == rhs); + } + std::vector< Point< T, NDIMS > > getControlPoints() const { return m_controlpoints; @@ -191,11 +202,19 @@ class BezierCurve return ptval; } + /*! + * \brief Splits a Bezier Curve into two Bezier Curves at particular parameter value between 0 and 1 + * + * \param [in] t parameter value between 0 and 1 at which to evaluate + * \param [in] c1, c2 two BezierCurves objects to store output in + * \param [out] c1, c2 two new Bezier Curves that split the original + * \return p the value of the Bezier Curve at t + * + */ + void split_bezier(T t, BezierCurve< T, NDIMS >& c1, BezierCurve< T, NDIMS>& c2) { int ord = m_controlpoints.size()-1; - c1.setOrder(ord); - c2.setOrder(ord); T* dCarray = new T[NDIMS*2*(ord+1)]; for ( int i=0; i < NDIMS; i++) { diff --git a/src/axom/primal/tests/primal_bezier.cpp b/src/axom/primal/tests/primal_bezier.cpp index 38ec80f948..36712762e9 100644 --- a/src/axom/primal/tests/primal_bezier.cpp +++ b/src/axom/primal/tests/primal_bezier.cpp @@ -15,10 +15,6 @@ using namespace axom; using namespace primal; // _using_end - constexpr int DIM = 3; - typedef double CoordType; - typedef primal::Point< CoordType, DIM > PointType; - typedef primal::BezierCurve< CoordType, DIM > BezierCurveType; //---------------------------------------------------------------------------------- TEST( primal_beziercurve, bezier_constructor ) { @@ -147,7 +143,7 @@ TEST( primal_beziercurve, bezier_split ) typedef double CoordType; typedef primal::Point< CoordType, DIM > PointType; typedef primal::BezierCurve< CoordType, DIM > BezierCurveType; - SLIC_INFO("\nprimal: testing coordinate array constructor"); + SLIC_INFO("\nprimal: testing bezier splitting"); const int nbr_points = 4; PointType data[nbr_points]; @@ -162,51 +158,18 @@ TEST( primal_beziercurve, bezier_split ) b2Curve.split_bezier(.5,b3Curve,b4Curve); CoordType b3Coords[12] = {.6, .95, 1.525, 2.05, 1.2, 1.4, 1.7, 2.0875, 1.0, 1.4, 1.725, 2.0375}; - CoordType b4Coords[12] = {2.05, 2.575, 3.05, 3.2, 2.0875, 2.475, 2.95, 3.5, 2.0375, 2.35, 2.64, 3.0}; + CoordType b4Coords[12] = {2.05, 2.575, 3.05, 3.2, 2.0875, 2.475, 2.95, 3.5, 2.0375, 2.35, 2.65, 3.0}; + BezierCurveType b3True(b3Coords,3); + BezierCurveType b4True(b4Coords,3); for ( int i=0; i Date: Mon, 1 Jul 2019 16:33:07 -0700 Subject: [PATCH 10/44] Finished first draft bezier intersection hpp file, added test file for intersection as well as one test --- src/axom/primal/CMakeLists.txt | 1 + src/axom/primal/geometry/BezierCurve.hpp | 27 +++- .../primal/operators/intersect_bezier.hpp | 129 ++++++++++++++++++ src/axom/primal/tests/CMakeLists.txt | 3 +- ...mal_bezier.cpp => primal_bezier_curve.cpp} | 0 .../primal/tests/primal_bezier_intersect.cpp | 72 ++++++++++ 6 files changed, 229 insertions(+), 3 deletions(-) create mode 100644 src/axom/primal/operators/intersect_bezier.hpp rename src/axom/primal/tests/{primal_bezier.cpp => primal_bezier_curve.cpp} (100%) create mode 100644 src/axom/primal/tests/primal_bezier_intersect.cpp diff --git a/src/axom/primal/CMakeLists.txt b/src/axom/primal/CMakeLists.txt index 9ecc42267c..d5b42b0e2b 100644 --- a/src/axom/primal/CMakeLists.txt +++ b/src/axom/primal/CMakeLists.txt @@ -38,6 +38,7 @@ set( primal_headers operators/squared_distance.hpp operators/compute_bounding_box.hpp operators/in_sphere.hpp + operators/intersect_bezier.hpp operators/detail/clip_impl.hpp operators/detail/intersect_impl.hpp diff --git a/src/axom/primal/geometry/BezierCurve.hpp b/src/axom/primal/geometry/BezierCurve.hpp index 669235fb22..08a7fa9c2f 100644 --- a/src/axom/primal/geometry/BezierCurve.hpp +++ b/src/axom/primal/geometry/BezierCurve.hpp @@ -11,6 +11,9 @@ #include "axom/primal/geometry/Point.hpp" #include "axom/primal/geometry/Vector.hpp" #include "axom/primal/geometry/NumericArray.hpp" +#include "axom/primal/geometry/Segment.hpp" + +#include "axom/primal/operators/squared_distance.hpp" #include "fmt/fmt.hpp" #include @@ -47,7 +50,7 @@ class BezierCurve typedef Point< T,NDIMS > PointType; typedef Vector< T,NDIMS > VectorType; typedef NumericArray< T,NDIMS > NumArrayType; - + typedef Segment< T, NDIMS > SegmentType; private: typedef std::vector< PointType > CoordsVec; @@ -212,7 +215,7 @@ class BezierCurve * */ - void split_bezier(T t, BezierCurve< T, NDIMS >& c1, BezierCurve< T, NDIMS>& c2) + void split_bezier(T t, BezierCurve< T, NDIMS >& c1, BezierCurve< T, NDIMS>& c2) const { int ord = m_controlpoints.size()-1; T* dCarray = new T[NDIMS*2*(ord+1)]; @@ -238,12 +241,32 @@ class BezierCurve return; } + /*! + * \brief + * + * \param [in] tol a tolerance parameter controlling definition of near-linearity + * \param [out] boolean TRUE if c1 is near-linear + */ + + bool is_linear(double tol) const + { + int ord1 = m_controlpoints.size()-1; + SegmentType linear1(m_controlpoints[0],m_controlpoints[ord1]); + double d1=0.0; + for (int i=1; i +bool intersect_bezier( const BezierCurve< T, NDIMS>& c1, + const BezierCurve< T, NDIMS>& c2, + std::vector< T >& sp, + std::vector< T >& tp) + { + int ord1=c1.getOrder(); + int ord2=c2.getOrder(); + if ( c1.is_linear(1e-31) && c2.is_linear(1e-31)) + { + T s; + T t; + intersect_2d_linear(c1[0],c1[ord1],c2[0],c2[ord2],s,t); + if (s>=0.0 && s<1.0 && t >=0.0 && t<1.0) + { + sp.push_back(s); + tp.push_back(t); + } + } + else + { + BezierCurve< T, NDIMS> c3(ord1); BezierCurve< T, NDIMS> c4(ord1); + c1.split_bezier(.5,c3,c4); + + std::vector> Ptv3 = c3.getControlPoints(); + BoundingBox < T, NDIMS> b3(Ptv3.data(),ord1+1); + + std::vector> Ptv4 = c4.getControlPoints(); + BoundingBox < T, NDIMS> b4(Ptv4.data(),ord1+1); + + std::vector> Ptv2 = c2.getControlPoints(); + BoundingBox < T, NDIMS> b2(Ptv2.data(),ord1+1); + bool checkint = false; + int intcount = sp.size(); + if (intersect(b3,b2)) + { + intcount=sp.size(); + checkint=intersect_bezier(c2,c3,tp,sp); + if (checkint) + { + for (int i=intcount; i(sp.size()); i++) + { + sp[i] = 0.5*sp[i]; + } + } + } + if (intersect(b4,b2)) + { + intcount=sp.size(); + checkint = intersect_bezier(c2,c4,tp,sp); + if (checkint) + { + for (int i=intcount; i(sp.size()); i++) + { + sp[i] = .5+0.5*sp[i]; + } + } + } + } + + if (sp.size()>0) + { + return true; + } + else + { + return false; + } + } + +/*! + * \brief Intersects two segments defined by their end points (a,d) and (c,b) + * + * \param [in] a,d,c,b the endpoints of the segments + * \param [out] The parametrized s and t values at which intersection occurs (note these could be outside [0,1] + */ + +template < typename T, int NDIMS> +void intersect_2d_linear( const Point &a, const Point &d, const Point &c, const Point &b, T &s, T &t) +{ + T determ = a[1]*b[0]-a[0]*b[1]-a[1]*c[0]+a[0]*c[1]+b[1]*d[0]-c[1]*d[0]-b[0]*d[1]+c[0]*d[1]; + s = (1.0/determ)*((b[1]-c[1])*(b[0]-a[0])+(c[0]-b[0])*(b[1]-a[1])); + t = 1.0-(1.0/determ)*((a[1]-d[1])*(b[0]-a[0])+(d[0]-a[0])*(b[1]-a[1])); +} +} // namespace primal +} // namespace axom + +#endif // PRIMAL_INTERSECTION_BEZIER_HPP_ diff --git a/src/axom/primal/tests/CMakeLists.txt b/src/axom/primal/tests/CMakeLists.txt index a9153e1199..1dfce12cb0 100644 --- a/src/axom/primal/tests/CMakeLists.txt +++ b/src/axom/primal/tests/CMakeLists.txt @@ -21,7 +21,8 @@ set( primal_tests primal_triangle.cpp primal_tetrahedron.cpp primal_vector.cpp - primal_bezier.cpp + primal_bezier_curve.cpp + primal_bezier_intersect.cpp ) set(primal_test_depends fmt primal slic mint gtest) diff --git a/src/axom/primal/tests/primal_bezier.cpp b/src/axom/primal/tests/primal_bezier_curve.cpp similarity index 100% rename from src/axom/primal/tests/primal_bezier.cpp rename to src/axom/primal/tests/primal_bezier_curve.cpp diff --git a/src/axom/primal/tests/primal_bezier_intersect.cpp b/src/axom/primal/tests/primal_bezier_intersect.cpp new file mode 100644 index 0000000000..d291c08d9b --- /dev/null +++ b/src/axom/primal/tests/primal_bezier_intersect.cpp @@ -0,0 +1,72 @@ +// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +/* /file bezier_test.cpp + * /brief This file tests the BezierCurve.hpp and eval_bezier.hpp files +*/ + +#include "gtest/gtest.h" + +#include "axom/primal/geometry/BezierCurve.hpp" +#include "axom/primal/operators/intersect_bezier.hpp" + +// _using_start +using namespace axom; +using namespace primal; +// _using_end +//---------------------------------------------------------------------------------- +TEST( primal_bezier_inter, bezier_inters ) +{ + static const int DIM =2; + typedef double CoordType; + typedef primal::Point< CoordType, DIM > PointType; + typedef primal::BezierCurve< CoordType, DIM > BezierCurveType; + SLIC_INFO("\nprimal: testing bezier intersection"); + + const int nbr_points = 4; + PointType data[nbr_points]; + data[0] = PointType::make_point(0.0, 0.0); + data[1] = PointType::make_point(1.0, 0.0); + data[2] = PointType::make_point(2.0, 0.0); + data[3] = PointType::make_point(3.0, 0.0); + BezierCurveType b2Curve(data, nbr_points-1); + + data[0] = PointType::make_point(0.0, 0.5); + data[1] = PointType::make_point(1.0,-1.0); + data[2] = PointType::make_point(2.0, 1.0); + data[3] = PointType::make_point(3.0,-0.5); + BezierCurveType b5Curve(data, nbr_points-1); + + CoordType actualintersects[3]; + actualintersects[0] = 0.17267316464601146; + actualintersects[1] = 0.5; + actualintersects[2] = 0.827326835353989; + + std::vector t1; + std::vector s1; + intersect_bezier(b2Curve, b5Curve, s1, t1); + for (int i=0; i(s1.size()); i++) + { + EXPECT_DOUBLE_EQ(s1[i], actualintersects[i]); + EXPECT_DOUBLE_EQ(t1[i], actualintersects[i]); + } +} +int main(int argc, char* argv[]) + { + int result = 0; + + ::testing::InitGoogleTest(&argc, argv); + + axom::slic::UnitTestLogger logger; // create & initialize test logger, + + // finalized when exiting main scope + + result = RUN_ALL_TESTS(); + + + return result; + } + + From 89153cef8cc7287630f1e32388b01452b6e0804f Mon Sep 17 00:00:00 2001 From: uncrustify-robot Date: Mon, 8 Jul 2019 18:14:45 -0700 Subject: [PATCH 11/44] Uncrustified primal --- src/axom/primal/geometry/BezierCurve.hpp | 157 ++++++++++-------- .../primal/operators/intersect_bezier.hpp | 48 +++--- src/axom/primal/tests/primal_bezier_curve.cpp | 121 ++++++++------ .../primal/tests/primal_bezier_intersect.cpp | 35 ++-- 4 files changed, 192 insertions(+), 169 deletions(-) diff --git a/src/axom/primal/geometry/BezierCurve.hpp b/src/axom/primal/geometry/BezierCurve.hpp index 08a7fa9c2f..3c73047f50 100644 --- a/src/axom/primal/geometry/BezierCurve.hpp +++ b/src/axom/primal/geometry/BezierCurve.hpp @@ -24,7 +24,7 @@ namespace axom { namespace primal -{ +{ // Forward declare the templated classes and operator functions template < typename T, int NDIMS > @@ -32,10 +32,11 @@ class BezierCurve; /*! \brief Overloaded output operator for Bezier Curves*/ template < typename T,int NDIMS > -std::ostream& operator<<(std::ostream & os, const BezierCurve< T,NDIMS > & bCurve); +std::ostream& operator<<(std::ostream & os, + const BezierCurve< T,NDIMS > & bCurve); /*! - * \class BezierCurve + * \class BezierCurve * * \brief Represents a Bezier Curve defined by an array of points * \tparam T the coordinate type, e.g., double, float, etc. @@ -72,13 +73,15 @@ class BezierCurve m_controlpoints.reserve(ord+1); m_controlpoints.resize(ord+1); } - + /*! - * \brief Constructor for a Bezier Curve from a list of Points - * \verbatim {x_0, x_1, x_2, x_3, y_0, y_1, y_2, y_3, z_0, z_1, z_2, z_3} + * \brief Constructor for a Bezier Curve from a list of Points + * \verbatim {x_0, x_1, x_2, x_3, y_0, y_1, y_2, y_3, z_0, z_1, z_2, z_3} * - * \param [in] pts an array with (n+1)*NDIMS entries, ordered by coordinate then by control point order - * \param [in] ord number of control points minus 1 for which to reserve control point space + * \param [in] pts an array with (n+1)*NDIMS entries, ordered by coordinate + * then by control point order + * \param [in] ord number of control points minus 1 for which to reserve + * control point space * \pre order is not negative * */ @@ -93,7 +96,7 @@ class BezierCurve SLIC_ASSERT(pts != nullptr); m_controlpoints.reserve(ord+1); - + T tempar[NDIMS]; for ( int i = 0 ; i <= ord ; i++) { @@ -106,10 +109,11 @@ class BezierCurve } /*! - * \brief Constructor for a Bezier Curve from an array of coordinates + * \brief Constructor for a Bezier Curve from an array of coordinates * * \param [in] pts a vector with ord+1 points in it - * \param [in] ord number of control points minus 1 for which to reserve control point space + * \param [in] ord number of control points minus 1 for which to reserve + * control point space * \pre order is not negative * */ @@ -155,96 +159,104 @@ class BezierCurve PointType& operator[](int idx) { return m_controlpoints[idx]; } /*! Retrieves the control point at index idx */ const PointType& operator[](int idx) const { return m_controlpoints[idx]; } - + /* Checks equality of two Bezier Curve */ - friend inline bool operator==(const BezierCurve< T, NDIMS>& lhs, const BezierCurve< T, NDIMS>& rhs) + friend inline bool operator==(const BezierCurve< T, NDIMS>& lhs, + const BezierCurve< T, NDIMS>& rhs) { return lhs.m_controlpoints == rhs.m_controlpoints; } - friend inline bool operator!=(const BezierCurve< T, NDIMS>& lhs, const BezierCurve< T, NDIMS>& rhs) + friend inline bool operator!=(const BezierCurve< T, NDIMS>& lhs, + const BezierCurve< T, NDIMS>& rhs) { return !(lhs == rhs); } std::vector< Point< T, NDIMS > > getControlPoints() const { - return m_controlpoints; + return m_controlpoints; } /*! - * \brief Evaluates a Bezier Curve at particular parameter value between 0 and 1 - * - * \param [in] t parameter value between 0 and 1 at which to evaluate - * \return p the value of the Bezier Curve at t - * - */ + * \brief Evaluates a Bezier Curve at a particular parameter value \a t + * + * \param [in] t parameter value at which to evaluate + * \return p the value of the Bezier Curve at t + * + * \note We typically evaluate the curve at \a t between 0 and 1 + */ Point< T, NDIMS > eval_bezier(T t) { Point< T, NDIMS > ptval(0.0,NDIMS); int ord = m_controlpoints.size()-1; T dCarray[ord+1]; - - for ( int i=0; i < NDIMS; i++) + + for ( int i=0 ; i < NDIMS ; i++) { - for ( int j=0 ; j <= ord ; j++) - { - dCarray[j] = m_controlpoints[j][i]; - } - for ( int j=1 ; j <= ord ; j++) - { - for ( int k=0 ; k <= ord-j ; k++) - { - dCarray[k]=(1-t)*dCarray[k]+t*dCarray[k+1]; - } - } - ptval[i]=dCarray[0]; + for ( int j=0 ; j <= ord ; j++) + { + dCarray[j] = m_controlpoints[j][i]; + } + for ( int j=1 ; j <= ord ; j++) + { + for ( int k=0 ; k <= ord-j ; k++) + { + dCarray[k]=(1-t)*dCarray[k]+t*dCarray[k+1]; + } + } + ptval[i]=dCarray[0]; } - + return ptval; } - + /*! - * \brief Splits a Bezier Curve into two Bezier Curves at particular parameter value between 0 and 1 - * - * \param [in] t parameter value between 0 and 1 at which to evaluate - * \param [in] c1, c2 two BezierCurves objects to store output in - * \param [out] c1, c2 two new Bezier Curves that split the original - * \return p the value of the Bezier Curve at t - * - */ + * \brief Splits a Bezier Curve into two Bezier Curves at particular parameter + * value between 0 and 1 + * + * \param [in] t parameter value between 0 and 1 at which to evaluate + * \param [in] c1, c2 two BezierCurves objects to store output in + * \param [out] c1, c2 two new Bezier Curves that split the original + * \return p the value of the Bezier Curve at t + * + */ - void split_bezier(T t, BezierCurve< T, NDIMS >& c1, BezierCurve< T, NDIMS>& c2) const - { + void split_bezier(T t, + BezierCurve< T, NDIMS >& c1, + BezierCurve< T, NDIMS>& c2) const + { int ord = m_controlpoints.size()-1; T* dCarray = new T[NDIMS*2*(ord+1)]; - for ( int i=0; i < NDIMS; i++) + for ( int i=0 ; i < NDIMS ; i++) { - for ( int j=0 ; j <= ord ; j++) - { - dCarray[i*((ord+1))+j] = m_controlpoints[j][i]; - } - dCarray[NDIMS*(ord+1)+(i)*((ord+1))]= dCarray[i*((ord+1))]; - for ( int j=1 ; j <= ord ; j++) - { - for ( int k=0 ; k <= ord-j ; k++) - { - dCarray[i*((ord+1))+k]=(1-t)*dCarray[i*((ord+1))+k]+t*dCarray[i*((ord+1))+k+1]; - } - dCarray[NDIMS*(ord+1)+(i)*((ord+1))+j]=dCarray[i*((ord+1))]; - } + for ( int j=0 ; j <= ord ; j++) + { + dCarray[i*((ord+1))+j] = m_controlpoints[j][i]; + } + dCarray[NDIMS*(ord+1)+(i)*((ord+1))]= dCarray[i*((ord+1))]; + for ( int j=1 ; j <= ord ; j++) + { + for ( int k=0 ; k <= ord-j ; k++) + { + dCarray[i*((ord+1))+k]=(1-t)*dCarray[i*((ord+1))+k] + + t*dCarray[i*((ord+1))+k+1]; + } + dCarray[NDIMS*(ord+1)+(i)*((ord+1))+j]=dCarray[i*((ord+1))]; + } } - c2=BezierCurve< T, NDIMS> (dCarray,ord); - c1=BezierCurve< T, NDIMS> (dCarray+NDIMS*(ord+1),ord); - delete [] dCarray; - return; + c2=BezierCurve< T, NDIMS> (dCarray,ord); + c1=BezierCurve< T, NDIMS> (dCarray+NDIMS*(ord+1),ord); + delete [] dCarray; + return; } /*! * \brief * - * \param [in] tol a tolerance parameter controlling definition of near-linearity + * \param [in] tol a tolerance parameter controlling definition of + * near-linearity * \param [out] boolean TRUE if c1 is near-linear */ @@ -253,11 +265,11 @@ class BezierCurve int ord1 = m_controlpoints.size()-1; SegmentType linear1(m_controlpoints[0],m_controlpoints[ord1]); double d1=0.0; - for (int i=1; i -std::ostream& operator<<(std::ostream & os, const BezierCurve< T,NDIMS > & bCurve) +std::ostream& operator<<(std::ostream & os, + const BezierCurve< T,NDIMS > & bCurve) { bCurve.print(os); return os; diff --git a/src/axom/primal/operators/intersect_bezier.hpp b/src/axom/primal/operators/intersect_bezier.hpp index 34ecd6323a..c77ba29ee6 100644 --- a/src/axom/primal/operators/intersect_bezier.hpp +++ b/src/axom/primal/operators/intersect_bezier.hpp @@ -36,17 +36,17 @@ namespace primal * \return status true iff c1 intersects with c2, otherwise false. * * \param c1, c2 BezierCurve objects to intersect - * \param sp, tp vector of type T parameter space intersection points (t-values and s-values) - * for c1 and c2, respectively + * \param sp, tp vector of type T parameter space intersection points (t-values + * and s-values) for c1 and c2, respectively */ template < typename T, int NDIMS> bool intersect_bezier( const BezierCurve< T, NDIMS>& c1, - const BezierCurve< T, NDIMS>& c2, - std::vector< T >& sp, - std::vector< T >& tp) - { - int ord1=c1.getOrder(); - int ord2=c2.getOrder(); + const BezierCurve< T, NDIMS>& c2, + std::vector< T >& sp, + std::vector< T >& tp) +{ + int ord1=c1.getOrder(); + int ord2=c2.getOrder(); if ( c1.is_linear(1e-31) && c2.is_linear(1e-31)) { T s; @@ -62,14 +62,14 @@ bool intersect_bezier( const BezierCurve< T, NDIMS>& c1, { BezierCurve< T, NDIMS> c3(ord1); BezierCurve< T, NDIMS> c4(ord1); c1.split_bezier(.5,c3,c4); - - std::vector> Ptv3 = c3.getControlPoints(); + + std::vector > Ptv3 = c3.getControlPoints(); BoundingBox < T, NDIMS> b3(Ptv3.data(),ord1+1); - - std::vector> Ptv4 = c4.getControlPoints(); + + std::vector > Ptv4 = c4.getControlPoints(); BoundingBox < T, NDIMS> b4(Ptv4.data(),ord1+1); - - std::vector> Ptv2 = c2.getControlPoints(); + + std::vector > Ptv2 = c2.getControlPoints(); BoundingBox < T, NDIMS> b2(Ptv2.data(),ord1+1); bool checkint = false; int intcount = sp.size(); @@ -79,9 +79,9 @@ bool intersect_bezier( const BezierCurve< T, NDIMS>& c1, checkint=intersect_bezier(c2,c3,tp,sp); if (checkint) { - for (int i=intcount; i(sp.size()); i++) + for (int i=intcount ; i(sp.size()) ; i++) { - sp[i] = 0.5*sp[i]; + sp[i] = 0.5*sp[i]; } } } @@ -91,7 +91,7 @@ bool intersect_bezier( const BezierCurve< T, NDIMS>& c1, checkint = intersect_bezier(c2,c4,tp,sp); if (checkint) { - for (int i=intcount; i(sp.size()); i++) + for (int i=intcount ; i(sp.size()) ; i++) { sp[i] = .5+0.5*sp[i]; } @@ -107,19 +107,23 @@ bool intersect_bezier( const BezierCurve< T, NDIMS>& c1, { return false; } - } +} /*! * \brief Intersects two segments defined by their end points (a,d) and (c,b) - * + * * \param [in] a,d,c,b the endpoints of the segments - * \param [out] The parametrized s and t values at which intersection occurs (note these could be outside [0,1] + * \param [out] The parametrized s and t values at which intersection occurs + * (note these could be outside [0,1] */ template < typename T, int NDIMS> -void intersect_2d_linear( const Point &a, const Point &d, const Point &c, const Point &b, T &s, T &t) +void intersect_2d_linear( const Point &a, const Point &d, + const Point &c, const Point &b, + T &s, T &t) { - T determ = a[1]*b[0]-a[0]*b[1]-a[1]*c[0]+a[0]*c[1]+b[1]*d[0]-c[1]*d[0]-b[0]*d[1]+c[0]*d[1]; + T determ = a[1]*b[0]-a[0]*b[1] -a[1]*c[0]+a[0]*c[1] + +b[1]*d[0]-c[1]*d[0] -b[0]*d[1]+c[0]*d[1]; s = (1.0/determ)*((b[1]-c[1])*(b[0]-a[0])+(c[0]-b[0])*(b[1]-a[1])); t = 1.0-(1.0/determ)*((a[1]-d[1])*(b[0]-a[0])+(d[0]-a[0])*(b[1]-a[1])); } diff --git a/src/axom/primal/tests/primal_bezier_curve.cpp b/src/axom/primal/tests/primal_bezier_curve.cpp index 36712762e9..b5a0cd888a 100644 --- a/src/axom/primal/tests/primal_bezier_curve.cpp +++ b/src/axom/primal/tests/primal_bezier_curve.cpp @@ -5,7 +5,7 @@ /* /file bezier_test.cpp * /brief This file tests the BezierCurve.hpp and eval_bezier.hpp files -*/ + */ #include "gtest/gtest.h" @@ -21,10 +21,11 @@ TEST( primal_beziercurve, bezier_constructor ) static const int DIM = 3; typedef double CoordType; typedef primal::BezierCurve< CoordType, DIM > BezierCurveType; - SLIC_INFO("\nprimal: testing default bezier constructor ") ; + SLIC_INFO("primal: testing default bezier constructor "); BezierCurveType bCurve; - EXPECT_EQ(bCurve.getControlPoints() ,(std::vector< Point< CoordType, DIM > >())); + EXPECT_EQ(bCurve.getControlPoints(), + (std::vector< Point< CoordType, DIM > >())); } //---------------------------------------------------------------------------------- @@ -33,10 +34,10 @@ TEST( primal_beziercurve, bezier_order_constructor ) static const int DIM = 3; typedef double CoordType; typedef primal::BezierCurve< CoordType, DIM > BezierCurveType; - SLIC_INFO("\nprimal: testing bezier order constructor ") ; + SLIC_INFO("primal: testing bezier order constructor "); BezierCurveType bCurve(1); - + EXPECT_EQ(bCurve.getOrder(), 1); } @@ -47,20 +48,23 @@ TEST( primal_beziercurve, bezier_add_controlpoints ) typedef double CoordType; typedef primal::Point< CoordType, DIM > PointType; typedef primal::BezierCurve< CoordType, DIM > BezierCurveType; - SLIC_INFO("\nprimal: testing adding control points to empty beziercurve"); + SLIC_INFO("primal: testing adding control points to empty beziercurve"); BezierCurveType bCurve; - + CoordType coords[6] = {.6,1.2,1.0,0.0,1.6,1.8}; - bCurve.addControlpoint( PointType::make_point( coords[0], coords[1], coords[2] ) ); - bCurve.addControlpoint( PointType::make_point( coords[3], coords[4], coords[5] ) ); - + bCurve.addControlpoint( + PointType::make_point( coords[0], coords[1], coords[2] ) ); + bCurve.addControlpoint( + PointType::make_point( coords[3], coords[4], coords[5] ) ); + EXPECT_EQ(bCurve.getOrder(), 1); - for (int i=0; i PointType; typedef primal::BezierCurve< CoordType, DIM > BezierCurveType; SLIC_INFO("\nprimal: testing point array constructor"); - + CoordType coords[6] = {.6,1.2,1.0,0.0,1.6,1.8}; - PointType pts[2] = {PointType::make_point(coords[0], coords[1], coords[2]),PointType::make_point(coords[3], coords[4], coords[5])}; + PointType pts[2] = { + PointType::make_point(coords[0], coords[1], coords[2]), + PointType::make_point(coords[3], coords[4], coords[5]) + }; BezierCurveType bCurve(pts,1); EXPECT_EQ(bCurve.getOrder(), 1); - for (int i=0; i BezierCurveType; SLIC_INFO("\nprimal: testing coordinate array constructor"); - + CoordType coords[6] = {.6,1.2,1.0,0.0,1.6,1.8}; - CoordType coordsToPass[6] = {coords[0], coords[3], coords[1],coords[4], coords[2], coords[5]}; + CoordType coordsToPass[6] = {coords[0], coords[3], coords[1], + coords[4], coords[2], coords[5]}; BezierCurveType bCurve(coordsToPass,1); EXPECT_EQ(bCurve.getOrder(), 1); - for (int i=0; i PointType; typedef primal::BezierCurve< CoordType, DIM > BezierCurveType; SLIC_INFO("\nprimal: testing bezier evaluation"); - + const int nbr_points = 4; PointType data[nbr_points]; data[0] = PointType::make_point(0.6, 1.2, 1.0); @@ -126,13 +136,16 @@ TEST( primal_beziercurve, bezier_evaluation ) data[2] = PointType::make_point(2.9, 2.4, 2.3); data[3] = PointType::make_point(3.2, 3.5, 3.0); BezierCurveType b2Curve(data, nbr_points-1); - - PointType midtval = PointType::make_point(2.05,2.0875,2.0375); - for ( int i=0; i PointType; typedef primal::BezierCurve< CoordType, DIM > BezierCurveType; SLIC_INFO("\nprimal: testing bezier splitting"); - + const int nbr_points = 4; PointType data[nbr_points]; data[0] = PointType::make_point(0.6, 1.2, 1.0); @@ -153,40 +166,38 @@ TEST( primal_beziercurve, bezier_split ) data[3] = PointType::make_point(3.2, 3.5, 3.0); BezierCurveType b2Curve(data, nbr_points-1); - BezierCurveType b3Curve(3);// Checks that split works with order constructor input - BezierCurveType b4Curve;// Checks that split works with default constructor input + BezierCurveType b3Curve(3); // Checks split_bezier with order constructor + BezierCurveType b4Curve; // Checks split_bezier with default constructor b2Curve.split_bezier(.5,b3Curve,b4Curve); - - CoordType b3Coords[12] = {.6, .95, 1.525, 2.05, 1.2, 1.4, 1.7, 2.0875, 1.0, 1.4, 1.725, 2.0375}; - CoordType b4Coords[12] = {2.05, 2.575, 3.05, 3.2, 2.0875, 2.475, 2.95, 3.5, 2.0375, 2.35, 2.65, 3.0}; + + CoordType b3Coords[12] = {.6, .95, 1.525, 2.05, + 1.2, 1.4, 1.7, 2.0875, + 1.0, 1.4, 1.725, 2.0375}; + CoordType b4Coords[12] = {2.05, 2.575, 3.05, 3.2, + 2.0875, 2.475, 2.95, 3.5, + 2.0375, 2.35, 2.65, 3.0}; BezierCurveType b3True(b3Coords,3); BezierCurveType b4True(b4Coords,3); - for ( int i=0; i PointType; typedef primal::BezierCurve< CoordType, DIM > BezierCurveType; SLIC_INFO("\nprimal: testing bezier intersection"); - + const int nbr_points = 4; PointType data[nbr_points]; data[0] = PointType::make_point(0.0, 0.0); @@ -32,13 +32,13 @@ TEST( primal_bezier_inter, bezier_inters ) data[2] = PointType::make_point(2.0, 0.0); data[3] = PointType::make_point(3.0, 0.0); BezierCurveType b2Curve(data, nbr_points-1); - + data[0] = PointType::make_point(0.0, 0.5); data[1] = PointType::make_point(1.0,-1.0); data[2] = PointType::make_point(2.0, 1.0); data[3] = PointType::make_point(3.0,-0.5); BezierCurveType b5Curve(data, nbr_points-1); - + CoordType actualintersects[3]; actualintersects[0] = 0.17267316464601146; actualintersects[1] = 0.5; @@ -47,26 +47,21 @@ TEST( primal_bezier_inter, bezier_inters ) std::vector t1; std::vector s1; intersect_bezier(b2Curve, b5Curve, s1, t1); - for (int i=0; i(s1.size()); i++) + for (int i=0 ; i(s1.size()) ; i++) { - EXPECT_DOUBLE_EQ(s1[i], actualintersects[i]); - EXPECT_DOUBLE_EQ(t1[i], actualintersects[i]); + EXPECT_DOUBLE_EQ(s1[i], actualintersects[i]); + EXPECT_DOUBLE_EQ(t1[i], actualintersects[i]); } } int main(int argc, char* argv[]) - { - int result = 0; - - ::testing::InitGoogleTest(&argc, argv); - - axom::slic::UnitTestLogger logger; // create & initialize test logger, - - // finalized when exiting main scope - - result = RUN_ALL_TESTS(); - +{ + int result = 0; + + ::testing::InitGoogleTest(&argc, argv); - return result; - } + axom::slic::UnitTestLogger logger; // create & initialize test logger, + result = RUN_ALL_TESTS(); + return result; +} From 4ccaf52398a653a31de9833155faee391b9fddf1 Mon Sep 17 00:00:00 2001 From: Kenneth Weiss Date: Mon, 8 Jul 2019 21:20:50 -0700 Subject: [PATCH 12/44] Some refactoring and tests for primal's BezierCurve class --- src/axom/primal/geometry/BezierCurve.hpp | 160 ++++--- .../primal/operators/intersect_bezier.hpp | 2 +- src/axom/primal/tests/primal_bezier_curve.cpp | 398 ++++++++++++------ 3 files changed, 351 insertions(+), 209 deletions(-) diff --git a/src/axom/primal/geometry/BezierCurve.hpp b/src/axom/primal/geometry/BezierCurve.hpp index 3c73047f50..fe909751f2 100644 --- a/src/axom/primal/geometry/BezierCurve.hpp +++ b/src/axom/primal/geometry/BezierCurve.hpp @@ -1,25 +1,28 @@ +// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + /*! * \file BezierCurve.hpp * - * \brief A BezierCurve primitive for primal + * \brief A BezierCurve primitive */ #ifndef PRIMAL_BEZIERCURVE_HPP_ #define PRIMAL_BEZIERCURVE_HPP_ #include "axom/slic.hpp" + #include "axom/primal/geometry/Point.hpp" #include "axom/primal/geometry/Vector.hpp" #include "axom/primal/geometry/NumericArray.hpp" #include "axom/primal/geometry/Segment.hpp" - #include "axom/primal/operators/squared_distance.hpp" #include "fmt/fmt.hpp" #include -#include // for std::ostream -#include -#include +#include namespace axom { @@ -38,9 +41,11 @@ std::ostream& operator<<(std::ostream & os, /*! * \class BezierCurve * - * \brief Represents a Bezier Curve defined by an array of points + * \brief Represents a Bezier curve defined by an array of control points * \tparam T the coordinate type, e.g., double, float, etc. * \tparam NDIMS the number of dimensions + * + * \note The order of a Bezier curve with N+1 control points is N * \note The control points should be ordered from t=0 to t=1 */ @@ -48,16 +53,15 @@ template < typename T,int NDIMS > class BezierCurve { public: - typedef Point< T,NDIMS > PointType; - typedef Vector< T,NDIMS > VectorType; - typedef NumericArray< T,NDIMS > NumArrayType; - typedef Segment< T, NDIMS > SegmentType; -private: - typedef std::vector< PointType > CoordsVec; + using PointType = Point< T,NDIMS >; + using VectorType = Vector< T,NDIMS >; + using NumArrayType = NumericArray< T,NDIMS >; + using SegmentType = Segment< T, NDIMS >; + using CoordsVec = std::vector< PointType >; public: /*! Default constructor for an empty Bezier Curve*/ - BezierCurve() {} + BezierCurve() = default; /*! * \brief Constructor for an empty Bezier Curve that reserves space for @@ -65,7 +69,6 @@ class BezierCurve * * \param [in] order the order of the resulting Bezier curve * \pre order is not negative - * */ BezierCurve(int ord) { @@ -76,16 +79,16 @@ class BezierCurve /*! * \brief Constructor for a Bezier Curve from a list of Points - * \verbatim {x_0, x_1, x_2, x_3, y_0, y_1, y_2, y_3, z_0, z_1, z_2, z_3} + * \verbatim {x_0, x_1, x_2, x_3, + * y_0, y_1, y_2, y_3, + * z_0, z_1, z_2, z_3} * * \param [in] pts an array with (n+1)*NDIMS entries, ordered by coordinate * then by control point order * \param [in] ord number of control points minus 1 for which to reserve * control point space * \pre order is not negative - * */ - BezierCurve(T* pts, int ord) { if ( ord <= 0 ) @@ -98,13 +101,13 @@ class BezierCurve m_controlpoints.reserve(ord+1); T tempar[NDIMS]; - for ( int i = 0 ; i <= ord ; i++) + for ( int p = 0 ; p <= ord ; p++) { for ( int j = 0 ; j < NDIMS ; j++) { - tempar[j]=pts[j*(ord+1)+i]; + tempar[j]=pts[j*(ord+1)+p]; } - this->addControlpoint(tempar); + this->addControlPoint(tempar); } } @@ -129,9 +132,9 @@ class BezierCurve m_controlpoints.reserve(ord+1); - for (int i = 0 ; i <= ord ; i++) + for (int p = 0 ; p <= ord ; ++p) { - this->addControlpoint(pts[i]); + this->addControlPoint(pts[p]); } } @@ -144,7 +147,7 @@ class BezierCurve { return m_controlpoints.size()-1; } /*! Appends a control point to the list of control points*/ - void addControlpoint(const PointType& pt) + void addControlPoint(const PointType& pt) { m_controlpoints.push_back(pt); } @@ -155,9 +158,9 @@ class BezierCurve m_controlpoints.clear(); } - /*! Retrieves the control point at index idx */ + /*! Retrieves the control point at index \a idx */ PointType& operator[](int idx) { return m_controlpoints[idx]; } - /*! Retrieves the control point at index idx */ + /*! Retrieves the control point at index \a idx */ const PointType& operator[](int idx) const { return m_controlpoints[idx]; } /* Checks equality of two Bezier Curve */ @@ -173,37 +176,41 @@ class BezierCurve return !(lhs == rhs); } - std::vector< Point< T, NDIMS > > getControlPoints() const + CoordsVec getControlPoints() const { return m_controlpoints; } /*! - * \brief Evaluates a Bezier Curve at a particular parameter value \a t + * \brief Evaluates a Bezier curve at a particular parameter value \a t * * \param [in] t parameter value at which to evaluate - * \return p the value of the Bezier Curve at t + * \return p the value of the Bezier curve at t * * \note We typically evaluate the curve at \a t between 0 and 1 */ - Point< T, NDIMS > eval_bezier(T t) + PointType evaluate(T t) { - Point< T, NDIMS > ptval(0.0,NDIMS); - int ord = m_controlpoints.size()-1; - T dCarray[ord+1]; + PointType ptval; + + const int ord = getOrder(); + std::vector dCarray(ord+1); - for ( int i=0 ; i < NDIMS ; i++) + // Run de Casteljau algorithm on each dimension + for ( int i=0 ; i < NDIMS ; ++i) { - for ( int j=0 ; j <= ord ; j++) + for ( int p=0 ; p <= ord ; ++p) { - dCarray[j] = m_controlpoints[j][i]; + dCarray[p] = m_controlpoints[p][i]; } - for ( int j=1 ; j <= ord ; j++) + + for ( int p=1 ; p <= ord ; ++p) { - for ( int k=0 ; k <= ord-j ; k++) + const int end = ord-p; + for ( int k=0 ; k <= end ; ++k) { - dCarray[k]=(1-t)*dCarray[k]+t*dCarray[k+1]; + dCarray[k]=(1-t)*dCarray[k] + t*dCarray[k+1]; } } ptval[i]=dCarray[0]; @@ -213,47 +220,46 @@ class BezierCurve } /*! - * \brief Splits a Bezier Curve into two Bezier Curves at particular parameter + * \brief Splits a Bezier curve into two Bezier curves at particular parameter * value between 0 and 1 * * \param [in] t parameter value between 0 and 1 at which to evaluate - * \param [in] c1, c2 two BezierCurves objects to store output in - * \param [out] c1, c2 two new Bezier Curves that split the original - * \return p the value of the Bezier Curve at t - * + * \param [out] c1, c2 Bezier curves that split the original */ - - void split_bezier(T t, - BezierCurve< T, NDIMS >& c1, - BezierCurve< T, NDIMS>& c2) const + void split(T t, BezierCurve& c1, BezierCurve& c2) const { - int ord = m_controlpoints.size()-1; - T* dCarray = new T[NDIMS*2*(ord+1)]; - for ( int i=0 ; i < NDIMS ; i++) + int ord = getOrder(); + SLIC_ASSERT(ord >= 0); + + // Note: the second curve's control points are computed inline + // as we find the first curve's control points + c2 = *this; + + c1.setOrder(ord); + c1[0] = c2[0]; + + // Run de Casteljau algorithm + // After each iteration, save the first control point into c1 + for ( int p=1 ; p <= ord ; ++p) { - for ( int j=0 ; j <= ord ; j++) - { - dCarray[i*((ord+1))+j] = m_controlpoints[j][i]; - } - dCarray[NDIMS*(ord+1)+(i)*((ord+1))]= dCarray[i*((ord+1))]; - for ( int j=1 ; j <= ord ; j++) + const int end = ord-p; + for(int k=0 ; k<= end ; ++k) { - for ( int k=0 ; k <= ord-j ; k++) + PointType& pt1 = c2[k]; + const PointType& pt2 = c2[k+1]; + for(int i=0 ; i< NDIMS ; ++i) { - dCarray[i*((ord+1))+k]=(1-t)*dCarray[i*((ord+1))+k] - + t*dCarray[i*((ord+1))+k+1]; + pt1[i] = (1-t)*pt1[i] + t*pt2[i]; } - dCarray[NDIMS*(ord+1)+(i)*((ord+1))+j]=dCarray[i*((ord+1))]; } + c1[p] = c2[0]; } - c2=BezierCurve< T, NDIMS> (dCarray,ord); - c1=BezierCurve< T, NDIMS> (dCarray+NDIMS*(ord+1),ord); - delete [] dCarray; + return; } /*! - * \brief + * \brief Predicate to check if the Bezier curve is approximately linear * * \param [in] tol a tolerance parameter controlling definition of * near-linearity @@ -265,9 +271,9 @@ class BezierCurve int ord1 = m_controlpoints.size()-1; SegmentType linear1(m_controlpoints[0],m_controlpoints[ord1]); double d1=0.0; - for (int i=1 ; i= 2; - } private: CoordsVec m_controlpoints; }; // class BezierCurve @@ -317,7 +313,7 @@ std::ostream& operator<<(std::ostream & os, { bCurve.print(os); return os; -} // std::ostream& operator <<... +} } // namespace primal } // namespace axom diff --git a/src/axom/primal/operators/intersect_bezier.hpp b/src/axom/primal/operators/intersect_bezier.hpp index c77ba29ee6..d03c309816 100644 --- a/src/axom/primal/operators/intersect_bezier.hpp +++ b/src/axom/primal/operators/intersect_bezier.hpp @@ -61,7 +61,7 @@ bool intersect_bezier( const BezierCurve< T, NDIMS>& c1, else { BezierCurve< T, NDIMS> c3(ord1); BezierCurve< T, NDIMS> c4(ord1); - c1.split_bezier(.5,c3,c4); + c1.split(.5,c3,c4); std::vector > Ptv3 = c3.getControlPoints(); BoundingBox < T, NDIMS> b3(Ptv3.data(),ord1+1); diff --git a/src/axom/primal/tests/primal_bezier_curve.cpp b/src/axom/primal/tests/primal_bezier_curve.cpp index b5a0cd888a..13d6333bd3 100644 --- a/src/axom/primal/tests/primal_bezier_curve.cpp +++ b/src/axom/primal/tests/primal_bezier_curve.cpp @@ -11,184 +11,330 @@ #include "axom/primal/geometry/BezierCurve.hpp" -// _using_start -using namespace axom; -using namespace primal; -// _using_end -//---------------------------------------------------------------------------------- -TEST( primal_beziercurve, bezier_constructor ) +namespace primal = axom::primal; + +//------------------------------------------------------------------------------ +TEST( primal_beziercurve, constructor ) { - static const int DIM = 3; - typedef double CoordType; - typedef primal::BezierCurve< CoordType, DIM > BezierCurveType; - SLIC_INFO("primal: testing default bezier constructor "); - BezierCurveType bCurve; + const int DIM = 3; + using CoordType = double; + using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + using CoordsVec = BezierCurveType::CoordsVec; - EXPECT_EQ(bCurve.getControlPoints(), - (std::vector< Point< CoordType, DIM > >())); -} + { + SLIC_INFO("Testing default BezierCurve constructor "); + BezierCurveType bCurve; -//---------------------------------------------------------------------------------- -TEST( primal_beziercurve, bezier_order_constructor ) -{ - static const int DIM = 3; - typedef double CoordType; - typedef primal::BezierCurve< CoordType, DIM > BezierCurveType; - SLIC_INFO("primal: testing bezier order constructor "); + int expOrder = -1; + EXPECT_EQ(expOrder, bCurve.getOrder()); + EXPECT_EQ(expOrder+1, bCurve.getControlPoints().size()); + EXPECT_EQ(CoordsVec(), bCurve.getControlPoints()); + } - BezierCurveType bCurve(1); + { + SLIC_INFO("Testing BezierCurve order constructor "); - EXPECT_EQ(bCurve.getOrder(), 1); + BezierCurveType bCurve(1); + int expOrder = 1; + EXPECT_EQ(expOrder, bCurve.getOrder()); + EXPECT_EQ(expOrder+1, static_cast(bCurve.getControlPoints().size())); + } } //---------------------------------------------------------------------------------- -TEST( primal_beziercurve, bezier_add_controlpoints ) +TEST( primal_beziercurve, add_controlpoints ) { - static const int DIM =3; - typedef double CoordType; - typedef primal::Point< CoordType, DIM > PointType; - typedef primal::BezierCurve< CoordType, DIM > BezierCurveType; - SLIC_INFO("primal: testing adding control points to empty beziercurve"); + const int DIM = 3; + using CoordType = double; + using PointType = primal::Point< CoordType, DIM >; + using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + + SLIC_INFO("Test adding control points to empty Bezier curve"); BezierCurveType bCurve; + EXPECT_EQ(-1, bCurve.getOrder()); - CoordType coords[6] = {.6,1.2,1.0,0.0,1.6,1.8}; - bCurve.addControlpoint( - PointType::make_point( coords[0], coords[1], coords[2] ) ); - bCurve.addControlpoint( - PointType::make_point( coords[3], coords[4], coords[5] ) ); + PointType controlPoints[2] = { + PointType::make_point(0.6, 1.2, 1.0), + PointType::make_point(0.0, 1.6, 1.8) + }; + + bCurve.addControlPoint(controlPoints[0]); + bCurve.addControlPoint(controlPoints[1]); - EXPECT_EQ(bCurve.getOrder(), 1); - for (int i=0 ; i PointType; - typedef primal::BezierCurve< CoordType, DIM > BezierCurveType; - SLIC_INFO("\nprimal: testing point array constructor"); - - CoordType coords[6] = {.6,1.2,1.0,0.0,1.6,1.8}; - PointType pts[2] = { - PointType::make_point(coords[0], coords[1], coords[2]), - PointType::make_point(coords[3], coords[4], coords[5]) + SLIC_INFO("Testing point array constructor"); + + const int DIM = 3; + using CoordType = double; + using PointType = primal::Point< CoordType, DIM >; + using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + + PointType controlPoints[2] = { + PointType::make_point(0.6, 1.2, 1.0), + PointType::make_point(0.0, 1.6, 1.8) }; - BezierCurveType bCurve(pts,1); - EXPECT_EQ(bCurve.getOrder(), 1); - for (int i=0 ; i BezierCurveType; - SLIC_INFO("\nprimal: testing coordinate array constructor"); - - CoordType coords[6] = {.6,1.2,1.0,0.0,1.6,1.8}; - CoordType coordsToPass[6] = {coords[0], coords[3], coords[1], - coords[4], coords[2], coords[5]}; - - BezierCurveType bCurve(coordsToPass,1); - EXPECT_EQ(bCurve.getOrder(), 1); - for (int i=0 ; i; + + // Note: Order of coordinates is by dimension + CoordType coords[6] = {0.6, 0.0, // x-coords for control points + 1.2, 1.6, // y-coords for control points + 1.0, 1.8}; // z-coords for control points + + BezierCurveType bCurve(coords,1); + EXPECT_EQ(1, bCurve.getOrder()); + + + EXPECT_DOUBLE_EQ(coords[0], bCurve[0][0]); + EXPECT_DOUBLE_EQ(coords[2], bCurve[0][1]); + EXPECT_DOUBLE_EQ(coords[4], bCurve[0][2]); + + EXPECT_DOUBLE_EQ(coords[1], bCurve[1][0]); + EXPECT_DOUBLE_EQ(coords[3], bCurve[1][1]); + EXPECT_DOUBLE_EQ(coords[5], bCurve[1][2]); } -//---------------------------------------------------------------------------------- -TEST( primal_beziercurve, bezier_evaluation ) +//------------------------------------------------------------------------------ +TEST( primal_beziercurve, evaluate) { - static const int DIM =3; - typedef double CoordType; - typedef primal::Point< CoordType, DIM > PointType; - typedef primal::BezierCurve< CoordType, DIM > BezierCurveType; - SLIC_INFO("\nprimal: testing bezier evaluation"); - - const int nbr_points = 4; - PointType data[nbr_points]; - data[0] = PointType::make_point(0.6, 1.2, 1.0); - data[1] = PointType::make_point(1.3, 1.6, 1.8); - data[2] = PointType::make_point(2.9, 2.4, 2.3); - data[3] = PointType::make_point(3.2, 3.5, 3.0); - BezierCurveType b2Curve(data, nbr_points-1); + SLIC_INFO("Testing Bezier evaluation"); + + const int DIM = 3; + using CoordType = double; + using PointType = primal::Point< CoordType, DIM >; + using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + + const int order = 3; + PointType data[order+1] = { PointType::make_point(0.6, 1.2, 1.0), + PointType::make_point(1.3, 1.6, 1.8), + PointType::make_point(2.9, 2.4, 2.3), + PointType::make_point(3.2, 3.5, 3.0) }; + + BezierCurveType b2Curve(data, order); PointType midtval = PointType::make_point(2.05,2.0875,2.0375); - for ( int i=0 ; i PointType; - typedef primal::BezierCurve< CoordType, DIM > BezierCurveType; - SLIC_INFO("\nprimal: testing bezier splitting"); - - const int nbr_points = 4; - PointType data[nbr_points]; - data[0] = PointType::make_point(0.6, 1.2, 1.0); - data[1] = PointType::make_point(1.3, 1.6, 1.8); - data[2] = PointType::make_point(2.9, 2.4, 2.3); - data[3] = PointType::make_point(3.2, 3.5, 3.0); - BezierCurveType b2Curve(data, nbr_points-1); - - BezierCurveType b3Curve(3); // Checks split_bezier with order constructor - BezierCurveType b4Curve; // Checks split_bezier with default constructor - b2Curve.split_bezier(.5,b3Curve,b4Curve); - - CoordType b3Coords[12] = {.6, .95, 1.525, 2.05, - 1.2, 1.4, 1.7, 2.0875, + SLIC_INFO("Testing Bezier splitting of a cubic"); + + const int DIM = 3; + using CoordType = double; + using PointType = primal::Point< CoordType, DIM >; + using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + + const int order = 3; + PointType data[order+1] = { PointType::make_point(0.6, 1.2, 1.0), + PointType::make_point(1.3, 1.6, 1.8), + PointType::make_point(2.9, 2.4, 2.3), + PointType::make_point(3.2, 3.5, 3.0) }; + BezierCurveType b2Curve(data, order); + + BezierCurveType b3Curve(order); // Checks split with order constructor + BezierCurveType b4Curve; // Checks split with default constructor + b2Curve.split(.5, b3Curve, b4Curve); + + CoordType b3Coords[12] = {0.6, .95, 1.525, 2.05, + 1.2, 1.4, 1.7, 2.0875, 1.0, 1.4, 1.725, 2.0375}; - CoordType b4Coords[12] = {2.05, 2.575, 3.05, 3.2, + CoordType b4Coords[12] = {2.05, 2.575, 3.05, 3.2, 2.0875, 2.475, 2.95, 3.5, - 2.0375, 2.35, 2.65, 3.0}; + 2.0375, 2.35, 2.65, 3.0}; BezierCurveType b3True(b3Coords,3); BezierCurveType b4True(b4Coords,3); - for ( int i=0 ; i; + using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + + // Test order-0 { - for ( int j=0 ; j<(nbr_points-1) ; j++) + const int order = 0; + PointType data[order+1] = { PointType::make_point(0.6, 1.2)}; + BezierCurveType b(data, order); + + BezierCurveType c1,c2; + b.split(0.5, c1, c2); + + for ( int i=0 ; i; + using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + + const int order = 1; + PointType data[order+1] = { PointType::make_point(-1, -5), + PointType::make_point( 1, 5) }; + BezierCurveType b(data, order); + + { + BezierCurveType c1,c2; + b.split(0.5, c1, c2); + + EXPECT_DOUBLE_EQ(-1., c1[0][0]); + EXPECT_DOUBLE_EQ(-5., c1[0][1]); + EXPECT_DOUBLE_EQ( 0., c1[1][0]); + EXPECT_DOUBLE_EQ( 0., c1[1][1]); + + EXPECT_DOUBLE_EQ( 0., c2[0][0]); + EXPECT_DOUBLE_EQ( 0., c2[0][1]); + EXPECT_DOUBLE_EQ( 1., c2[1][0]); + EXPECT_DOUBLE_EQ( 5., c2[1][1]); + + } + { + BezierCurveType c1,c2; + const double t = 0.25; + b.split(0.25, c1, c2); + + PointType interp = PointType::lerp(data[0], data[1], t); + + for ( int i=0 ; i; + using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + + const double t = .42; + const int order = 2; + + // Control points for the three levels of the quadratic de Casteljau algorithm + PointType lev0[3] = { PointType::make_point(1.1, 1.1), + PointType::make_point( 5.5, 5.5), + PointType::make_point( 9.9, 2.2) }; + + PointType lev1[2] = { PointType::lerp(lev0[0], lev0[1], t), + PointType::lerp(lev0[1], lev0[2], t) }; + + PointType lev2[1] = { PointType::lerp(lev1[0], lev1[1], t) }; + + BezierCurveType b(lev0, order); + + // Define expected control points for curves 1 and 2 + BezierCurveType expC1(order); + expC1[0] = lev0[0]; + expC1[1] = lev1[0]; + expC1[2] = lev2[0]; + + BezierCurveType expC2(order); + expC2[0] = lev2[0]; + expC2[1] = lev1[1]; + expC2[2] = lev0[2]; + + // Split the curve + BezierCurveType c1,c2; + b.split(t, c1, c2); + + SLIC_INFO("" + <<"Original quadratic: "<< b + << "\nCurves after splitting at t = "<< t + << "\n\t c1: " << c1 + << "\n\t c2: " << c2); + + // Check values + for(int p=0 ; p <= order ; ++p) + { + for ( int i=0 ; i Date: Wed, 17 Jul 2019 14:31:13 -0700 Subject: [PATCH 13/44] Added sectorArea() to BezierCurve, CurvedPolygon class with area function --- src/axom/core/utilities/Utilities.cpp | 12 + src/axom/core/utilities/Utilities.hpp | 2 +- src/axom/primal/CMakeLists.txt | 1 + src/axom/primal/geometry/BezierCurve.hpp | 46 ++ src/axom/primal/geometry/CurvedPolygon.hpp | 217 +++++++++ src/axom/primal/tests/CMakeLists.txt | 1 + src/axom/primal/tests/primal_bezier_curve.cpp | 42 ++ .../primal/tests/primal_curved_polygon.cpp | 419 ++++++++++++++++++ 8 files changed, 739 insertions(+), 1 deletion(-) create mode 100644 src/axom/primal/geometry/CurvedPolygon.hpp create mode 100644 src/axom/primal/tests/primal_curved_polygon.cpp diff --git a/src/axom/core/utilities/Utilities.cpp b/src/axom/core/utilities/Utilities.cpp index 66a05ceaa3..fd5b1c9b90 100644 --- a/src/axom/core/utilities/Utilities.cpp +++ b/src/axom/core/utilities/Utilities.cpp @@ -25,6 +25,18 @@ namespace axom namespace utilities { +int binomial_coefficient(int n, int k) +{ + if (k > n-k) {k= n-k;} + int val = 1; + for (int i=1; i<=k; ++i) + { + val*=(n-k+i); + val/=i; + } + return val; +} + void processAbort() { #ifndef AXOM_USE_MPI diff --git a/src/axom/core/utilities/Utilities.hpp b/src/axom/core/utilities/Utilities.hpp index d891db13e2..f4c4ceabe1 100644 --- a/src/axom/core/utilities/Utilities.hpp +++ b/src/axom/core/utilities/Utilities.hpp @@ -34,7 +34,7 @@ namespace utilities * \brief Gracefully aborts the application */ void processAbort(); - +int binomial_coefficient(int n, int k); /*! * \brief Returns the absolute value of x. * \param [in] x value whose absolute value is computed. diff --git a/src/axom/primal/CMakeLists.txt b/src/axom/primal/CMakeLists.txt index d5b42b0e2b..70e221f5bd 100644 --- a/src/axom/primal/CMakeLists.txt +++ b/src/axom/primal/CMakeLists.txt @@ -29,6 +29,7 @@ set( primal_headers geometry/Triangle.hpp geometry/Vector.hpp geometry/BezierCurve.hpp + geometry/CurvedPolygon.hpp ## operators operators/clip.hpp diff --git a/src/axom/primal/geometry/BezierCurve.hpp b/src/axom/primal/geometry/BezierCurve.hpp index fe909751f2..c164fc1d8c 100644 --- a/src/axom/primal/geometry/BezierCurve.hpp +++ b/src/axom/primal/geometry/BezierCurve.hpp @@ -38,6 +38,9 @@ template < typename T,int NDIMS > std::ostream& operator<<(std::ostream & os, const BezierCurve< T,NDIMS > & bCurve); +// UedaAreaMats is where all of the area matrices from Ueda '99 are stored +extern std::map > UedaAreaMats; + /*! * \class BezierCurve * @@ -258,6 +261,49 @@ class BezierCurve return; } + + /* + * \brief Calculates the sector area (area between curve and origin) of a Bezier Curve + * + * \param [in] tol a tolerance parameter controlling definition of + * near-linearity + * \param [out] boolean TRUE if c1 is near-linear + */ + T sectorArea() const + { + T A = 0; + int ord = getOrder(); + if (UedaAreaMats.find(ord)==UedaAreaMats.end()) + { + std::vector newUedaAreaMat((ord+1)*(ord+1)); + int twonchoosen=axom::utilities::binomial_coefficient(2*ord,ord); + for (int i=0; i<=ord; ++i) + { + for (int j=0; j<=ord; ++j) + { + if ((i==0 && j==0) || (i==(ord)&& j==(ord))) + {newUedaAreaMat[i*(ord+1)+j]=0.0;} + else + { + newUedaAreaMat[i*(ord+1)+j]=((1.0*j-i)/2)*(2.0*(ord)/(1.0*twonchoosen))* + (1.0*axom::utilities::binomial_coefficient(i+j,i)/(1.0*i+j))* + (1.0*axom::utilities::binomial_coefficient(2*(ord)-i-j,(ord)-j)/(1.0*(ord)-j+(ord)-i)); + } + } + } + UedaAreaMats.insert(std::pair>(ord,newUedaAreaMat)); + } + const std::vector &whicharea = (UedaAreaMats.find(ord)->second); + for (int i=0; i<=ord; ++i) + { + for (int j=0; j<=ord; ++j) + { + A+=static_cast(whicharea[i*(ord+1)+j])*m_controlpoints[i][1]*m_controlpoints[j][0]; + } + } + return A; + } + /*! * \brief Predicate to check if the Bezier curve is approximately linear * diff --git a/src/axom/primal/geometry/CurvedPolygon.hpp b/src/axom/primal/geometry/CurvedPolygon.hpp new file mode 100644 index 0000000000..44b2f02a14 --- /dev/null +++ b/src/axom/primal/geometry/CurvedPolygon.hpp @@ -0,0 +1,217 @@ +// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +/*! + * \file CurvedPolygon.hpp + * + * \brief A CurvedPolygon primitive for primal based on Bezier Curves + */ + +#ifndef PRIMAL_CURVEDPOLYGON_HPP_ +#define PRIMAL_CURVEDPOLYGON_HPP_ + +#include "axom/slic.hpp" + +#include "axom/primal/geometry/Point.hpp" +#include "axom/primal/geometry/Vector.hpp" +#include "axom/primal/geometry/NumericArray.hpp" +#include "axom/primal/geometry/BezierCurve.hpp" +#include "axom/primal/operators/intersect_bezier.hpp" + +#include "fmt/fmt.hpp" +#include +#include // for std::ostream + +namespace axom +{ +namespace primal +{ + +// Forward declare the templated classes and operator functions +template < typename T, int NDIMS > +class CurvedPolygon; + +/*! \brief Overloaded output operator for polygons */ +template < typename T,int NDIMS > +std::ostream& operator<<(std::ostream & os, const CurvedPolygon< T,NDIMS > & poly); + +/*! + * \class CurvedPolygon + * + * \brief Represents a curved polygon defined by a vector of BezierCurves + * \tparam T the coordinate type, e.g., double, float, etc. + * \tparam NDIMS the number of dimensions + * \note The component curves should be ordered in a counter clockwise + * orientation with respect to the polygon's desired normal vector + */ +template < typename T,int NDIMS > +class CurvedPolygon +{ +public: + using PointType = Point< T,NDIMS >; + using VectorType = Vector< T,NDIMS >; + using NumArrayType = NumericArray< T,NDIMS >; + using BezierCurveType = BezierCurve; +public: + /*! Default constructor for an empty polygon */ + CurvedPolygon() = default; + + /*! + * \brief Constructor for an empty CurvedPolygon that reserves space for + * the given number of Edges + * + * \param [in] numExpectedEdges number of edges for which to reserve space + * \pre numExpectedEdges is at least 1 + * + */ + CurvedPolygon(int nEdges) + { + SLIC_ASSERT(nEdges >= 1); + m_edges.reserve(nEdges); + m_edges.resize(nEdges); + } + + /*! Return the number of edges in the polygon */ + int numEdges() const { return m_edges.size(); } + + void setNumEdges(int ngon) + { + SLIC_ASSERT(ngon >= 0); + m_edges.resize(ngon); + } + + /* Checks equality of two Bezier Curve */ + friend inline bool operator==(const CurvedPolygon< T, NDIMS>& lhs, + const CurvedPolygon< T, NDIMS>& rhs) + { + return lhs.m_edges == rhs.m_edges; + } + + friend inline bool operator!=(const CurvedPolygon< T, NDIMS>& lhs, + const CurvedPolygon< T, NDIMS>& rhs) + { + return !(lhs == rhs); + } + + /*! Appends a BezierCurve to the list of edges */ + void addEdge(const BezierCurveType& c1) + { + m_edges.push_back(c1); + } + + /*! Clears the list of edges */ + void clear() + { + m_edges.clear(); + } + + std::vector> getEdges() const + { + return m_edges; + } + + /*! Retrieves the Bezier Curve at index idx */ + BezierCurveType& operator[](int idx) { return m_edges[idx]; } + /*! Retrieves the vertex at index idx */ + const BezierCurveType& operator[](int idx) const { return m_edges[idx]; } + + /*! + * \brief Simple formatted print of a CurvedPolygon instance + * + * \param os The output stream to write to + * \return A reference to the modified ostream + */ + std::ostream& print(std::ostream& os) const + { + const int sz = numEdges(); + + os <<"{" << sz <<"-sided Bezier polygon:"; + for (int i=0 ; i< sz-1 ; ++i) + { + os << m_edges[i] << ","; + } + if (sz >= 2) + { + os< > m_edges; +}; + + +//------------------------------------------------------------------------------ +/// Free functions implementing Polygon's operators +//------------------------------------------------------------------------------ +template < typename T, int NDIMS > +std::ostream& operator<<(std::ostream & os, const CurvedPolygon< T,NDIMS > & poly) +{ + poly.print(os); + return os; +} + +} // namespace primal +} // namespace axom + +#endif // PRIMAL_CURVEDPOLYGON_HPP_ diff --git a/src/axom/primal/tests/CMakeLists.txt b/src/axom/primal/tests/CMakeLists.txt index 1dfce12cb0..d2cf7d10fe 100644 --- a/src/axom/primal/tests/CMakeLists.txt +++ b/src/axom/primal/tests/CMakeLists.txt @@ -23,6 +23,7 @@ set( primal_tests primal_vector.cpp primal_bezier_curve.cpp primal_bezier_intersect.cpp + primal_curved_polygon.cpp ) set(primal_test_depends fmt primal slic mint gtest) diff --git a/src/axom/primal/tests/primal_bezier_curve.cpp b/src/axom/primal/tests/primal_bezier_curve.cpp index 13d6333bd3..649b188e4f 100644 --- a/src/axom/primal/tests/primal_bezier_curve.cpp +++ b/src/axom/primal/tests/primal_bezier_curve.cpp @@ -13,6 +13,7 @@ namespace primal = axom::primal; +std::map > primal::UedaAreaMats;//TODO: put this in the BezierCurve.cpp file //------------------------------------------------------------------------------ TEST( primal_beziercurve, constructor ) { @@ -162,6 +163,47 @@ TEST( primal_beziercurve, evaluate) } } +//------------------------------------------------------------------------------ +TEST( primal_beziercurve, sector_area_cubic ) +{ + const int DIM = 2; + using CoordType = double; + using PointType = primal::Point< CoordType, DIM >; + using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + + { + SLIC_INFO("Testing Bezier sector area calculation for a cubic"); + const int order = 3; + PointType data[order+1] = { PointType::make_point(0.6, 1.2), + PointType::make_point(1.3, 1.6), + PointType::make_point(2.9, 2.4), + PointType::make_point(3.2, 3.5) }; + + BezierCurveType bCurve(data, order); + EXPECT_TRUE(axom::utilities::isNearlyEqual(bCurve.sectorArea(),.1455)); + } +} + + +//------------------------------------------------------------------------------ +TEST( primal_beziercurve, sector_area_point ) +{ + const int DIM = 2; + using CoordType = double; + using PointType = primal::Point< CoordType, DIM >; + using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + + { + SLIC_INFO("Testing Bezier sector area calculation for a cubic"); + const int order = 0; + PointType data[order+1] = { PointType::make_point(0.6, 1.2)}; + + BezierCurveType bCurve(data, order); + EXPECT_DOUBLE_EQ(bCurve.sectorArea(),0.0); + } +} + + //------------------------------------------------------------------------------ TEST( primal_beziercurve, split_cubic ) { diff --git a/src/axom/primal/tests/primal_curved_polygon.cpp b/src/axom/primal/tests/primal_curved_polygon.cpp new file mode 100644 index 0000000000..5536b99451 --- /dev/null +++ b/src/axom/primal/tests/primal_curved_polygon.cpp @@ -0,0 +1,419 @@ +// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +/* /file bezier_test.cpp + * /brief This file tests the BezierCurve.hpp and eval_bezier.hpp files + */ + +#include "gtest/gtest.h" + +#include "axom/primal/geometry/CurvedPolygon.hpp" + +namespace primal = axom::primal; + +std::map > primal::UedaAreaMats; //TODO: put this in the BezierCurve.cpp file +//------------------------------------------------------------------------------ +TEST( primal_curvedpolygon, constructor ) +{ + const int DIM = 3; + using CoordType = double; + using CurvedPolygonType = primal::CurvedPolygon< CoordType, DIM>; + using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + + { + SLIC_INFO("Testing default CurvedPolygon constructor "); + CurvedPolygonType bPolygon; + + int expNumEdges = 0; + EXPECT_EQ(expNumEdges, bPolygon.numEdges()); + EXPECT_EQ(expNumEdges, bPolygon.getEdges().size()); + EXPECT_EQ(std::vector(), bPolygon.getEdges()); + } + + { + SLIC_INFO("Testing CurvedPolygon order constructor "); + + CurvedPolygonType bPolygon(1); + int expNumEdges = 1; + EXPECT_EQ(expNumEdges, bPolygon.numEdges()); + EXPECT_EQ(expNumEdges, static_cast(bPolygon.getEdges().size())); + } +} + +//---------------------------------------------------------------------------------- +TEST( primal_curvedpolygon, add_edges ) +{ + const int DIM = 2; + using CoordType = double; + using CurvedPolygonType = primal::CurvedPolygon< CoordType, DIM>; + using PointType = primal::Point< CoordType, DIM >; + using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + + SLIC_INFO("Test adding edges to empty CurvedPolygon"); + + CurvedPolygonType bPolygon; + EXPECT_EQ(0, bPolygon.numEdges()); + + PointType controlPoints[2] = { + PointType::make_point(0.6, 1.2), + PointType::make_point(0.0, 1.6) + }; + + BezierCurveType bCurve(controlPoints,1); + + bPolygon.addEdge(bCurve); + bPolygon.addEdge(bCurve); + + EXPECT_EQ(2, bPolygon.numEdges()); + for (int p=0 ; p; + using PointType = primal::Point< CoordType, DIM >; + using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + + SLIC_INFO("Test checking if CurvedPolygon is closed."); + + CurvedPolygonType bPolygon; + EXPECT_EQ(0, bPolygon.numEdges()); + + PointType controlPoints[2] = { + PointType::make_point(0.6, 1.2), + PointType::make_point(0.0, 1.6) + }; + + PointType controlPoints2[2] = { + PointType::make_point( 0.0, 1.6), + PointType::make_point( 0.3 , 2.0) + }; + + PointType controlPoints3[2] = { + PointType::make_point( 0.3 , 2.0), + PointType::make_point( 0.6, 1.2) + }; + + BezierCurveType bCurve(controlPoints,1); + bPolygon.addEdge(bCurve); + + BezierCurveType bCurve2(controlPoints2,1); + bPolygon.addEdge(bCurve2); + + EXPECT_EQ(2,bPolygon.numEdges()); + EXPECT_EQ(false,bPolygon.isClosed()); + + BezierCurveType bCurve3(controlPoints3,1); + bPolygon.addEdge(bCurve3); + + EXPECT_EQ(3,bPolygon.numEdges()); + EXPECT_EQ(true,bPolygon.isClosed()); +} + +//---------------------------------------------------------------------------------- +TEST( primal_beziercurve, area ) +{ + const int DIM = 2; + using CoordType = double; + using CurvedPolygonType = primal::CurvedPolygon< CoordType, DIM>; + using PointType = primal::Point< CoordType, DIM >; + using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + + SLIC_INFO("Test checking if CurvedPolygon is closed."); + + CurvedPolygonType bPolygon; + EXPECT_EQ(0, bPolygon.numEdges()); + + PointType controlPoints[2] = { + PointType::make_point(0.6, 1.2), + PointType::make_point(0.0, 1.6) + }; + + PointType controlPoints2[2] = { + PointType::make_point( 0.0, 1.6), + PointType::make_point( 0.3 , 2.0) + }; + + PointType controlPoints3[2] = { + PointType::make_point( 0.3 , 2.0), + PointType::make_point( 0.6, 1.2) + }; + + BezierCurveType bCurve(controlPoints,1); + bPolygon.addEdge(bCurve); + + BezierCurveType bCurve2(controlPoints2,1); + bPolygon.addEdge(bCurve2); + + BezierCurveType bCurve3(controlPoints3,1); + bPolygon.addEdge(bCurve3); + + CoordType A= bPolygon.area(); + CoordType trueA= .18; + + EXPECT_TRUE(axom::utilities::isNearlyEqual(trueA,A)); +} + +/* +//---------------------------------------------------------------------------------- +TEST( primal_beziercurve, coordinate_array_constructor ) +{ + SLIC_INFO("Testing coordinate array constructor"); + + const int DIM = 3; + using CoordType = double; + using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + + // Note: Order of coordinates is by dimension + CoordType coords[6] = {0.6, 0.0, // x-coords for control points + 1.2, 1.6, // y-coords for control points + 1.0, 1.8}; // z-coords for control points + + BezierCurveType bCurve(coords,1); + EXPECT_EQ(1, bCurve.getOrder()); + + + EXPECT_DOUBLE_EQ(coords[0], bCurve[0][0]); + EXPECT_DOUBLE_EQ(coords[2], bCurve[0][1]); + EXPECT_DOUBLE_EQ(coords[4], bCurve[0][2]); + + EXPECT_DOUBLE_EQ(coords[1], bCurve[1][0]); + EXPECT_DOUBLE_EQ(coords[3], bCurve[1][1]); + EXPECT_DOUBLE_EQ(coords[5], bCurve[1][2]); +} + +//------------------------------------------------------------------------------ +TEST( primal_beziercurve, evaluate) +{ + SLIC_INFO("Testing Bezier evaluation"); + + const int DIM = 3; + using CoordType = double; + using PointType = primal::Point< CoordType, DIM >; + using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + + const int order = 3; + PointType data[order+1] = { PointType::make_point(0.6, 1.2, 1.0), + PointType::make_point(1.3, 1.6, 1.8), + PointType::make_point(2.9, 2.4, 2.3), + PointType::make_point(3.2, 3.5, 3.0) }; + + BezierCurveType b2Curve(data, order); + + PointType midtval = PointType::make_point(2.05,2.0875,2.0375); + + // Evaluate the curve at several parameter values + // Curve should interpolate endpoints + PointType eval0 = b2Curve.evaluate(0.0); + PointType eval1 = b2Curve.evaluate(1.0); + PointType evalMid = b2Curve.evaluate(0.5); + + for ( int i=0 ; i; + using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + + const int order = 3; + PointType data[order+1] = { PointType::make_point(0.6, 1.2, 1.0), + PointType::make_point(1.3, 1.6, 1.8), + PointType::make_point(2.9, 2.4, 2.3), + PointType::make_point(3.2, 3.5, 3.0) }; + BezierCurveType b2Curve(data, order); + + BezierCurveType b3Curve(order); // Checks split with order constructor + BezierCurveType b4Curve; // Checks split with default constructor + b2Curve.split(.5, b3Curve, b4Curve); + + CoordType b3Coords[12] = {0.6, .95, 1.525, 2.05, + 1.2, 1.4, 1.7, 2.0875, + 1.0, 1.4, 1.725, 2.0375}; + CoordType b4Coords[12] = {2.05, 2.575, 3.05, 3.2, + 2.0875, 2.475, 2.95, 3.5, + 2.0375, 2.35, 2.65, 3.0}; + BezierCurveType b3True(b3Coords,3); + BezierCurveType b4True(b4Coords,3); + for ( int i=0 ; i; + using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + + // Test order-0 + { + const int order = 0; + PointType data[order+1] = { PointType::make_point(0.6, 1.2)}; + BezierCurveType b(data, order); + + BezierCurveType c1,c2; + b.split(0.5, c1, c2); + + for ( int i=0 ; i; + using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + + const int order = 1; + PointType data[order+1] = { PointType::make_point(-1, -5), + PointType::make_point( 1, 5) }; + BezierCurveType b(data, order); + + { + BezierCurveType c1,c2; + b.split(0.5, c1, c2); + + EXPECT_DOUBLE_EQ(-1., c1[0][0]); + EXPECT_DOUBLE_EQ(-5., c1[0][1]); + EXPECT_DOUBLE_EQ( 0., c1[1][0]); + EXPECT_DOUBLE_EQ( 0., c1[1][1]); + + EXPECT_DOUBLE_EQ( 0., c2[0][0]); + EXPECT_DOUBLE_EQ( 0., c2[0][1]); + EXPECT_DOUBLE_EQ( 1., c2[1][0]); + EXPECT_DOUBLE_EQ( 5., c2[1][1]); + + } + + { + BezierCurveType c1,c2; + const double t = 0.25; + b.split(0.25, c1, c2); + + PointType interp = PointType::lerp(data[0], data[1], t); + + for ( int i=0 ; i; + using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + + const double t = .42; + const int order = 2; + + // Control points for the three levels of the quadratic de Casteljau algorithm + PointType lev0[3] = { PointType::make_point(1.1, 1.1), + PointType::make_point( 5.5, 5.5), + PointType::make_point( 9.9, 2.2) }; + + PointType lev1[2] = { PointType::lerp(lev0[0], lev0[1], t), + PointType::lerp(lev0[1], lev0[2], t) }; + + PointType lev2[1] = { PointType::lerp(lev1[0], lev1[1], t) }; + + BezierCurveType b(lev0, order); + + // Define expected control points for curves 1 and 2 + BezierCurveType expC1(order); + expC1[0] = lev0[0]; + expC1[1] = lev1[0]; + expC1[2] = lev2[0]; + + BezierCurveType expC2(order); + expC2[0] = lev2[0]; + expC2[1] = lev1[1]; + expC2[2] = lev0[2]; + + // Split the curve + BezierCurveType c1,c2; + b.split(t, c1, c2); + + SLIC_INFO("" + <<"Original quadratic: "<< b + << "\nCurves after splitting at t = "<< t + << "\n\t c1: " << c1 + << "\n\t c2: " << c2); + + // Check values + for(int p=0 ; p <= order ; ++p) + { + for ( int i=0 ; i Date: Tue, 23 Jul 2019 12:07:24 -0700 Subject: [PATCH 14/44] Added intersection of curved polygons, one test, and some helper functions in BezierCurve and CurvedPolygon --- src/axom/primal/CMakeLists.txt | 1 + src/axom/primal/geometry/BezierCurve.hpp | 2 +- src/axom/primal/geometry/CurvedPolygon.hpp | 21 ++ .../operators/intersect_curved_poly.hpp | 225 ++++++++++++++ .../primal/tests/primal_curved_polygon.cpp | 274 ++++-------------- 5 files changed, 301 insertions(+), 222 deletions(-) create mode 100644 src/axom/primal/operators/intersect_curved_poly.hpp diff --git a/src/axom/primal/CMakeLists.txt b/src/axom/primal/CMakeLists.txt index 70e221f5bd..0b0a320429 100644 --- a/src/axom/primal/CMakeLists.txt +++ b/src/axom/primal/CMakeLists.txt @@ -40,6 +40,7 @@ set( primal_headers operators/compute_bounding_box.hpp operators/in_sphere.hpp operators/intersect_bezier.hpp + operators/intersect_curved_poly.hpp operators/detail/clip_impl.hpp operators/detail/intersect_impl.hpp diff --git a/src/axom/primal/geometry/BezierCurve.hpp b/src/axom/primal/geometry/BezierCurve.hpp index c164fc1d8c..3f186ae249 100644 --- a/src/axom/primal/geometry/BezierCurve.hpp +++ b/src/axom/primal/geometry/BezierCurve.hpp @@ -193,7 +193,7 @@ class BezierCurve * \note We typically evaluate the curve at \a t between 0 and 1 */ - PointType evaluate(T t) + PointType evaluate(T t) const { PointType ptval; diff --git a/src/axom/primal/geometry/CurvedPolygon.hpp b/src/axom/primal/geometry/CurvedPolygon.hpp index 44b2f02a14..6e85e8813e 100644 --- a/src/axom/primal/geometry/CurvedPolygon.hpp +++ b/src/axom/primal/geometry/CurvedPolygon.hpp @@ -73,6 +73,19 @@ class CurvedPolygon m_edges.resize(nEdges); } + CurvedPolygon(BezierCurveType* curves, int nEdges) + { + SLIC_ASSERT(curves != nullptr); + SLIC_ASSERT(nEdges >=1); + + m_edges.reserve(nEdges); + + for (int e =0; eaddEdge(curves[e]); + } + } + /*! Return the number of edges in the polygon */ int numEdges() const { return m_edges.size(); } @@ -101,6 +114,14 @@ class CurvedPolygon m_edges.push_back(c1); } + /*! Splits an edge "in place" */ + void splitEdge(int idx, T t) + { + m_edges.insert(m_edges.begin()+idx+1, 1, m_edges[idx]); + BezierCurve< T, NDIMS> csplit=m_edges[idx]; + csplit.split(t,m_edges[idx],m_edges[idx+1]); + } + /*! Clears the list of edges */ void clear() { diff --git a/src/axom/primal/operators/intersect_curved_poly.hpp b/src/axom/primal/operators/intersect_curved_poly.hpp new file mode 100644 index 0000000000..898a7148cc --- /dev/null +++ b/src/axom/primal/operators/intersect_curved_poly.hpp @@ -0,0 +1,225 @@ +// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +/*! + * \file intersect.hpp + * + * \brief Consists of functions to test intersection among geometric primitives. + */ + +#ifndef INTERSECTION_CURVED_POLYGON_HPP_ +#define INTERSECTION_CURVED_POLYGON_HPP_ + +#include "axom/primal/geometry/BoundingBox.hpp" +#include "axom/primal/geometry/OrientedBoundingBox.hpp" +#include "axom/primal/geometry/Point.hpp" +#include "axom/primal/geometry/Ray.hpp" +#include "axom/primal/geometry/Segment.hpp" +#include "axom/primal/geometry/Sphere.hpp" +#include "axom/primal/geometry/Triangle.hpp" +#include "axom/primal/geometry/BezierCurve.hpp" +#include "axom/primal/geometry/CurvedPolygon.hpp" + +#include "axom/core/utilities/Utilities.hpp" + +#include "axom/primal/operators/squared_distance.hpp" +#include "axom/primal/operators/intersect.hpp" +#include "axom/primal/operators/intersect_bezier.hpp" + +namespace axom +{ +namespace primal +{ + +template < typename T > +class IntersectionInfo; +/* +template +bool orient(BezierCurve c1, BezierCurve c2, T s, T t); +*/ +/*! + * \brief Tests if Bezier Curves c1 and c2 intersect. + * \return status true iff c1 intersects with c2, otherwise false. + * + * \param c1, c2 BezierCurve objects to intersect + * \param sp, tp vector of type T parameter space intersection points (t-values + * and s-values) for c1 and c2, respectively + */ +template < typename T, int NDIMS> +bool intersect_polygon(CurvedPolygon< T, NDIMS>& p1, + CurvedPolygon< T, NDIMS>& p2, + std::vector>& pnew + ) +{ + // Object to store intersections + std::vector>> E1IntData(p1.numEdges()); + std::vector>> E2IntData(p2.numEdges()); + + IntersectionInfo firstinter; + + // Find all intersections and store + int numinters=0; + for (int i = 0; i < p1.numEdges(); ++i) + { + for (int j = 0; j < p2.numEdges(); ++j) + { + std::vector p1times; + std::vector p2times; + intersect_bezier(p1[i],p2[j],p1times,p2times); + for (int k =0; k< p1times.size(); ++k) + { + E1IntData[i].push_back({p1times[k],i,p2times[k],j,numinters+k+1}); + E2IntData[j].push_back({p2times[k],j,p1times[k],i,numinters+k+1}); + if (numinters==0) + { + firstinter={p1times[0],i,p2times[0],j,1}; + } + } + numinters+=p1times.size(); + } + } + for (int i = 0; i < p1.numEdges(); ++i) + { + std::sort( E1IntData[i].begin(), E1IntData[i].end() ); + std::sort( E2IntData[i].begin(), E2IntData[i].end() ); + } + + + bool orientation = orient(p1[firstinter.myEdge],p2[firstinter.otherEdge],firstinter.myTime,firstinter.otherTime); + std::vector edgelabels[2]; + + CurvedPolygon psplit[2]; + psplit[0]=p1; + psplit[1]=p2; + int addedints=0; + for (int i = 0; i < p1.numEdges(); ++i) + { + edgelabels[0].push_back(0); + for (int j = 0; j < E1IntData[i].size(); ++j) + { + psplit[0].splitEdge(i+addedints,E1IntData[i][j].myTime); + edgelabels[0].insert(edgelabels[0].begin()+i+addedints,E1IntData[i][j].numinter); + addedints+=1; + for (int k = j+1; k < (E1IntData[i].size()); ++k) + { + E1IntData[i][k].myTime=(E1IntData[i][k].myTime-E1IntData[i][j].myTime)/(1-E1IntData[i][j].myTime); + } + } + } + /*std::cout << psplit[0] << std::endl; + for (int i=0; i < edgelabels[0].size(); ++i) + { + std::cout << edgelabels[0][i] << std::endl; + }*/ + + addedints=0; + for (int i = 0; i < p2.numEdges(); ++i) + { + edgelabels[1].push_back(0); + for (int j = 0; j < E2IntData[i].size(); ++j) + { + psplit[1].splitEdge(i+addedints,E2IntData[i][j].myTime); + edgelabels[1].insert(edgelabels[1].begin()+i+addedints,E2IntData[i][j].numinter); + addedints+=1; + for (int k = j+1; k < (E2IntData[i].size()); ++k) + { + E2IntData[i][k].myTime=(E2IntData[i][k].myTime-E2IntData[i][j].myTime)/(1-E2IntData[i][j].myTime); + } + } + } + /*std::cout << psplit[1] << std::endl; + for (int i=0; i < edgelabels[1].size(); ++i) + { + std::cout << edgelabels[1][i] << std::endl; + }*/ + std::vector::iterator> usedlabels; + if (numinters==0) {return false;} // If there are no intersections, return false + else + { + bool addingcurves=true; + int startinter=1; // Start at the first intersection + int nextinter; + bool currentelement=orientation; + int currentit= std::find(edgelabels[currentelement].begin(),edgelabels[currentelement].end(),startinter)-edgelabels[currentelement].begin(); + int startit = currentit; + int nextit = (currentit+1)%edgelabels[0].size(); + nextinter=edgelabels[currentelement][nextit]; + while (numinters>0) + { + CurvedPolygon aPart; // To store the current intersection polygon (could be multiple) + while (!(nextit==startit && currentelement==orientation)) + { + if (nextit==currentit) + { + currentit=nextit; + nextit=(currentit+1)%edgelabels[0].size(); + } + nextinter=edgelabels[currentelement][nextit]; + while (nextinter==0) + { + currentit=nextit; + if (addingcurves) + { + aPart.addEdge(psplit[currentelement][nextit]); + } + nextit=(currentit+1)%edgelabels[0].size(); + nextinter=edgelabels[currentelement][nextit]; + } + if (addingcurves) + { + aPart.addEdge(psplit[currentelement][nextit]); + currentelement=!currentelement; + currentit=nextit; + nextit = std::find(edgelabels[currentelement].begin(),edgelabels[currentelement].end(),nextinter)-edgelabels[currentelement].begin(); + numinters-=1; + } + else + { + addingcurves=true; + currentit=nextit; + nextit = std::find(edgelabels[currentelement].begin(),edgelabels[currentelement].end(),nextinter)-edgelabels[currentelement].begin(); + } + std::cout << aPart << std::endl; + } + pnew.push_back(aPart); + } + addingcurves=false; + } + std::cout << pnew[0] << std::endl; + + + return true; +} + +template +bool orient(const BezierCurve c1, const BezierCurve c2, T s, T t) +{ + Point c1val1 = c1.evaluate(s+(1e-13)); + Point c1val2 = c1.evaluate(s-(1e-13)); + Point c2val = c2.evaluate(t+(1e-13)); + + return ((-(c1val1[0] - c1val2[0])*(c2val[1]-c1val2[1]))+(c1val1[1]-c1val2[1])*(c2val[0]-c1val2[0]))>0; +} + +// A class for storing intersection points so they can be easily sorted by parameter value +template +class IntersectionInfo +{ + public: + T myTime; + int myEdge; + T otherTime; + int otherEdge; + int numinter; + bool operator<(IntersectionInfo other) + { + return myTime; using BezierCurveType = primal::BezierCurve< CoordType, DIM >; - SLIC_INFO("Test checking if CurvedPolygon is closed."); + SLIC_INFO("Test checking CurvedPolygon area computation."); CurvedPolygonType bPolygon; EXPECT_EQ(0, bPolygon.numEdges()); - PointType controlPoints[2] = { - PointType::make_point(0.6, 1.2), - PointType::make_point(0.0, 1.6) + PointType controlPoints3[2] = { + PointType::make_point(0.0, 1.6), + PointType::make_point(0.6, 1.2) }; PointType controlPoints2[2] = { - PointType::make_point( 0.0, 1.6), - PointType::make_point( 0.3 , 2.0) + PointType::make_point( 0.3 , 2.0), + PointType::make_point( 0.0, 1.6) }; - PointType controlPoints3[2] = { - PointType::make_point( 0.3 , 2.0), - PointType::make_point( 0.6, 1.2) + PointType controlPoints[2] = { + PointType::make_point( 0.6, 1.2), + PointType::make_point( 0.3 , 2.0) }; BezierCurveType bCurve(controlPoints,1); @@ -165,246 +166,77 @@ TEST( primal_beziercurve, area ) bPolygon.addEdge(bCurve3); CoordType A= bPolygon.area(); - CoordType trueA= .18; + CoordType trueA= -.18; EXPECT_TRUE(axom::utilities::isNearlyEqual(trueA,A)); } -/* //---------------------------------------------------------------------------------- -TEST( primal_beziercurve, coordinate_array_constructor ) +TEST( primal_beziercurve, split_edge) { - SLIC_INFO("Testing coordinate array constructor"); - - const int DIM = 3; - using CoordType = double; - using BezierCurveType = primal::BezierCurve< CoordType, DIM >; - - // Note: Order of coordinates is by dimension - CoordType coords[6] = {0.6, 0.0, // x-coords for control points - 1.2, 1.6, // y-coords for control points - 1.0, 1.8}; // z-coords for control points - - BezierCurveType bCurve(coords,1); - EXPECT_EQ(1, bCurve.getOrder()); - - - EXPECT_DOUBLE_EQ(coords[0], bCurve[0][0]); - EXPECT_DOUBLE_EQ(coords[2], bCurve[0][1]); - EXPECT_DOUBLE_EQ(coords[4], bCurve[0][2]); - - EXPECT_DOUBLE_EQ(coords[1], bCurve[1][0]); - EXPECT_DOUBLE_EQ(coords[3], bCurve[1][1]); - EXPECT_DOUBLE_EQ(coords[5], bCurve[1][2]); -} - -//------------------------------------------------------------------------------ -TEST( primal_beziercurve, evaluate) -{ - SLIC_INFO("Testing Bezier evaluation"); - - const int DIM = 3; - using CoordType = double; - using PointType = primal::Point< CoordType, DIM >; - using BezierCurveType = primal::BezierCurve< CoordType, DIM >; - - const int order = 3; - PointType data[order+1] = { PointType::make_point(0.6, 1.2, 1.0), - PointType::make_point(1.3, 1.6, 1.8), - PointType::make_point(2.9, 2.4, 2.3), - PointType::make_point(3.2, 3.5, 3.0) }; - - BezierCurveType b2Curve(data, order); - - PointType midtval = PointType::make_point(2.05,2.0875,2.0375); - - // Evaluate the curve at several parameter values - // Curve should interpolate endpoints - PointType eval0 = b2Curve.evaluate(0.0); - PointType eval1 = b2Curve.evaluate(1.0); - PointType evalMid = b2Curve.evaluate(0.5); - - for ( int i=0 ; i; - using BezierCurveType = primal::BezierCurve< CoordType, DIM >; - - const int order = 3; - PointType data[order+1] = { PointType::make_point(0.6, 1.2, 1.0), - PointType::make_point(1.3, 1.6, 1.8), - PointType::make_point(2.9, 2.4, 2.3), - PointType::make_point(3.2, 3.5, 3.0) }; - BezierCurveType b2Curve(data, order); - - BezierCurveType b3Curve(order); // Checks split with order constructor - BezierCurveType b4Curve; // Checks split with default constructor - b2Curve.split(.5, b3Curve, b4Curve); - - CoordType b3Coords[12] = {0.6, .95, 1.525, 2.05, - 1.2, 1.4, 1.7, 2.0875, - 1.0, 1.4, 1.725, 2.0375}; - CoordType b4Coords[12] = {2.05, 2.575, 3.05, 3.2, - 2.0875, 2.475, 2.95, 3.5, - 2.0375, 2.35, 2.65, 3.0}; - BezierCurveType b3True(b3Coords,3); - BezierCurveType b4True(b4Coords,3); - for ( int i=0 ; i; using PointType = primal::Point< CoordType, DIM >; using BezierCurveType = primal::BezierCurve< CoordType, DIM >; - // Test order-0 - { - const int order = 0; - PointType data[order+1] = { PointType::make_point(0.6, 1.2)}; - BezierCurveType b(data, order); - - BezierCurveType c1,c2; - b.split(0.5, c1, c2); - - for ( int i=0 ; i; - using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + CurvedPolygonType bPolygon; + EXPECT_EQ(0, bPolygon.numEdges()); - const int order = 1; - PointType data[order+1] = { PointType::make_point(-1, -5), - PointType::make_point( 1, 5) }; - BezierCurveType b(data, order); + PointType controlPoints[2] = { + PointType::make_point(0.6, 1.2), + PointType::make_point(0.0, 1.6) + }; - { - BezierCurveType c1,c2; - b.split(0.5, c1, c2); + PointType controlPoints2[2] = { + PointType::make_point( 0.0, 1.6), + PointType::make_point( 0.3 , 2.0) + }; - EXPECT_DOUBLE_EQ(-1., c1[0][0]); - EXPECT_DOUBLE_EQ(-5., c1[0][1]); - EXPECT_DOUBLE_EQ( 0., c1[1][0]); - EXPECT_DOUBLE_EQ( 0., c1[1][1]); + PointType controlPoints3[2] = { + PointType::make_point( 0.3 , 2.0), + PointType::make_point( 0.6, 1.2) + }; - EXPECT_DOUBLE_EQ( 0., c2[0][0]); - EXPECT_DOUBLE_EQ( 0., c2[0][1]); - EXPECT_DOUBLE_EQ( 1., c2[1][0]); - EXPECT_DOUBLE_EQ( 5., c2[1][1]); + BezierCurveType bCurve(controlPoints,1); + bPolygon.addEdge(bCurve); + + BezierCurveType bCurve2(controlPoints2,1); + bPolygon.addEdge(bCurve2); - } + BezierCurveType bCurve3(controlPoints3,1); + bPolygon.addEdge(bCurve3); +/* bPolygon.splitEdge(0,.5); + bCurve.split(.5,bCurve2,bCurve3); +*/ + CurvedPolygonType bPolygon2=bPolygon; + std::vector bPolygon3; + + EXPECT_EQ(bPolygon.numEdges(),3); + for (int i=0; i< bPolygon[0].getOrder(); ++i) { - BezierCurveType c1,c2; - const double t = 0.25; - b.split(0.25, c1, c2); - - PointType interp = PointType::lerp(data[0], data[1], t); - - for ( int i=0 ; i; - using BezierCurveType = primal::BezierCurve< CoordType, DIM >; - - const double t = .42; - const int order = 2; - - // Control points for the three levels of the quadratic de Casteljau algorithm - PointType lev0[3] = { PointType::make_point(1.1, 1.1), - PointType::make_point( 5.5, 5.5), - PointType::make_point( 9.9, 2.2) }; - - PointType lev1[2] = { PointType::lerp(lev0[0], lev0[1], t), - PointType::lerp(lev0[1], lev0[2], t) }; - - PointType lev2[1] = { PointType::lerp(lev1[0], lev1[1], t) }; - - BezierCurveType b(lev0, order); - - // Define expected control points for curves 1 and 2 - BezierCurveType expC1(order); - expC1[0] = lev0[0]; - expC1[1] = lev1[0]; - expC1[2] = lev2[0]; - - BezierCurveType expC2(order); - expC2[0] = lev2[0]; - expC2[1] = lev1[1]; - expC2[2] = lev0[2]; - - // Split the curve - BezierCurveType c1,c2; - b.split(t, c1, c2); - - SLIC_INFO("" - <<"Original quadratic: "<< b - << "\nCurves after splitting at t = "<< t - << "\n\t c1: " << c1 - << "\n\t c2: " << c2); - - // Check values - for(int p=0 ; p <= order ; ++p) + for (int j=0; j< bPolygon.numEdges(); ++j) { - for ( int i=0 ; i Date: Tue, 23 Jul 2019 14:42:48 -0700 Subject: [PATCH 15/44] Added more CurvedPolygon tests --- src/axom/primal/geometry/CurvedPolygon.hpp | 4 +- .../operators/intersect_curved_poly.hpp | 40 +++--- .../primal/tests/primal_curved_polygon.cpp | 135 ++++++++++++++---- 3 files changed, 135 insertions(+), 44 deletions(-) diff --git a/src/axom/primal/geometry/CurvedPolygon.hpp b/src/axom/primal/geometry/CurvedPolygon.hpp index 6e85e8813e..af37d78b2a 100644 --- a/src/axom/primal/geometry/CurvedPolygon.hpp +++ b/src/axom/primal/geometry/CurvedPolygon.hpp @@ -181,12 +181,12 @@ class CurvedPolygon { for (int i=0; i<(ngon-1); ++i) { - if (!axom::utilities::isNearlyEqual(m_edges[i][m_edges[i].getOrder()][p],m_edges[i+1][0][p])) + if (!axom::utilities::isNearlyEqual(m_edges[i][m_edges[i].getOrder()][p],m_edges[i+1][0][p],1e-15)) { return false; } } - if (!axom::utilities::isNearlyEqual(m_edges[ngon-1][m_edges[ngon-1].getOrder()][p],m_edges[0][0][p])) + if (!axom::utilities::isNearlyEqual(m_edges[ngon-1][m_edges[ngon-1].getOrder()][p],m_edges[0][0][p],1e-15)) { return false; } diff --git a/src/axom/primal/operators/intersect_curved_poly.hpp b/src/axom/primal/operators/intersect_curved_poly.hpp index 898a7148cc..6249ffd159 100644 --- a/src/axom/primal/operators/intersect_curved_poly.hpp +++ b/src/axom/primal/operators/intersect_curved_poly.hpp @@ -40,12 +40,11 @@ template bool orient(BezierCurve c1, BezierCurve c2, T s, T t); */ /*! - * \brief Tests if Bezier Curves c1 and c2 intersect. - * \return status true iff c1 intersects with c2, otherwise false. + * \brief Test whether CurvedPolygons p1 and p2 intersect. + * \return status true iff p1 intersects with p2, otherwise false. * - * \param c1, c2 BezierCurve objects to intersect - * \param sp, tp vector of type T parameter space intersection points (t-values - * and s-values) for c1 and c2, respectively + * \param p1, p2 CurvedPolygon objects to intersect + * \param pnew vector of type CurvedPolygon holding intersection regions oriented as the original curves were. */ template < typename T, int NDIMS> bool intersect_polygon(CurvedPolygon< T, NDIMS>& p1, @@ -56,8 +55,7 @@ bool intersect_polygon(CurvedPolygon< T, NDIMS>& p1, // Object to store intersections std::vector>> E1IntData(p1.numEdges()); std::vector>> E2IntData(p2.numEdges()); - - IntersectionInfo firstinter; + IntersectionInfo firstinter; // Need to do orientation test on first intersection // Find all intersections and store int numinters=0; @@ -68,7 +66,7 @@ bool intersect_polygon(CurvedPolygon< T, NDIMS>& p1, std::vector p1times; std::vector p2times; intersect_bezier(p1[i],p2[j],p1times,p2times); - for (int k =0; k< p1times.size(); ++k) + for (int k =0; k< static_cast(p1times.size()); ++k) { E1IntData[i].push_back({p1times[k],i,p2times[k],j,numinters+k+1}); E2IntData[j].push_back({p2times[k],j,p1times[k],i,numinters+k+1}); @@ -85,11 +83,13 @@ bool intersect_polygon(CurvedPolygon< T, NDIMS>& p1, std::sort( E1IntData[i].begin(), E1IntData[i].end() ); std::sort( E2IntData[i].begin(), E2IntData[i].end() ); } - + // Orient the first intersection point to be sure we get the intersection bool orientation = orient(p1[firstinter.myEdge],p2[firstinter.otherEdge],firstinter.myTime,firstinter.otherTime); - std::vector edgelabels[2]; + // Objects to store completely split polygons (split at every intersection point) and vector with unique id for each + // intersection and zeros for corners of original polygons. + std::vector edgelabels[2]; CurvedPolygon psplit[2]; psplit[0]=p1; psplit[1]=p2; @@ -97,17 +97,19 @@ bool intersect_polygon(CurvedPolygon< T, NDIMS>& p1, for (int i = 0; i < p1.numEdges(); ++i) { edgelabels[0].push_back(0); - for (int j = 0; j < E1IntData[i].size(); ++j) + for (int j = 0; j < static_cast(E1IntData[i].size()); ++j) { psplit[0].splitEdge(i+addedints,E1IntData[i][j].myTime); edgelabels[0].insert(edgelabels[0].begin()+i+addedints,E1IntData[i][j].numinter); addedints+=1; - for (int k = j+1; k < (E1IntData[i].size()); ++k) + for (int k = j+1; k < static_cast(E1IntData[i].size()); ++k) { E1IntData[i][k].myTime=(E1IntData[i][k].myTime-E1IntData[i][j].myTime)/(1-E1IntData[i][j].myTime); } } } + + // Debugging code /*std::cout << psplit[0] << std::endl; for (int i=0; i < edgelabels[0].size(); ++i) { @@ -118,22 +120,26 @@ bool intersect_polygon(CurvedPolygon< T, NDIMS>& p1, for (int i = 0; i < p2.numEdges(); ++i) { edgelabels[1].push_back(0); - for (int j = 0; j < E2IntData[i].size(); ++j) + for (int j = 0; j < static_cast(E2IntData[i].size()); ++j) { psplit[1].splitEdge(i+addedints,E2IntData[i][j].myTime); edgelabels[1].insert(edgelabels[1].begin()+i+addedints,E2IntData[i][j].numinter); addedints+=1; - for (int k = j+1; k < (E2IntData[i].size()); ++k) + for (int k = j+1; k < static_cast(E2IntData[i].size()); ++k) { E2IntData[i][k].myTime=(E2IntData[i][k].myTime-E2IntData[i][j].myTime)/(1-E2IntData[i][j].myTime); } } } + + // Debugging code /*std::cout << psplit[1] << std::endl; for (int i=0; i < edgelabels[1].size(); ++i) { std::cout << edgelabels[1][i] << std::endl; }*/ + + // This performs the directional walking method using the completely split polygon std::vector::iterator> usedlabels; if (numinters==0) {return false;} // If there are no intersections, return false else @@ -181,18 +187,20 @@ bool intersect_polygon(CurvedPolygon< T, NDIMS>& p1, currentit=nextit; nextit = std::find(edgelabels[currentelement].begin(),edgelabels[currentelement].end(),nextinter)-edgelabels[currentelement].begin(); } - std::cout << aPart << std::endl; + // std::cout << aPart << std::endl; } pnew.push_back(aPart); } addingcurves=false; } - std::cout << pnew[0] << std::endl; + // std::cout << pnew[0] << std::endl; return true; } +// This determines with curve is "more" counterclockwise using signed distance +// It could potentially be changed to use a cross product of tangents instead template bool orient(const BezierCurve c1, const BezierCurve c2, T s, T t) { diff --git a/src/axom/primal/tests/primal_curved_polygon.cpp b/src/axom/primal/tests/primal_curved_polygon.cpp index cccd5fd739..bba444a6c7 100644 --- a/src/axom/primal/tests/primal_curved_polygon.cpp +++ b/src/axom/primal/tests/primal_curved_polygon.cpp @@ -128,7 +128,7 @@ TEST( primal_curvedpolygon, is_Valid ) } //---------------------------------------------------------------------------------- -TEST( primal_beziercurve, area ) +TEST( primal_beziercurve, area_triangle_linear ) { const int DIM = 2; using CoordType = double; @@ -136,24 +136,24 @@ TEST( primal_beziercurve, area ) using PointType = primal::Point< CoordType, DIM >; using BezierCurveType = primal::BezierCurve< CoordType, DIM >; - SLIC_INFO("Test checking CurvedPolygon area computation."); + SLIC_INFO("Test checking CurvedPolygon linear area triangle computation."); CurvedPolygonType bPolygon; EXPECT_EQ(0, bPolygon.numEdges()); - PointType controlPoints3[2] = { - PointType::make_point(0.0, 1.6), - PointType::make_point(0.6, 1.2) + PointType controlPoints[2] = { + PointType::make_point(0.6, 1.2), + PointType::make_point(0.0, 1.6) }; PointType controlPoints2[2] = { - PointType::make_point( 0.3 , 2.0), - PointType::make_point( 0.0, 1.6) + PointType::make_point( 0.0, 1.6), + PointType::make_point( 0.3 , 2.0) }; - PointType controlPoints[2] = { - PointType::make_point( 0.6, 1.2), - PointType::make_point( 0.3 , 2.0) + PointType controlPoints3[2] = { + PointType::make_point( 0.3 , 2.0), + PointType::make_point( 0.6, 1.2) }; BezierCurveType bCurve(controlPoints,1); @@ -166,9 +166,103 @@ TEST( primal_beziercurve, area ) bPolygon.addEdge(bCurve3); CoordType A= bPolygon.area(); - CoordType trueA= -.18; + CoordType trueA= .18; - EXPECT_TRUE(axom::utilities::isNearlyEqual(trueA,A)); + EXPECT_DOUBLE_EQ(trueA,A); +} + +//---------------------------------------------------------------------------------- +TEST( primal_beziercurve, area_triangle_quadratic ) +{ + const int DIM = 2; + const int order =2; + using CoordType = double; + using CurvedPolygonType = primal::CurvedPolygon< CoordType, DIM>; + using PointType = primal::Point< CoordType, DIM >; + using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + + SLIC_INFO("Test checking CurvedPolygon linear area triangle computation."); + + CurvedPolygonType bPolygon; + EXPECT_EQ(0, bPolygon.numEdges()); + + PointType controlPoints[order+1] = { + PointType::make_point(0.6, 1.2), + PointType::make_point(0.4, 1.3), + PointType::make_point(0.3, 2.0) + }; + + PointType controlPoints2[order+1] = { + PointType::make_point( 0.3, 2.0), + PointType::make_point( 0.27, 1.5), + PointType::make_point( 0.0 , 1.6) + }; + + PointType controlPoints3[order+1] = { + PointType::make_point( 0.0 , 1.6), + PointType::make_point( 0.1 , 1.5), + PointType::make_point( 0.6, 1.2) + }; + + BezierCurveType bCurve(controlPoints,order); + bPolygon.addEdge(bCurve); + + BezierCurveType bCurve2(controlPoints2,order); + bPolygon.addEdge(bCurve2); + + BezierCurveType bCurve3(controlPoints3,order); + bPolygon.addEdge(bCurve3); + + CoordType A = bPolygon.area(); + + CoordType trueA = -.09733333333333333333; + EXPECT_DOUBLE_EQ(trueA,A); +} + +//---------------------------------------------------------------------------------- +TEST( primal_beziercurve, area_triangle_mixed_order) +{ + const int DIM = 2; + using CoordType = double; + using CurvedPolygonType = primal::CurvedPolygon< CoordType, DIM>; + using PointType = primal::Point< CoordType, DIM >; + using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + + SLIC_INFO("Test checking CurvedPolygon linear area triangle computation."); + + CurvedPolygonType bPolygon; + EXPECT_EQ(0, bPolygon.numEdges()); + + PointType controlPoints[3] = { + PointType::make_point(0.6, 1.2), + PointType::make_point(0.4, 1.3), + PointType::make_point(0.3, 2.0) + }; + + PointType controlPoints2[3] = { + PointType::make_point( 0.3, 2.0), + PointType::make_point( 0.27, 1.5), + PointType::make_point( 0.0 , 1.6) + }; + + PointType controlPoints3[2] = { + PointType::make_point( 0.0 , 1.6), + PointType::make_point( 0.6, 1.2) + }; + + BezierCurveType bCurve(controlPoints,2); + bPolygon.addEdge(bCurve); + + BezierCurveType bCurve2(controlPoints2,2); + bPolygon.addEdge(bCurve2); + + BezierCurveType bCurve3(controlPoints3,1); + bPolygon.addEdge(bCurve3); + + CoordType A = bPolygon.area(); + + CoordType trueA = -.0906666666666666666666; + EXPECT_DOUBLE_EQ(trueA,A); } //---------------------------------------------------------------------------------- @@ -209,13 +303,13 @@ TEST( primal_beziercurve, split_edge) BezierCurveType bCurve3(controlPoints3,1); bPolygon.addEdge(bCurve3); -/* bPolygon.splitEdge(0,.5); + bPolygon.splitEdge(0,.5); bCurve.split(.5,bCurve2,bCurve3); -*/ + CurvedPolygonType bPolygon2=bPolygon; std::vector bPolygon3; - EXPECT_EQ(bPolygon.numEdges(),3); + EXPECT_EQ(bPolygon.numEdges(),4); for (int i=0; i< bPolygon[0].getOrder(); ++i) { for (int dimi=0; dimi< DIM; ++dimi) @@ -224,17 +318,6 @@ TEST( primal_beziercurve, split_edge) EXPECT_EQ(bPolygon[1][i][dimi],bCurve3[i][dimi]); } } - for (int j=0; j< bPolygon.numEdges(); ++j) - { - for (int i=0; i<= bPolygon[j].getOrder(); ++i) - { - for (int dimi=0; dimi< DIM; ++dimi) - { - bPolygon2[j][i][dimi]+=.11; - } - } - } - bool TF = intersect_polygon(bPolygon,bPolygon2,bPolygon3); } int main(int argc, char* argv[]) From fa87f12d2527b45f08a788e93122f1980a48ba24 Mon Sep 17 00:00:00 2001 From: David Gunderman Date: Wed, 17 Jul 2019 14:31:13 -0700 Subject: [PATCH 16/44] Added sectorArea() to BezierCurve, CurvedPolygon class with area function --- src/axom/core/utilities/Utilities.cpp | 12 + src/axom/core/utilities/Utilities.hpp | 2 +- src/axom/primal/CMakeLists.txt | 1 + src/axom/primal/geometry/BezierCurve.hpp | 46 ++ src/axom/primal/geometry/CurvedPolygon.hpp | 217 +++++++++ src/axom/primal/tests/CMakeLists.txt | 1 + src/axom/primal/tests/primal_bezier_curve.cpp | 42 ++ .../primal/tests/primal_curved_polygon.cpp | 419 ++++++++++++++++++ 8 files changed, 739 insertions(+), 1 deletion(-) create mode 100644 src/axom/primal/geometry/CurvedPolygon.hpp create mode 100644 src/axom/primal/tests/primal_curved_polygon.cpp diff --git a/src/axom/core/utilities/Utilities.cpp b/src/axom/core/utilities/Utilities.cpp index 66a05ceaa3..fd5b1c9b90 100644 --- a/src/axom/core/utilities/Utilities.cpp +++ b/src/axom/core/utilities/Utilities.cpp @@ -25,6 +25,18 @@ namespace axom namespace utilities { +int binomial_coefficient(int n, int k) +{ + if (k > n-k) {k= n-k;} + int val = 1; + for (int i=1; i<=k; ++i) + { + val*=(n-k+i); + val/=i; + } + return val; +} + void processAbort() { #ifndef AXOM_USE_MPI diff --git a/src/axom/core/utilities/Utilities.hpp b/src/axom/core/utilities/Utilities.hpp index d891db13e2..f4c4ceabe1 100644 --- a/src/axom/core/utilities/Utilities.hpp +++ b/src/axom/core/utilities/Utilities.hpp @@ -34,7 +34,7 @@ namespace utilities * \brief Gracefully aborts the application */ void processAbort(); - +int binomial_coefficient(int n, int k); /*! * \brief Returns the absolute value of x. * \param [in] x value whose absolute value is computed. diff --git a/src/axom/primal/CMakeLists.txt b/src/axom/primal/CMakeLists.txt index b76851f80f..89789685ca 100644 --- a/src/axom/primal/CMakeLists.txt +++ b/src/axom/primal/CMakeLists.txt @@ -20,6 +20,7 @@ set( primal_headers ## geometry geometry/BezierCurve.hpp geometry/BoundingBox.hpp + geometry/CurvedPolygon.hpp geometry/OrientedBoundingBox.hpp geometry/OrientationResult.hpp geometry/NumericArray.hpp diff --git a/src/axom/primal/geometry/BezierCurve.hpp b/src/axom/primal/geometry/BezierCurve.hpp index 97c1fe1354..efd8a49e6e 100644 --- a/src/axom/primal/geometry/BezierCurve.hpp +++ b/src/axom/primal/geometry/BezierCurve.hpp @@ -42,6 +42,9 @@ template < typename T,int NDIMS > std::ostream& operator<<(std::ostream & os, const BezierCurve< T,NDIMS > & bCurve); +// UedaAreaMats is where all of the area matrices from Ueda '99 are stored +extern std::map > UedaAreaMats; + /*! * \class BezierCurve * @@ -266,6 +269,49 @@ class BezierCurve return; } + + /* + * \brief Calculates the sector area (area between curve and origin) of a Bezier Curve + * + * \param [in] tol a tolerance parameter controlling definition of + * near-linearity + * \param [out] boolean TRUE if c1 is near-linear + */ + T sectorArea() const + { + T A = 0; + int ord = getOrder(); + if (UedaAreaMats.find(ord)==UedaAreaMats.end()) + { + std::vector newUedaAreaMat((ord+1)*(ord+1)); + int twonchoosen=axom::utilities::binomial_coefficient(2*ord,ord); + for (int i=0; i<=ord; ++i) + { + for (int j=0; j<=ord; ++j) + { + if ((i==0 && j==0) || (i==(ord)&& j==(ord))) + {newUedaAreaMat[i*(ord+1)+j]=0.0;} + else + { + newUedaAreaMat[i*(ord+1)+j]=((1.0*j-i)/2)*(2.0*(ord)/(1.0*twonchoosen))* + (1.0*axom::utilities::binomial_coefficient(i+j,i)/(1.0*i+j))* + (1.0*axom::utilities::binomial_coefficient(2*(ord)-i-j,(ord)-j)/(1.0*(ord)-j+(ord)-i)); + } + } + } + UedaAreaMats.insert(std::pair>(ord,newUedaAreaMat)); + } + const std::vector &whicharea = (UedaAreaMats.find(ord)->second); + for (int i=0; i<=ord; ++i) + { + for (int j=0; j<=ord; ++j) + { + A+=static_cast(whicharea[i*(ord+1)+j])*m_controlpoints[i][1]*m_controlpoints[j][0]; + } + } + return A; + } + /*! * \brief Predicate to check if the Bezier curve is approximately linear * diff --git a/src/axom/primal/geometry/CurvedPolygon.hpp b/src/axom/primal/geometry/CurvedPolygon.hpp new file mode 100644 index 0000000000..44b2f02a14 --- /dev/null +++ b/src/axom/primal/geometry/CurvedPolygon.hpp @@ -0,0 +1,217 @@ +// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +/*! + * \file CurvedPolygon.hpp + * + * \brief A CurvedPolygon primitive for primal based on Bezier Curves + */ + +#ifndef PRIMAL_CURVEDPOLYGON_HPP_ +#define PRIMAL_CURVEDPOLYGON_HPP_ + +#include "axom/slic.hpp" + +#include "axom/primal/geometry/Point.hpp" +#include "axom/primal/geometry/Vector.hpp" +#include "axom/primal/geometry/NumericArray.hpp" +#include "axom/primal/geometry/BezierCurve.hpp" +#include "axom/primal/operators/intersect_bezier.hpp" + +#include "fmt/fmt.hpp" +#include +#include // for std::ostream + +namespace axom +{ +namespace primal +{ + +// Forward declare the templated classes and operator functions +template < typename T, int NDIMS > +class CurvedPolygon; + +/*! \brief Overloaded output operator for polygons */ +template < typename T,int NDIMS > +std::ostream& operator<<(std::ostream & os, const CurvedPolygon< T,NDIMS > & poly); + +/*! + * \class CurvedPolygon + * + * \brief Represents a curved polygon defined by a vector of BezierCurves + * \tparam T the coordinate type, e.g., double, float, etc. + * \tparam NDIMS the number of dimensions + * \note The component curves should be ordered in a counter clockwise + * orientation with respect to the polygon's desired normal vector + */ +template < typename T,int NDIMS > +class CurvedPolygon +{ +public: + using PointType = Point< T,NDIMS >; + using VectorType = Vector< T,NDIMS >; + using NumArrayType = NumericArray< T,NDIMS >; + using BezierCurveType = BezierCurve; +public: + /*! Default constructor for an empty polygon */ + CurvedPolygon() = default; + + /*! + * \brief Constructor for an empty CurvedPolygon that reserves space for + * the given number of Edges + * + * \param [in] numExpectedEdges number of edges for which to reserve space + * \pre numExpectedEdges is at least 1 + * + */ + CurvedPolygon(int nEdges) + { + SLIC_ASSERT(nEdges >= 1); + m_edges.reserve(nEdges); + m_edges.resize(nEdges); + } + + /*! Return the number of edges in the polygon */ + int numEdges() const { return m_edges.size(); } + + void setNumEdges(int ngon) + { + SLIC_ASSERT(ngon >= 0); + m_edges.resize(ngon); + } + + /* Checks equality of two Bezier Curve */ + friend inline bool operator==(const CurvedPolygon< T, NDIMS>& lhs, + const CurvedPolygon< T, NDIMS>& rhs) + { + return lhs.m_edges == rhs.m_edges; + } + + friend inline bool operator!=(const CurvedPolygon< T, NDIMS>& lhs, + const CurvedPolygon< T, NDIMS>& rhs) + { + return !(lhs == rhs); + } + + /*! Appends a BezierCurve to the list of edges */ + void addEdge(const BezierCurveType& c1) + { + m_edges.push_back(c1); + } + + /*! Clears the list of edges */ + void clear() + { + m_edges.clear(); + } + + std::vector> getEdges() const + { + return m_edges; + } + + /*! Retrieves the Bezier Curve at index idx */ + BezierCurveType& operator[](int idx) { return m_edges[idx]; } + /*! Retrieves the vertex at index idx */ + const BezierCurveType& operator[](int idx) const { return m_edges[idx]; } + + /*! + * \brief Simple formatted print of a CurvedPolygon instance + * + * \param os The output stream to write to + * \return A reference to the modified ostream + */ + std::ostream& print(std::ostream& os) const + { + const int sz = numEdges(); + + os <<"{" << sz <<"-sided Bezier polygon:"; + for (int i=0 ; i< sz-1 ; ++i) + { + os << m_edges[i] << ","; + } + if (sz >= 2) + { + os< > m_edges; +}; + + +//------------------------------------------------------------------------------ +/// Free functions implementing Polygon's operators +//------------------------------------------------------------------------------ +template < typename T, int NDIMS > +std::ostream& operator<<(std::ostream & os, const CurvedPolygon< T,NDIMS > & poly) +{ + poly.print(os); + return os; +} + +} // namespace primal +} // namespace axom + +#endif // PRIMAL_CURVEDPOLYGON_HPP_ diff --git a/src/axom/primal/tests/CMakeLists.txt b/src/axom/primal/tests/CMakeLists.txt index fd1c3b8ca7..158bfc058d 100644 --- a/src/axom/primal/tests/CMakeLists.txt +++ b/src/axom/primal/tests/CMakeLists.txt @@ -26,6 +26,7 @@ set( primal_tests primal_vector.cpp primal_bezier_curve.cpp primal_bezier_intersect.cpp + primal_curved_polygon.cpp ) set(primal_test_depends fmt primal slic mint gtest) diff --git a/src/axom/primal/tests/primal_bezier_curve.cpp b/src/axom/primal/tests/primal_bezier_curve.cpp index a99bc79869..56a473e39e 100644 --- a/src/axom/primal/tests/primal_bezier_curve.cpp +++ b/src/axom/primal/tests/primal_bezier_curve.cpp @@ -14,6 +14,7 @@ namespace primal = axom::primal; +std::map > primal::UedaAreaMats;//TODO: put this in the BezierCurve.cpp file //------------------------------------------------------------------------------ TEST( primal_beziercurve, constructor ) { @@ -167,6 +168,47 @@ TEST( primal_beziercurve, evaluate) } } +//------------------------------------------------------------------------------ +TEST( primal_beziercurve, sector_area_cubic ) +{ + const int DIM = 2; + using CoordType = double; + using PointType = primal::Point< CoordType, DIM >; + using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + + { + SLIC_INFO("Testing Bezier sector area calculation for a cubic"); + const int order = 3; + PointType data[order+1] = { PointType::make_point(0.6, 1.2), + PointType::make_point(1.3, 1.6), + PointType::make_point(2.9, 2.4), + PointType::make_point(3.2, 3.5) }; + + BezierCurveType bCurve(data, order); + EXPECT_TRUE(axom::utilities::isNearlyEqual(bCurve.sectorArea(),.1455)); + } +} + + +//------------------------------------------------------------------------------ +TEST( primal_beziercurve, sector_area_point ) +{ + const int DIM = 2; + using CoordType = double; + using PointType = primal::Point< CoordType, DIM >; + using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + + { + SLIC_INFO("Testing Bezier sector area calculation for a cubic"); + const int order = 0; + PointType data[order+1] = { PointType::make_point(0.6, 1.2)}; + + BezierCurveType bCurve(data, order); + EXPECT_DOUBLE_EQ(bCurve.sectorArea(),0.0); + } +} + + //------------------------------------------------------------------------------ TEST( primal_beziercurve, split_cubic ) { diff --git a/src/axom/primal/tests/primal_curved_polygon.cpp b/src/axom/primal/tests/primal_curved_polygon.cpp new file mode 100644 index 0000000000..5536b99451 --- /dev/null +++ b/src/axom/primal/tests/primal_curved_polygon.cpp @@ -0,0 +1,419 @@ +// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +/* /file bezier_test.cpp + * /brief This file tests the BezierCurve.hpp and eval_bezier.hpp files + */ + +#include "gtest/gtest.h" + +#include "axom/primal/geometry/CurvedPolygon.hpp" + +namespace primal = axom::primal; + +std::map > primal::UedaAreaMats; //TODO: put this in the BezierCurve.cpp file +//------------------------------------------------------------------------------ +TEST( primal_curvedpolygon, constructor ) +{ + const int DIM = 3; + using CoordType = double; + using CurvedPolygonType = primal::CurvedPolygon< CoordType, DIM>; + using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + + { + SLIC_INFO("Testing default CurvedPolygon constructor "); + CurvedPolygonType bPolygon; + + int expNumEdges = 0; + EXPECT_EQ(expNumEdges, bPolygon.numEdges()); + EXPECT_EQ(expNumEdges, bPolygon.getEdges().size()); + EXPECT_EQ(std::vector(), bPolygon.getEdges()); + } + + { + SLIC_INFO("Testing CurvedPolygon order constructor "); + + CurvedPolygonType bPolygon(1); + int expNumEdges = 1; + EXPECT_EQ(expNumEdges, bPolygon.numEdges()); + EXPECT_EQ(expNumEdges, static_cast(bPolygon.getEdges().size())); + } +} + +//---------------------------------------------------------------------------------- +TEST( primal_curvedpolygon, add_edges ) +{ + const int DIM = 2; + using CoordType = double; + using CurvedPolygonType = primal::CurvedPolygon< CoordType, DIM>; + using PointType = primal::Point< CoordType, DIM >; + using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + + SLIC_INFO("Test adding edges to empty CurvedPolygon"); + + CurvedPolygonType bPolygon; + EXPECT_EQ(0, bPolygon.numEdges()); + + PointType controlPoints[2] = { + PointType::make_point(0.6, 1.2), + PointType::make_point(0.0, 1.6) + }; + + BezierCurveType bCurve(controlPoints,1); + + bPolygon.addEdge(bCurve); + bPolygon.addEdge(bCurve); + + EXPECT_EQ(2, bPolygon.numEdges()); + for (int p=0 ; p; + using PointType = primal::Point< CoordType, DIM >; + using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + + SLIC_INFO("Test checking if CurvedPolygon is closed."); + + CurvedPolygonType bPolygon; + EXPECT_EQ(0, bPolygon.numEdges()); + + PointType controlPoints[2] = { + PointType::make_point(0.6, 1.2), + PointType::make_point(0.0, 1.6) + }; + + PointType controlPoints2[2] = { + PointType::make_point( 0.0, 1.6), + PointType::make_point( 0.3 , 2.0) + }; + + PointType controlPoints3[2] = { + PointType::make_point( 0.3 , 2.0), + PointType::make_point( 0.6, 1.2) + }; + + BezierCurveType bCurve(controlPoints,1); + bPolygon.addEdge(bCurve); + + BezierCurveType bCurve2(controlPoints2,1); + bPolygon.addEdge(bCurve2); + + EXPECT_EQ(2,bPolygon.numEdges()); + EXPECT_EQ(false,bPolygon.isClosed()); + + BezierCurveType bCurve3(controlPoints3,1); + bPolygon.addEdge(bCurve3); + + EXPECT_EQ(3,bPolygon.numEdges()); + EXPECT_EQ(true,bPolygon.isClosed()); +} + +//---------------------------------------------------------------------------------- +TEST( primal_beziercurve, area ) +{ + const int DIM = 2; + using CoordType = double; + using CurvedPolygonType = primal::CurvedPolygon< CoordType, DIM>; + using PointType = primal::Point< CoordType, DIM >; + using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + + SLIC_INFO("Test checking if CurvedPolygon is closed."); + + CurvedPolygonType bPolygon; + EXPECT_EQ(0, bPolygon.numEdges()); + + PointType controlPoints[2] = { + PointType::make_point(0.6, 1.2), + PointType::make_point(0.0, 1.6) + }; + + PointType controlPoints2[2] = { + PointType::make_point( 0.0, 1.6), + PointType::make_point( 0.3 , 2.0) + }; + + PointType controlPoints3[2] = { + PointType::make_point( 0.3 , 2.0), + PointType::make_point( 0.6, 1.2) + }; + + BezierCurveType bCurve(controlPoints,1); + bPolygon.addEdge(bCurve); + + BezierCurveType bCurve2(controlPoints2,1); + bPolygon.addEdge(bCurve2); + + BezierCurveType bCurve3(controlPoints3,1); + bPolygon.addEdge(bCurve3); + + CoordType A= bPolygon.area(); + CoordType trueA= .18; + + EXPECT_TRUE(axom::utilities::isNearlyEqual(trueA,A)); +} + +/* +//---------------------------------------------------------------------------------- +TEST( primal_beziercurve, coordinate_array_constructor ) +{ + SLIC_INFO("Testing coordinate array constructor"); + + const int DIM = 3; + using CoordType = double; + using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + + // Note: Order of coordinates is by dimension + CoordType coords[6] = {0.6, 0.0, // x-coords for control points + 1.2, 1.6, // y-coords for control points + 1.0, 1.8}; // z-coords for control points + + BezierCurveType bCurve(coords,1); + EXPECT_EQ(1, bCurve.getOrder()); + + + EXPECT_DOUBLE_EQ(coords[0], bCurve[0][0]); + EXPECT_DOUBLE_EQ(coords[2], bCurve[0][1]); + EXPECT_DOUBLE_EQ(coords[4], bCurve[0][2]); + + EXPECT_DOUBLE_EQ(coords[1], bCurve[1][0]); + EXPECT_DOUBLE_EQ(coords[3], bCurve[1][1]); + EXPECT_DOUBLE_EQ(coords[5], bCurve[1][2]); +} + +//------------------------------------------------------------------------------ +TEST( primal_beziercurve, evaluate) +{ + SLIC_INFO("Testing Bezier evaluation"); + + const int DIM = 3; + using CoordType = double; + using PointType = primal::Point< CoordType, DIM >; + using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + + const int order = 3; + PointType data[order+1] = { PointType::make_point(0.6, 1.2, 1.0), + PointType::make_point(1.3, 1.6, 1.8), + PointType::make_point(2.9, 2.4, 2.3), + PointType::make_point(3.2, 3.5, 3.0) }; + + BezierCurveType b2Curve(data, order); + + PointType midtval = PointType::make_point(2.05,2.0875,2.0375); + + // Evaluate the curve at several parameter values + // Curve should interpolate endpoints + PointType eval0 = b2Curve.evaluate(0.0); + PointType eval1 = b2Curve.evaluate(1.0); + PointType evalMid = b2Curve.evaluate(0.5); + + for ( int i=0 ; i; + using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + + const int order = 3; + PointType data[order+1] = { PointType::make_point(0.6, 1.2, 1.0), + PointType::make_point(1.3, 1.6, 1.8), + PointType::make_point(2.9, 2.4, 2.3), + PointType::make_point(3.2, 3.5, 3.0) }; + BezierCurveType b2Curve(data, order); + + BezierCurveType b3Curve(order); // Checks split with order constructor + BezierCurveType b4Curve; // Checks split with default constructor + b2Curve.split(.5, b3Curve, b4Curve); + + CoordType b3Coords[12] = {0.6, .95, 1.525, 2.05, + 1.2, 1.4, 1.7, 2.0875, + 1.0, 1.4, 1.725, 2.0375}; + CoordType b4Coords[12] = {2.05, 2.575, 3.05, 3.2, + 2.0875, 2.475, 2.95, 3.5, + 2.0375, 2.35, 2.65, 3.0}; + BezierCurveType b3True(b3Coords,3); + BezierCurveType b4True(b4Coords,3); + for ( int i=0 ; i; + using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + + // Test order-0 + { + const int order = 0; + PointType data[order+1] = { PointType::make_point(0.6, 1.2)}; + BezierCurveType b(data, order); + + BezierCurveType c1,c2; + b.split(0.5, c1, c2); + + for ( int i=0 ; i; + using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + + const int order = 1; + PointType data[order+1] = { PointType::make_point(-1, -5), + PointType::make_point( 1, 5) }; + BezierCurveType b(data, order); + + { + BezierCurveType c1,c2; + b.split(0.5, c1, c2); + + EXPECT_DOUBLE_EQ(-1., c1[0][0]); + EXPECT_DOUBLE_EQ(-5., c1[0][1]); + EXPECT_DOUBLE_EQ( 0., c1[1][0]); + EXPECT_DOUBLE_EQ( 0., c1[1][1]); + + EXPECT_DOUBLE_EQ( 0., c2[0][0]); + EXPECT_DOUBLE_EQ( 0., c2[0][1]); + EXPECT_DOUBLE_EQ( 1., c2[1][0]); + EXPECT_DOUBLE_EQ( 5., c2[1][1]); + + } + + { + BezierCurveType c1,c2; + const double t = 0.25; + b.split(0.25, c1, c2); + + PointType interp = PointType::lerp(data[0], data[1], t); + + for ( int i=0 ; i; + using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + + const double t = .42; + const int order = 2; + + // Control points for the three levels of the quadratic de Casteljau algorithm + PointType lev0[3] = { PointType::make_point(1.1, 1.1), + PointType::make_point( 5.5, 5.5), + PointType::make_point( 9.9, 2.2) }; + + PointType lev1[2] = { PointType::lerp(lev0[0], lev0[1], t), + PointType::lerp(lev0[1], lev0[2], t) }; + + PointType lev2[1] = { PointType::lerp(lev1[0], lev1[1], t) }; + + BezierCurveType b(lev0, order); + + // Define expected control points for curves 1 and 2 + BezierCurveType expC1(order); + expC1[0] = lev0[0]; + expC1[1] = lev1[0]; + expC1[2] = lev2[0]; + + BezierCurveType expC2(order); + expC2[0] = lev2[0]; + expC2[1] = lev1[1]; + expC2[2] = lev0[2]; + + // Split the curve + BezierCurveType c1,c2; + b.split(t, c1, c2); + + SLIC_INFO("" + <<"Original quadratic: "<< b + << "\nCurves after splitting at t = "<< t + << "\n\t c1: " << c1 + << "\n\t c2: " << c2); + + // Check values + for(int p=0 ; p <= order ; ++p) + { + for ( int i=0 ; i Date: Tue, 23 Jul 2019 12:07:24 -0700 Subject: [PATCH 17/44] Added intersection of curved polygons, one test, and some helper functions in BezierCurve and CurvedPolygon --- src/axom/primal/CMakeLists.txt | 1 + src/axom/primal/geometry/CurvedPolygon.hpp | 21 ++ .../operators/intersect_curved_poly.hpp | 225 ++++++++++++++ .../primal/tests/primal_curved_polygon.cpp | 274 ++++-------------- 4 files changed, 300 insertions(+), 221 deletions(-) create mode 100644 src/axom/primal/operators/intersect_curved_poly.hpp diff --git a/src/axom/primal/CMakeLists.txt b/src/axom/primal/CMakeLists.txt index 89789685ca..e6195a1c76 100644 --- a/src/axom/primal/CMakeLists.txt +++ b/src/axom/primal/CMakeLists.txt @@ -42,6 +42,7 @@ set( primal_headers operators/squared_distance.hpp operators/compute_bounding_box.hpp operators/in_sphere.hpp + operators/intersect_curved_poly.hpp operators/detail/clip_impl.hpp operators/detail/intersect_bezier_impl.hpp diff --git a/src/axom/primal/geometry/CurvedPolygon.hpp b/src/axom/primal/geometry/CurvedPolygon.hpp index 44b2f02a14..6e85e8813e 100644 --- a/src/axom/primal/geometry/CurvedPolygon.hpp +++ b/src/axom/primal/geometry/CurvedPolygon.hpp @@ -73,6 +73,19 @@ class CurvedPolygon m_edges.resize(nEdges); } + CurvedPolygon(BezierCurveType* curves, int nEdges) + { + SLIC_ASSERT(curves != nullptr); + SLIC_ASSERT(nEdges >=1); + + m_edges.reserve(nEdges); + + for (int e =0; eaddEdge(curves[e]); + } + } + /*! Return the number of edges in the polygon */ int numEdges() const { return m_edges.size(); } @@ -101,6 +114,14 @@ class CurvedPolygon m_edges.push_back(c1); } + /*! Splits an edge "in place" */ + void splitEdge(int idx, T t) + { + m_edges.insert(m_edges.begin()+idx+1, 1, m_edges[idx]); + BezierCurve< T, NDIMS> csplit=m_edges[idx]; + csplit.split(t,m_edges[idx],m_edges[idx+1]); + } + /*! Clears the list of edges */ void clear() { diff --git a/src/axom/primal/operators/intersect_curved_poly.hpp b/src/axom/primal/operators/intersect_curved_poly.hpp new file mode 100644 index 0000000000..898a7148cc --- /dev/null +++ b/src/axom/primal/operators/intersect_curved_poly.hpp @@ -0,0 +1,225 @@ +// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +/*! + * \file intersect.hpp + * + * \brief Consists of functions to test intersection among geometric primitives. + */ + +#ifndef INTERSECTION_CURVED_POLYGON_HPP_ +#define INTERSECTION_CURVED_POLYGON_HPP_ + +#include "axom/primal/geometry/BoundingBox.hpp" +#include "axom/primal/geometry/OrientedBoundingBox.hpp" +#include "axom/primal/geometry/Point.hpp" +#include "axom/primal/geometry/Ray.hpp" +#include "axom/primal/geometry/Segment.hpp" +#include "axom/primal/geometry/Sphere.hpp" +#include "axom/primal/geometry/Triangle.hpp" +#include "axom/primal/geometry/BezierCurve.hpp" +#include "axom/primal/geometry/CurvedPolygon.hpp" + +#include "axom/core/utilities/Utilities.hpp" + +#include "axom/primal/operators/squared_distance.hpp" +#include "axom/primal/operators/intersect.hpp" +#include "axom/primal/operators/intersect_bezier.hpp" + +namespace axom +{ +namespace primal +{ + +template < typename T > +class IntersectionInfo; +/* +template +bool orient(BezierCurve c1, BezierCurve c2, T s, T t); +*/ +/*! + * \brief Tests if Bezier Curves c1 and c2 intersect. + * \return status true iff c1 intersects with c2, otherwise false. + * + * \param c1, c2 BezierCurve objects to intersect + * \param sp, tp vector of type T parameter space intersection points (t-values + * and s-values) for c1 and c2, respectively + */ +template < typename T, int NDIMS> +bool intersect_polygon(CurvedPolygon< T, NDIMS>& p1, + CurvedPolygon< T, NDIMS>& p2, + std::vector>& pnew + ) +{ + // Object to store intersections + std::vector>> E1IntData(p1.numEdges()); + std::vector>> E2IntData(p2.numEdges()); + + IntersectionInfo firstinter; + + // Find all intersections and store + int numinters=0; + for (int i = 0; i < p1.numEdges(); ++i) + { + for (int j = 0; j < p2.numEdges(); ++j) + { + std::vector p1times; + std::vector p2times; + intersect_bezier(p1[i],p2[j],p1times,p2times); + for (int k =0; k< p1times.size(); ++k) + { + E1IntData[i].push_back({p1times[k],i,p2times[k],j,numinters+k+1}); + E2IntData[j].push_back({p2times[k],j,p1times[k],i,numinters+k+1}); + if (numinters==0) + { + firstinter={p1times[0],i,p2times[0],j,1}; + } + } + numinters+=p1times.size(); + } + } + for (int i = 0; i < p1.numEdges(); ++i) + { + std::sort( E1IntData[i].begin(), E1IntData[i].end() ); + std::sort( E2IntData[i].begin(), E2IntData[i].end() ); + } + + + bool orientation = orient(p1[firstinter.myEdge],p2[firstinter.otherEdge],firstinter.myTime,firstinter.otherTime); + std::vector edgelabels[2]; + + CurvedPolygon psplit[2]; + psplit[0]=p1; + psplit[1]=p2; + int addedints=0; + for (int i = 0; i < p1.numEdges(); ++i) + { + edgelabels[0].push_back(0); + for (int j = 0; j < E1IntData[i].size(); ++j) + { + psplit[0].splitEdge(i+addedints,E1IntData[i][j].myTime); + edgelabels[0].insert(edgelabels[0].begin()+i+addedints,E1IntData[i][j].numinter); + addedints+=1; + for (int k = j+1; k < (E1IntData[i].size()); ++k) + { + E1IntData[i][k].myTime=(E1IntData[i][k].myTime-E1IntData[i][j].myTime)/(1-E1IntData[i][j].myTime); + } + } + } + /*std::cout << psplit[0] << std::endl; + for (int i=0; i < edgelabels[0].size(); ++i) + { + std::cout << edgelabels[0][i] << std::endl; + }*/ + + addedints=0; + for (int i = 0; i < p2.numEdges(); ++i) + { + edgelabels[1].push_back(0); + for (int j = 0; j < E2IntData[i].size(); ++j) + { + psplit[1].splitEdge(i+addedints,E2IntData[i][j].myTime); + edgelabels[1].insert(edgelabels[1].begin()+i+addedints,E2IntData[i][j].numinter); + addedints+=1; + for (int k = j+1; k < (E2IntData[i].size()); ++k) + { + E2IntData[i][k].myTime=(E2IntData[i][k].myTime-E2IntData[i][j].myTime)/(1-E2IntData[i][j].myTime); + } + } + } + /*std::cout << psplit[1] << std::endl; + for (int i=0; i < edgelabels[1].size(); ++i) + { + std::cout << edgelabels[1][i] << std::endl; + }*/ + std::vector::iterator> usedlabels; + if (numinters==0) {return false;} // If there are no intersections, return false + else + { + bool addingcurves=true; + int startinter=1; // Start at the first intersection + int nextinter; + bool currentelement=orientation; + int currentit= std::find(edgelabels[currentelement].begin(),edgelabels[currentelement].end(),startinter)-edgelabels[currentelement].begin(); + int startit = currentit; + int nextit = (currentit+1)%edgelabels[0].size(); + nextinter=edgelabels[currentelement][nextit]; + while (numinters>0) + { + CurvedPolygon aPart; // To store the current intersection polygon (could be multiple) + while (!(nextit==startit && currentelement==orientation)) + { + if (nextit==currentit) + { + currentit=nextit; + nextit=(currentit+1)%edgelabels[0].size(); + } + nextinter=edgelabels[currentelement][nextit]; + while (nextinter==0) + { + currentit=nextit; + if (addingcurves) + { + aPart.addEdge(psplit[currentelement][nextit]); + } + nextit=(currentit+1)%edgelabels[0].size(); + nextinter=edgelabels[currentelement][nextit]; + } + if (addingcurves) + { + aPart.addEdge(psplit[currentelement][nextit]); + currentelement=!currentelement; + currentit=nextit; + nextit = std::find(edgelabels[currentelement].begin(),edgelabels[currentelement].end(),nextinter)-edgelabels[currentelement].begin(); + numinters-=1; + } + else + { + addingcurves=true; + currentit=nextit; + nextit = std::find(edgelabels[currentelement].begin(),edgelabels[currentelement].end(),nextinter)-edgelabels[currentelement].begin(); + } + std::cout << aPart << std::endl; + } + pnew.push_back(aPart); + } + addingcurves=false; + } + std::cout << pnew[0] << std::endl; + + + return true; +} + +template +bool orient(const BezierCurve c1, const BezierCurve c2, T s, T t) +{ + Point c1val1 = c1.evaluate(s+(1e-13)); + Point c1val2 = c1.evaluate(s-(1e-13)); + Point c2val = c2.evaluate(t+(1e-13)); + + return ((-(c1val1[0] - c1val2[0])*(c2val[1]-c1val2[1]))+(c1val1[1]-c1val2[1])*(c2val[0]-c1val2[0]))>0; +} + +// A class for storing intersection points so they can be easily sorted by parameter value +template +class IntersectionInfo +{ + public: + T myTime; + int myEdge; + T otherTime; + int otherEdge; + int numinter; + bool operator<(IntersectionInfo other) + { + return myTime; using BezierCurveType = primal::BezierCurve< CoordType, DIM >; - SLIC_INFO("Test checking if CurvedPolygon is closed."); + SLIC_INFO("Test checking CurvedPolygon area computation."); CurvedPolygonType bPolygon; EXPECT_EQ(0, bPolygon.numEdges()); - PointType controlPoints[2] = { - PointType::make_point(0.6, 1.2), - PointType::make_point(0.0, 1.6) + PointType controlPoints3[2] = { + PointType::make_point(0.0, 1.6), + PointType::make_point(0.6, 1.2) }; PointType controlPoints2[2] = { - PointType::make_point( 0.0, 1.6), - PointType::make_point( 0.3 , 2.0) + PointType::make_point( 0.3 , 2.0), + PointType::make_point( 0.0, 1.6) }; - PointType controlPoints3[2] = { - PointType::make_point( 0.3 , 2.0), - PointType::make_point( 0.6, 1.2) + PointType controlPoints[2] = { + PointType::make_point( 0.6, 1.2), + PointType::make_point( 0.3 , 2.0) }; BezierCurveType bCurve(controlPoints,1); @@ -165,246 +166,77 @@ TEST( primal_beziercurve, area ) bPolygon.addEdge(bCurve3); CoordType A= bPolygon.area(); - CoordType trueA= .18; + CoordType trueA= -.18; EXPECT_TRUE(axom::utilities::isNearlyEqual(trueA,A)); } -/* //---------------------------------------------------------------------------------- -TEST( primal_beziercurve, coordinate_array_constructor ) +TEST( primal_beziercurve, split_edge) { - SLIC_INFO("Testing coordinate array constructor"); - - const int DIM = 3; - using CoordType = double; - using BezierCurveType = primal::BezierCurve< CoordType, DIM >; - - // Note: Order of coordinates is by dimension - CoordType coords[6] = {0.6, 0.0, // x-coords for control points - 1.2, 1.6, // y-coords for control points - 1.0, 1.8}; // z-coords for control points - - BezierCurveType bCurve(coords,1); - EXPECT_EQ(1, bCurve.getOrder()); - - - EXPECT_DOUBLE_EQ(coords[0], bCurve[0][0]); - EXPECT_DOUBLE_EQ(coords[2], bCurve[0][1]); - EXPECT_DOUBLE_EQ(coords[4], bCurve[0][2]); - - EXPECT_DOUBLE_EQ(coords[1], bCurve[1][0]); - EXPECT_DOUBLE_EQ(coords[3], bCurve[1][1]); - EXPECT_DOUBLE_EQ(coords[5], bCurve[1][2]); -} - -//------------------------------------------------------------------------------ -TEST( primal_beziercurve, evaluate) -{ - SLIC_INFO("Testing Bezier evaluation"); - - const int DIM = 3; - using CoordType = double; - using PointType = primal::Point< CoordType, DIM >; - using BezierCurveType = primal::BezierCurve< CoordType, DIM >; - - const int order = 3; - PointType data[order+1] = { PointType::make_point(0.6, 1.2, 1.0), - PointType::make_point(1.3, 1.6, 1.8), - PointType::make_point(2.9, 2.4, 2.3), - PointType::make_point(3.2, 3.5, 3.0) }; - - BezierCurveType b2Curve(data, order); - - PointType midtval = PointType::make_point(2.05,2.0875,2.0375); - - // Evaluate the curve at several parameter values - // Curve should interpolate endpoints - PointType eval0 = b2Curve.evaluate(0.0); - PointType eval1 = b2Curve.evaluate(1.0); - PointType evalMid = b2Curve.evaluate(0.5); - - for ( int i=0 ; i; - using BezierCurveType = primal::BezierCurve< CoordType, DIM >; - - const int order = 3; - PointType data[order+1] = { PointType::make_point(0.6, 1.2, 1.0), - PointType::make_point(1.3, 1.6, 1.8), - PointType::make_point(2.9, 2.4, 2.3), - PointType::make_point(3.2, 3.5, 3.0) }; - BezierCurveType b2Curve(data, order); - - BezierCurveType b3Curve(order); // Checks split with order constructor - BezierCurveType b4Curve; // Checks split with default constructor - b2Curve.split(.5, b3Curve, b4Curve); - - CoordType b3Coords[12] = {0.6, .95, 1.525, 2.05, - 1.2, 1.4, 1.7, 2.0875, - 1.0, 1.4, 1.725, 2.0375}; - CoordType b4Coords[12] = {2.05, 2.575, 3.05, 3.2, - 2.0875, 2.475, 2.95, 3.5, - 2.0375, 2.35, 2.65, 3.0}; - BezierCurveType b3True(b3Coords,3); - BezierCurveType b4True(b4Coords,3); - for ( int i=0 ; i; using PointType = primal::Point< CoordType, DIM >; using BezierCurveType = primal::BezierCurve< CoordType, DIM >; - // Test order-0 - { - const int order = 0; - PointType data[order+1] = { PointType::make_point(0.6, 1.2)}; - BezierCurveType b(data, order); - - BezierCurveType c1,c2; - b.split(0.5, c1, c2); - - for ( int i=0 ; i; - using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + CurvedPolygonType bPolygon; + EXPECT_EQ(0, bPolygon.numEdges()); - const int order = 1; - PointType data[order+1] = { PointType::make_point(-1, -5), - PointType::make_point( 1, 5) }; - BezierCurveType b(data, order); + PointType controlPoints[2] = { + PointType::make_point(0.6, 1.2), + PointType::make_point(0.0, 1.6) + }; - { - BezierCurveType c1,c2; - b.split(0.5, c1, c2); + PointType controlPoints2[2] = { + PointType::make_point( 0.0, 1.6), + PointType::make_point( 0.3 , 2.0) + }; - EXPECT_DOUBLE_EQ(-1., c1[0][0]); - EXPECT_DOUBLE_EQ(-5., c1[0][1]); - EXPECT_DOUBLE_EQ( 0., c1[1][0]); - EXPECT_DOUBLE_EQ( 0., c1[1][1]); + PointType controlPoints3[2] = { + PointType::make_point( 0.3 , 2.0), + PointType::make_point( 0.6, 1.2) + }; - EXPECT_DOUBLE_EQ( 0., c2[0][0]); - EXPECT_DOUBLE_EQ( 0., c2[0][1]); - EXPECT_DOUBLE_EQ( 1., c2[1][0]); - EXPECT_DOUBLE_EQ( 5., c2[1][1]); + BezierCurveType bCurve(controlPoints,1); + bPolygon.addEdge(bCurve); + + BezierCurveType bCurve2(controlPoints2,1); + bPolygon.addEdge(bCurve2); - } + BezierCurveType bCurve3(controlPoints3,1); + bPolygon.addEdge(bCurve3); +/* bPolygon.splitEdge(0,.5); + bCurve.split(.5,bCurve2,bCurve3); +*/ + CurvedPolygonType bPolygon2=bPolygon; + std::vector bPolygon3; + + EXPECT_EQ(bPolygon.numEdges(),3); + for (int i=0; i< bPolygon[0].getOrder(); ++i) { - BezierCurveType c1,c2; - const double t = 0.25; - b.split(0.25, c1, c2); - - PointType interp = PointType::lerp(data[0], data[1], t); - - for ( int i=0 ; i; - using BezierCurveType = primal::BezierCurve< CoordType, DIM >; - - const double t = .42; - const int order = 2; - - // Control points for the three levels of the quadratic de Casteljau algorithm - PointType lev0[3] = { PointType::make_point(1.1, 1.1), - PointType::make_point( 5.5, 5.5), - PointType::make_point( 9.9, 2.2) }; - - PointType lev1[2] = { PointType::lerp(lev0[0], lev0[1], t), - PointType::lerp(lev0[1], lev0[2], t) }; - - PointType lev2[1] = { PointType::lerp(lev1[0], lev1[1], t) }; - - BezierCurveType b(lev0, order); - - // Define expected control points for curves 1 and 2 - BezierCurveType expC1(order); - expC1[0] = lev0[0]; - expC1[1] = lev1[0]; - expC1[2] = lev2[0]; - - BezierCurveType expC2(order); - expC2[0] = lev2[0]; - expC2[1] = lev1[1]; - expC2[2] = lev0[2]; - - // Split the curve - BezierCurveType c1,c2; - b.split(t, c1, c2); - - SLIC_INFO("" - <<"Original quadratic: "<< b - << "\nCurves after splitting at t = "<< t - << "\n\t c1: " << c1 - << "\n\t c2: " << c2); - - // Check values - for(int p=0 ; p <= order ; ++p) + for (int j=0; j< bPolygon.numEdges(); ++j) { - for ( int i=0 ; i Date: Tue, 23 Jul 2019 14:42:48 -0700 Subject: [PATCH 18/44] Added more CurvedPolygon tests --- src/axom/primal/geometry/CurvedPolygon.hpp | 4 +- .../operators/intersect_curved_poly.hpp | 40 +++--- .../primal/tests/primal_curved_polygon.cpp | 135 ++++++++++++++---- 3 files changed, 135 insertions(+), 44 deletions(-) diff --git a/src/axom/primal/geometry/CurvedPolygon.hpp b/src/axom/primal/geometry/CurvedPolygon.hpp index 6e85e8813e..af37d78b2a 100644 --- a/src/axom/primal/geometry/CurvedPolygon.hpp +++ b/src/axom/primal/geometry/CurvedPolygon.hpp @@ -181,12 +181,12 @@ class CurvedPolygon { for (int i=0; i<(ngon-1); ++i) { - if (!axom::utilities::isNearlyEqual(m_edges[i][m_edges[i].getOrder()][p],m_edges[i+1][0][p])) + if (!axom::utilities::isNearlyEqual(m_edges[i][m_edges[i].getOrder()][p],m_edges[i+1][0][p],1e-15)) { return false; } } - if (!axom::utilities::isNearlyEqual(m_edges[ngon-1][m_edges[ngon-1].getOrder()][p],m_edges[0][0][p])) + if (!axom::utilities::isNearlyEqual(m_edges[ngon-1][m_edges[ngon-1].getOrder()][p],m_edges[0][0][p],1e-15)) { return false; } diff --git a/src/axom/primal/operators/intersect_curved_poly.hpp b/src/axom/primal/operators/intersect_curved_poly.hpp index 898a7148cc..6249ffd159 100644 --- a/src/axom/primal/operators/intersect_curved_poly.hpp +++ b/src/axom/primal/operators/intersect_curved_poly.hpp @@ -40,12 +40,11 @@ template bool orient(BezierCurve c1, BezierCurve c2, T s, T t); */ /*! - * \brief Tests if Bezier Curves c1 and c2 intersect. - * \return status true iff c1 intersects with c2, otherwise false. + * \brief Test whether CurvedPolygons p1 and p2 intersect. + * \return status true iff p1 intersects with p2, otherwise false. * - * \param c1, c2 BezierCurve objects to intersect - * \param sp, tp vector of type T parameter space intersection points (t-values - * and s-values) for c1 and c2, respectively + * \param p1, p2 CurvedPolygon objects to intersect + * \param pnew vector of type CurvedPolygon holding intersection regions oriented as the original curves were. */ template < typename T, int NDIMS> bool intersect_polygon(CurvedPolygon< T, NDIMS>& p1, @@ -56,8 +55,7 @@ bool intersect_polygon(CurvedPolygon< T, NDIMS>& p1, // Object to store intersections std::vector>> E1IntData(p1.numEdges()); std::vector>> E2IntData(p2.numEdges()); - - IntersectionInfo firstinter; + IntersectionInfo firstinter; // Need to do orientation test on first intersection // Find all intersections and store int numinters=0; @@ -68,7 +66,7 @@ bool intersect_polygon(CurvedPolygon< T, NDIMS>& p1, std::vector p1times; std::vector p2times; intersect_bezier(p1[i],p2[j],p1times,p2times); - for (int k =0; k< p1times.size(); ++k) + for (int k =0; k< static_cast(p1times.size()); ++k) { E1IntData[i].push_back({p1times[k],i,p2times[k],j,numinters+k+1}); E2IntData[j].push_back({p2times[k],j,p1times[k],i,numinters+k+1}); @@ -85,11 +83,13 @@ bool intersect_polygon(CurvedPolygon< T, NDIMS>& p1, std::sort( E1IntData[i].begin(), E1IntData[i].end() ); std::sort( E2IntData[i].begin(), E2IntData[i].end() ); } - + // Orient the first intersection point to be sure we get the intersection bool orientation = orient(p1[firstinter.myEdge],p2[firstinter.otherEdge],firstinter.myTime,firstinter.otherTime); - std::vector edgelabels[2]; + // Objects to store completely split polygons (split at every intersection point) and vector with unique id for each + // intersection and zeros for corners of original polygons. + std::vector edgelabels[2]; CurvedPolygon psplit[2]; psplit[0]=p1; psplit[1]=p2; @@ -97,17 +97,19 @@ bool intersect_polygon(CurvedPolygon< T, NDIMS>& p1, for (int i = 0; i < p1.numEdges(); ++i) { edgelabels[0].push_back(0); - for (int j = 0; j < E1IntData[i].size(); ++j) + for (int j = 0; j < static_cast(E1IntData[i].size()); ++j) { psplit[0].splitEdge(i+addedints,E1IntData[i][j].myTime); edgelabels[0].insert(edgelabels[0].begin()+i+addedints,E1IntData[i][j].numinter); addedints+=1; - for (int k = j+1; k < (E1IntData[i].size()); ++k) + for (int k = j+1; k < static_cast(E1IntData[i].size()); ++k) { E1IntData[i][k].myTime=(E1IntData[i][k].myTime-E1IntData[i][j].myTime)/(1-E1IntData[i][j].myTime); } } } + + // Debugging code /*std::cout << psplit[0] << std::endl; for (int i=0; i < edgelabels[0].size(); ++i) { @@ -118,22 +120,26 @@ bool intersect_polygon(CurvedPolygon< T, NDIMS>& p1, for (int i = 0; i < p2.numEdges(); ++i) { edgelabels[1].push_back(0); - for (int j = 0; j < E2IntData[i].size(); ++j) + for (int j = 0; j < static_cast(E2IntData[i].size()); ++j) { psplit[1].splitEdge(i+addedints,E2IntData[i][j].myTime); edgelabels[1].insert(edgelabels[1].begin()+i+addedints,E2IntData[i][j].numinter); addedints+=1; - for (int k = j+1; k < (E2IntData[i].size()); ++k) + for (int k = j+1; k < static_cast(E2IntData[i].size()); ++k) { E2IntData[i][k].myTime=(E2IntData[i][k].myTime-E2IntData[i][j].myTime)/(1-E2IntData[i][j].myTime); } } } + + // Debugging code /*std::cout << psplit[1] << std::endl; for (int i=0; i < edgelabels[1].size(); ++i) { std::cout << edgelabels[1][i] << std::endl; }*/ + + // This performs the directional walking method using the completely split polygon std::vector::iterator> usedlabels; if (numinters==0) {return false;} // If there are no intersections, return false else @@ -181,18 +187,20 @@ bool intersect_polygon(CurvedPolygon< T, NDIMS>& p1, currentit=nextit; nextit = std::find(edgelabels[currentelement].begin(),edgelabels[currentelement].end(),nextinter)-edgelabels[currentelement].begin(); } - std::cout << aPart << std::endl; + // std::cout << aPart << std::endl; } pnew.push_back(aPart); } addingcurves=false; } - std::cout << pnew[0] << std::endl; + // std::cout << pnew[0] << std::endl; return true; } +// This determines with curve is "more" counterclockwise using signed distance +// It could potentially be changed to use a cross product of tangents instead template bool orient(const BezierCurve c1, const BezierCurve c2, T s, T t) { diff --git a/src/axom/primal/tests/primal_curved_polygon.cpp b/src/axom/primal/tests/primal_curved_polygon.cpp index cccd5fd739..bba444a6c7 100644 --- a/src/axom/primal/tests/primal_curved_polygon.cpp +++ b/src/axom/primal/tests/primal_curved_polygon.cpp @@ -128,7 +128,7 @@ TEST( primal_curvedpolygon, is_Valid ) } //---------------------------------------------------------------------------------- -TEST( primal_beziercurve, area ) +TEST( primal_beziercurve, area_triangle_linear ) { const int DIM = 2; using CoordType = double; @@ -136,24 +136,24 @@ TEST( primal_beziercurve, area ) using PointType = primal::Point< CoordType, DIM >; using BezierCurveType = primal::BezierCurve< CoordType, DIM >; - SLIC_INFO("Test checking CurvedPolygon area computation."); + SLIC_INFO("Test checking CurvedPolygon linear area triangle computation."); CurvedPolygonType bPolygon; EXPECT_EQ(0, bPolygon.numEdges()); - PointType controlPoints3[2] = { - PointType::make_point(0.0, 1.6), - PointType::make_point(0.6, 1.2) + PointType controlPoints[2] = { + PointType::make_point(0.6, 1.2), + PointType::make_point(0.0, 1.6) }; PointType controlPoints2[2] = { - PointType::make_point( 0.3 , 2.0), - PointType::make_point( 0.0, 1.6) + PointType::make_point( 0.0, 1.6), + PointType::make_point( 0.3 , 2.0) }; - PointType controlPoints[2] = { - PointType::make_point( 0.6, 1.2), - PointType::make_point( 0.3 , 2.0) + PointType controlPoints3[2] = { + PointType::make_point( 0.3 , 2.0), + PointType::make_point( 0.6, 1.2) }; BezierCurveType bCurve(controlPoints,1); @@ -166,9 +166,103 @@ TEST( primal_beziercurve, area ) bPolygon.addEdge(bCurve3); CoordType A= bPolygon.area(); - CoordType trueA= -.18; + CoordType trueA= .18; - EXPECT_TRUE(axom::utilities::isNearlyEqual(trueA,A)); + EXPECT_DOUBLE_EQ(trueA,A); +} + +//---------------------------------------------------------------------------------- +TEST( primal_beziercurve, area_triangle_quadratic ) +{ + const int DIM = 2; + const int order =2; + using CoordType = double; + using CurvedPolygonType = primal::CurvedPolygon< CoordType, DIM>; + using PointType = primal::Point< CoordType, DIM >; + using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + + SLIC_INFO("Test checking CurvedPolygon linear area triangle computation."); + + CurvedPolygonType bPolygon; + EXPECT_EQ(0, bPolygon.numEdges()); + + PointType controlPoints[order+1] = { + PointType::make_point(0.6, 1.2), + PointType::make_point(0.4, 1.3), + PointType::make_point(0.3, 2.0) + }; + + PointType controlPoints2[order+1] = { + PointType::make_point( 0.3, 2.0), + PointType::make_point( 0.27, 1.5), + PointType::make_point( 0.0 , 1.6) + }; + + PointType controlPoints3[order+1] = { + PointType::make_point( 0.0 , 1.6), + PointType::make_point( 0.1 , 1.5), + PointType::make_point( 0.6, 1.2) + }; + + BezierCurveType bCurve(controlPoints,order); + bPolygon.addEdge(bCurve); + + BezierCurveType bCurve2(controlPoints2,order); + bPolygon.addEdge(bCurve2); + + BezierCurveType bCurve3(controlPoints3,order); + bPolygon.addEdge(bCurve3); + + CoordType A = bPolygon.area(); + + CoordType trueA = -.09733333333333333333; + EXPECT_DOUBLE_EQ(trueA,A); +} + +//---------------------------------------------------------------------------------- +TEST( primal_beziercurve, area_triangle_mixed_order) +{ + const int DIM = 2; + using CoordType = double; + using CurvedPolygonType = primal::CurvedPolygon< CoordType, DIM>; + using PointType = primal::Point< CoordType, DIM >; + using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + + SLIC_INFO("Test checking CurvedPolygon linear area triangle computation."); + + CurvedPolygonType bPolygon; + EXPECT_EQ(0, bPolygon.numEdges()); + + PointType controlPoints[3] = { + PointType::make_point(0.6, 1.2), + PointType::make_point(0.4, 1.3), + PointType::make_point(0.3, 2.0) + }; + + PointType controlPoints2[3] = { + PointType::make_point( 0.3, 2.0), + PointType::make_point( 0.27, 1.5), + PointType::make_point( 0.0 , 1.6) + }; + + PointType controlPoints3[2] = { + PointType::make_point( 0.0 , 1.6), + PointType::make_point( 0.6, 1.2) + }; + + BezierCurveType bCurve(controlPoints,2); + bPolygon.addEdge(bCurve); + + BezierCurveType bCurve2(controlPoints2,2); + bPolygon.addEdge(bCurve2); + + BezierCurveType bCurve3(controlPoints3,1); + bPolygon.addEdge(bCurve3); + + CoordType A = bPolygon.area(); + + CoordType trueA = -.0906666666666666666666; + EXPECT_DOUBLE_EQ(trueA,A); } //---------------------------------------------------------------------------------- @@ -209,13 +303,13 @@ TEST( primal_beziercurve, split_edge) BezierCurveType bCurve3(controlPoints3,1); bPolygon.addEdge(bCurve3); -/* bPolygon.splitEdge(0,.5); + bPolygon.splitEdge(0,.5); bCurve.split(.5,bCurve2,bCurve3); -*/ + CurvedPolygonType bPolygon2=bPolygon; std::vector bPolygon3; - EXPECT_EQ(bPolygon.numEdges(),3); + EXPECT_EQ(bPolygon.numEdges(),4); for (int i=0; i< bPolygon[0].getOrder(); ++i) { for (int dimi=0; dimi< DIM; ++dimi) @@ -224,17 +318,6 @@ TEST( primal_beziercurve, split_edge) EXPECT_EQ(bPolygon[1][i][dimi],bCurve3[i][dimi]); } } - for (int j=0; j< bPolygon.numEdges(); ++j) - { - for (int i=0; i<= bPolygon[j].getOrder(); ++i) - { - for (int dimi=0; dimi< DIM; ++dimi) - { - bPolygon2[j][i][dimi]+=.11; - } - } - } - bool TF = intersect_polygon(bPolygon,bPolygon2,bPolygon3); } int main(int argc, char* argv[]) From 575be5a00c9ab172da7a076a741ee2faa5e2a67b Mon Sep 17 00:00:00 2001 From: David Gunderman Date: Tue, 23 Jul 2019 17:52:09 -0700 Subject: [PATCH 19/44] Added intersection tests. TODO: precision of curvedpolygon intersection appears to be only 10^-10 --- .../operators/intersect_curved_poly.hpp | 26 +- .../primal/tests/primal_curved_polygon.cpp | 306 ++++++++++++++++-- 2 files changed, 292 insertions(+), 40 deletions(-) diff --git a/src/axom/primal/operators/intersect_curved_poly.hpp b/src/axom/primal/operators/intersect_curved_poly.hpp index 6249ffd159..f626b604c8 100644 --- a/src/axom/primal/operators/intersect_curved_poly.hpp +++ b/src/axom/primal/operators/intersect_curved_poly.hpp @@ -85,7 +85,7 @@ bool intersect_polygon(CurvedPolygon< T, NDIMS>& p1, } // Orient the first intersection point to be sure we get the intersection - bool orientation = orient(p1[firstinter.myEdge],p2[firstinter.otherEdge],firstinter.myTime,firstinter.otherTime); + bool orientation = !orient(p1[firstinter.myEdge],p2[firstinter.otherEdge],firstinter.myTime,firstinter.otherTime); // Objects to store completely split polygons (split at every intersection point) and vector with unique id for each // intersection and zeros for corners of original polygons. @@ -110,11 +110,11 @@ bool intersect_polygon(CurvedPolygon< T, NDIMS>& p1, } // Debugging code - /*std::cout << psplit[0] << std::endl; - for (int i=0; i < edgelabels[0].size(); ++i) + std::cout << psplit[0] << std::endl; + for (int i=0; i < static_cast(edgelabels[0].size()); ++i) { std::cout << edgelabels[0][i] << std::endl; - }*/ + } addedints=0; for (int i = 0; i < p2.numEdges(); ++i) @@ -133,11 +133,11 @@ bool intersect_polygon(CurvedPolygon< T, NDIMS>& p1, } // Debugging code - /*std::cout << psplit[1] << std::endl; - for (int i=0; i < edgelabels[1].size(); ++i) + std::cout << psplit[1] << std::endl; + for (int i=0; i < static_cast(edgelabels[1].size()); ++i) { std::cout << edgelabels[1][i] << std::endl; - }*/ + } // This performs the directional walking method using the completely split polygon std::vector::iterator> usedlabels; @@ -157,9 +157,8 @@ bool intersect_polygon(CurvedPolygon< T, NDIMS>& p1, CurvedPolygon aPart; // To store the current intersection polygon (could be multiple) while (!(nextit==startit && currentelement==orientation)) { - if (nextit==currentit) + if (nextit==currentit ) { - currentit=nextit; nextit=(currentit+1)%edgelabels[0].size(); } nextinter=edgelabels[currentelement][nextit]; @@ -169,16 +168,18 @@ bool intersect_polygon(CurvedPolygon< T, NDIMS>& p1, if (addingcurves) { aPart.addEdge(psplit[currentelement][nextit]); + std::cout << 0 ; } nextit=(currentit+1)%edgelabels[0].size(); nextinter=edgelabels[currentelement][nextit]; + std::cout << 0 << std::endl; } if (addingcurves) { aPart.addEdge(psplit[currentelement][nextit]); currentelement=!currentelement; - currentit=nextit; nextit = std::find(edgelabels[currentelement].begin(),edgelabels[currentelement].end(),nextinter)-edgelabels[currentelement].begin(); + currentit=nextit; numinters-=1; } else @@ -187,13 +188,14 @@ bool intersect_polygon(CurvedPolygon< T, NDIMS>& p1, currentit=nextit; nextit = std::find(edgelabels[currentelement].begin(),edgelabels[currentelement].end(),nextinter)-edgelabels[currentelement].begin(); } - // std::cout << aPart << std::endl; + std::cout << numinters; + std::cout << aPart << std::endl; } pnew.push_back(aPart); } addingcurves=false; } - // std::cout << pnew[0] << std::endl; + std::cout << pnew[0] << std::endl; return true; diff --git a/src/axom/primal/tests/primal_curved_polygon.cpp b/src/axom/primal/tests/primal_curved_polygon.cpp index bba444a6c7..aad7a13329 100644 --- a/src/axom/primal/tests/primal_curved_polygon.cpp +++ b/src/axom/primal/tests/primal_curved_polygon.cpp @@ -34,7 +34,7 @@ TEST( primal_curvedpolygon, constructor ) } { - SLIC_INFO("Testing CurvedPolygon order constructor "); + SLIC_INFO("Testing CurvedPolygon numEdges constructor "); CurvedPolygonType bPolygon(1); int expNumEdges = 1; @@ -83,7 +83,7 @@ TEST( primal_curvedpolygon, add_edges ) } //---------------------------------------------------------------------------------- -TEST( primal_curvedpolygon, is_Valid ) +TEST( primal_curvedpolygon, isClosed ) { const int DIM = 2; using CoordType = double; @@ -95,6 +95,7 @@ TEST( primal_curvedpolygon, is_Valid ) CurvedPolygonType bPolygon; EXPECT_EQ(0, bPolygon.numEdges()); + EXPECT_EQ(false,bPolygon.isClosed()); PointType controlPoints[2] = { PointType::make_point(0.6, 1.2), @@ -106,6 +107,7 @@ TEST( primal_curvedpolygon, is_Valid ) PointType::make_point( 0.3 , 2.0) }; + PointType controlPoints3[2] = { PointType::make_point( 0.3 , 2.0), PointType::make_point( 0.6, 1.2) @@ -113,6 +115,7 @@ TEST( primal_curvedpolygon, is_Valid ) BezierCurveType bCurve(controlPoints,1); bPolygon.addEdge(bCurve); + EXPECT_EQ(false,bPolygon.isClosed()); BezierCurveType bCurve2(controlPoints2,1); bPolygon.addEdge(bCurve2); @@ -125,10 +128,65 @@ TEST( primal_curvedpolygon, is_Valid ) EXPECT_EQ(3,bPolygon.numEdges()); EXPECT_EQ(true,bPolygon.isClosed()); + + bPolygon[2][1][0]-=2e-15; + EXPECT_EQ(false,bPolygon.isClosed()); +} + +//---------------------------------------------------------------------------------- +TEST( primal_curvedpolygon, split_edge) +{ + const int DIM = 2; + using CoordType = double; + using CurvedPolygonType = primal::CurvedPolygon< CoordType, DIM>; + using PointType = primal::Point< CoordType, DIM >; + using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + + SLIC_INFO("Test checking CurvedPolygon edge split."); + + CurvedPolygonType bPolygon; + EXPECT_EQ(0, bPolygon.numEdges()); + + PointType controlPoints[2] = { + PointType::make_point(0.6, 1.2), + PointType::make_point(0.0, 1.6) + }; + + PointType controlPoints2[2] = { + PointType::make_point( 0.0, 1.6), + PointType::make_point( 0.3 , 2.0) + }; + + PointType controlPoints3[2] = { + PointType::make_point( 0.3 , 2.0), + PointType::make_point( 0.6, 1.2) + }; + + BezierCurveType bCurve(controlPoints,1); + bPolygon.addEdge(bCurve); + + BezierCurveType bCurve2(controlPoints2,1); + bPolygon.addEdge(bCurve2); + + BezierCurveType bCurve3(controlPoints3,1); + bPolygon.addEdge(bCurve3); + + bPolygon.splitEdge(0,.5); + bCurve.split(.5,bCurve2,bCurve3); + + EXPECT_EQ(bPolygon.numEdges(),4); + for (int i=0; i< bPolygon[0].getOrder(); ++i) + { + for (int dimi=0; dimi< DIM; ++dimi) + { + EXPECT_EQ(bPolygon[0][i][dimi],bCurve2[i][dimi]); + EXPECT_EQ(bPolygon[1][i][dimi],bCurve3[i][dimi]); + } + } } //---------------------------------------------------------------------------------- -TEST( primal_beziercurve, area_triangle_linear ) +TEST( primal_curvedpolygon, area_triangle_degenerate) { const int DIM = 2; using CoordType = double; @@ -136,7 +194,53 @@ TEST( primal_beziercurve, area_triangle_linear ) using PointType = primal::Point< CoordType, DIM >; using BezierCurveType = primal::BezierCurve< CoordType, DIM >; - SLIC_INFO("Test checking CurvedPolygon linear area triangle computation."); + SLIC_INFO("Test checking CurvedPolygon degenerate triangle area computation."); + + CurvedPolygonType bPolygon; + EXPECT_EQ(0, bPolygon.numEdges()); + EXPECT_EQ(0.0, bPolygon.area()); + + PointType controlPoints[2] = { + PointType::make_point(0.6, 1.2), + PointType::make_point(0.0, 1.6) + }; + + PointType controlPoints2[2] = { + PointType::make_point( 0.0, 1.6), + PointType::make_point( 0.3 , 2.0) + }; + + PointType controlPoints3[2] = { + PointType::make_point( 0.3 , 2.0), + PointType::make_point( 0.6, 1.2) + }; + + BezierCurveType bCurve(controlPoints,1); + bPolygon.addEdge(bCurve); + EXPECT_EQ(0.0, bPolygon.area()); + + BezierCurveType bCurve2(controlPoints2,1); + bPolygon.addEdge(bCurve2); + EXPECT_EQ(0.0, bPolygon.area()); + + BezierCurveType bCurve3(controlPoints3,1); + bPolygon.addEdge(bCurve3); + + bPolygon[2][1][0]-=2e-15; + EXPECT_EQ(0.0, bPolygon.area()); + +} + +//---------------------------------------------------------------------------------- +TEST( primal_curvedpolygon, area_triangle_linear ) +{ + const int DIM = 2; + using CoordType = double; + using CurvedPolygonType = primal::CurvedPolygon< CoordType, DIM>; + using PointType = primal::Point< CoordType, DIM >; + using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + + SLIC_INFO("Test checking CurvedPolygon linear triangle area computation."); CurvedPolygonType bPolygon; EXPECT_EQ(0, bPolygon.numEdges()); @@ -172,7 +276,7 @@ TEST( primal_beziercurve, area_triangle_linear ) } //---------------------------------------------------------------------------------- -TEST( primal_beziercurve, area_triangle_quadratic ) +TEST( primal_curvedpolygon, area_triangle_quadratic ) { const int DIM = 2; const int order =2; @@ -181,7 +285,7 @@ TEST( primal_beziercurve, area_triangle_quadratic ) using PointType = primal::Point< CoordType, DIM >; using BezierCurveType = primal::BezierCurve< CoordType, DIM >; - SLIC_INFO("Test checking CurvedPolygon linear area triangle computation."); + SLIC_INFO("Test checking CurvedPolygon quadratic triangle area computation."); CurvedPolygonType bPolygon; EXPECT_EQ(0, bPolygon.numEdges()); @@ -220,7 +324,7 @@ TEST( primal_beziercurve, area_triangle_quadratic ) } //---------------------------------------------------------------------------------- -TEST( primal_beziercurve, area_triangle_mixed_order) +TEST( primal_curvedpolygon, area_triangle_mixed_order) { const int DIM = 2; using CoordType = double; @@ -228,7 +332,7 @@ TEST( primal_beziercurve, area_triangle_mixed_order) using PointType = primal::Point< CoordType, DIM >; using BezierCurveType = primal::BezierCurve< CoordType, DIM >; - SLIC_INFO("Test checking CurvedPolygon linear area triangle computation."); + SLIC_INFO("Test checking CurvedPolygon mixed order triangle area computation."); CurvedPolygonType bPolygon; EXPECT_EQ(0, bPolygon.numEdges()); @@ -266,60 +370,206 @@ TEST( primal_beziercurve, area_triangle_mixed_order) } //---------------------------------------------------------------------------------- -TEST( primal_beziercurve, split_edge) +TEST( primal_curvedpolygon, intersection_triangle_linear) { const int DIM = 2; + const int order = 1; using CoordType = double; using CurvedPolygonType = primal::CurvedPolygon< CoordType, DIM>; using PointType = primal::Point< CoordType, DIM >; using BezierCurveType = primal::BezierCurve< CoordType, DIM >; - SLIC_INFO("Test checking CurvedPolygon edge split."); + SLIC_INFO("Test intersecting two linear triangular CurvedPolygons (single region)."); CurvedPolygonType bPolygon; EXPECT_EQ(0, bPolygon.numEdges()); - PointType controlPoints[2] = { + PointType controlPoints[order+1] = { PointType::make_point(0.6, 1.2), - PointType::make_point(0.0, 1.6) + PointType::make_point(0.3, 2.0) }; - PointType controlPoints2[2] = { - PointType::make_point( 0.0, 1.6), - PointType::make_point( 0.3 , 2.0) + PointType controlPoints2[order+1] = { + PointType::make_point( 0.3, 2.0), + PointType::make_point( 0.0 , 1.6) }; - PointType controlPoints3[2] = { - PointType::make_point( 0.3 , 2.0), + PointType controlPoints3[order+1] = { + PointType::make_point( 0.0 , 1.6), PointType::make_point( 0.6, 1.2) }; - BezierCurveType bCurve(controlPoints,1); + BezierCurveType bCurve(controlPoints,order); bPolygon.addEdge(bCurve); - BezierCurveType bCurve2(controlPoints2,1); + BezierCurveType bCurve2(controlPoints2,order); bPolygon.addEdge(bCurve2); - BezierCurveType bCurve3(controlPoints3,1); + BezierCurveType bCurve3(controlPoints3,order); bPolygon.addEdge(bCurve3); + CurvedPolygonType bPolygon2=bPolygon; + for (int i=0; i bPolygons3; + bool didIntersect=intersect_polygon(bPolygon,bPolygon2,bPolygons3); + EXPECT_TRUE(didIntersect); + + for (int i=0; i(bPolygons3.size()); ++idxcurve) + { + for (int k=0; k; + using PointType = primal::Point< CoordType, DIM >; + using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + + SLIC_INFO("Test intersecting two quadratic triangular CurvedPolygons (single region)."); + + CurvedPolygonType bPolygon; + EXPECT_EQ(0, bPolygon.numEdges()); + PointType controlPoints[order+1] = { + PointType::make_point(0.6, 1.2), + PointType::make_point(0.4, 1.3), + PointType::make_point(0.3, 2.0) + }; + + PointType controlPoints2[order+1] = { + PointType::make_point( 0.3, 2.0), + PointType::make_point( 0.27, 1.5), + PointType::make_point( 0.0 , 1.6) + }; + + PointType controlPoints3[order+1] = { + PointType::make_point( 0.0 , 1.6), + PointType::make_point( 0.1 , 1.5), + PointType::make_point( 0.6, 1.2) + }; + + BezierCurveType bCurve(controlPoints,order); + bPolygon.addEdge(bCurve); + + BezierCurveType bCurve2(controlPoints2,order); + bPolygon.addEdge(bCurve2); + + BezierCurveType bCurve3(controlPoints3,order); + bPolygon.addEdge(bCurve3); CurvedPolygonType bPolygon2=bPolygon; - std::vector bPolygon3; + for (int i=0; i bPolygons3; + bool didIntersect=intersect_polygon(bPolygon,bPolygon2,bPolygons3); + EXPECT_TRUE(didIntersect); + + for (int i=0; i(bPolygons3.size()); ++idxcurve) + { + for (int k=0; k Date: Thu, 25 Jul 2019 13:14:06 -0700 Subject: [PATCH 20/44] Added tolerances to CurvedPolygon functions, unit tests for area and intersections, dt function (tangent vector of BezierCurve), and stable orientation function for directional walking method --- src/axom/primal/geometry/BezierCurve.hpp | 40 +++- src/axom/primal/geometry/CurvedPolygon.hpp | 20 +- .../operators/intersect_curved_poly.hpp | 37 +--- src/axom/primal/tests/primal_bezier_curve.cpp | 38 +++- .../primal/tests/primal_curved_polygon.cpp | 185 +++++++++++++++--- 5 files changed, 250 insertions(+), 70 deletions(-) diff --git a/src/axom/primal/geometry/BezierCurve.hpp b/src/axom/primal/geometry/BezierCurve.hpp index efd8a49e6e..3bfc7f5e2c 100644 --- a/src/axom/primal/geometry/BezierCurve.hpp +++ b/src/axom/primal/geometry/BezierCurve.hpp @@ -230,6 +230,44 @@ class BezierCurve return ptval; } + /*! + * \brief Computes the tangent of a Bezier curve at a particular parameter value \a t + * + * \param [in] t parameter value at which to compute tangent + * \return p the tangent vector of the Bezier curve at t + * + * \note We typically find the tangent of the curve at \a t between 0 and 1 + */ + + PointType dt(T t) const + { + PointType ptval; + + const int ord = getOrder(); + std::vector dCarray(ord+1); + + // Run de Casteljau algorithm on each dimension + for ( int i=0 ; i < NDIMS ; ++i) + { + for ( int p=0 ; p <= ord ; ++p) + { + dCarray[p] = m_controlPoints[p][i]; + } + + for ( int p=1 ; p <= ord-1 ; ++p) + { + const int end = ord-p; + for ( int k=0 ; k <= end ; ++k) + { + dCarray[k]=(1-t)*dCarray[k] + t*dCarray[k+1]; + } + } + ptval[i]=ord*(dCarray[1]-dCarray[0]); + } + + return ptval; + } + /*! * \brief Splits a Bezier curve into two Bezier curves at particular parameter * value between 0 and 1 @@ -306,7 +344,7 @@ class BezierCurve { for (int j=0; j<=ord; ++j) { - A+=static_cast(whicharea[i*(ord+1)+j])*m_controlpoints[i][1]*m_controlpoints[j][0]; + A+=static_cast(whicharea[i*(ord+1)+j])*m_controlPoints[i][1]*m_controlPoints[j][0]; } } return A; diff --git a/src/axom/primal/geometry/CurvedPolygon.hpp b/src/axom/primal/geometry/CurvedPolygon.hpp index af37d78b2a..be805ecdd0 100644 --- a/src/axom/primal/geometry/CurvedPolygon.hpp +++ b/src/axom/primal/geometry/CurvedPolygon.hpp @@ -18,7 +18,7 @@ #include "axom/primal/geometry/Vector.hpp" #include "axom/primal/geometry/NumericArray.hpp" #include "axom/primal/geometry/BezierCurve.hpp" -#include "axom/primal/operators/intersect_bezier.hpp" +#include "axom/primal/operators/intersect.hpp" #include "fmt/fmt.hpp" #include @@ -168,10 +168,10 @@ class CurvedPolygon * Check is that the endpoint of each edge coincides with startpoint of next edge * \return True, if the polygon is closed, False otherwise */ - bool isClosed() const + bool isClosed(double tol = 1e-15) const { const int ngon = numEdges(); - if (ngon <= 1) + if (ngon <= 2) { return false; } @@ -181,12 +181,12 @@ class CurvedPolygon { for (int i=0; i<(ngon-1); ++i) { - if (!axom::utilities::isNearlyEqual(m_edges[i][m_edges[i].getOrder()][p],m_edges[i+1][0][p],1e-15)) + if (!axom::utilities::isNearlyEqual(m_edges[i][m_edges[i].getOrder()][p],m_edges[i+1][0][p],tol)) { return false; } } - if (!axom::utilities::isNearlyEqual(m_edges[ngon-1][m_edges[ngon-1].getOrder()][p],m_edges[0][0][p],1e-15)) + if (!axom::utilities::isNearlyEqual(m_edges[ngon-1][m_edges[ngon-1].getOrder()][p],m_edges[0][0][p],tol)) { return false; } @@ -202,16 +202,16 @@ class CurvedPolygon * \return True, if the polygon is closed, False otherwise */ - T area() const + T area(double tol = 1e-15) const { const int ngon = numEdges(); T A = 0.0; - if (!isClosed()) { return A ;} - else - { + if (!isClosed(tol)) {return A; SLIC_INFO("Warning! The area is 0 because the element is not closed.");} + else + { for (int ed = 0; ed& p1, { std::vector p1times; std::vector p2times; - intersect_bezier(p1[i],p2[j],p1times,p2times); + intersect(p1[i],p2[j],p1times,p2times,1e-15); for (int k =0; k< static_cast(p1times.size()); ++k) { E1IntData[i].push_back({p1times[k],i,p2times[k],j,numinters+k+1}); @@ -109,13 +108,6 @@ bool intersect_polygon(CurvedPolygon< T, NDIMS>& p1, } } - // Debugging code - std::cout << psplit[0] << std::endl; - for (int i=0; i < static_cast(edgelabels[0].size()); ++i) - { - std::cout << edgelabels[0][i] << std::endl; - } - addedints=0; for (int i = 0; i < p2.numEdges(); ++i) { @@ -132,13 +124,6 @@ bool intersect_polygon(CurvedPolygon< T, NDIMS>& p1, } } - // Debugging code - std::cout << psplit[1] << std::endl; - for (int i=0; i < static_cast(edgelabels[1].size()); ++i) - { - std::cout << edgelabels[1][i] << std::endl; - } - // This performs the directional walking method using the completely split polygon std::vector::iterator> usedlabels; if (numinters==0) {return false;} // If there are no intersections, return false @@ -168,11 +153,9 @@ bool intersect_polygon(CurvedPolygon< T, NDIMS>& p1, if (addingcurves) { aPart.addEdge(psplit[currentelement][nextit]); - std::cout << 0 ; } nextit=(currentit+1)%edgelabels[0].size(); nextinter=edgelabels[currentelement][nextit]; - std::cout << 0 << std::endl; } if (addingcurves) { @@ -188,29 +171,23 @@ bool intersect_polygon(CurvedPolygon< T, NDIMS>& p1, currentit=nextit; nextit = std::find(edgelabels[currentelement].begin(),edgelabels[currentelement].end(),nextinter)-edgelabels[currentelement].begin(); } - std::cout << numinters; - std::cout << aPart << std::endl; } pnew.push_back(aPart); } addingcurves=false; } - std::cout << pnew[0] << std::endl; - - return true; } -// This determines with curve is "more" counterclockwise using signed distance -// It could potentially be changed to use a cross product of tangents instead +// This determines with curve is "more" counterclockwise using the cross product of tangents template bool orient(const BezierCurve c1, const BezierCurve c2, T s, T t) { - Point c1val1 = c1.evaluate(s+(1e-13)); - Point c1val2 = c1.evaluate(s-(1e-13)); - Point c2val = c2.evaluate(t+(1e-13)); - - return ((-(c1val1[0] - c1val2[0])*(c2val[1]-c1val2[1]))+(c1val1[1]-c1val2[1])*(c2val[0]-c1val2[0]))>0; + Point dc1s = c1.dt(s); + Point dc2t = c2.dt(t); + Point origin = primal::Point< T, NDIMS >::make_point(0.0, 0.0); + auto orientation = detail::twoDcross(dc1s,dc2t, origin); + return (orientation<0); } // A class for storing intersection points so they can be easily sorted by parameter value diff --git a/src/axom/primal/tests/primal_bezier_curve.cpp b/src/axom/primal/tests/primal_bezier_curve.cpp index 56a473e39e..2978292960 100644 --- a/src/axom/primal/tests/primal_bezier_curve.cpp +++ b/src/axom/primal/tests/primal_bezier_curve.cpp @@ -168,6 +168,42 @@ TEST( primal_beziercurve, evaluate) } } +//------------------------------------------------------------------------------ +TEST( primal_beziercurve_, tangent) +{ + SLIC_INFO("Testing Bezier tangent calculation"); + + const int DIM = 3; + using CoordType = double; + using PointType = primal::Point< CoordType, DIM >; + using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + + const int order = 3; + PointType data[order+1] = { PointType::make_point(0.6, 1.2, 1.0), + PointType::make_point(1.3, 1.6, 1.8), + PointType::make_point(2.9, 2.4, 2.3), + PointType::make_point(3.2, 3.5, 3.0) }; + + BezierCurveType b2Curve(data, order); + + PointType midtval = PointType::make_point(3.15,2.325,1.875); + PointType starttval = PointType::make_point(2.1,1.2,2.4); + PointType endtval = PointType::make_point(.9,3.3,2.1); + + // Evaluate the curve at several parameter values + // Curve should be tangent to control net at endpoints + PointType eval0 = b2Curve.dt(0.0); + PointType eval1 = b2Curve.dt(1.0); + PointType evalMid = b2Curve.dt(0.5); + + for ( int i=0 ; i; { - SLIC_INFO("Testing Bezier sector area calculation for a cubic"); + SLIC_INFO("Testing Bezier sector area calculation for a point"); const int order = 0; PointType data[order+1] = { PointType::make_point(0.6, 1.2)}; diff --git a/src/axom/primal/tests/primal_curved_polygon.cpp b/src/axom/primal/tests/primal_curved_polygon.cpp index aad7a13329..25b0ef6fda 100644 --- a/src/axom/primal/tests/primal_curved_polygon.cpp +++ b/src/axom/primal/tests/primal_curved_polygon.cpp @@ -98,18 +98,18 @@ TEST( primal_curvedpolygon, isClosed ) EXPECT_EQ(false,bPolygon.isClosed()); PointType controlPoints[2] = { - PointType::make_point(0.6, 1.2), - PointType::make_point(0.0, 1.6) + PointType::make_point( 0.6, 1.2), + PointType::make_point( 0.3, 2.0) }; PointType controlPoints2[2] = { - PointType::make_point( 0.0, 1.6), - PointType::make_point( 0.3 , 2.0) + PointType::make_point( 0.3, 2.0), + PointType::make_point( 0.0, 1.6) }; PointType controlPoints3[2] = { - PointType::make_point( 0.3 , 2.0), + PointType::make_point( 0.0, 1.6), PointType::make_point( 0.6, 1.2) }; @@ -130,7 +130,7 @@ TEST( primal_curvedpolygon, isClosed ) EXPECT_EQ(true,bPolygon.isClosed()); bPolygon[2][1][0]-=2e-15; - EXPECT_EQ(false,bPolygon.isClosed()); + EXPECT_EQ(false,bPolygon.isClosed(1e-15)); } //---------------------------------------------------------------------------------- @@ -148,17 +148,18 @@ TEST( primal_curvedpolygon, split_edge) EXPECT_EQ(0, bPolygon.numEdges()); PointType controlPoints[2] = { - PointType::make_point(0.6, 1.2), - PointType::make_point(0.0, 1.6) + PointType::make_point( 0.6, 1.2), + PointType::make_point( 0.3, 2.0) }; PointType controlPoints2[2] = { - PointType::make_point( 0.0, 1.6), - PointType::make_point( 0.3 , 2.0) + PointType::make_point( 0.3, 2.0), + PointType::make_point( 0.0, 1.6) }; + PointType controlPoints3[2] = { - PointType::make_point( 0.3 , 2.0), + PointType::make_point( 0.0, 1.6), PointType::make_point( 0.6, 1.2) }; @@ -201,17 +202,16 @@ TEST( primal_curvedpolygon, area_triangle_degenerate) EXPECT_EQ(0.0, bPolygon.area()); PointType controlPoints[2] = { - PointType::make_point(0.6, 1.2), - PointType::make_point(0.0, 1.6) + PointType::make_point( 0.6, 1.2), + PointType::make_point( 0.3, 2.0) }; PointType controlPoints2[2] = { - PointType::make_point( 0.0, 1.6), - PointType::make_point( 0.3 , 2.0) + PointType::make_point( 0.3, 2.0), + PointType::make_point( 0.0, 1.6) }; - PointType controlPoints3[2] = { - PointType::make_point( 0.3 , 2.0), + PointType::make_point( 0.0, 1.6), PointType::make_point( 0.6, 1.2) }; @@ -246,17 +246,16 @@ TEST( primal_curvedpolygon, area_triangle_linear ) EXPECT_EQ(0, bPolygon.numEdges()); PointType controlPoints[2] = { - PointType::make_point(0.6, 1.2), - PointType::make_point(0.0, 1.6) + PointType::make_point( 0.6, 1.2), + PointType::make_point( 0.3, 2.0) }; PointType controlPoints2[2] = { - PointType::make_point( 0.0, 1.6), - PointType::make_point( 0.3 , 2.0) + PointType::make_point( 0.3, 2.0), + PointType::make_point( 0.0, 1.6) }; - PointType controlPoints3[2] = { - PointType::make_point( 0.3 , 2.0), + PointType::make_point( 0.0, 1.6), PointType::make_point( 0.6, 1.2) }; @@ -270,7 +269,7 @@ TEST( primal_curvedpolygon, area_triangle_linear ) bPolygon.addEdge(bCurve3); CoordType A= bPolygon.area(); - CoordType trueA= .18; + CoordType trueA= -.18; EXPECT_DOUBLE_EQ(trueA,A); } @@ -455,7 +454,7 @@ TEST( primal_curvedpolygon, intersection_triangle_linear) { for (int k=0; k; + using PointType = primal::Point< CoordType, DIM >; + using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + + SLIC_INFO("Test finding area of intersection two linear triangular CurvedPolygons (single region)."); + + CurvedPolygonType bPolygon; + EXPECT_EQ(0, bPolygon.numEdges()); + + PointType controlPoints[order+1] = { + PointType::make_point(0.6, 1.2), + PointType::make_point(0.3, 2.0) + }; + + PointType controlPoints2[order+1] = { + PointType::make_point( 0.3, 2.0), + PointType::make_point( 0.0 , 1.6) + }; + + PointType controlPoints3[order+1] = { + PointType::make_point( 0.0 , 1.6), + PointType::make_point( 0.6, 1.2) + }; + + BezierCurveType bCurve(controlPoints,order); + bPolygon.addEdge(bCurve); + + BezierCurveType bCurve2(controlPoints2,order); + bPolygon.addEdge(bCurve2); + + BezierCurveType bCurve3(controlPoints3,order); + bPolygon.addEdge(bCurve3); + CurvedPolygonType bPolygon2=bPolygon; + for (int i=0; i bPolygons3; + bool didIntersect=intersect_polygon(bPolygon,bPolygon2,bPolygons3); + EXPECT_TRUE(didIntersect); + + + CoordType A=0.0; + for (int i=0; i(bPolygons3.size()); ++i) + { + A+=bPolygons3[i].area(1e-14); + } + CoordType expA=-0.0793347222222222222; + EXPECT_NEAR(A,expA,1e-14); +} + +//---------------------------------------------------------------------------------- +TEST( primal_curvedpolygon, area_intersection_triangle_quadratic) +{ + const int DIM = 2; + const int order = 2; + using CoordType = double; + using CurvedPolygonType = primal::CurvedPolygon< CoordType, DIM>; + using PointType = primal::Point< CoordType, DIM >; + using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + + SLIC_INFO("Test intersecting two quadratic triangular CurvedPolygons (single region)."); + + CurvedPolygonType bPolygon; + EXPECT_EQ(0, bPolygon.numEdges()); + + PointType controlPoints[order+1] = { + PointType::make_point(0.6, 1.2), + PointType::make_point(0.4, 1.3), + PointType::make_point(0.3, 2.0) + }; + + PointType controlPoints2[order+1] = { + PointType::make_point( 0.3, 2.0), + PointType::make_point( 0.27, 1.5), + PointType::make_point( 0.0 , 1.6) + }; + + PointType controlPoints3[order+1] = { + PointType::make_point( 0.0 , 1.6), + PointType::make_point( 0.1 , 1.5), + PointType::make_point( 0.6, 1.2) + }; + + BezierCurveType bCurve(controlPoints,order); + bPolygon.addEdge(bCurve); + + BezierCurveType bCurve2(controlPoints2,order); + bPolygon.addEdge(bCurve2); + + BezierCurveType bCurve3(controlPoints3,order); + bPolygon.addEdge(bCurve3); + CurvedPolygonType bPolygon2=bPolygon; + for (int i=0; i bPolygons3; + bool didIntersect=intersect_polygon(bPolygon,bPolygon2,bPolygons3); + EXPECT_TRUE(didIntersect); + + CoordType A=0.0; + for (int i=0; i(bPolygons3.size()); ++i) + { + A+=bPolygons3[i].area(1e-13); + } + CoordType expA=-0.024649833203616; + EXPECT_NEAR(A,expA,1e-14); +} + //---------------------------------------------------------------------------------- int main(int argc, char* argv[]) { From 8081c8fa8f04a1c51b0173e26f8e5bbeadb481a0 Mon Sep 17 00:00:00 2001 From: Kenneth Weiss Date: Thu, 25 Jul 2019 18:27:03 -0700 Subject: [PATCH 21/44] Improves documentation, error checking and testing of binomialCoefficient function --- src/axom/core/tests/utils_utilities.cpp | 87 +++++++++++++++++++++++++ src/axom/core/utilities/Utilities.cpp | 36 ++++++---- src/axom/core/utilities/Utilities.hpp | 10 ++- 3 files changed, 120 insertions(+), 13 deletions(-) diff --git a/src/axom/core/tests/utils_utilities.cpp b/src/axom/core/tests/utils_utilities.cpp index 85c092b7e8..b321462ec7 100644 --- a/src/axom/core/tests/utils_utilities.cpp +++ b/src/axom/core/tests/utils_utilities.cpp @@ -116,3 +116,90 @@ TEST(core_Utilities,minmax) EXPECT_EQ(a, axom::utilities::max(a,b)); } } + +TEST(core_Utilities, binomial_coefficient) +{ + std::cout<<"Testing binomial coefficient function."<< std::endl; + + // test n less than zero + { + const int n = -1; + const int exp = 0; + for(int k=-1 ; k < 10 ; ++k) + { + auto binom_k_n = axom::utilities::binomialCoefficient(n,k); + EXPECT_EQ( exp, binom_k_n); + } + } + + // test n := 0 + { + const int n = 0; + + EXPECT_EQ( 1, axom::utilities::binomialCoefficient(n,0)); + + EXPECT_EQ( 0, axom::utilities::binomialCoefficient(n,-1)); + EXPECT_EQ( 0, axom::utilities::binomialCoefficient(n, 1)); + + } + + // test n := 1 + { + const int n = 1; + + EXPECT_EQ( 0, axom::utilities::binomialCoefficient(n,-1)); + + EXPECT_EQ( 1, axom::utilities::binomialCoefficient(n, 0)); + EXPECT_EQ( 1, axom::utilities::binomialCoefficient(n, 1)); + + EXPECT_EQ( 0, axom::utilities::binomialCoefficient(n, 2)); + + } + + // test n := 2 + { + const int n = 2; + + EXPECT_EQ( 1, axom::utilities::binomialCoefficient(n,0)); + EXPECT_EQ( 2, axom::utilities::binomialCoefficient(n,1)); + EXPECT_EQ( 1, axom::utilities::binomialCoefficient(n,2)); + + } + + // test n := 3 + { + const int n = 3; + + EXPECT_EQ( 1, axom::utilities::binomialCoefficient(n,0)); + EXPECT_EQ( 3, axom::utilities::binomialCoefficient(n,1)); + EXPECT_EQ( 3, axom::utilities::binomialCoefficient(n,2)); + EXPECT_EQ( 1, axom::utilities::binomialCoefficient(n,3)); + } + + // test n := 4 + { + const int n = 4; + + EXPECT_EQ( 1, axom::utilities::binomialCoefficient(n,0)); + EXPECT_EQ( 4, axom::utilities::binomialCoefficient(n,1)); + EXPECT_EQ( 6, axom::utilities::binomialCoefficient(n,2)); + EXPECT_EQ( 4, axom::utilities::binomialCoefficient(n,3)); + EXPECT_EQ( 1, axom::utilities::binomialCoefficient(n,4)); + } + + // test recurrence relation nCk = (n-1)C(k-1) + (n-1)C(k) + { + for(int n = 1 ; n < 10 ; ++n) + { + for(int k=1 ; k <= n ; ++k) + { + auto binom_n_k = axom::utilities::binomialCoefficient(n,k); + auto binom_n1_k1 = axom::utilities::binomialCoefficient(n-1,k-1); + auto binom_n1_k = axom::utilities::binomialCoefficient(n-1,k); + + EXPECT_EQ(binom_n_k, binom_n1_k1 + binom_n1_k ); + } + } + } + +} diff --git a/src/axom/core/utilities/Utilities.cpp b/src/axom/core/utilities/Utilities.cpp index fd5b1c9b90..f7573fd25b 100644 --- a/src/axom/core/utilities/Utilities.cpp +++ b/src/axom/core/utilities/Utilities.cpp @@ -25,18 +25,6 @@ namespace axom namespace utilities { -int binomial_coefficient(int n, int k) -{ - if (k > n-k) {k= n-k;} - int val = 1; - for (int i=1; i<=k; ++i) - { - val*=(n-k+i); - val/=i; - } - return val; -} - void processAbort() { #ifndef AXOM_USE_MPI @@ -52,5 +40,29 @@ void processAbort() #endif } +int binomialCoefficient(int n, int k) +{ + if(k > n || k < 0) // check if out-of-bounds + { + return 0; + } + if(k == n || k == 0) // early return + { + return 1; + } + if (k > n-k) // exploit symmetry to reduce work + { + k= n-k; + } + + int val = 1; + for (int i=1 ; i<=k ; ++i) + { + val*=(n-k+i); + val/=i; + } + return val; +} + } // end namespace utilities } // end namespace axom diff --git a/src/axom/core/utilities/Utilities.hpp b/src/axom/core/utilities/Utilities.hpp index f4c4ceabe1..3ef614607c 100644 --- a/src/axom/core/utilities/Utilities.hpp +++ b/src/axom/core/utilities/Utilities.hpp @@ -34,7 +34,7 @@ namespace utilities * \brief Gracefully aborts the application */ void processAbort(); -int binomial_coefficient(int n, int k); + /*! * \brief Returns the absolute value of x. * \param [in] x value whose absolute value is computed. @@ -144,6 +144,14 @@ T clampLower(T val, T lower) return val < lower ? lower : val; } +/*! + * \brief Computes the binomial coefficient `n choose k` + * + * \return \f$ {n\choose k} = n! / (k! * (n-k)!)\f$ + * when \f$ n \ge k \ge 0 \f$, 0 otherwise. + */ +int binomialCoefficient(int n, int k); + /*! * \brief Returns a random real number within the specified interval * From d73cd800dcdb773c44900e3fa19d3705284e92f2 Mon Sep 17 00:00:00 2001 From: Kenneth Weiss Date: Thu, 25 Jul 2019 18:36:08 -0700 Subject: [PATCH 22/44] Makes Matrix's default constructor public Matrix can now be used as a value type for containers like std::map. --- src/axom/core/numerics/Matrix.hpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/axom/core/numerics/Matrix.hpp b/src/axom/core/numerics/Matrix.hpp index b93ee04d87..6820657a57 100644 --- a/src/axom/core/numerics/Matrix.hpp +++ b/src/axom/core/numerics/Matrix.hpp @@ -118,6 +118,11 @@ class Matrix { public: + /*! + * \brief Default constructor + */ + Matrix() : m_rows(0), m_cols(0), m_data(nullptr), m_usingExternal(false) {} + /*! * \brief Constructor, creates a Matrix with the given rows and columns. * @@ -530,12 +535,6 @@ class Matrix private: - /*! - * \brief Default constructor. Does nothing. - * \note Made private to prevent host-code from calling this. - */ - Matrix() : m_rows(0), m_cols(0), m_data(nullptr) { }; - /// \name Private Helper Methods /// @{ From a978e13aadc89fef5ab077f9236f2689d8570429 Mon Sep 17 00:00:00 2001 From: Kenneth Weiss Date: Thu, 25 Jul 2019 21:42:28 -0700 Subject: [PATCH 23/44] Refactors and tests the BezierCurve sectorArea() weights calculation --- src/axom/primal/geometry/BezierCurve.hpp | 129 ++++++++++++------ src/axom/primal/tests/primal_bezier_curve.cpp | 118 +++++++++++++++- .../primal/tests/primal_curved_polygon.cpp | 1 - 3 files changed, 205 insertions(+), 43 deletions(-) diff --git a/src/axom/primal/geometry/BezierCurve.hpp b/src/axom/primal/geometry/BezierCurve.hpp index 3bfc7f5e2c..b9f5672ff3 100644 --- a/src/axom/primal/geometry/BezierCurve.hpp +++ b/src/axom/primal/geometry/BezierCurve.hpp @@ -26,6 +26,7 @@ #include "fmt/fmt.hpp" #include +#include #include namespace axom @@ -42,8 +43,21 @@ template < typename T,int NDIMS > std::ostream& operator<<(std::ostream & os, const BezierCurve< T,NDIMS > & bCurve); -// UedaAreaMats is where all of the area matrices from Ueda '99 are stored -extern std::map > UedaAreaMats; +/*! + * \brief Computes the weights for BezierCurve's sectorArea() function + * + * \param order The polynomial order of the curve + * \return An anti-symmetric matrix with (order+1)*{order+1) entries + * containing the integration weights for entry (i,j) + * + * The derivation is provided in: + * Ueda, K. "Signed area of sectors between spline curves and the origin" + * IEEE International Conference on Information Visualization, 1999. + */ +template < typename T> +numerics::Matrix generateBezierCurveSectorWeights(int order); + + /*! * \class BezierCurve @@ -69,6 +83,13 @@ class BezierCurve using BoundingBoxType = BoundingBox< T, NDIMS >; using OrientedBoundingBoxType = OrientedBoundingBox< T, NDIMS >; +private: + // Caches of precomputed coefficients for sector area calculations + // on a given polynomial order + using SectorWeights = numerics::Matrix; + using WeightsMap = std::map; + static WeightsMap s_sectorWeightsMap; + public: /*! @@ -307,49 +328,38 @@ class BezierCurve return; } - - /* - * \brief Calculates the sector area (area between curve and origin) of a Bezier Curve + /* + * \brief Calculates the sector area of a Bezier Curve * - * \param [in] tol a tolerance parameter controlling definition of - * near-linearity - * \param [out] boolean TRUE if c1 is near-linear + * The sector area is the area between the curve and the origin. + * The equation and derivation is described in: + * Ueda, K. "Signed area of sectors between spline curves and the origin" + * IEEE International Conference on Information Visualization, 1999. */ T sectorArea() const { T A = 0; - int ord = getOrder(); - if (UedaAreaMats.find(ord)==UedaAreaMats.end()) - { - std::vector newUedaAreaMat((ord+1)*(ord+1)); - int twonchoosen=axom::utilities::binomial_coefficient(2*ord,ord); - for (int i=0; i<=ord; ++i) - { - for (int j=0; j<=ord; ++j) - { - if ((i==0 && j==0) || (i==(ord)&& j==(ord))) - {newUedaAreaMat[i*(ord+1)+j]=0.0;} - else - { - newUedaAreaMat[i*(ord+1)+j]=((1.0*j-i)/2)*(2.0*(ord)/(1.0*twonchoosen))* - (1.0*axom::utilities::binomial_coefficient(i+j,i)/(1.0*i+j))* - (1.0*axom::utilities::binomial_coefficient(2*(ord)-i-j,(ord)-j)/(1.0*(ord)-j+(ord)-i)); - } - } - } - UedaAreaMats.insert(std::pair>(ord,newUedaAreaMat)); - } - const std::vector &whicharea = (UedaAreaMats.find(ord)->second); - for (int i=0; i<=ord; ++i) - { - for (int j=0; j<=ord; ++j) - { - A+=static_cast(whicharea[i*(ord+1)+j])*m_controlPoints[i][1]*m_controlPoints[j][0]; - } - } - return A; - } - + const int ord = getOrder(); + + // Compute and cache the weights if they are not already available + if (s_sectorWeightsMap.find(ord)==s_sectorWeightsMap.end()) + { + auto wts = generateBezierCurveSectorWeights(ord); + s_sectorWeightsMap.emplace(std::make_pair(ord,wts)); + } + + const auto& weights = s_sectorWeightsMap[ord]; + + for (int p=0 ; p<=ord ; ++p) + { + for (int q=0 ; q<=ord ; ++q) + { + A+= weights(p,q)*m_controlPoints[p][1]* m_controlPoints[q][0]; + } + } + return A; + } + /*! * \brief Predicate to check if the Bezier curve is approximately linear * @@ -402,8 +412,14 @@ class BezierCurve CoordsVec m_controlPoints; }; +// Declaration of sectorArea weights map +template < typename T, int NDIMS > +typename BezierCurve::WeightsMap +BezierCurve::s_sectorWeightsMap; + + //------------------------------------------------------------------------------ -/// Free functions implementing BezierCurve's operators +/// Free functions related to BezierCurve //------------------------------------------------------------------------------ template < typename T, int NDIMS > std::ostream& operator<<(std::ostream & os, @@ -413,6 +429,37 @@ std::ostream& operator<<(std::ostream & os, return os; } +template +numerics::Matrix generateBezierCurveSectorWeights(int ord) +{ + numerics::Matrix weights(ord+1,ord+1); + T binom_2n_n = static_cast(utilities::binomialCoefficient(2*ord,ord)); + for (int i=0 ; i<=ord ; ++i) + { + weights(i,i) = 0.; // zero on the diagonal + for (int j=i+1 ; j<=ord ; ++j) + { + double val = 0.; + if (i != j) + { + T binom_ij_i = + static_cast(utilities::binomialCoefficient(i+j,i)); + T binom_2nij_nj = + static_cast(utilities::binomialCoefficient(2*ord-i-j,ord-j)); + + val = ((j-i) * ord) / binom_2n_n + * (binom_ij_i / static_cast(i+j)) + * (binom_2nij_nj / (2.*ord -j-i)); + } + weights(i,j) = val; // antisymmetric + weights(j,i) = -val; + } + } + return weights; +} + + + } // namespace primal } // namespace axom diff --git a/src/axom/primal/tests/primal_bezier_curve.cpp b/src/axom/primal/tests/primal_bezier_curve.cpp index 2978292960..e1fb674f64 100644 --- a/src/axom/primal/tests/primal_bezier_curve.cpp +++ b/src/axom/primal/tests/primal_bezier_curve.cpp @@ -14,7 +14,6 @@ namespace primal = axom::primal; -std::map > primal::UedaAreaMats;//TODO: put this in the BezierCurve.cpp file //------------------------------------------------------------------------------ TEST( primal_beziercurve, constructor ) { @@ -486,6 +485,123 @@ TEST( primal_beziercurve, isLinear) } +TEST( primal_beziercurve, sector_weights) +{ + SLIC_INFO("Testing weights for BezierCurve::sectorArea()"); + + // NOTE: Expected weights are provided in the reference paper [Ueda99] + // See doxygen comment for BezierCurve::sectorArea() + + using CoordType = double; + + // order 1 + { + const int ord = 1; + auto weights = primal::generateBezierCurveSectorWeights(ord); + + double binomInv = 1. / axom::utilities::binomialCoefficient(2,1); + axom::numerics::Matrix exp(ord+1,ord+1); + exp(0,0) = 0; exp(0,1) = 1; + exp(1,0) = -1; exp(1,1) = 0; + + for (int i=0 ; i<=ord ; ++i) + { + for (int j=0 ; j<=ord ; ++j) + { + EXPECT_DOUBLE_EQ( exp(i,j) * binomInv, weights(i,j) ); + } + } + } + + // order 2 + { + const int ord = 2; + auto weights = primal::generateBezierCurveSectorWeights(ord); + + double binomInv = 1. / axom::utilities::binomialCoefficient(4,2); + axom::numerics::Matrix exp(ord+1,ord+1); + exp(0,0) = 0; exp(0,1) = 2; exp(0,2) = 1; + exp(1,0) = -2; exp(1,1) = 0; exp(1,2) = 2; + exp(2,0) = -1; exp(2,1) = -2; exp(2,2) = 0; + + for (int i=0 ; i<=ord ; ++i) + { + for (int j=0 ; j<=ord ; ++j) + { + EXPECT_DOUBLE_EQ( exp(i,j) * binomInv, weights(i,j) ); + } + } + } + + // order 3 + { + const int ord = 3; + auto weights = primal::generateBezierCurveSectorWeights(ord); + + double binomInv = 1. / axom::utilities::binomialCoefficient(6,3); + axom::numerics::Matrix exp(ord+1,ord+1); + exp(0,0) = 0; exp(0,1) = 6; exp(0,2) = 3; exp(0,3) = 1; + exp(1,0) = -6; exp(1,1) = 0; exp(1,2) = 3; exp(1,3) = 3; + exp(2,0) = -3; exp(2,1) = -3; exp(2,2) = 0; exp(2,3) = 6; + exp(3,0) = -1; exp(3,1) = -3; exp(3,2) = -6; exp(3,3) = 0; + + for (int i=0 ; i<=ord ; ++i) + { + for (int j=0 ; j<=ord ; ++j) + { + EXPECT_DOUBLE_EQ( exp(i,j) * binomInv, weights(i,j) ); + } + } + } + + // order 4 + { + const int ord = 4; + auto weights = primal::generateBezierCurveSectorWeights(ord); + + double binomInv = 1. / axom::utilities::binomialCoefficient(8,4); + axom::numerics::Matrix exp(ord+1,ord+1); + exp(0,0) = 0; exp(0,1) = 20; exp(0,2) = 10; exp(0,3) = 4; exp(0,4) = 1; + exp(1,0) =-20; exp(1,1) = 0; exp(1,2) = 8; exp(1,3) = 8; exp(1,4) = 4; + exp(2,0) =-10; exp(2,1) = -8; exp(2,2) = 0; exp(2,3) = 8; exp(2,4) = 10; + exp(3,0) = -4; exp(3,1) = -8; exp(3,2) = -8; exp(3,3) = 0; exp(3,4) = 20; + exp(4,0) = -1; exp(4,1) = -4; exp(4,2) =-10; exp(4,3) =-20; exp(4,4) = 0; + + for (int i=0 ; i<=ord ; ++i) + { + for (int j=0 ; j<=ord ; ++j) + { + EXPECT_DOUBLE_EQ( exp(i,j) * binomInv, weights(i,j) ); + } + } + } + + + // order 5 + { + const int ord = 5; + auto weights = primal::generateBezierCurveSectorWeights(ord); + + double binomInv = 1. / axom::utilities::binomialCoefficient(10,5); + axom::numerics::Matrix exp(ord+1,ord+1); + exp(0,0) = 0; exp(0,1) = 70; exp(0,2) = 35; exp(0,3) = 15; exp(0,4) = 5; exp(0,5) = 1; + exp(1,0) =-70; exp(1,1) = 0; exp(1,2) = 25; exp(1,3) = 25; exp(1,4) = 15; exp(1,5) = 5; + exp(2,0) =-35; exp(2,1) =-25; exp(2,2) = 0; exp(2,3) = 20; exp(2,4) = 25; exp(2,5) = 15; + exp(3,0) =-15; exp(3,1) =-25; exp(3,2) =-20; exp(3,3) = 0; exp(3,4) = 25; exp(3,5) = 35; + exp(4,0) = -5; exp(4,1) =-15; exp(4,2) =-25; exp(4,3) =-25; exp(4,4) = 0; exp(4,5) = 70; + exp(5,0) = -1; exp(5,1) = -5; exp(5,2) =-15; exp(5,3) =-35; exp(5,4) =-70; exp(5,5) = 0; + + for (int i=0 ; i<=ord ; ++i) + { + for (int j=0 ; j<=ord ; ++j) + { + EXPECT_DOUBLE_EQ( exp(i,j) * binomInv, weights(i,j) ); + } + } + } +} + + //------------------------------------------------------------------------------ int main(int argc, char* argv[]) diff --git a/src/axom/primal/tests/primal_curved_polygon.cpp b/src/axom/primal/tests/primal_curved_polygon.cpp index 25b0ef6fda..75551b2b94 100644 --- a/src/axom/primal/tests/primal_curved_polygon.cpp +++ b/src/axom/primal/tests/primal_curved_polygon.cpp @@ -14,7 +14,6 @@ namespace primal = axom::primal; -std::map > primal::UedaAreaMats; //TODO: put this in the BezierCurve.cpp file //------------------------------------------------------------------------------ TEST( primal_curvedpolygon, constructor ) { From 751e7fd095f075979dbfc5276b9a95268588aa10 Mon Sep 17 00:00:00 2001 From: David Gunderman Date: Fri, 26 Jul 2019 13:07:58 -0700 Subject: [PATCH 24/44] Added sectorMoment() to BezierCurve, moment() to CurvedPolygon, tests for each --- src/axom/primal/geometry/BezierCurve.hpp | 94 +++++++++++++++++++ src/axom/primal/geometry/CurvedPolygon.hpp | 20 ++++ src/axom/primal/tests/primal_bezier_curve.cpp | 43 +++++++++ .../primal/tests/primal_curved_polygon.cpp | 93 ++++++++++++++++++ 4 files changed, 250 insertions(+) diff --git a/src/axom/primal/geometry/BezierCurve.hpp b/src/axom/primal/geometry/BezierCurve.hpp index b9f5672ff3..dba7a6791a 100644 --- a/src/axom/primal/geometry/BezierCurve.hpp +++ b/src/axom/primal/geometry/BezierCurve.hpp @@ -57,6 +57,19 @@ std::ostream& operator<<(std::ostream & os, template < typename T> numerics::Matrix generateBezierCurveSectorWeights(int order); +/*! + * \brief Computes the weights for BezierCurve's sectorMoment() function + * + * \param order The polynomial order of the curve + * \return An anti-symmetric matrix with (order+1)*{order+1) entries + * containing the integration weights for entry (i,j) + * + * The derivation is provided in: + * Ueda, K. "Signed area of sectors between spline curves and the origin" + * IEEE International Conference on Information Visualization, 1999. + */ +template < typename T> +std::vector> generateBezierCurveSectorMomentsWeights(int order); /*! @@ -88,7 +101,9 @@ class BezierCurve // on a given polynomial order using SectorWeights = numerics::Matrix; using WeightsMap = std::map; + using MomentsWeightsMap = std::map>; static WeightsMap s_sectorWeightsMap; + static MomentsWeightsMap s_sectorMomentsWeightsMap; public: @@ -328,6 +343,44 @@ class BezierCurve return; } + + /* + * \brief Calculates the sector moment of a Bezier Curve + * + * The sector moment is the moment between the curve and the origin. + * The equation and derivation are generalizations of: + * Ueda, K. "Signed area of sectors between spline curves and the origin" + * IEEE International Conference on Information Visualization, 1999. + */ + PointType sectorMoment() const + { + T Mx = 0; + T My = 0; + const int ord = getOrder(); + + // Compute and cache the weights if they are not already available + if (s_sectorMomentsWeightsMap.find(ord)==s_sectorMomentsWeightsMap.end()) + { + auto wts = generateBezierCurveSectorMomentsWeights(ord); + s_sectorMomentsWeightsMap.emplace(std::make_pair(ord,wts)); + } + + const auto& weights = s_sectorMomentsWeightsMap[ord]; + for (int r=0 ; r<=ord ; ++r) + { + for (int p=0 ; p<=ord ; ++p) + { + for (int q=0 ; q<=ord ; ++q) + { + Mx+= weights[r](p,q)*m_controlPoints[p][1]* m_controlPoints[q][0]* m_controlPoints[r][0]; + My+= weights[r](p,q)*m_controlPoints[p][1]* m_controlPoints[q][0]* m_controlPoints[r][1]; + } + } + } + PointType M= PointType::make_point(Mx,My); + return M; + } + /* * \brief Calculates the sector area of a Bezier Curve * @@ -417,6 +470,11 @@ template < typename T, int NDIMS > typename BezierCurve::WeightsMap BezierCurve::s_sectorWeightsMap; +// Declaration of sectorMoments weights map +template < typename T, int NDIMS > +typename BezierCurve::MomentsWeightsMap +BezierCurve::s_sectorMomentsWeightsMap; + //------------------------------------------------------------------------------ /// Free functions related to BezierCurve @@ -458,6 +516,42 @@ numerics::Matrix generateBezierCurveSectorWeights(int ord) return weights; } +template +std::vector> generateBezierCurveSectorMomentsWeights(int ord) +{ + std::vector> weights; + weights.resize(ord+1); + numerics::Matrix weightsr(ord+1,ord+1); + for (int k=0 ; k<=ord ; ++k) + { + for (int i=0 ; i<=ord ; ++i) + { + weightsr(i,i) = 0.; // zero on the diagonal + for (int j=i+1 ; j<=ord ; ++j) + { + double val = 0.; + if (i != j) + { + T binom_n_i = + static_cast(utilities::binomialCoefficient(ord,i)); + T binom_n_j = + static_cast(utilities::binomialCoefficient(ord,j)); + T binom_n_k = + static_cast(utilities::binomialCoefficient(ord,k)); + T binom_3n2_ijk1 = + static_cast(utilities::binomialCoefficient(3*ord-2,i+j+k-1)); + + val = (1.*(j-i) ) / (3.*(3*ord-1)) + * (1.*binom_n_i*binom_n_j*binom_n_k/ (1.*binom_3n2_ijk1)); + } + weightsr(i,j) = val; // antisymmetric + weightsr(j,i) = -val; + } + } + weights[k]=weightsr; + } + return weights; +} } // namespace primal diff --git a/src/axom/primal/geometry/CurvedPolygon.hpp b/src/axom/primal/geometry/CurvedPolygon.hpp index be805ecdd0..f0d19bd26b 100644 --- a/src/axom/primal/geometry/CurvedPolygon.hpp +++ b/src/axom/primal/geometry/CurvedPolygon.hpp @@ -217,6 +217,26 @@ class CurvedPolygon } } + PointType moment(double tol = 1e-15) const + { + const int ngon = numEdges(); + PointType M = PointType::make_point(0.0,0.0); + if (!isClosed(tol)) {return M; SLIC_INFO("Warning! The moments are 0 because the element is not closed.");} + else + { + for (int ed = 0; ed > m_edges; }; diff --git a/src/axom/primal/tests/primal_bezier_curve.cpp b/src/axom/primal/tests/primal_bezier_curve.cpp index e1fb674f64..714a3b6c75 100644 --- a/src/axom/primal/tests/primal_bezier_curve.cpp +++ b/src/axom/primal/tests/primal_bezier_curve.cpp @@ -224,6 +224,29 @@ TEST( primal_beziercurve, sector_area_cubic ) } } +//------------------------------------------------------------------------------ +TEST( primal_beziercurve, sector_moment_cubic) +{ + const int DIM = 2; + using CoordType = double; + using PointType = primal::Point< CoordType, DIM >; + using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + + { + SLIC_INFO("Testing Bezier sector moment calculation for a cubic"); + const int order = 3; + PointType data[order+1] = { PointType::make_point(0.6, 1.2), + PointType::make_point(1.3, 1.6), + PointType::make_point(2.9, 2.4), + PointType::make_point(3.2, 3.5) }; + + BezierCurveType bCurve(data, order); + PointType M = bCurve.sectorMoment(); + EXPECT_NEAR(M[0],-.429321428571429,2e-15); + EXPECT_NEAR(M[1],-.354010714285715,2e-15); + } +} + //------------------------------------------------------------------------------ TEST( primal_beziercurve, sector_area_point ) @@ -244,6 +267,26 @@ TEST( primal_beziercurve, sector_area_point ) } +//------------------------------------------------------------------------------ +TEST( primal_beziercurve, sector_moment_point ) +{ + const int DIM = 2; + using CoordType = double; + using PointType = primal::Point< CoordType, DIM >; + using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + + { + SLIC_INFO("Testing Bezier sector moment calculation for a point"); + const int order = 0; + PointType data[order+1] = { PointType::make_point(0.6, 1.2)}; + + BezierCurveType bCurve(data, order); + PointType M = bCurve.sectorMoment(); + EXPECT_DOUBLE_EQ(M[0],0.0); + EXPECT_DOUBLE_EQ(M[1],0.0); + } +} + //------------------------------------------------------------------------------ TEST( primal_beziercurve, split_cubic ) { diff --git a/src/axom/primal/tests/primal_curved_polygon.cpp b/src/axom/primal/tests/primal_curved_polygon.cpp index 75551b2b94..831e9ef7e9 100644 --- a/src/axom/primal/tests/primal_curved_polygon.cpp +++ b/src/axom/primal/tests/primal_curved_polygon.cpp @@ -367,6 +367,99 @@ TEST( primal_curvedpolygon, area_triangle_mixed_order) EXPECT_DOUBLE_EQ(trueA,A); } +//---------------------------------------------------------------------------------- +TEST( primal_curvedpolygon, moment_triangle_linear ) +{ + const int DIM = 2; + using CoordType = double; + using CurvedPolygonType = primal::CurvedPolygon< CoordType, DIM>; + using PointType = primal::Point< CoordType, DIM >; + using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + + SLIC_INFO("Test checking CurvedPolygon linear triangle moment computation."); + + CurvedPolygonType bPolygon; + EXPECT_EQ(0, bPolygon.numEdges()); + + PointType controlPoints[2] = { + PointType::make_point( 0.6, 1.2), + PointType::make_point( 0.3, 2.0) + }; + + PointType controlPoints2[2] = { + PointType::make_point( 0.3, 2.0), + PointType::make_point( 0.0, 1.6) + }; + PointType controlPoints3[2] = { + PointType::make_point( 0.0, 1.6), + PointType::make_point( 0.6, 1.2) + }; + + BezierCurveType bCurve(controlPoints,1); + bPolygon.addEdge(bCurve); + + BezierCurveType bCurve2(controlPoints2,1); + bPolygon.addEdge(bCurve2); + + BezierCurveType bCurve3(controlPoints3,1); + bPolygon.addEdge(bCurve3); + + PointType M= bPolygon.moment(); + CoordType trueM1= 0.3; + CoordType trueM2= 1.6; + + EXPECT_DOUBLE_EQ(trueM1,M[0]); + EXPECT_DOUBLE_EQ(trueM2,M[1]); +} + +//---------------------------------------------------------------------------------- +TEST( primal_curvedpolygon, moment_triangle_mixed_order) +{ + const int DIM = 2; + using CoordType = double; + using CurvedPolygonType = primal::CurvedPolygon< CoordType, DIM>; + using PointType = primal::Point< CoordType, DIM >; + using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + + SLIC_INFO("Test checking CurvedPolygon mixed order triangle area computation."); + + CurvedPolygonType bPolygon; + EXPECT_EQ(0, bPolygon.numEdges()); + + PointType controlPoints[3] = { + PointType::make_point(0.6, 1.2), + PointType::make_point(0.4, 1.3), + PointType::make_point(0.3, 2.0) + }; + + PointType controlPoints2[3] = { + PointType::make_point( 0.3, 2.0), + PointType::make_point( 0.27, 1.5), + PointType::make_point( 0.0 , 1.6) + }; + + PointType controlPoints3[2] = { + PointType::make_point( 0.0 , 1.6), + PointType::make_point( 0.6, 1.2) + }; + + BezierCurveType bCurve(controlPoints,2); + bPolygon.addEdge(bCurve); + + BezierCurveType bCurve2(controlPoints2,2); + bPolygon.addEdge(bCurve2); + + BezierCurveType bCurve3(controlPoints3,1); + bPolygon.addEdge(bCurve3); + + PointType M= bPolygon.moment(); + CoordType trueM2= 1.55764705882353; + CoordType trueM1= .2970147058823527; + + EXPECT_DOUBLE_EQ(trueM1,M[0]); + EXPECT_DOUBLE_EQ(trueM2,M[1]); +} + //---------------------------------------------------------------------------------- TEST( primal_curvedpolygon, intersection_triangle_linear) { From 553bd7fb7be3ac01a64d86b559d6b4a54437817e Mon Sep 17 00:00:00 2001 From: Kenneth Weiss Date: Mon, 29 Jul 2019 08:18:43 -0700 Subject: [PATCH 25/44] Beginnings of a field transfer application for high-order meshes The current example generates two mfem meshes and finds the pairs of possibly intersecting elements. --- src/axom/quest/examples/CMakeLists.txt | 23 +- .../quest/examples/quest_high_order_remap.cpp | 464 ++++++++++++++++++ src/axom/quest/tests/CMakeLists.txt | 1 + 3 files changed, 486 insertions(+), 2 deletions(-) create mode 100644 src/axom/quest/examples/quest_high_order_remap.cpp diff --git a/src/axom/quest/examples/CMakeLists.txt b/src/axom/quest/examples/CMakeLists.txt index 63663c5f32..aba90ec95d 100644 --- a/src/axom/quest/examples/CMakeLists.txt +++ b/src/axom/quest/examples/CMakeLists.txt @@ -14,9 +14,9 @@ set(quest_example_depends quest ) -blt_list_append(TO quest_example_depends ELEMENTS openmp IF ENABLE_OPENMP) +blt_list_append(TO quest_example_depends ELEMENTS openmp IF ${ENABLE_OPENMP}) -blt_list_append(TO quest_example_depends ELEMENTS cuda IF ENABLE_CUDA) +blt_list_append(TO quest_example_depends ELEMENTS cuda IF ${ENABLE_CUDA}) blt_add_executable( NAME quest_containment_driver_ex @@ -110,3 +110,22 @@ if (ENABLE_FORTRAN) endif() endif() endif() + +if(MFEM_FOUND) + blt_add_executable( + NAME quest_high_order_remap_ex + SOURCES quest_high_order_remap.cpp + OUTPUT_DIR ${EXAMPLE_OUTPUT_DIRECTORY} + DEPENDS_ON ${quest_example_depends} mfem fmt + FOLDER axom/quest/examples + ) + + #string(STRIP "${AXOM_DISABLE_UNUSED_PARAMETER_WARNINGS}" MFEM_COMPILE_FLAGS) + + #if (ENABLE_CUDA) + # set(MFEM_COMPILE_FLAGS "-Xcompiler=${MFEM_COMPILE_FLAGS}") + #endif() + + #blt_add_target_compile_flags( TO quest_high_order_remap_ex FLAGS "${MFEM_COMPILE_FLAGS}" ) + +endif() \ No newline at end of file diff --git a/src/axom/quest/examples/quest_high_order_remap.cpp b/src/axom/quest/examples/quest_high_order_remap.cpp new file mode 100644 index 0000000000..36e694e259 --- /dev/null +++ b/src/axom/quest/examples/quest_high_order_remap.cpp @@ -0,0 +1,464 @@ +// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + + +/*! + * \file quest_high_order_remap.cpp + * \brief Demonstrates conservative field remap on 2D high order meshes + */ + +// Axom includes +#include "axom/core.hpp" +#include "axom/slic.hpp" +#include "axom/primal.hpp" +#include "axom/spin.hpp" + +#ifdef AXOM_USE_MFEM + #include "mfem.hpp" +#else + #error "This example requires mfem" +#endif + +#include "fmt/fmt.hpp" + +#include + +//namespace mint = axom::mint; +namespace primal = axom::primal; +namespace spin = axom::spin; + +/** + * \brief Wrapper for a 2D mfem mesh + * + * Helps with conversion to BezierCurves and CurvedPolygons + * + * The meshwrapper assumes ownership of the wrapped mesh + */ +class MeshWrapper +{ +public: + using BBox = primal::BoundingBox; + using Point = primal::Point; + + using CurvedPolygonType = primal::CurvedPolygon; + using BCurve = CurvedPolygonType::BezierCurveType; + +private: + /*! \brief Checks if the mesh's nodes are in the Bernstein basis */ + bool isBernsteinBasis() const + { + auto* fec = m_mesh->GetNodalFESpace()->FEColl(); + + if (fec == nullptr) + { + return false; + } + + if (const mfem::H1_FECollection* h1Fec = + dynamic_cast(fec)) + { + return h1Fec->GetBasisType() == mfem::BasisType::Positive; + } + + if (const mfem::L2_FECollection* l2Fec = + dynamic_cast(fec)) + { + return l2Fec->GetBasisType() == mfem::BasisType::Positive; + } + + if (dynamic_cast(fec) || + dynamic_cast(fec) || + dynamic_cast(fec)) + { + return true; + } + + return false; + } +public: + + MeshWrapper() : m_mesh(nullptr) {} + + MeshWrapper(mfem::Mesh* mesh) + { + setMesh(mesh); + } + + ~MeshWrapper() + { + if (m_mesh != nullptr) + { + delete m_mesh; + m_mesh = nullptr; + } + } + + /*! + * Sets the mfem mesh pointer for this MeshWrapper instance + */ + void setMesh(mfem::Mesh* mesh) + { + SLIC_ASSERT(mesh != nullptr); + m_mesh = mesh; + + bool isHighOrder = (m_mesh->GetNodalFESpace() != nullptr) + && (m_mesh->GetNE() > 0); + SLIC_ASSERT_MSG(isHighOrder, "The mesh must be high order."); + + bool isBernstein = isBernsteinBasis(); + SLIC_ASSERT_MSG(isBernstein, "The mesh must be in the Bernstein basis"); + + const double tol = 1E-8; + computeBoundingBoxes(1 + tol); + } + + int numVertices() const { return m_mesh->GetNV(); } + int numElements() const { return m_mesh->GetNE(); } + + bool hasMesh() const { return m_mesh != nullptr; } + mfem::Mesh* getMesh() { return m_mesh; } + const mfem::Mesh* getMesh() const { return m_mesh; } + + const BBox& elementBoundingBox(int i) const { return m_boundingBoxes[i]; } + const BBox& meshBoundingBox() const { return m_meshBBox; } + + /*! + * \brief Transform the mfem element into a primal CurvedPolygon + * + * \param elemId The index of the element + * \return The element as a CurvedPolygon composed of BezierPolygon + */ + CurvedPolygonType elemAsCurvedPolygon(int elemId) + { + SLIC_ASSERT(elemId >= 0 && elemId < numElements()); + + auto* fes = m_mesh->GetNodalFESpace(); + auto* nodes = m_mesh->GetNodes(); + + // Get the edge Ids for this element + mfem::Array edgeIds, edgeOrients; + m_mesh->GetElementEdges(elemId, edgeIds, edgeOrients); + const int nEdges = edgeIds.Size(); + + CurvedPolygonType poly(nEdges); + const int order = fes->GetOrder(0); + + // Get the grid function data associated with this edge + mfem::Array dofIndices; + BCurve curve(order); + for (int e = 0 ; e < nEdges ; ++e) + { + // get the dof (degree of freedom) indices for this edge + fes->GetEdgeDofs(edgeIds[e], dofIndices); + + //SLIC_INFO("Elem " << elemId + // << " edge " << edgeIds[e] << " w/ orient " << edgeOrients[e] + // << " -- dof inds " + // << dofIndices[0] << " " << dofIndices[1] << " " << dofIndices[2] + // << " -- points " + // << spacePointFromDof(dofIndices[0], fes, nodes) << " " + // << spacePointFromDof(dofIndices[1], fes, nodes) << " " + // << spacePointFromDof(dofIndices[2], fes, nodes) + // ); + + // possibly reverse the dofs, based on the orientation + // Note: The dofs are ordered by vertices, then by edge + const bool bReverse = (edgeOrients[e] > 0); + if (bReverse) + { + curve[0] = spacePointFromDof(dofIndices[1], fes, nodes); + for (int p = 1 ; p < order ; ++p) + { + curve[p] = spacePointFromDof(dofIndices[order-(p-1)], fes, nodes); + } + curve[order] = spacePointFromDof(dofIndices[0], fes, nodes); + } + else + { + curve[0] = spacePointFromDof(dofIndices[0], fes, nodes); + for (int p = 1 ; p < order ; ++p) + { + curve[p] = spacePointFromDof(dofIndices[p+1], fes, nodes); + } + curve[order] = spacePointFromDof(dofIndices[1], fes, nodes); + } + + // Note: mfem's orientation is reversed w.r.t. primal's + //SLIC_INFO("Elem " << elemId << " edge " << e << " -- curve: " << curve); + poly[nEdges - e - 1] = curve; + } + + return poly; + } + +private: + + /*! Get the coordinates of the point from the dof index */ + Point spacePointFromDof(int idx, + const mfem::FiniteElementSpace* fes, + const mfem::GridFunction* nodes) + { + return Point::make_point( + (*nodes)(fes->DofToVDof(idx, 0)), + (*nodes)(fes->DofToVDof(idx, 1))); + } + + /*! + * \brief Compute the element and mesh bounding boxes + * + * \param[in] bboxScaleFac Scale factor to increase the bounding boxes + * \pre bboxScaleFac >= 1. + */ + void computeBoundingBoxes(double bboxScaleFac) + { + SLIC_ASSERT(bboxScaleFac >= 1.); + + m_boundingBoxes.resize(numElements()); + m_meshBBox.clear(); + + auto* nodes = m_mesh->GetNodes(); + auto* fes = m_mesh->GetNodalFESpace(); + mfem::Array dofIndices; + + const int NE = numElements(); + for (int elem = 0 ; elem < NE ; ++elem) + { + auto& bbox = m_boundingBoxes[elem]; + bbox.clear(); + + // Add each dof of the element to the bbox + // Note: positivity of Bernstein bases ensures that convex + // hull of element nodes contain entire element + fes->GetElementDofs(elem, dofIndices); + for (int i = 0 ; i< dofIndices.Size() ; ++i) + { + int nIdx = dofIndices[i]; + bbox.addPoint(spacePointFromDof(nIdx, fes, nodes)); + } + + // Slightly scale the bbox to account for numerical noise + bbox.scale(bboxScaleFac); + + m_meshBBox.addBox(bbox); + } + } + +private: + mfem::Mesh* m_mesh; + + std::vector m_boundingBoxes; + BBox m_meshBBox; +}; + + +struct Remapper +{ +private: + using GridType = spin::ImplicitGrid<2, int>; +public: + using CandidateList = std::vector; + +public: + Remapper() = default; + + ~Remapper() + { + fecMap.DeleteData(true); + fesMap.DeleteData(true); + } + + + /*! Set up the source and target meshes */ + void setupMeshes() + { + const auto quadType = mfem::Element::QUADRILATERAL; + const int dim = 2; + + // paramters for target mesh -- quad mesh covering unit square + const int src_res = 2; + const int src_ord = 2; + + // paramters for target mesh -- quad mesh covering (part of) unit square + const int tgt_res = 3; + const int tgt_ord = 3; + const double tgt_scale = .9; + const double tgt_trans = .05; + + // create the source mesh + { + // create mfem mesh + auto* mesh = new mfem::Mesh(src_res, src_res, quadType, true); + + // create finite element collection for nodes + auto* fec = new mfem::H1_FECollection(src_ord, dim, + mfem::BasisType::Positive); + fecMap.Register("src_fec", fec, true); + + // create finite element space for nodes + auto* fes = new mfem::FiniteElementSpace(mesh, fec, dim); + fesMap.Register("src_fes", fes, true); + mesh->SetNodalFESpace(fes); + + SLIC_INFO("Writing to: " << axom::utilities::filesystem::getCWD()); + { + std::ofstream file; + file.open("source_mesh.mfem"); + mesh->Print(file); + } + + srcMesh.setMesh(mesh); + } + + // create the target mesh + { + auto* mesh = new mfem::Mesh(tgt_res, tgt_res, quadType, true); + xformMesh(mesh, tgt_scale, tgt_trans); + + auto* fec = new mfem::H1_FECollection(tgt_ord, dim, + mfem::BasisType::Positive); + fecMap.Register("tgt_fec", fec, true); + + auto* fes = new mfem::FiniteElementSpace(mesh, fec, dim); + fesMap.Register("tgt_fes", fes, true); + mesh->SetNodalFESpace(fes); + + { + std::ofstream file; + file.open("target_mesh.mfem"); + mesh->Print(file); + } + + tgtMesh.setMesh(mesh); + } + } + + /*! Setup the implicit grid spatial index over the source mesh */ + void setupGrid() + { + const int NE = srcMesh.numElements(); + grid.initialize(srcMesh.meshBoundingBox(), nullptr, NE); + + for (int i = 0 ; i < NE ; ++i) + { + grid.insert(srcMesh.elementBoundingBox(i), i); + } + } + + + /*! + * Computes the overlap areas between the elements of the target mesh + * to the elements of the source mesh + */ + void computeOverlapAreas() + { + const int nTargetElems = tgtMesh.numElements(); + for (int i = 0 ; i < nTargetElems ; ++i) + { + // Finds the candidates from the source mesh that + // can intersect this target element + auto candidates = getSourceCandidates(i); + + if (candidates.empty()) + break; + + auto tgtPoly = tgtMesh.elemAsCurvedPolygon(i); + SLIC_INFO("Target elem " << i + << " -- area " << tgtPoly.area() + //<< " -- bbox " << tgtMesh.elementBoundingBox(i) + ); + + for (int srcElem : candidates) + { + auto srcPoly = srcMesh.elemAsCurvedPolygon(srcElem); + SLIC_INFO("* Source elem " << srcElem + << " -- area " << srcPoly.area() + //<< " -- bbox " << srcMesh.elementBoundingBox(srcElem) + ); + + // TODO: Compute intersections and areas for this pairs + // Note -- there can be more than one CurvedPolygon in the intersection + + } + } + } + + /*! + * Gets the IDs of the candidate elements from the source mesh + * that might intersect with \a targetElemID from the target mesh + */ + CandidateList getSourceCandidates(int targetElemID) const + { + SLIC_ASSERT(targetElemID < tgtMesh.numElements()); + using BitsetType = GridType::BitsetType; + + CandidateList filteredCandidates; + + auto& targetBBox = tgtMesh.elementBoundingBox(targetElemID); + auto candidateBits = grid.getCandidates(targetBBox); + + // Filter the candidates; return as std vec + for (auto idx = candidateBits.find_first() ; + idx != BitsetType::npos ; + idx = candidateBits.find_next(idx)) + { + if (primal::intersect(targetBBox, srcMesh.elementBoundingBox(idx))) + filteredCandidates.push_back(idx); + } + + return filteredCandidates; + } + +public: + MeshWrapper srcMesh; + MeshWrapper tgtMesh; + GridType grid; + +private: + // Named fields map manage memory associated with the + // finite element collection and spaces + mfem::NamedFieldsMap fecMap; + mfem::NamedFieldsMap fesMap; + +private: + // scale and translate the vertices of the given mesh + void xformMesh(mfem::Mesh* mesh, double sc, double off) + { + for (int v = 0 ; v < mesh->GetNV() ; ++v) + { + double* pt = mesh->GetVertex(v); + pt[0] = sc*pt[0] + off; + pt[1] = sc*pt[1] + off; + } + } +}; + + + + + +//------------------------------------------------------------------------------ +int main( int argc, char** argv ) +{ + axom::slic::UnitTestLogger logger; // create & initialize logger + + SLIC_INFO("The application conservatively maps fields from a source \n" + <<"high order mesh to a target high order mesh!"); + + Remapper remap; + + // Setup the two meshes in the Bernstein basis + // The current implementation hard-codes the two meshes + // TODO: Read in two meshes from disk. + // In that case, we will need to convert the FEC to the Bernstein basis + remap.setupMeshes(); + + // Set up the spatial index + remap.setupGrid(); + + // Computes the overlaps between elements of the target and source meshes + remap.computeOverlapAreas(); + + return 0; +} diff --git a/src/axom/quest/tests/CMakeLists.txt b/src/axom/quest/tests/CMakeLists.txt index 496c3089ab..81f4a58364 100644 --- a/src/axom/quest/tests/CMakeLists.txt +++ b/src/axom/quest/tests/CMakeLists.txt @@ -56,6 +56,7 @@ if(MFEM_FOUND) SOURCES ${test_name}.cpp OUTPUT_DIR ${TEST_OUTPUT_DIRECTORY} DEPENDS_ON ${quest_tests_depends} mfem core + FOLDER axom/quest/tests ) string(STRIP "${AXOM_DISABLE_UNUSED_PARAMETER_WARNINGS}" MFEM_COMPILE_FLAGS) From c2fa969f0384af44188c0825fb4100224cbd2472 Mon Sep 17 00:00:00 2001 From: David Gunderman Date: Wed, 31 Jul 2019 09:50:38 -0700 Subject: [PATCH 26/44] Added doxygen comments for tangent intersections, overlapping curves, 3-space curves. Added tests curves of different orders, non-intersecting curves. --- .../detail/intersect_bezier_impl.hpp | 7 ++ src/axom/primal/operators/intersect.hpp | 7 ++ .../primal/tests/primal_bezier_intersect.cpp | 95 ++++++++++++++++++- .../quest/examples/quest_high_order_remap.cpp | 2 +- 4 files changed, 109 insertions(+), 2 deletions(-) diff --git a/src/axom/primal/operators/detail/intersect_bezier_impl.hpp b/src/axom/primal/operators/detail/intersect_bezier_impl.hpp index 11db161e95..da0065d7de 100644 --- a/src/axom/primal/operators/detail/intersect_bezier_impl.hpp +++ b/src/axom/primal/operators/detail/intersect_bezier_impl.hpp @@ -86,6 +86,12 @@ bool intersect_bezier_curves( const BezierCurve< T, NDIMS>& c1, * As such, the we do not consider the lines to intersect if they do so * at the endpoints \a b or \d, respectively. * + * \note This function assumes the all intersections have multiplicity + * one, i.e. there are no points at which the curves and their derivatives + * both intersect. Thus, the function does not find tangencies. + * + * \note This function assumes two dimensional curves in a plane. + * * \note This function does not properly handle collinear lines */ @@ -111,6 +117,7 @@ bool intersect_bezier_curves( const BezierCurve< T, NDIMS>& c1, double t_offset, double t_scale) { using BCurve = BezierCurve< T, NDIMS>; + SLIC_ASSERT(NDIMS==2); // Check bounding boxes to short-circuit the intersection if(!intersect(c1.boundingBox(), c2.boundingBox()) ) diff --git a/src/axom/primal/operators/intersect.hpp b/src/axom/primal/operators/intersect.hpp index 04fe16ddc5..e16e008b9a 100644 --- a/src/axom/primal/operators/intersect.hpp +++ b/src/axom/primal/operators/intersect.hpp @@ -322,10 +322,16 @@ bool intersect(const OrientedBoundingBox< T, 3 >& b1, * * Finds all intersection points between the two curves. * + * \note This function assumes two dimensional curves in a plane. + * * \note This function assumes that the curves are in general position. * Specifically, we assume that all intersections are at points and that * the curves don't overlap. * + * \note This function assumes the all intersections have multiplicity + * one, i.e. there are no points at which the curves and their derivatives + * both intersect. Thus, the function does not find tangencies. + * * \note This function assumes that the curves are half-open, i.e. they * contain their first endpoint, but not their last endpoint. Thus, the * curves do not intersect at \f$ s==1 \f$ or at \f$ t==1 \f$. @@ -337,6 +343,7 @@ bool intersect( const BezierCurve< T, NDIMS>& c1, std::vector< T >& tp, double tol = 1E-8) { + const double offset = 0.; const double scale = 1.; diff --git a/src/axom/primal/tests/primal_bezier_intersect.cpp b/src/axom/primal/tests/primal_bezier_intersect.cpp index 1df671987f..8061de2d74 100644 --- a/src/axom/primal/tests/primal_bezier_intersect.cpp +++ b/src/axom/primal/tests/primal_bezier_intersect.cpp @@ -199,7 +199,7 @@ TEST( primal_bezier_inter, linear_bezier) TEST( primal_bezier_inter, linear_bezier_interp_params) { - static const int DIM =2; +static const int DIM =2; using CoordType = double; using PointType = primal::Point< CoordType, DIM >; @@ -247,6 +247,99 @@ TEST( primal_bezier_inter, linear_bezier_interp_params) } } +//------------------------------------------------------------------------------ +TEST( primal_bezier_inter, no_intersections_bezier ) +{ + static const int DIM =2; + using CoordType = double; + using PointType = primal::Point< CoordType, DIM >; + using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + + SLIC_INFO("primal: testing bezier intersection"); + SCOPED_TRACE("no intersections"); + + const int order = 3; + + // cubic line + PointType data1[order+1] = { PointType::make_point(0.0, 0.0), + PointType::make_point(1.0, 0.0), + PointType::make_point(2.0, 0.0), + PointType::make_point(3.0, 0.0)}; + + BezierCurveType curve1(data1, order); + + // Cubic curve + PointType data2[order+1] = { PointType::make_point(0.0, 0.5), + PointType::make_point(1.0,1.0), + PointType::make_point(2.0, 3.0), + PointType::make_point(3.0,1.5)}; + BezierCurveType curve2(data2, order); + + std::vector exp_intersections; + + const double eps = 1E-16; + const double eps_test = 1E-10; + + checkIntersections(curve1, curve2, + exp_intersections, exp_intersections, + eps, eps_test); +} + +//------------------------------------------------------------------------------ +TEST( primal_bezier_inter, cubic_quadratic_bezier ) +{ + static const int DIM =2; + using CoordType = double; + using PointType = primal::Point< CoordType, DIM >; + using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + + SLIC_INFO("primal: testing bezier intersection"); + SLIC_INFO("different orders"); + + const int order2 = 3; + + // cubic line + PointType data1 = PointType::make_point(0.0, 0.0); + + BezierCurveType curve1(0); + curve1[0]=data1; + + // Cubic curve + PointType data2[order2+1] = { PointType::make_point(0.0, 0.5), + PointType::make_point(1.0,-1.0), + PointType::make_point(2.0, 1.0), + PointType::make_point(3.0,-0.5)}; + BezierCurveType curve2(data2, order2); + + // Note: same intersection params for curve and line + std::vector exp_intersections = { 0.17267316464601146, + 0.5, + 0.827326835353989}; + + const double eps = 1E-16; + const double eps_test = 1E-10; + + for(int otherorder=1 ; otherorder<=20 ; ++otherorder) + { + curve1.setOrder(otherorder); + for (int i=0; i Date: Wed, 31 Jul 2019 16:44:35 -0700 Subject: [PATCH 27/44] Changed recursion base case to improve robustness. Added intersect to high order mesh intersection example. TODO: deal with case of total inclusion and separation --- .../operators/detail/intersect_bezier_impl.hpp | 2 +- .../primal/operators/intersect_curved_poly.hpp | 6 +++++- src/axom/quest/examples/quest_high_order_remap.cpp | 14 +++++++++++++- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/axom/primal/operators/detail/intersect_bezier_impl.hpp b/src/axom/primal/operators/detail/intersect_bezier_impl.hpp index da0065d7de..afbc4a4738 100644 --- a/src/axom/primal/operators/detail/intersect_bezier_impl.hpp +++ b/src/axom/primal/operators/detail/intersect_bezier_impl.hpp @@ -127,7 +127,7 @@ bool intersect_bezier_curves( const BezierCurve< T, NDIMS>& c1, bool foundIntersection = false; - if ( c1.isLinear(sq_tol) && c2.isLinear(sq_tol)) + if ( s_scale*s_scale < sq_tol && t_scale*t_scale < sq_tol) { T s,t; if(intersect_2d_linear(c1[0],c1[order1],c2[0],c2[order2],s,t)) diff --git a/src/axom/primal/operators/intersect_curved_poly.hpp b/src/axom/primal/operators/intersect_curved_poly.hpp index 5f953bbe17..fbc88b132e 100644 --- a/src/axom/primal/operators/intersect_curved_poly.hpp +++ b/src/axom/primal/operators/intersect_curved_poly.hpp @@ -77,6 +77,8 @@ bool intersect_polygon(CurvedPolygon< T, NDIMS>& p1, numinters+=p1times.size(); } } + if (numinters>0) + { for (int i = 0; i < p1.numEdges(); ++i) { std::sort( E1IntData[i].begin(), E1IntData[i].end() ); @@ -177,6 +179,8 @@ bool intersect_polygon(CurvedPolygon< T, NDIMS>& p1, addingcurves=false; } return true; + } + return false; } // This determines with curve is "more" counterclockwise using the cross product of tangents @@ -187,7 +191,7 @@ bool orient(const BezierCurve c1, const BezierCurve c2, T s, T Point dc2t = c2.dt(t); Point origin = primal::Point< T, NDIMS >::make_point(0.0, 0.0); auto orientation = detail::twoDcross(dc1s,dc2t, origin); - return (orientation<0); + return (orientation>0); } // A class for storing intersection points so they can be easily sorted by parameter value diff --git a/src/axom/quest/examples/quest_high_order_remap.cpp b/src/axom/quest/examples/quest_high_order_remap.cpp index bf06497df1..785a0c21b2 100644 --- a/src/axom/quest/examples/quest_high_order_remap.cpp +++ b/src/axom/quest/examples/quest_high_order_remap.cpp @@ -369,6 +369,7 @@ struct Remapper //<< " -- bbox " << tgtMesh.elementBoundingBox(i) ); + double A=0.0; for (int srcElem : candidates) { auto srcPoly = srcMesh.elemAsCurvedPolygon(srcElem); @@ -376,11 +377,22 @@ struct Remapper << " -- area " << srcPoly.area() //<< " -- bbox " << srcMesh.elementBoundingBox(srcElem) ); - + + std::vector> pnew; + if (primal::intersect_polygon(tgtPoly,srcPoly,pnew)) + { + for (int i=0; i< static_cast(pnew.size()); ++i) + { + A=A+pnew[i].area(); + SLIC_INFO("** Intersection area :" << pnew[i].area() + ); + } + } // TODO: Compute intersections and areas for this pairs // Note -- there can be more than one CurvedPolygon in the intersection } + SLIC_INFO("Calculated Area :" << A); } } From 79b42ccbc286bebf5806abd9ae51ff90b68032de Mon Sep 17 00:00:00 2001 From: David Gunderman Date: Wed, 14 Aug 2019 16:23:39 -0700 Subject: [PATCH 28/44] Adds inclusion/separation test to intersect_polygon, reverts base case in intersect_bezier, and adds two region test for intersect_polygon --- .../detail/intersect_bezier_impl.hpp | 2 +- .../operators/intersect_curved_poly.hpp | 242 +++++++++++++++--- src/axom/primal/tests/primal_bezier_curve.cpp | 2 +- .../primal/tests/primal_curved_polygon.cpp | 77 +++++- 4 files changed, 279 insertions(+), 44 deletions(-) diff --git a/src/axom/primal/operators/detail/intersect_bezier_impl.hpp b/src/axom/primal/operators/detail/intersect_bezier_impl.hpp index afbc4a4738..511e8f0bba 100644 --- a/src/axom/primal/operators/detail/intersect_bezier_impl.hpp +++ b/src/axom/primal/operators/detail/intersect_bezier_impl.hpp @@ -127,7 +127,7 @@ bool intersect_bezier_curves( const BezierCurve< T, NDIMS>& c1, bool foundIntersection = false; - if ( s_scale*s_scale < sq_tol && t_scale*t_scale < sq_tol) + if ( c1.isLinear(sq_tol) && c2.isLinear(sq_tol)) { T s,t; if(intersect_2d_linear(c1[0],c1[order1],c2[0],c2[order2],s,t)) diff --git a/src/axom/primal/operators/intersect_curved_poly.hpp b/src/axom/primal/operators/intersect_curved_poly.hpp index fbc88b132e..83a63437c0 100644 --- a/src/axom/primal/operators/intersect_curved_poly.hpp +++ b/src/axom/primal/operators/intersect_curved_poly.hpp @@ -37,14 +37,18 @@ class IntersectionInfo; /* template bool orient(BezierCurve c1, BezierCurve c2, T s, T t); + */ /*! - * \brief Test whether CurvedPolygons p1 and p2 intersect. + * \brief Test whether CurvedPolygons p1 and p2 intersect and find intersection + * points * \return status true iff p1 intersects with p2, otherwise false. * * \param p1, p2 CurvedPolygon objects to intersect * \param pnew vector of type CurvedPolygon holding intersection regions oriented as the original curves were. */ + + template < typename T, int NDIMS> bool intersect_polygon(CurvedPolygon< T, NDIMS>& p1, CurvedPolygon< T, NDIMS>& p2, @@ -86,14 +90,16 @@ bool intersect_polygon(CurvedPolygon< T, NDIMS>& p1, } // Orient the first intersection point to be sure we get the intersection - bool orientation = !orient(p1[firstinter.myEdge],p2[firstinter.otherEdge],firstinter.myTime,firstinter.otherTime); + bool orientation = orient(p1[firstinter.myEdge],p2[firstinter.otherEdge],firstinter.myTime,firstinter.otherTime); // Objects to store completely split polygons (split at every intersection point) and vector with unique id for each // intersection and zeros for corners of original polygons. - std::vector edgelabels[2]; - CurvedPolygon psplit[2]; + std::vector edgelabels[2]; // 0 for curves that end in original vertices, unique id for curves that end in intersection points + CurvedPolygon psplit[2]; // The two completely split polygons will be stored in this array psplit[0]=p1; psplit[1]=p2; + + //split polygon 1 at all the intersection points and store as psplit[0] int addedints=0; for (int i = 0; i < p1.numEdges(); ++i) { @@ -109,7 +115,8 @@ bool intersect_polygon(CurvedPolygon< T, NDIMS>& p1, } } } - + + //split polygon 2 at all the intersection points and store as psplit[1] addedints=0; for (int i = 0; i < p2.numEdges(); ++i) { @@ -126,63 +133,218 @@ bool intersect_polygon(CurvedPolygon< T, NDIMS>& p1, } } - // This performs the directional walking method using the completely split polygon + // This performs the directional walking method using the completely split polygons std::vector::iterator> usedlabels; - if (numinters==0) {return false;} // If there are no intersections, return false - else - { - bool addingcurves=true; - int startinter=1; // Start at the first intersection - int nextinter; - bool currentelement=orientation; - int currentit= std::find(edgelabels[currentelement].begin(),edgelabels[currentelement].end(),startinter)-edgelabels[currentelement].begin(); - int startit = currentit; - int nextit = (currentit+1)%edgelabels[0].size(); - nextinter=edgelabels[currentelement][nextit]; - while (numinters>0) - { - CurvedPolygon aPart; // To store the current intersection polygon (could be multiple) - while (!(nextit==startit && currentelement==orientation)) - { - if (nextit==currentit ) - { - nextit=(currentit+1)%edgelabels[0].size(); - } - nextinter=edgelabels[currentelement][nextit]; - while (nextinter==0) + bool addingcurves=true; // When this is false, we are walking between intersection regions + int startvertex=1; // Start at the vertex with "unique id" 1 + int nextvertex; // The next vertex id is unknown at this time + bool currentelement=orientation; // This variable allows us to switch between the two elements + int currentit= std::find(edgelabels[currentelement].begin(),edgelabels[currentelement].end(),startvertex)-edgelabels[currentelement].begin(); // This is the iterator pointing to the end vertex of the edge of the completely split polygon we are on + int startit = currentit; // This is the iterator to the end vertex of the starting edge on the starting polygon + int nextit = (currentit+1)%edgelabels[0].size(); // This is the iterator to the end vertex of the next edge of whichever polygon we will be on next + nextvertex=edgelabels[currentelement][nextit]; // This is the next vertex id + while (numinters>0) + { + CurvedPolygon aPart; // Object to store the current intersection polygon (could be multiple) + while (!(nextit==startit && currentelement==orientation) || addingcurves==false ) // Once the end vertex of the current edge is the start vertex, we need to switch regions + { + if (nextit==currentit ) { - currentit=nextit; - if (addingcurves) - { - aPart.addEdge(psplit[currentelement][nextit]); - } nextit=(currentit+1)%edgelabels[0].size(); - nextinter=edgelabels[currentelement][nextit]; } + nextvertex=edgelabels[currentelement][nextit]; + while (nextvertex==0) + { + currentit=nextit; if (addingcurves) { aPart.addEdge(psplit[currentelement][nextit]); + } + nextit=(currentit+1)%edgelabels[0].size(); + nextvertex=edgelabels[currentelement][nextit]; + } + if (edgelabels[currentelement][nextit]>0) + { + if (addingcurves) + { + aPart.addEdge(psplit[currentelement][nextit]); + edgelabels[currentelement][nextit]=-edgelabels[currentelement][nextit]; currentelement=!currentelement; - nextit = std::find(edgelabels[currentelement].begin(),edgelabels[currentelement].end(),nextinter)-edgelabels[currentelement].begin(); + nextit = std::find(edgelabels[currentelement].begin(),edgelabels[currentelement].end(),nextvertex)-edgelabels[currentelement].begin(); + edgelabels[currentelement][nextit]=-edgelabels[currentelement][nextit]; currentit=nextit; numinters-=1; } - else + else { addingcurves=true; + startit=nextit; currentit=nextit; - nextit = std::find(edgelabels[currentelement].begin(),edgelabels[currentelement].end(),nextinter)-edgelabels[currentelement].begin(); + nextit=(currentit+1)%edgelabels[0].size(); + orientation=currentelement; } } - pnew.push_back(aPart); + else + { + currentelement=!currentelement; + nextit = std::find(edgelabels[currentelement].begin(),edgelabels[currentelement].end(),nextvertex)-edgelabels[currentelement].begin(); + edgelabels[currentelement][nextit]=-edgelabels[currentelement][nextit]; + currentit=nextit; + } + } + pnew.push_back(aPart); + currentelement=!currentelement; + currentit = std::find(edgelabels[currentelement].begin(),edgelabels[currentelement].end(),-nextvertex)-edgelabels[currentelement].begin(); + nextit=(currentit+1)%edgelabels[0].size(); + if (numinters>0) + { + addingcurves=false; } - addingcurves=false; } return true; } - return false; + else + { + int containment= isContained(p1,p2); + if (containment==0) {return false;} + else + { + if (containment==1) + { + pnew.push_back(p1); + } + else + { + pnew.push_back(p2); + } + return true; + } + } // If there are no intersections, return false } +/*! Checks if two polygons are mutually exclusive or if one includes the other, + * assuming that they have no intersection points + * + * \param [in] p1, p2 CurvedPolygons to be tested + * \return 0 if mutually exclusive, 1 if p1 is in p2, 2 if p2 is in p1 + */ +template +int isContained(const CurvedPolygon p1, const CurvedPolygon p2) +{ + const int NDIMS=2; + using PointType = primal::Point< T, NDIMS >; + using BCurve = BezierCurve< T, NDIMS >; + int p1c = 0; + int p2c = 0; + T p1t = .5; + T p2t = .5; + PointType controlPoints[2] = { + p1[p1c].evaluate(p1t), + p2[p2c].evaluate(p2t) + }; + BCurve LineGuess = BCurve(controlPoints,1); + T line1s=0.0; + T line2s=0.0; + for (int j=0 ; j temps; + std::vector tempt; + intersect(LineGuess,p1[j],temps,tempt); + for (int i=0 ; iline1s) + { + line1s = temps[i]; + p1c = j; + p1t = tempt[i]; + } + } + } + for (int j=0 ; j temps; + std::vector tempt; + intersect(LineGuess,p2[j],temps,tempt); + for (int i=0 ; iline1s) + { + line2s = temps[i]; + p2c = j; + p2t = tempt[i]; + } + } + } + + PointType origin = PointType::make_point(0.0, 0.0); + bool E1inE2 = (detail::twoDcross(p1[p1c].dt(p1t),LineGuess.dt(line1s),origin)<0); + bool E2inE1 = (detail::twoDcross(p2[p2c].dt(p2t),LineGuess.dt(line2s),origin)<0); + if (E1inE2 && E2inE1) {return 1;} + else if (!E1inE2 && !E2inE1) {return 2;} + else {return 0;} +} +/* +template +bool isContained(const CurvedPolygon, p1, const CurvedPolygon p2) +{ + BezierCurve lineGuess({p1[0].eval(.5), p2[0].eval(.5));a + T startTime=0.0; + T endTime=1.0; + std::vector lineInts; + for (int i=0; i otherInts; + std::vector tempLineInts; + intersect(lineGuess[i],p1[j],tempLineInts,otherInts,1e-15); + for (int j=0; j otherInts; + std::vector tempLineInts; + intersect(lineGuess[i],p2[j],tempLineInts,otherInts,1e-15); + for (int j=0; j dc1s = p1[0].dt(.5); + Point dc2t = p2[0].dt(.5); + Point dc1line = lineGuess.dt(0.0); + Point dc2line = lineGuess.dt(1.0); + Point origin = primal::Point< T, NDIMS >::make_point(0.0, 0.0); + bool contains12 = (detail::twoDcross(dc1s,dc1line,origin)>0); + bool contains21 = (detail::twoDcross(dc2t,dc2line,origin)>0); + } + else + { + Point dc1s = p1[lineInts[counterint].myEdge].dt(lineInts[counterint].otherTime); + Point dc2t = p2[lineInts[counterint+1].myEdge].dt(lineInts[counterint+1].otherTime); + Point dc1line = lineGuess.dt(lineInts[counterint].myTime); + Point dc2line = lineGuess.dt(lineInts[counterint+1].myTime); + Point origin = primal::Point< T, NDIMS >::make_point(0.0, 0.0); + bool contains12 = (detail::twoDcross(dc1s,dc1line,origin)>0); + bool contains21 = (detail::twoDcross(dc2t,dc2line,origin)>0); + } + return true; +} +*/ // This determines with curve is "more" counterclockwise using the cross product of tangents template bool orient(const BezierCurve c1, const BezierCurve c2, T s, T t) diff --git a/src/axom/primal/tests/primal_bezier_curve.cpp b/src/axom/primal/tests/primal_bezier_curve.cpp index 714a3b6c75..d5f5c7ac66 100644 --- a/src/axom/primal/tests/primal_bezier_curve.cpp +++ b/src/axom/primal/tests/primal_bezier_curve.cpp @@ -56,7 +56,7 @@ TEST( primal_beziercurve, set_order ) EXPECT_EQ(-1, bCurve.getOrder()); const int order = 1; - PointType controlPoints[] = { + PointType controlPoints[2] = { PointType::make_point(0.6, 1.2, 1.0), PointType::make_point(0.0, 1.6, 1.8) }; diff --git a/src/axom/primal/tests/primal_curved_polygon.cpp b/src/axom/primal/tests/primal_curved_polygon.cpp index 831e9ef7e9..4dc883d9c6 100644 --- a/src/axom/primal/tests/primal_curved_polygon.cpp +++ b/src/axom/primal/tests/primal_curved_polygon.cpp @@ -460,7 +460,8 @@ TEST( primal_curvedpolygon, moment_triangle_mixed_order) EXPECT_DOUBLE_EQ(trueM2,M[1]); } -//---------------------------------------------------------------------------------- + + //---------------------------------------------------------------------------------- TEST( primal_curvedpolygon, intersection_triangle_linear) { const int DIM = 2; @@ -784,12 +785,84 @@ TEST( primal_curvedpolygon, area_intersection_triangle_quadratic) CoordType A=0.0; for (int i=0; i(bPolygons3.size()); ++i) { - A+=bPolygons3[i].area(1e-13); + A+=bPolygons3[i].area(1e-14); } CoordType expA=-0.024649833203616; EXPECT_NEAR(A,expA,1e-14); } +TEST( primal_curvedpolygon, area_intersection_triangle_quadratic_two_regions) +{ + const int DIM = 2; + const int order = 2; + using CoordType = double; + using CurvedPolygonType = primal::CurvedPolygon< CoordType, DIM>; + using PointType = primal::Point< CoordType, DIM >; + using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + + SLIC_INFO("Test intersecting two quadratic triangular CurvedPolygons (two regions)."); + + CurvedPolygonType bPolygon; + EXPECT_EQ(0, bPolygon.numEdges()); + + PointType controlPoints[order+1] = { + PointType::make_point(0.6 , 1.2), + PointType::make_point(0.4 , 1.3), + PointType::make_point(0.3 , 2.0) + }; + + PointType controlPoints2[order+1] = { + PointType::make_point(0.3 , 2.0), + PointType::make_point(0.27, 1.5), + PointType::make_point(0.0 , 1.6) + }; + + PointType controlPoints3[order+1] = { + PointType::make_point(0.0 , 1.6), + PointType::make_point(0.1 , 1.5), + PointType::make_point(0.6 , 1.2) + }; + + PointType controlPoints4[order+1] = { + PointType::make_point(1.0205, 1.6699), + PointType::make_point(0.8339, 1.5467), + PointType::make_point(0.1777, 1.8101) + }; + + PointType controlPoints5[order+1] = { + PointType::make_point(0.1777, 1.8101), + PointType::make_point(0.5957, 1.5341), + PointType::make_point(0.3741, 1.3503) + }; + + PointType controlPoints6[order+1] = { + PointType::make_point(0.3741, 1.3503), + PointType::make_point(0.5107, 1.3869), + PointType::make_point(1.0205, 1.6699) + }; + CurvedPolygonType bPolygon2; + + BezierCurveType bCurve(controlPoints,order); + BezierCurveType bCurve4(controlPoints4,order); + bPolygon2.addEdge(bCurve4); + bPolygon.addEdge(bCurve); + + BezierCurveType bCurve2(controlPoints2,order); + BezierCurveType bCurve5(controlPoints5,order); + bPolygon2.addEdge(bCurve5); + bPolygon.addEdge(bCurve2); + + BezierCurveType bCurve3(controlPoints3,order); + BezierCurveType bCurve6(controlPoints6,order); + bPolygon2.addEdge(bCurve6); + bPolygon.addEdge(bCurve3); + + std::vector bPolygons3; + bool didIntersect=intersect_polygon(bPolygon,bPolygon2,bPolygons3); + EXPECT_TRUE(didIntersect); + EXPECT_EQ(bPolygons3.size(),2); +} + //---------------------------------------------------------------------------------- int main(int argc, char* argv[]) { From eb7019f0c6bb393f4239738cb6c9a2572dd1c1a7 Mon Sep 17 00:00:00 2001 From: David Gunderman Date: Fri, 16 Aug 2019 08:49:10 -0700 Subject: [PATCH 29/44] Added reverseorientation functions to BezierCurve and CurvedPolygon classes, added test --- src/axom/primal/geometry/BezierCurve.hpp | 11 ++++ src/axom/primal/geometry/CurvedPolygon.hpp | 14 +++++ .../primal/tests/primal_curved_polygon.cpp | 60 +++++++++++++++++++ 3 files changed, 85 insertions(+) diff --git a/src/axom/primal/geometry/BezierCurve.hpp b/src/axom/primal/geometry/BezierCurve.hpp index dba7a6791a..0b16f9b030 100644 --- a/src/axom/primal/geometry/BezierCurve.hpp +++ b/src/axom/primal/geometry/BezierCurve.hpp @@ -215,6 +215,17 @@ class BezierCurve return m_controlPoints; } + /*! Reverses the order of the Bezier curve's control points */ + void reverseOrientation() + { + const int ord = getOrder(); + CoordsVec old_controlPoints= m_controlPoints; + for (int i=0 ; i<=ord ; ++i) + { + m_controlPoints[i] = old_controlPoints[ord-i]; + } + } + /*! Returns an axis-aligned bounding box containing the Bezier curve */ BoundingBoxType boundingBox() const { diff --git a/src/axom/primal/geometry/CurvedPolygon.hpp b/src/axom/primal/geometry/CurvedPolygon.hpp index f0d19bd26b..762e65ed2e 100644 --- a/src/axom/primal/geometry/CurvedPolygon.hpp +++ b/src/axom/primal/geometry/CurvedPolygon.hpp @@ -235,6 +235,20 @@ class CurvedPolygon M[1]=M[1]/area(); return M; } + } + + /*! + * \brief Reverses orientation of a CurvedPolygon + */ + void reverseOrientation() + { + const int ngon=numEdges(); + std::vector< BezierCurve> old_edges=m_edges; + for (int i=0 ; i; + using PointType = primal::Point< CoordType, DIM >; + using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + + SLIC_INFO("Test intersecting two quadratic triangular CurvedPolygons (inclusion)."); + + CurvedPolygonType bPolygon; + EXPECT_EQ(0, bPolygon.numEdges()); + + PointType controlPoints[order+1] = { + PointType::make_point(0.0 , 0.0), + PointType::make_point(0.5 , 0.0), + PointType::make_point(1.0 , 0.0) + }; + + PointType controlPoints2[order+1] = { + PointType::make_point(1.0 , 0.0), + PointType::make_point(0.5, 0.5), + PointType::make_point(0.0 , 1.0) + }; + + PointType controlPoints3[order+1] = { + PointType::make_point(0.0 , 1.0), + PointType::make_point(0.0 , 0.5), + PointType::make_point(0.0 , 0.0) + }; + + BezierCurveType bCurve(controlPoints,order); + bPolygon.addEdge(bCurve); + + BezierCurveType bCurve2(controlPoints2,order); + bPolygon.addEdge(bCurve2); + + BezierCurveType bCurve3(controlPoints3,order); + bPolygon.addEdge(bCurve3); + + CurvedPolygonType bPolygon2=bPolygon; + for (int i=0; i bPolygons3; + bool didIntersect=intersect_polygon(bPolygon,bPolygon2,bPolygons3); + bPolygons3.clear(); + bool didIntersect2=intersect_polygon(bPolygon2,bPolygon,bPolygons3); + EXPECT_TRUE(didIntersect); + EXPECT_EQ(bPolygons3.size(),1); +} //---------------------------------------------------------------------------------- int main(int argc, char* argv[]) { From c443305757c0cbc552d65e9ee4df40c2fa978f21 Mon Sep 17 00:00:00 2001 From: David Gunderman Date: Mon, 26 Aug 2019 10:19:38 -0700 Subject: [PATCH 30/44] index on feature/gunderman/bezier-polygon: eb7019f Added reverseorientation functions to BezierCurve and CurvedPolygon classes, added test From 02fbd42ed7c1e81496341e664692ce2d097012cf Mon Sep 17 00:00:00 2001 From: David Gunderman Date: Tue, 27 Aug 2019 09:31:59 -0700 Subject: [PATCH 31/44] Debugged quest high order remap example --- src/axom/quest/examples/quest_high_order_remap.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/axom/quest/examples/quest_high_order_remap.cpp b/src/axom/quest/examples/quest_high_order_remap.cpp index c3fb917608..19b6963f6a 100644 --- a/src/axom/quest/examples/quest_high_order_remap.cpp +++ b/src/axom/quest/examples/quest_high_order_remap.cpp @@ -377,7 +377,7 @@ struct Remapper } // create the target mesh { - auto* mesh = new mfem::Mesh("./disc-nurbs.mesh",1,1); + auto* mesh = new mfem::Mesh("./disc-nurbs-80.mesh",1,1); if (mesh->NURBSext) { int order =tgt_ord; From 0d62e5ba37a4e4fb45cf982ceb01b30a4c152984 Mon Sep 17 00:00:00 2001 From: David Gunderman Date: Tue, 17 Sep 2019 16:03:15 -0700 Subject: [PATCH 32/44] Moved intersect curved poly into detail --- src/axom/primal/CMakeLists.txt | 2 +- src/axom/primal/geometry/BezierCurve.hpp | 26 ++++++------ src/axom/primal/geometry/CurvedPolygon.hpp | 14 +++---- .../intersect_curved_poly_impl.hpp} | 27 ++++++++---- src/axom/primal/operators/intersect.hpp | 42 ++++++++++++++++++- src/axom/primal/tests/primal_bezier_curve.cpp | 4 +- .../primal/tests/primal_curved_polygon.cpp | 30 ++++++------- 7 files changed, 98 insertions(+), 47 deletions(-) rename src/axom/primal/operators/{intersect_curved_poly.hpp => detail/intersect_curved_poly_impl.hpp} (92%) diff --git a/src/axom/primal/CMakeLists.txt b/src/axom/primal/CMakeLists.txt index e6195a1c76..7d32e207b1 100644 --- a/src/axom/primal/CMakeLists.txt +++ b/src/axom/primal/CMakeLists.txt @@ -42,10 +42,10 @@ set( primal_headers operators/squared_distance.hpp operators/compute_bounding_box.hpp operators/in_sphere.hpp - operators/intersect_curved_poly.hpp operators/detail/clip_impl.hpp operators/detail/intersect_bezier_impl.hpp + operators/detail/intersect_curved_poly_impl.hpp operators/detail/intersect_impl.hpp ) diff --git a/src/axom/primal/geometry/BezierCurve.hpp b/src/axom/primal/geometry/BezierCurve.hpp index 0b16f9b030..ee42c7c62e 100644 --- a/src/axom/primal/geometry/BezierCurve.hpp +++ b/src/axom/primal/geometry/BezierCurve.hpp @@ -58,7 +58,7 @@ template < typename T> numerics::Matrix generateBezierCurveSectorWeights(int order); /*! - * \brief Computes the weights for BezierCurve's sectorMoment() function + * \brief Computes the weights for BezierCurve's sectorCentroid() function * * \param order The polynomial order of the curve * \return An anti-symmetric matrix with (order+1)*{order+1) entries @@ -69,7 +69,7 @@ numerics::Matrix generateBezierCurveSectorWeights(int order); * IEEE International Conference on Information Visualization, 1999. */ template < typename T> -std::vector> generateBezierCurveSectorMomentsWeights(int order); +std::vector> generateBezierCurveSectorCentroidsWeights(int order); /*! @@ -101,9 +101,9 @@ class BezierCurve // on a given polynomial order using SectorWeights = numerics::Matrix; using WeightsMap = std::map; - using MomentsWeightsMap = std::map>; + using CentroidsWeightsMap = std::map>; static WeightsMap s_sectorWeightsMap; - static MomentsWeightsMap s_sectorMomentsWeightsMap; + static CentroidsWeightsMap s_sectorCentroidsWeightsMap; public: @@ -363,20 +363,20 @@ class BezierCurve * Ueda, K. "Signed area of sectors between spline curves and the origin" * IEEE International Conference on Information Visualization, 1999. */ - PointType sectorMoment() const + PointType sectorCentroid() const { T Mx = 0; T My = 0; const int ord = getOrder(); // Compute and cache the weights if they are not already available - if (s_sectorMomentsWeightsMap.find(ord)==s_sectorMomentsWeightsMap.end()) + if (s_sectorCentroidsWeightsMap.find(ord)==s_sectorCentroidsWeightsMap.end()) { - auto wts = generateBezierCurveSectorMomentsWeights(ord); - s_sectorMomentsWeightsMap.emplace(std::make_pair(ord,wts)); + auto wts = generateBezierCurveSectorCentroidsWeights(ord); + s_sectorCentroidsWeightsMap.emplace(std::make_pair(ord,wts)); } - const auto& weights = s_sectorMomentsWeightsMap[ord]; + const auto& weights = s_sectorCentroidsWeightsMap[ord]; for (int r=0 ; r<=ord ; ++r) { for (int p=0 ; p<=ord ; ++p) @@ -481,10 +481,10 @@ template < typename T, int NDIMS > typename BezierCurve::WeightsMap BezierCurve::s_sectorWeightsMap; -// Declaration of sectorMoments weights map +// Declaration of sectorCentroids weights map template < typename T, int NDIMS > -typename BezierCurve::MomentsWeightsMap -BezierCurve::s_sectorMomentsWeightsMap; +typename BezierCurve::CentroidsWeightsMap +BezierCurve::s_sectorCentroidsWeightsMap; //------------------------------------------------------------------------------ @@ -528,7 +528,7 @@ numerics::Matrix generateBezierCurveSectorWeights(int ord) } template -std::vector> generateBezierCurveSectorMomentsWeights(int ord) +std::vector> generateBezierCurveSectorCentroidsWeights(int ord) { std::vector> weights; weights.resize(ord+1); diff --git a/src/axom/primal/geometry/CurvedPolygon.hpp b/src/axom/primal/geometry/CurvedPolygon.hpp index 291fb7eafd..a123c10956 100644 --- a/src/axom/primal/geometry/CurvedPolygon.hpp +++ b/src/axom/primal/geometry/CurvedPolygon.hpp @@ -18,7 +18,6 @@ #include "axom/primal/geometry/Vector.hpp" #include "axom/primal/geometry/NumericArray.hpp" #include "axom/primal/geometry/BezierCurve.hpp" -#include "axom/primal/operators/intersect.hpp" #include "fmt/fmt.hpp" #include @@ -66,7 +65,7 @@ class CurvedPolygon * \pre numExpectedEdges is at least 1 * */ - CurvedPolygon(int nEdges) + explicit CurvedPolygon(int nEdges) { SLIC_ASSERT(nEdges >= 1); m_edges.reserve(nEdges); @@ -117,9 +116,10 @@ class CurvedPolygon /*! Splits an edge "in place" */ void splitEdge(int idx, T t) { - m_edges.insert(m_edges.begin()+idx+1, 1, m_edges[idx]); - BezierCurve< T, NDIMS> csplit=m_edges[idx]; - csplit.split(t,m_edges[idx],m_edges[idx+1]); + SLIC_ASSERT(idx csplit=m_edges[idx]; + csplit.split(t,m_edges[idx],m_edges[idx+1]); } /*! Clears the list of edges */ @@ -206,7 +206,7 @@ class CurvedPolygon { const int ngon = numEdges(); T A = 0.0; - if (false /*!isClosed(tol)*/) {return A; SLIC_INFO("Warning! The area is 0 because the element is not closed.");} + if (!isClosed(tol)) {return A; SLIC_INFO("Warning! The area is 0 because the element is not closed.");} else { for (int ed = 0; ed class IntersectionInfo; + +template +bool orient(const BezierCurve c1, const BezierCurve c2, T s, T t); + +template +int isContained(const CurvedPolygon p1, const CurvedPolygon p2, double sq_tol = 1e-10); + /* template bool orient(BezierCurve c1, BezierCurve c2, T s, T t); @@ -52,7 +60,8 @@ bool orient(BezierCurve c1, BezierCurve c2, T s, T t); template < typename T, int NDIMS> bool intersect_polygon(CurvedPolygon< T, NDIMS>& p1, CurvedPolygon< T, NDIMS>& p2, - std::vector>& pnew + std::vector>& pnew, + double sq_tol ) { // Object to store intersections @@ -68,7 +77,7 @@ bool intersect_polygon(CurvedPolygon< T, NDIMS>& p1, { std::vector p1times; std::vector p2times; - intersect(p1[i],p2[j],p1times,p2times,1e-10); + intersect_bezier_curves(p1[i],p2[j],p1times,p2times,sq_tol,p1[i].getOrder(),p2[j].getOrder(),1.,0.,1.,0.); for (int k =0; k< static_cast(p1times.size()); ++k) { E1IntData[i].push_back({p1times[k],i,p2times[k],j,numinters+k+1}); @@ -90,7 +99,7 @@ bool intersect_polygon(CurvedPolygon< T, NDIMS>& p1, } // Orient the first intersection point to be sure we get the intersection - bool orientation = orient(p1[firstinter.myEdge],p2[firstinter.otherEdge],firstinter.myTime,firstinter.otherTime); + bool orientation = detail::orient(p1[firstinter.myEdge],p2[firstinter.otherEdge],firstinter.myTime,firstinter.otherTime); // Objects to store completely split polygons (split at every intersection point) and vector with unique id for each // intersection and zeros for corners of original polygons. @@ -205,7 +214,7 @@ bool intersect_polygon(CurvedPolygon< T, NDIMS>& p1, } else { - int containment= isContained(p1,p2); + int containment= isContained(p1,p2,sq_tol); if (containment==0) {return false;} else { @@ -229,7 +238,7 @@ bool intersect_polygon(CurvedPolygon< T, NDIMS>& p1, * \return 0 if mutually exclusive, 1 if p1 is in p2, 2 if p2 is in p1 */ template -int isContained(const CurvedPolygon p1, const CurvedPolygon p2) +int isContained(const CurvedPolygon p1, const CurvedPolygon p2,double sq_tol) { const int NDIMS=2; using PointType = primal::Point< T, NDIMS >; @@ -249,7 +258,7 @@ int isContained(const CurvedPolygon p1, const CurvedPolygon p2) { std::vector temps; std::vector tempt; - intersect(LineGuess,p1[j],temps,tempt); + intersect_bezier_curves(LineGuess,p1[j],temps,tempt,sq_tol,1,p1[j].getOrder(),1.,0.,1.,0.); for (int i=0 ; iline1s) @@ -264,6 +273,7 @@ int isContained(const CurvedPolygon p1, const CurvedPolygon p2) { std::vector temps; std::vector tempt; + intersect_bezier_curves(LineGuess,p2[j],temps,tempt,sq_tol,1,p2[j].getOrder(),1.,0.,1.,0.); intersect(LineGuess,p2[j],temps,tempt); for (int i=0 ; i& b1, } +/*! + * \brief Tests if two CurvedPolygon \a p1 and \a p2 intersect. + * \return status true iff \a p1 intersects \a p2, otherwise false. + * + * \param [in] p1 the first CurvedPolygon + * \param [in] p2 the second CurvedPolygon + * \param [out] pnew vector of resulting CurvedPolygons + * \return True if the curvedPolygons intersect, false otherwise. + * + * Finds the set of curved polygons that bound the region of intersection between p1 and p2. + * + * \note This function assumes two dimensional curved polygons in a plane. + * + * \note This function assumes that the curved polygons are in general + * position. Specifically, we assume that all intersections are at points + * and that the component curves don't overlap. + * + * \note This function assumes the all intersections have multiplicity + * one, i.e. there are no points at which the component curves and their + * derivatives both intersect. Thus, the function does not find tangencies. + * + * \note This function assumes that the component curves are half-open, + * i.e. they contain their first endpoint, but not their last endpoint. + * Thus, component curves do not intersect at \f$ s==1 \f$ or at + * \f$ t==1 \f$. + */ +template < typename T, int NDIMS> +bool intersect( CurvedPolygon< T, NDIMS>& p1, + CurvedPolygon< T, NDIMS>& p2, + std::vector< CurvedPolygon< T, NDIMS>>& pnew, + double tol=1e-7) +{ + // for efficiency, linearity check actually uses a squared tolerance + const double sq_tol = tol * tol; + + return detail::intersect_polygon(p1, p2, pnew,sq_tol); +} + /*! * \brief Tests if two Bezier Curves \a c1 and \a c2 intersect. * \return status true iff \a c1 intersects \a c2, otherwise false. @@ -354,7 +395,6 @@ bool intersect( const BezierCurve< T, NDIMS>& c1, c1.getOrder(), c2.getOrder(), offset, scale, offset, scale); } - } /* namespace primal */ } /* namespace axom */ diff --git a/src/axom/primal/tests/primal_bezier_curve.cpp b/src/axom/primal/tests/primal_bezier_curve.cpp index d5f5c7ac66..48dbefbe4b 100644 --- a/src/axom/primal/tests/primal_bezier_curve.cpp +++ b/src/axom/primal/tests/primal_bezier_curve.cpp @@ -241,7 +241,7 @@ TEST( primal_beziercurve, sector_moment_cubic) PointType::make_point(3.2, 3.5) }; BezierCurveType bCurve(data, order); - PointType M = bCurve.sectorMoment(); + PointType M = bCurve.sectorCentroid(); EXPECT_NEAR(M[0],-.429321428571429,2e-15); EXPECT_NEAR(M[1],-.354010714285715,2e-15); } @@ -281,7 +281,7 @@ TEST( primal_beziercurve, sector_moment_point ) PointType data[order+1] = { PointType::make_point(0.6, 1.2)}; BezierCurveType bCurve(data, order); - PointType M = bCurve.sectorMoment(); + PointType M = bCurve.sectorCentroid(); EXPECT_DOUBLE_EQ(M[0],0.0); EXPECT_DOUBLE_EQ(M[1],0.0); } diff --git a/src/axom/primal/tests/primal_curved_polygon.cpp b/src/axom/primal/tests/primal_curved_polygon.cpp index 9c21d70c20..39fe88cc40 100644 --- a/src/axom/primal/tests/primal_curved_polygon.cpp +++ b/src/axom/primal/tests/primal_curved_polygon.cpp @@ -10,7 +10,7 @@ #include "gtest/gtest.h" #include "axom/primal/geometry/CurvedPolygon.hpp" -#include "axom/primal/operators/intersect_curved_poly.hpp" +#include "axom/primal/operators/intersect.hpp" namespace primal = axom::primal; @@ -225,8 +225,8 @@ TEST( primal_curvedpolygon, area_triangle_degenerate) BezierCurveType bCurve3(controlPoints3,1); bPolygon.addEdge(bCurve3); - bPolygon[2][1][0]-=2e-15; - EXPECT_EQ(0.0, bPolygon.area()); + bPolygon[2][1][0]-=1e-10; + EXPECT_EQ(0.0, bPolygon.area(1e-11)); } @@ -536,7 +536,7 @@ TEST( primal_curvedpolygon, intersection_triangle_linear) expbPolygon.addEdge(expbCurve3); std::vector bPolygons3; - bool didIntersect=intersect_polygon(bPolygon,bPolygon2,bPolygons3); + bool didIntersect=intersect(bPolygon,bPolygon2,bPolygons3); EXPECT_TRUE(didIntersect); for (int i=0; i bPolygons3; - bool didIntersect=intersect_polygon(bPolygon,bPolygon2,bPolygons3); + bool didIntersect=intersect(bPolygon,bPolygon2,bPolygons3); EXPECT_TRUE(didIntersect); for (int i=0; i bPolygons3; - bool didIntersect=intersect_polygon(bPolygon,bPolygon2,bPolygons3); + bool didIntersect=intersect(bPolygon,bPolygon2,bPolygons3); EXPECT_TRUE(didIntersect); @@ -722,7 +722,7 @@ TEST( primal_curvedpolygon, area_intersection_triangle_linear) A+=bPolygons3[i].area(1e-14); } CoordType expA=-0.0793347222222222222; - EXPECT_NEAR(A,expA,1e-14); + EXPECT_NEAR(A,expA,1e-10); } //---------------------------------------------------------------------------------- @@ -779,16 +779,16 @@ TEST( primal_curvedpolygon, area_intersection_triangle_quadratic) } std::vector bPolygons3; - bool didIntersect=intersect_polygon(bPolygon,bPolygon2,bPolygons3); + bool didIntersect=intersect(bPolygon,bPolygon2,bPolygons3); EXPECT_TRUE(didIntersect); CoordType A=0.0; for (int i=0; i(bPolygons3.size()); ++i) { - A+=bPolygons3[i].area(1e-14); + A+=bPolygons3[i].area(1e-8); } CoordType expA=-0.024649833203616; - EXPECT_NEAR(A,expA,1e-14); + EXPECT_NEAR(A,expA,1e-10); } TEST( primal_curvedpolygon, area_intersection_triangle_quadratic_two_regions) @@ -858,7 +858,7 @@ TEST( primal_curvedpolygon, area_intersection_triangle_quadratic_two_regions) bPolygon.addEdge(bCurve3); std::vector bPolygons3; - bool didIntersect=intersect_polygon(bPolygon,bPolygon2,bPolygons3); + bool didIntersect=intersect(bPolygon,bPolygon2,bPolygons3); EXPECT_TRUE(didIntersect); EXPECT_EQ(bPolygons3.size(),2); } @@ -917,9 +917,9 @@ TEST( primal_curvedpolygon, area_intersection_triangle_inclusion) } std::vector bPolygons3; - bool didIntersect=intersect_polygon(bPolygon,bPolygon2,bPolygons3); + bool didIntersect=intersect(bPolygon,bPolygon2,bPolygons3); bPolygons3.clear(); - bool didIntersect2=intersect_polygon(bPolygon2,bPolygon,bPolygons3); + bool didIntersect2=intersect(bPolygon2,bPolygon,bPolygons3); EXPECT_TRUE(didIntersect); EXPECT_EQ(bPolygons3.size(),1); } From b06a34773cc1593d9ee4622a2faa54e7d44a0336 Mon Sep 17 00:00:00 2001 From: David Gunderman Date: Wed, 18 Sep 2019 09:44:56 -0700 Subject: [PATCH 33/44] refactoring intersect_curved_polygon --- src/axom/primal/geometry/CurvedPolygon.hpp | 6 +- .../detail/intersect_curved_poly_impl.hpp | 133 ++++++++++++------ src/axom/primal/operators/intersect.hpp | 2 +- .../primal/tests/primal_curved_polygon.cpp | 16 ++- 4 files changed, 102 insertions(+), 55 deletions(-) diff --git a/src/axom/primal/geometry/CurvedPolygon.hpp b/src/axom/primal/geometry/CurvedPolygon.hpp index a123c10956..9e78370a87 100644 --- a/src/axom/primal/geometry/CurvedPolygon.hpp +++ b/src/axom/primal/geometry/CurvedPolygon.hpp @@ -168,7 +168,7 @@ class CurvedPolygon * Check is that the endpoint of each edge coincides with startpoint of next edge * \return True, if the polygon is closed, False otherwise */ - bool isClosed(double tol = 1e-8) const + bool isClosed(double tol = 1e-5) const { const int ngon = numEdges(); if (ngon <= 2) @@ -206,7 +206,7 @@ class CurvedPolygon { const int ngon = numEdges(); T A = 0.0; - if (!isClosed(tol)) {return A; SLIC_INFO("Warning! The area is 0 because the element is not closed.");} + if (!isClosed(1e3*tol)) {return A; SLIC_INFO("Warning! The area is 0 because the element is not closed.");} else { for (int ed = 0; ed class IntersectionInfo; template -bool orient(const BezierCurve c1, const BezierCurve c2, T s, T t); +bool orient( const BezierCurve c1, + const BezierCurve c2, + T s, + T t); template -int isContained(const CurvedPolygon p1, const CurvedPolygon p2, double sq_tol = 1e-10); +int isContained( const CurvedPolygon p1, + const CurvedPolygon p2, + double sq_tol = 1e-10); -/* -template -bool orient(BezierCurve c1, BezierCurve c2, T s, T t); - -*/ /*! - * \brief Test whether CurvedPolygons p1 and p2 intersect and find intersection - * points + * \brief Test whether the regions within CurvedPolygons p1 and p2 intersect. * \return status true iff p1 intersects with p2, otherwise false. * - * \param p1, p2 CurvedPolygon objects to intersect - * \param pnew vector of type CurvedPolygon holding intersection regions oriented as the original curves were. + * \param [in] p1, p2 CurvedPolygon objects to intersect + * \param [in] sq_tol tolerance parameter for the base case of intersect_bezier_curve + * \param [out] pnew vector of type CurvedPolygon holding CurvedPolygon objects representing boundaries of intersection regions. */ - - template < typename T, int NDIMS> -bool intersect_polygon(CurvedPolygon< T, NDIMS>& p1, - CurvedPolygon< T, NDIMS>& p2, - std::vector>& pnew, - double sq_tol +bool intersect_polygon( CurvedPolygon< T, NDIMS>& p1, + CurvedPolygon< T, NDIMS>& p2, + std::vector>& pnew, + double sq_tol ) { // Object to store intersections @@ -77,11 +75,12 @@ bool intersect_polygon(CurvedPolygon< T, NDIMS>& p1, { std::vector p1times; std::vector p2times; - intersect_bezier_curves(p1[i],p2[j],p1times,p2times,sq_tol,p1[i].getOrder(),p2[j].getOrder(),1.,0.,1.,0.); + intersect_bezier_curves(p1[i],p2[j],p1times,p2times,sq_tol,p1[i].getOrder(),p2[j].getOrder(),0.,1.,0.,1.); for (int k =0; k< static_cast(p1times.size()); ++k) { E1IntData[i].push_back({p1times[k],i,p2times[k],j,numinters+k+1}); E2IntData[j].push_back({p2times[k],j,p1times[k],i,numinters+k+1}); + // std::cout << p1times[k] << ',' << p2times[k] << std::endl; if (numinters==0) { firstinter={p1times[0],i,p2times[0],j,1}; @@ -101,15 +100,14 @@ bool intersect_polygon(CurvedPolygon< T, NDIMS>& p1, // Orient the first intersection point to be sure we get the intersection bool orientation = detail::orient(p1[firstinter.myEdge],p2[firstinter.otherEdge],firstinter.myTime,firstinter.otherTime); - // Objects to store completely split polygons (split at every intersection point) and vector with unique id for each - // intersection and zeros for corners of original polygons. + // Objects to store completely split polygons (split at every intersection point) and vector with unique id for each intersection and zeros for corners of original polygons. std::vector edgelabels[2]; // 0 for curves that end in original vertices, unique id for curves that end in intersection points CurvedPolygon psplit[2]; // The two completely split polygons will be stored in this array psplit[0]=p1; psplit[1]=p2; //split polygon 1 at all the intersection points and store as psplit[0] - int addedints=0; + /*int addedints=0; for (int i = 0; i < p1.numEdges(); ++i) { edgelabels[0].push_back(0); @@ -124,8 +122,6 @@ bool intersect_polygon(CurvedPolygon< T, NDIMS>& p1, } } } - - //split polygon 2 at all the intersection points and store as psplit[1] addedints=0; for (int i = 0; i < p2.numEdges(); ++i) { @@ -140,7 +136,12 @@ bool intersect_polygon(CurvedPolygon< T, NDIMS>& p1, E2IntData[i][k].myTime=(E2IntData[i][k].myTime-E2IntData[i][j].myTime)/(1-E2IntData[i][j].myTime); } } - } + }*/ + + splitPolygon(psplit[0],E1IntData,edgelabels[0]); + splitPolygon(psplit[1],E2IntData,edgelabels[1]); + //split polygon 2 at all the intersection points and store as psplit[1] + // This performs the directional walking method using the completely split polygons std::vector::iterator> usedlabels; @@ -212,29 +213,28 @@ bool intersect_polygon(CurvedPolygon< T, NDIMS>& p1, } return true; } - else + else // If there are no intersection points, check for containment { int containment= isContained(p1,p2,sq_tol); - if (containment==0) {return false;} - else + switch(containment) { - if (containment==1) - { + case 0: + return false; + case 1: pnew.push_back(p1); - } - else - { + return true; + case 2: pnew.push_back(p2); - } - return true; + return true; } - } // If there are no intersections, return false + return false; // Catch + } } -/*! Checks if two polygons are mutually exclusive or if one includes the other, - * assuming that they have no intersection points +/*! \brief Checks if two polygons are mutually exclusive or if one includes the other, assuming that they have no intersection points * * \param [in] p1, p2 CurvedPolygons to be tested + * \param [in] sq_tol tolerance parameter for the base case of intersect_bezier_curves * \return 0 if mutually exclusive, 1 if p1 is in p2, 2 if p2 is in p1 */ template @@ -259,7 +259,7 @@ int isContained(const CurvedPolygon p1, const CurvedPolygon p2,double std::vector temps; std::vector tempt; intersect_bezier_curves(LineGuess,p1[j],temps,tempt,sq_tol,1,p1[j].getOrder(),1.,0.,1.,0.); - for (int i=0 ; i(temps.size()) ; ++i) { if (temps[i]>line1s) { @@ -275,7 +275,7 @@ int isContained(const CurvedPolygon p1, const CurvedPolygon p2,double std::vector tempt; intersect_bezier_curves(LineGuess,p2[j],temps,tempt,sq_tol,1,p2[j].getOrder(),1.,0.,1.,0.); intersect(LineGuess,p2[j],temps,tempt); - for (int i=0 ; i(temps.size()) ; ++i) { if (temps[i]line1s) { @@ -294,7 +294,41 @@ int isContained(const CurvedPolygon p1, const CurvedPolygon p2,double else {return 0;} } -// This determines with curve is "more" counterclockwise using the cross product of tangents +/* + * \brief Splits a CurvedPolygon p1 at every intersection point stored in IntersectionData + */ +template +void splitPolygon( CurvedPolygon< T, NDIMS>& p1, + std::vector>>& IntersectionData, + std::vector& edgelabels) +{ + const int nEd = p1.numEdges(); + //split polygon 2 at all the intersection points and store as psplit[1] + int addedints=0; + for (int i = 0; i < nEd; ++i) + { + edgelabels.push_back(0); + for (int j = 0; j < static_cast(IntersectionData[i].size()); ++j) + { + p1.splitEdge(i+addedints,IntersectionData[i][j].myTime); + edgelabels.insert(edgelabels.begin()+i+addedints,IntersectionData[i][j].numinter); + addedints+=1; + for (int k = j+1; k < static_cast(IntersectionData[i].size()); ++k) + { + IntersectionData[i][k].myTime=(IntersectionData[i][k].myTime-IntersectionData[i][j].myTime)/(1-IntersectionData[i][j].myTime); + } + } + } +} + +/* \brief Determines orientation of a bezier curve c1 with respect to another bezier curve c2, given that they intersect at parameter values s and t + * + * \param [in] c1 the first bezier curve + * \param [in] c2 the second bezier curve + * \param [in] s the parameter value of intersection on c1 + * \param [in] t the parameter value of intersection on c2 + * \return True if the c1's positive direction is counterclockwise from c2's positive direction + */ template bool orient(const BezierCurve c1, const BezierCurve c2, T s, T t) { @@ -305,16 +339,23 @@ bool orient(const BezierCurve c1, const BezierCurve c2, T s, T return (orientation>0); } -// A class for storing intersection points so they can be easily sorted by parameter value +/*! \class IntersectionInfo + * + * \brief For storing intersection points between CurvedPolygons so they can be easily sorted by parameter value using std::sort + */ template class IntersectionInfo { public: - T myTime; - int myEdge; - T otherTime; - int otherEdge; - int numinter; + T myTime; // parameter value of intersection on curve on first CurvePolygon + int myEdge; // index of curve on first CurvedPolygon + T otherTime; // parameter value of intersection on curve on second CurvedPolygon + int otherEdge; // index of curve on second CurvedPolygon + int numinter; // unique intersection point identifier + + /*! + * \brief Comparison operator for sorting by parameter value + */ bool operator<(IntersectionInfo other) { return myTime bool intersect( CurvedPolygon< T, NDIMS>& p1, CurvedPolygon< T, NDIMS>& p2, std::vector< CurvedPolygon< T, NDIMS>>& pnew, - double tol=1e-7) + double tol=1E-8) { // for efficiency, linearity check actually uses a squared tolerance const double sq_tol = tol * tol; diff --git a/src/axom/primal/tests/primal_curved_polygon.cpp b/src/axom/primal/tests/primal_curved_polygon.cpp index 39fe88cc40..e2455c9826 100644 --- a/src/axom/primal/tests/primal_curved_polygon.cpp +++ b/src/axom/primal/tests/primal_curved_polygon.cpp @@ -226,7 +226,7 @@ TEST( primal_curvedpolygon, area_triangle_degenerate) bPolygon.addEdge(bCurve3); bPolygon[2][1][0]-=1e-10; - EXPECT_EQ(0.0, bPolygon.area(1e-11)); + EXPECT_EQ(0.0, bPolygon.area(1e-14)); } @@ -538,7 +538,12 @@ TEST( primal_curvedpolygon, intersection_triangle_linear) std::vector bPolygons3; bool didIntersect=intersect(bPolygon,bPolygon2,bPolygons3); EXPECT_TRUE(didIntersect); - + std::cout << bPolygons3.size() << std::endl; + int nEd= bPolygons3.size(); + for (int i=0; i< bPolygons3.size(); ++i) + { + std::cout << bPolygons3[i] << std::endl; + } for (int i=0; i bPolygons3; - bool didIntersect=intersect(bPolygon,bPolygon2,bPolygons3); + bool didIntersect=intersect(bPolygon,bPolygon2,bPolygons3,1e-14); EXPECT_TRUE(didIntersect); CoordType A=0.0; From 0346c1ef77d7e9bbc6f63ae5a8ecd7cd4e974de0 Mon Sep 17 00:00:00 2001 From: David Gunderman Date: Mon, 30 Sep 2019 15:15:06 -0700 Subject: [PATCH 34/44] Reformatted tests for CurvedPolygon --- src/axom/primal/geometry/BezierCurve.hpp | 23 +++ src/axom/primal/geometry/CurvedPolygon.hpp | 2 +- .../primal/tests/primal_curved_polygon.cpp | 145 +++++++++++------- 3 files changed, 110 insertions(+), 60 deletions(-) diff --git a/src/axom/primal/geometry/BezierCurve.hpp b/src/axom/primal/geometry/BezierCurve.hpp index ee42c7c62e..917f5cfb0f 100644 --- a/src/axom/primal/geometry/BezierCurve.hpp +++ b/src/axom/primal/geometry/BezierCurve.hpp @@ -174,6 +174,29 @@ class BezierCurve } } + /*! + * \brief Constructor for a Bezier Curve from an vector of coordinates + * + * \param [in] pts a vector with ord+1 control points + * \param [in] ord The Curve's polynomial order + * \pre order is greater than or equal to zero + * + */ + + BezierCurve(std::vector pts, int ord) + { + SLIC_ASSERT(pts != nullptr); + SLIC_ASSERT(ord >= 0); + + const int sz = utilities::max(0, ord+1); + m_controlPoints.resize(sz); + m_controlPoints = pts; + /*for (int p = 0 ; p <= ord ; ++p) + { + m_controlPoints[p] = pts[p]; + }*/ + } + /*! Sets the order of the Bezier Curve*/ void setOrder( int ord) { m_controlPoints.resize(ord+1); } diff --git a/src/axom/primal/geometry/CurvedPolygon.hpp b/src/axom/primal/geometry/CurvedPolygon.hpp index 9e78370a87..1173df323e 100644 --- a/src/axom/primal/geometry/CurvedPolygon.hpp +++ b/src/axom/primal/geometry/CurvedPolygon.hpp @@ -217,7 +217,7 @@ class CurvedPolygon } } - PointType moment(double tol = 1e-8) const + PointType centroid(double tol = 1e-8) const { const int ngon = numEdges(); PointType M = PointType::make_point(0.0,0.0); diff --git a/src/axom/primal/tests/primal_curved_polygon.cpp b/src/axom/primal/tests/primal_curved_polygon.cpp index e2455c9826..5f90701f97 100644 --- a/src/axom/primal/tests/primal_curved_polygon.cpp +++ b/src/axom/primal/tests/primal_curved_polygon.cpp @@ -14,6 +14,57 @@ namespace primal = axom::primal; +/** + * Helper function to compute the area and centroid of a curved polygon and to check that they match expectations, stored in \a expArea and \a expCentroid. Areas and Moments are computed within tolerance \a eps and checks use \a test_eps. + */ +template +void checkMoments(const primal::CurvedPolygon& bPolygon, + const CoordType expArea, const primal::Point& expMoment, + double eps, double test_eps) +{ + using Array = std::vector; + + EXPECT_DOUBLE_EQ(expArea, bPolygon.area(eps)); + for (int i= 0; i< DIM; ++i) + { + EXPECT_DOUBLE_EQ(expMoment[i], bPolygon.centroid(eps)[i]); + } +} + +template +primal::CurvedPolygon createPolygon( + std::vector> ControlPoints, + std::vector orders) +{ + using PointType = primal::Point< CoordType, DIM>; + using CurvedPolygonType = primal::CurvedPolygon< CoordType, DIM>; + using BezierCurveType = primal::BezierCurve; + + const int num_edges = orders.size(); + const int num_unique_control_points = ControlPoints.size(); + + //std::cout << num_edges << ", " << num_unique_control_points << std::endl; + //std::cout << ControlPoints << std::endl; + for (int i=0; i subCP; + subCP.assign(ControlPoints.begin()+iter, ControlPoints.begin()+iter+orders[j]+1); + BezierCurveType addCurve(subCP,orders[j]); + bPolygon.addEdge(addCurve); + iter+=(orders[j]); + } + std::cout << bPolygon << std::endl; + return bPolygon; +} + //------------------------------------------------------------------------------ TEST( primal_curvedpolygon, constructor ) { @@ -88,7 +139,6 @@ TEST( primal_curvedpolygon, isClosed ) using CoordType = double; using CurvedPolygonType = primal::CurvedPolygon< CoordType, DIM>; using PointType = primal::Point< CoordType, DIM >; - using BezierCurveType = primal::BezierCurve< CoordType, DIM >; SLIC_INFO("Test checking if CurvedPolygon is closed."); @@ -96,38 +146,26 @@ TEST( primal_curvedpolygon, isClosed ) EXPECT_EQ(0, bPolygon.numEdges()); EXPECT_EQ(false,bPolygon.isClosed()); - PointType controlPoints[2] = { + std::vector CP = { PointType::make_point( 0.6, 1.2), - PointType::make_point( 0.3, 2.0) - }; - - PointType controlPoints2[2] = { PointType::make_point( 0.3, 2.0), - PointType::make_point( 0.0, 1.6) - }; - - - PointType controlPoints3[2] = { PointType::make_point( 0.0, 1.6), PointType::make_point( 0.6, 1.2) }; + std::vector orders = {1,1,1}; - BezierCurveType bCurve(controlPoints,1); - bPolygon.addEdge(bCurve); - EXPECT_EQ(false,bPolygon.isClosed()); - - BezierCurveType bCurve2(controlPoints2,1); - bPolygon.addEdge(bCurve2); - - EXPECT_EQ(2,bPolygon.numEdges()); - EXPECT_EQ(false,bPolygon.isClosed()); - - BezierCurveType bCurve3(controlPoints3,1); - bPolygon.addEdge(bCurve3); + std::vector subCP = { + PointType::make_point( 0.6, 1.2), + PointType::make_point( 0.3, 2.0) + }; + std::vector suborders = {1}; + CurvedPolygonType subPolygon = createPolygon(subCP,suborders); + EXPECT_EQ(false,subPolygon.isClosed()); + + bPolygon = createPolygon(CP,orders); EXPECT_EQ(3,bPolygon.numEdges()); EXPECT_EQ(true,bPolygon.isClosed()); - bPolygon[2][1][0]-=2e-15; EXPECT_EQ(false,bPolygon.isClosed(1e-15)); } @@ -143,50 +181,38 @@ TEST( primal_curvedpolygon, split_edge) SLIC_INFO("Test checking CurvedPolygon edge split."); - CurvedPolygonType bPolygon; - EXPECT_EQ(0, bPolygon.numEdges()); - - PointType controlPoints[2] = { + std::vector CP = { PointType::make_point( 0.6, 1.2), - PointType::make_point( 0.3, 2.0) - }; - - PointType controlPoints2[2] = { PointType::make_point( 0.3, 2.0), - PointType::make_point( 0.0, 1.6) - }; - - - PointType controlPoints3[2] = { PointType::make_point( 0.0, 1.6), PointType::make_point( 0.6, 1.2) }; - BezierCurveType bCurve(controlPoints,1); - bPolygon.addEdge(bCurve); - - BezierCurveType bCurve2(controlPoints2,1); - bPolygon.addEdge(bCurve2); + std::vector orders32 = {1,1,1}; + CurvedPolygonType bPolygon32 = createPolygon(CP,orders32); + std::cout << "Got here!! " << std::endl; + std::vector subCP; - BezierCurveType bCurve3(controlPoints3,1); - bPolygon.addEdge(bCurve3); + subCP.assign(CP.begin(), CP.begin()+2); + BezierCurveType bCurve(subCP,1); + bPolygon32.splitEdge(0,.5); - bPolygon.splitEdge(0,.5); + BezierCurveType bCurve2; BezierCurveType bCurve3; bCurve.split(.5,bCurve2,bCurve3); - EXPECT_EQ(bPolygon.numEdges(),4); - for (int i=0; i< bPolygon[0].getOrder(); ++i) + EXPECT_EQ(bPolygon32.numEdges(),4); + for (int i=0; i< bPolygon32[0].getOrder(); ++i) { for (int dimi=0; dimi< DIM; ++dimi) { - EXPECT_EQ(bPolygon[0][i][dimi],bCurve2[i][dimi]); - EXPECT_EQ(bPolygon[1][i][dimi],bCurve3[i][dimi]); + EXPECT_EQ(bPolygon32[0][i][dimi],bCurve2[i][dimi]); + EXPECT_EQ(bPolygon32[1][i][dimi],bCurve3[i][dimi]); } } } //---------------------------------------------------------------------------------- -TEST( primal_curvedpolygon, area_triangle_degenerate) +TEST( primal_curvedpolygon, moments_triangle_degenerate) { const int DIM = 2; using CoordType = double; @@ -199,6 +225,7 @@ TEST( primal_curvedpolygon, area_triangle_degenerate) CurvedPolygonType bPolygon; EXPECT_EQ(0, bPolygon.numEdges()); EXPECT_EQ(0.0, bPolygon.area()); + PointType origin = PointType::make_point(0.0,0.0); PointType controlPoints[2] = { PointType::make_point( 0.6, 1.2), @@ -216,18 +243,17 @@ TEST( primal_curvedpolygon, area_triangle_degenerate) BezierCurveType bCurve(controlPoints,1); bPolygon.addEdge(bCurve); - EXPECT_EQ(0.0, bPolygon.area()); + checkMoments(bPolygon, 0.0, origin, 1e-14, 1e-15); BezierCurveType bCurve2(controlPoints2,1); bPolygon.addEdge(bCurve2); - EXPECT_EQ(0.0, bPolygon.area()); + checkMoments(bPolygon, 0.0, origin, 1e-14, 1e-15); BezierCurveType bCurve3(controlPoints3,1); bPolygon.addEdge(bCurve3); - bPolygon[2][1][0]-=1e-10; - EXPECT_EQ(0.0, bPolygon.area(1e-14)); - + bPolygon[2][1][0]-=1e-11; + checkMoments(bPolygon, 0.0, origin, 1e-14, 1e-15); } //---------------------------------------------------------------------------------- @@ -404,7 +430,7 @@ TEST( primal_curvedpolygon, moment_triangle_linear ) BezierCurveType bCurve3(controlPoints3,1); bPolygon.addEdge(bCurve3); - PointType M= bPolygon.moment(); + PointType M= bPolygon.centroid(); CoordType trueM1= 0.3; CoordType trueM2= 1.6; @@ -452,7 +478,7 @@ TEST( primal_curvedpolygon, moment_triangle_mixed_order) BezierCurveType bCurve3(controlPoints3,1); bPolygon.addEdge(bCurve3); - PointType M= bPolygon.moment(); + PointType M= bPolygon.centroid(); CoordType trueM2= 1.55764705882353; CoordType trueM1= .2970147058823527; @@ -539,8 +565,8 @@ TEST( primal_curvedpolygon, intersection_triangle_linear) bool didIntersect=intersect(bPolygon,bPolygon2,bPolygons3); EXPECT_TRUE(didIntersect); std::cout << bPolygons3.size() << std::endl; - int nEd= bPolygons3.size(); - for (int i=0; i< bPolygons3.size(); ++i) + // int nEd= bPolygons3.size(); + for (int i=0; i< static_cast(bPolygons3.size()); ++i) { std::cout << bPolygons3[i] << std::endl; } @@ -927,6 +953,7 @@ TEST( primal_curvedpolygon, area_intersection_triangle_inclusion) bPolygons3.clear(); bool didIntersect2=intersect(bPolygon2,bPolygon,bPolygons3); EXPECT_TRUE(didIntersect); + EXPECT_TRUE(didIntersect2); EXPECT_EQ(bPolygons3.size(),1); } //---------------------------------------------------------------------------------- From 95d6f23254a1687e21fdc2613da835cff422f46a Mon Sep 17 00:00:00 2001 From: David Gunderman Date: Tue, 1 Oct 2019 11:45:39 -0700 Subject: [PATCH 35/44] Separated curvedpolygon tests, added some tests to curved_polygon_test --- src/axom/primal/tests/CMakeLists.txt | 1 + .../primal/tests/primal_curved_polygon.cpp | 721 ++---------------- .../tests/primal_curved_polygon_intersect.cpp | 546 +++++++++++++ 3 files changed, 624 insertions(+), 644 deletions(-) create mode 100644 src/axom/primal/tests/primal_curved_polygon_intersect.cpp diff --git a/src/axom/primal/tests/CMakeLists.txt b/src/axom/primal/tests/CMakeLists.txt index 158bfc058d..be2b00af83 100644 --- a/src/axom/primal/tests/CMakeLists.txt +++ b/src/axom/primal/tests/CMakeLists.txt @@ -27,6 +27,7 @@ set( primal_tests primal_bezier_curve.cpp primal_bezier_intersect.cpp primal_curved_polygon.cpp + primal_curved_polygon_intersect.cpp ) set(primal_test_depends fmt primal slic mint gtest) diff --git a/src/axom/primal/tests/primal_curved_polygon.cpp b/src/axom/primal/tests/primal_curved_polygon.cpp index 5f90701f97..e6750c0268 100644 --- a/src/axom/primal/tests/primal_curved_polygon.cpp +++ b/src/axom/primal/tests/primal_curved_polygon.cpp @@ -22,12 +22,10 @@ void checkMoments(const primal::CurvedPolygon& bPolygon, const CoordType expArea, const primal::Point& expMoment, double eps, double test_eps) { - using Array = std::vector; - - EXPECT_DOUBLE_EQ(expArea, bPolygon.area(eps)); + EXPECT_NEAR(expArea, bPolygon.area(eps),test_eps); for (int i= 0; i< DIM; ++i) { - EXPECT_DOUBLE_EQ(expMoment[i], bPolygon.centroid(eps)[i]); + EXPECT_NEAR(expMoment[i], bPolygon.centroid(eps)[i],test_eps); } } @@ -45,8 +43,6 @@ primal::CurvedPolygon createPolygon( //std::cout << num_edges << ", " << num_unique_control_points << std::endl; //std::cout << ControlPoints << std::endl; - for (int i=0; i createPolygon( bPolygon.addEdge(addCurve); iter+=(orders[j]); } - std::cout << bPolygon << std::endl; return bPolygon; } @@ -257,704 +252,142 @@ TEST( primal_curvedpolygon, moments_triangle_degenerate) } //---------------------------------------------------------------------------------- -TEST( primal_curvedpolygon, area_triangle_linear ) +TEST( primal_curvedpolygon, moments_triangle_linear) { const int DIM = 2; using CoordType = double; using CurvedPolygonType = primal::CurvedPolygon< CoordType, DIM>; using PointType = primal::Point< CoordType, DIM >; - using BezierCurveType = primal::BezierCurve< CoordType, DIM >; - - SLIC_INFO("Test checking CurvedPolygon linear triangle area computation."); - - CurvedPolygonType bPolygon; - EXPECT_EQ(0, bPolygon.numEdges()); - - PointType controlPoints[2] = { - PointType::make_point( 0.6, 1.2), - PointType::make_point( 0.3, 2.0) - }; - - PointType controlPoints2[2] = { - PointType::make_point( 0.3, 2.0), - PointType::make_point( 0.0, 1.6) - }; - PointType controlPoints3[2] = { - PointType::make_point( 0.0, 1.6), - PointType::make_point( 0.6, 1.2) - }; - BezierCurveType bCurve(controlPoints,1); - bPolygon.addEdge(bCurve); - - BezierCurveType bCurve2(controlPoints2,1); - bPolygon.addEdge(bCurve2); - - BezierCurveType bCurve3(controlPoints3,1); - bPolygon.addEdge(bCurve3); - - CoordType A= bPolygon.area(); - CoordType trueA= -.18; - - EXPECT_DOUBLE_EQ(trueA,A); + SLIC_INFO("Test checking CurvedPolygon linear triangle moment computation."); + std::vector CP = { + PointType::make_point( 0.6, 1.2), + PointType::make_point( 0.3, 2.0), + PointType::make_point( 0.0, 1.6), + PointType::make_point( 0.6, 1.2) + }; + + std::vector orders = {1,1,1}; + CurvedPolygonType bPolygon = createPolygon(CP,orders); + + CoordType trueA= -.18; + PointType trueC= PointType::make_point(0.3,1.6); + + checkMoments(bPolygon, trueA, trueC, 1e-14, 1e-15); } //---------------------------------------------------------------------------------- -TEST( primal_curvedpolygon, area_triangle_quadratic ) +TEST( primal_curvedpolygon, moments_triangle_quadratic ) { const int DIM = 2; - const int order =2; using CoordType = double; using CurvedPolygonType = primal::CurvedPolygon< CoordType, DIM>; using PointType = primal::Point< CoordType, DIM >; - using BezierCurveType = primal::BezierCurve< CoordType, DIM >; SLIC_INFO("Test checking CurvedPolygon quadratic triangle area computation."); - CurvedPolygonType bPolygon; - EXPECT_EQ(0, bPolygon.numEdges()); - PointType controlPoints[order+1] = { - PointType::make_point(0.6, 1.2), - PointType::make_point(0.4, 1.3), - PointType::make_point(0.3, 2.0) - }; + SLIC_INFO("Test checking CurvedPolygon mixed order triangle moment computation."); - PointType controlPoints2[order+1] = { + std::vector CP = { + PointType::make_point( 0.6, 1.2), + PointType::make_point( 0.4, 1.3), PointType::make_point( 0.3, 2.0), PointType::make_point( 0.27, 1.5), - PointType::make_point( 0.0 , 1.6) - }; - - PointType controlPoints3[order+1] = { - PointType::make_point( 0.0 , 1.6), + PointType::make_point( 0.0, 1.6), PointType::make_point( 0.1 , 1.5), PointType::make_point( 0.6, 1.2) }; - BezierCurveType bCurve(controlPoints,order); - bPolygon.addEdge(bCurve); - - BezierCurveType bCurve2(controlPoints2,order); - bPolygon.addEdge(bCurve2); - - BezierCurveType bCurve3(controlPoints3,order); - bPolygon.addEdge(bCurve3); - - CoordType A = bPolygon.area(); - - CoordType trueA = -.09733333333333333333; - EXPECT_DOUBLE_EQ(trueA,A); -} - -//---------------------------------------------------------------------------------- -TEST( primal_curvedpolygon, area_triangle_mixed_order) -{ - const int DIM = 2; - using CoordType = double; - using CurvedPolygonType = primal::CurvedPolygon< CoordType, DIM>; - using PointType = primal::Point< CoordType, DIM >; - using BezierCurveType = primal::BezierCurve< CoordType, DIM >; - - SLIC_INFO("Test checking CurvedPolygon mixed order triangle area computation."); - - CurvedPolygonType bPolygon; - EXPECT_EQ(0, bPolygon.numEdges()); - - PointType controlPoints[3] = { - PointType::make_point(0.6, 1.2), - PointType::make_point(0.4, 1.3), - PointType::make_point(0.3, 2.0) - }; - - PointType controlPoints2[3] = { - PointType::make_point( 0.3, 2.0), - PointType::make_point( 0.27, 1.5), - PointType::make_point( 0.0 , 1.6) - }; - - PointType controlPoints3[2] = { - PointType::make_point( 0.0 , 1.6), - PointType::make_point( 0.6, 1.2) - }; - - BezierCurveType bCurve(controlPoints,2); - bPolygon.addEdge(bCurve); - - BezierCurveType bCurve2(controlPoints2,2); - bPolygon.addEdge(bCurve2); + std::vector orders = {2,2,2}; + CurvedPolygonType bPolygon = createPolygon(CP,orders); - BezierCurveType bCurve3(controlPoints3,1); - bPolygon.addEdge(bCurve3); + CoordType trueA = -0.097333333333333; + PointType trueC= PointType::make_point(.294479452054794,1.548219178082190); - CoordType A = bPolygon.area(); - - CoordType trueA = -.0906666666666666666666; - EXPECT_DOUBLE_EQ(trueA,A); + checkMoments(bPolygon, trueA, trueC, 1e-15, 1e-14); } //---------------------------------------------------------------------------------- -TEST( primal_curvedpolygon, moment_triangle_linear ) +TEST( primal_curvedpolygon, moments_triangle_mixed_order) { const int DIM = 2; using CoordType = double; using CurvedPolygonType = primal::CurvedPolygon< CoordType, DIM>; using PointType = primal::Point< CoordType, DIM >; - using BezierCurveType = primal::BezierCurve< CoordType, DIM >; - SLIC_INFO("Test checking CurvedPolygon linear triangle moment computation."); + SLIC_INFO("Test checking CurvedPolygon mixed order triangle moment computation."); - CurvedPolygonType bPolygon; - EXPECT_EQ(0, bPolygon.numEdges()); - - PointType controlPoints[2] = { + std::vector CP = { PointType::make_point( 0.6, 1.2), - PointType::make_point( 0.3, 2.0) - }; - - PointType controlPoints2[2] = { + PointType::make_point( 0.4, 1.3), PointType::make_point( 0.3, 2.0), - PointType::make_point( 0.0, 1.6) - }; - PointType controlPoints3[2] = { + PointType::make_point( 0.27, 1.5), PointType::make_point( 0.0, 1.6), PointType::make_point( 0.6, 1.2) }; - BezierCurveType bCurve(controlPoints,1); - bPolygon.addEdge(bCurve); - - BezierCurveType bCurve2(controlPoints2,1); - bPolygon.addEdge(bCurve2); - - BezierCurveType bCurve3(controlPoints3,1); - bPolygon.addEdge(bCurve3); + std::vector orders = {2,2,1}; + CurvedPolygonType bPolygon = createPolygon(CP,orders); - PointType M= bPolygon.centroid(); - CoordType trueM1= 0.3; - CoordType trueM2= 1.6; + CoordType trueA = -.0906666666666666666666; + PointType trueC= PointType::make_point(.2970147058823527,1.55764705882353); - EXPECT_DOUBLE_EQ(trueM1,M[0]); - EXPECT_DOUBLE_EQ(trueM2,M[1]); + checkMoments(bPolygon, trueA, trueC, 1e-14, 1e-15); } //---------------------------------------------------------------------------------- -TEST( primal_curvedpolygon, moment_triangle_mixed_order) +TEST( primal_curvedpolygon, moments_quad_all_orders) { const int DIM = 2; using CoordType = double; using CurvedPolygonType = primal::CurvedPolygon< CoordType, DIM>; using PointType = primal::Point< CoordType, DIM >; - using BezierCurveType = primal::BezierCurve< CoordType, DIM >; - - SLIC_INFO("Test checking CurvedPolygon mixed order triangle area computation."); - CurvedPolygonType bPolygon; - EXPECT_EQ(0, bPolygon.numEdges()); - - PointType controlPoints[3] = { - PointType::make_point(0.6, 1.2), - PointType::make_point(0.4, 1.3), - PointType::make_point(0.3, 2.0) - }; - - PointType controlPoints2[3] = { - PointType::make_point( 0.3, 2.0), - PointType::make_point( 0.27, 1.5), - PointType::make_point( 0.0 , 1.6) - }; - - PointType controlPoints3[2] = { - PointType::make_point( 0.0 , 1.6), - PointType::make_point( 0.6, 1.2) - }; - - BezierCurveType bCurve(controlPoints,2); - bPolygon.addEdge(bCurve); - - BezierCurveType bCurve2(controlPoints2,2); - bPolygon.addEdge(bCurve2); - - BezierCurveType bCurve3(controlPoints3,1); - bPolygon.addEdge(bCurve3); - - PointType M= bPolygon.centroid(); - CoordType trueM2= 1.55764705882353; - CoordType trueM1= .2970147058823527; - - EXPECT_DOUBLE_EQ(trueM1,M[0]); - EXPECT_DOUBLE_EQ(trueM2,M[1]); -} - - - //---------------------------------------------------------------------------------- -TEST( primal_curvedpolygon, intersection_triangle_linear) -{ - const int DIM = 2; - const int order = 1; - using CoordType = double; - using CurvedPolygonType = primal::CurvedPolygon< CoordType, DIM>; - using PointType = primal::Point< CoordType, DIM >; - using BezierCurveType = primal::BezierCurve< CoordType, DIM >; - - SLIC_INFO("Test intersecting two linear triangular CurvedPolygons (single region)."); - - CurvedPolygonType bPolygon; - EXPECT_EQ(0, bPolygon.numEdges()); - - PointType controlPoints[order+1] = { - PointType::make_point(0.6, 1.2), - PointType::make_point(0.3, 2.0) - }; - - PointType controlPoints2[order+1] = { - PointType::make_point( 0.3, 2.0), - PointType::make_point( 0.0 , 1.6) - }; - - PointType controlPoints3[order+1] = { - PointType::make_point( 0.0 , 1.6), - PointType::make_point( 0.6, 1.2) - }; - - BezierCurveType bCurve(controlPoints,order); - bPolygon.addEdge(bCurve); - - BezierCurveType bCurve2(controlPoints2,order); - bPolygon.addEdge(bCurve2); - - BezierCurveType bCurve3(controlPoints3,order); - bPolygon.addEdge(bCurve3); - CurvedPolygonType bPolygon2=bPolygon; - for (int i=0; i bPolygons3; - bool didIntersect=intersect(bPolygon,bPolygon2,bPolygons3); - EXPECT_TRUE(didIntersect); - std::cout << bPolygons3.size() << std::endl; - // int nEd= bPolygons3.size(); - for (int i=0; i< static_cast(bPolygons3.size()); ++i) - { - std::cout << bPolygons3[i] << std::endl; - } - for (int i=0; i(bPolygons3.size()); ++idxcurve) - { - for (int k=0; k; - using PointType = primal::Point< CoordType, DIM >; - using BezierCurveType = primal::BezierCurve< CoordType, DIM >; - - SLIC_INFO("Test intersecting two quadratic triangular CurvedPolygons (single region)."); - - CurvedPolygonType bPolygon; - EXPECT_EQ(0, bPolygon.numEdges()); - - PointType controlPoints[order+1] = { - PointType::make_point(0.6, 1.2), - PointType::make_point(0.4, 1.3), - PointType::make_point(0.3, 2.0) - }; - - PointType controlPoints2[order+1] = { - PointType::make_point( 0.3, 2.0), - PointType::make_point( 0.27, 1.5), - PointType::make_point( 0.0 , 1.6) - }; - - PointType controlPoints3[order+1] = { - PointType::make_point( 0.0 , 1.6), - PointType::make_point( 0.1 , 1.5), - PointType::make_point( 0.6, 1.2) - }; - - BezierCurveType bCurve(controlPoints,order); - bPolygon.addEdge(bCurve); - - BezierCurveType bCurve2(controlPoints2,order); - bPolygon.addEdge(bCurve2); - - BezierCurveType bCurve3(controlPoints3,order); - bPolygon.addEdge(bCurve3); - CurvedPolygonType bPolygon2=bPolygon; - for (int i=0; i bPolygons3; - bool didIntersect=intersect(bPolygon,bPolygon2,bPolygons3); - EXPECT_TRUE(didIntersect); - - for (int i=0; i CPorig = { + PointType::make_point( 0.0, 0.0), + PointType::make_point( 0.0, 1.0), + PointType::make_point( 1.0, 1.0), + PointType::make_point( 1.0, 0.0), + PointType::make_point( 0.0, 0.0) + }; + + std::vector orders = {1,1,1,1}; + CurvedPolygonType bPolygon = createPolygon(CPorig,orders); + + CoordType trueA= 1.0; + PointType trueC= PointType::make_point(0.5,0.5); + + checkMoments(bPolygon, trueA, trueC, 1e-14, 1e-15); + for (int p=2; p<11; ++p) { - for (int idxcurve=0; idxcurve< static_cast(bPolygons3.size()); ++idxcurve) + std::vector CP = CPorig; + for (int side=0; side<4; ++side) { - for (int k=0; k; - using PointType = primal::Point< CoordType, DIM >; - using BezierCurveType = primal::BezierCurve< CoordType, DIM >; - - SLIC_INFO("Test finding area of intersection two linear triangular CurvedPolygons (single region)."); - - CurvedPolygonType bPolygon; - EXPECT_EQ(0, bPolygon.numEdges()); - - PointType controlPoints[order+1] = { - PointType::make_point(0.6, 1.2), - PointType::make_point(0.3, 2.0) - }; - - PointType controlPoints2[order+1] = { - PointType::make_point( 0.3, 2.0), - PointType::make_point( 0.0 , 1.6) - }; - - PointType controlPoints3[order+1] = { - PointType::make_point( 0.0 , 1.6), - PointType::make_point( 0.6, 1.2) - }; - - BezierCurveType bCurve(controlPoints,order); - bPolygon.addEdge(bCurve); - - BezierCurveType bCurve2(controlPoints2,order); - bPolygon.addEdge(bCurve2); - - BezierCurveType bCurve3(controlPoints3,order); - bPolygon.addEdge(bCurve3); - CurvedPolygonType bPolygon2=bPolygon; - for (int i=0; i bPolygons3; - bool didIntersect=intersect(bPolygon,bPolygon2,bPolygons3); - EXPECT_TRUE(didIntersect); - - - CoordType A=0.0; - for (int i=0; i(bPolygons3.size()); ++i) - { - A+=bPolygons3[i].area(1e-14); - } - CoordType expA=-0.0793347222222222222; - EXPECT_NEAR(A,expA,1e-10); -} - -//---------------------------------------------------------------------------------- -TEST( primal_curvedpolygon, area_intersection_triangle_quadratic) -{ - const int DIM = 2; - const int order = 2; - using CoordType = double; - using CurvedPolygonType = primal::CurvedPolygon< CoordType, DIM>; - using PointType = primal::Point< CoordType, DIM >; - using BezierCurveType = primal::BezierCurve< CoordType, DIM >; - - SLIC_INFO("Test intersecting two quadratic triangular CurvedPolygons (single region)."); - - CurvedPolygonType bPolygon; - EXPECT_EQ(0, bPolygon.numEdges()); - - PointType controlPoints[order+1] = { - PointType::make_point(0.6, 1.2), - PointType::make_point(0.4, 1.3), - PointType::make_point(0.3, 2.0) - }; - - PointType controlPoints2[order+1] = { - PointType::make_point( 0.3, 2.0), - PointType::make_point( 0.27, 1.5), - PointType::make_point( 0.0 , 1.6) - }; - - PointType controlPoints3[order+1] = { - PointType::make_point( 0.0 , 1.6), - PointType::make_point( 0.1 , 1.5), - PointType::make_point( 0.6, 1.2) - }; - - BezierCurveType bCurve(controlPoints,order); - bPolygon.addEdge(bCurve); - - BezierCurveType bCurve2(controlPoints2,order); - bPolygon.addEdge(bCurve2); - - BezierCurveType bCurve3(controlPoints3,order); - bPolygon.addEdge(bCurve3); - CurvedPolygonType bPolygon2=bPolygon; - for (int i=0; i bPolygons3; - bool didIntersect=intersect(bPolygon,bPolygon2,bPolygons3,1e-14); - EXPECT_TRUE(didIntersect); - - CoordType A=0.0; - for (int i=0; i(bPolygons3.size()); ++i) - { - A+=bPolygons3[i].area(1e-8); - } - CoordType expA=-0.024649833203616; - EXPECT_NEAR(A,expA,1e-10); -} - -TEST( primal_curvedpolygon, area_intersection_triangle_quadratic_two_regions) -{ - const int DIM = 2; - const int order = 2; - using CoordType = double; - using CurvedPolygonType = primal::CurvedPolygon< CoordType, DIM>; - using PointType = primal::Point< CoordType, DIM >; - using BezierCurveType = primal::BezierCurve< CoordType, DIM >; - - SLIC_INFO("Test intersecting two quadratic triangular CurvedPolygons (two regions)."); - - CurvedPolygonType bPolygon; - EXPECT_EQ(0, bPolygon.numEdges()); - - PointType controlPoints[order+1] = { - PointType::make_point(0.6 , 1.2), - PointType::make_point(0.4 , 1.3), - PointType::make_point(0.3 , 2.0) - }; - - PointType controlPoints2[order+1] = { - PointType::make_point(0.3 , 2.0), - PointType::make_point(0.27, 1.5), - PointType::make_point(0.0 , 1.6) - }; - - PointType controlPoints3[order+1] = { - PointType::make_point(0.0 , 1.6), - PointType::make_point(0.1 , 1.5), - PointType::make_point(0.6 , 1.2) - }; - - PointType controlPoints4[order+1] = { - PointType::make_point(1.0205, 1.6699), - PointType::make_point(0.8339, 1.5467), - PointType::make_point(0.1777, 1.8101) - }; - - PointType controlPoints5[order+1] = { - PointType::make_point(0.1777, 1.8101), - PointType::make_point(0.5957, 1.5341), - PointType::make_point(0.3741, 1.3503) - }; - - PointType controlPoints6[order+1] = { - PointType::make_point(0.3741, 1.3503), - PointType::make_point(0.5107, 1.3869), - PointType::make_point(1.0205, 1.6699) - }; - CurvedPolygonType bPolygon2; - - BezierCurveType bCurve(controlPoints,order); - BezierCurveType bCurve4(controlPoints4,order); - bPolygon2.addEdge(bCurve4); - bPolygon.addEdge(bCurve); - - BezierCurveType bCurve2(controlPoints2,order); - BezierCurveType bCurve5(controlPoints5,order); - bPolygon2.addEdge(bCurve5); - bPolygon.addEdge(bCurve2); - - BezierCurveType bCurve3(controlPoints3,order); - BezierCurveType bCurve6(controlPoints6,order); - bPolygon2.addEdge(bCurve6); - bPolygon.addEdge(bCurve3); - - std::vector bPolygons3; - bool didIntersect=intersect(bPolygon,bPolygon2,bPolygons3); - EXPECT_TRUE(didIntersect); - EXPECT_EQ(bPolygons3.size(),2); -} - -TEST( primal_curvedpolygon, area_intersection_triangle_inclusion) -{ - const int DIM = 2; - const int order = 2; - using CoordType = double; - using CurvedPolygonType = primal::CurvedPolygon< CoordType, DIM>; - using PointType = primal::Point< CoordType, DIM >; - using BezierCurveType = primal::BezierCurve< CoordType, DIM >; - - SLIC_INFO("Test intersecting two quadratic triangular CurvedPolygons (inclusion)."); - - CurvedPolygonType bPolygon; - EXPECT_EQ(0, bPolygon.numEdges()); - - PointType controlPoints[order+1] = { - PointType::make_point(0.0 , 0.0), - PointType::make_point(0.5 , 0.0), - PointType::make_point(1.0 , 0.0) - }; - - PointType controlPoints2[order+1] = { - PointType::make_point(1.0 , 0.0), - PointType::make_point(0.5, 0.5), - PointType::make_point(0.0 , 1.0) - }; - - PointType controlPoints3[order+1] = { - PointType::make_point(0.0 , 1.0), - PointType::make_point(0.0 , 0.5), - PointType::make_point(0.0 , 0.0) - }; - - BezierCurveType bCurve(controlPoints,order); - bPolygon.addEdge(bCurve); - - BezierCurveType bCurve2(controlPoints2,order); - bPolygon.addEdge(bCurve2); - - BezierCurveType bCurve3(controlPoints3,order); - bPolygon.addEdge(bCurve3); - - CurvedPolygonType bPolygon2=bPolygon; - for (int i=0; i bPolygons3; - bool didIntersect=intersect(bPolygon,bPolygon2,bPolygons3); - bPolygons3.clear(); - bool didIntersect2=intersect(bPolygon2,bPolygon,bPolygons3); - EXPECT_TRUE(didIntersect); - EXPECT_TRUE(didIntersect2); - EXPECT_EQ(bPolygons3.size(),1); } //---------------------------------------------------------------------------------- int main(int argc, char* argv[]) diff --git a/src/axom/primal/tests/primal_curved_polygon_intersect.cpp b/src/axom/primal/tests/primal_curved_polygon_intersect.cpp new file mode 100644 index 0000000000..e56f1ed0e3 --- /dev/null +++ b/src/axom/primal/tests/primal_curved_polygon_intersect.cpp @@ -0,0 +1,546 @@ +// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +/* /file bezier_test.cpp + * /brief This file tests the BezierCurve.hpp and eval_bezier.hpp files + */ + +#include "gtest/gtest.h" + +#include "axom/primal/geometry/CurvedPolygon.hpp" +#include "axom/primal/operators/intersect.hpp" + +namespace primal = axom::primal; + +/** + * Helper function to compute the area and centroid of a curved polygon and to check that they match expectations, stored in \a expArea and \a expCentroid. Areas and Moments are computed within tolerance \a eps and checks use \a test_eps. + */ +template +void checkMoments(const primal::CurvedPolygon& bPolygon, + const CoordType expArea, const primal::Point& expMoment, + double eps, double test_eps) +{ + EXPECT_NEAR(expArea, bPolygon.area(eps),test_eps); + for (int i= 0; i< DIM; ++i) + { + EXPECT_NEAR(expMoment[i], bPolygon.centroid(eps)[i],test_eps); + } +} + +template +primal::CurvedPolygon createPolygon( + std::vector> ControlPoints, + std::vector orders) +{ + using PointType = primal::Point< CoordType, DIM>; + using CurvedPolygonType = primal::CurvedPolygon< CoordType, DIM>; + using BezierCurveType = primal::BezierCurve; + + const int num_edges = orders.size(); + const int num_unique_control_points = ControlPoints.size(); + + //std::cout << num_edges << ", " << num_unique_control_points << std::endl; + //std::cout << ControlPoints << std::endl; + for (int i=0; i subCP; + subCP.assign(ControlPoints.begin()+iter, ControlPoints.begin()+iter+orders[j]+1); + BezierCurveType addCurve(subCP,orders[j]); + bPolygon.addEdge(addCurve); + iter+=(orders[j]); + } + std::cout << bPolygon << std::endl; + return bPolygon; +} + +TEST( primal_curvedpolygon, intersection_triangle_linear) +{ + const int DIM = 2; + const int order = 1; + using CoordType = double; + using CurvedPolygonType = primal::CurvedPolygon< CoordType, DIM>; + using PointType = primal::Point< CoordType, DIM >; + using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + + SLIC_INFO("Test intersecting two linear triangular CurvedPolygons (single region)."); + + CurvedPolygonType bPolygon; + EXPECT_EQ(0, bPolygon.numEdges()); + + PointType controlPoints[order+1] = { + PointType::make_point(0.6, 1.2), + PointType::make_point(0.3, 2.0) + }; + + PointType controlPoints2[order+1] = { + PointType::make_point( 0.3, 2.0), + PointType::make_point( 0.0 , 1.6) + }; + + PointType controlPoints3[order+1] = { + PointType::make_point( 0.0 , 1.6), + PointType::make_point( 0.6, 1.2) + }; + + BezierCurveType bCurve(controlPoints,order); + bPolygon.addEdge(bCurve); + + BezierCurveType bCurve2(controlPoints2,order); + bPolygon.addEdge(bCurve2); + + BezierCurveType bCurve3(controlPoints3,order); + bPolygon.addEdge(bCurve3); + CurvedPolygonType bPolygon2=bPolygon; + for (int i=0; i bPolygons3; + bool didIntersect=intersect(bPolygon,bPolygon2,bPolygons3); + EXPECT_TRUE(didIntersect); + std::cout << bPolygons3.size() << std::endl; + // int nEd= bPolygons3.size(); + for (int i=0; i< static_cast(bPolygons3.size()); ++i) + { + std::cout << bPolygons3[i] << std::endl; + } + for (int i=0; i(bPolygons3.size()); ++idxcurve) + { + for (int k=0; k; + using PointType = primal::Point< CoordType, DIM >; + using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + + SLIC_INFO("Test intersecting two quadratic triangular CurvedPolygons (single region)."); + + CurvedPolygonType bPolygon; + EXPECT_EQ(0, bPolygon.numEdges()); + + PointType controlPoints[order+1] = { + PointType::make_point(0.6, 1.2), + PointType::make_point(0.4, 1.3), + PointType::make_point(0.3, 2.0) + }; + + PointType controlPoints2[order+1] = { + PointType::make_point( 0.3, 2.0), + PointType::make_point( 0.27, 1.5), + PointType::make_point( 0.0 , 1.6) + }; + + PointType controlPoints3[order+1] = { + PointType::make_point( 0.0 , 1.6), + PointType::make_point( 0.1 , 1.5), + PointType::make_point( 0.6, 1.2) + }; + + BezierCurveType bCurve(controlPoints,order); + bPolygon.addEdge(bCurve); + + BezierCurveType bCurve2(controlPoints2,order); + bPolygon.addEdge(bCurve2); + + BezierCurveType bCurve3(controlPoints3,order); + bPolygon.addEdge(bCurve3); + CurvedPolygonType bPolygon2=bPolygon; + for (int i=0; i bPolygons3; + bool didIntersect=intersect(bPolygon,bPolygon2,bPolygons3); + EXPECT_TRUE(didIntersect); + + for (int i=0; i(bPolygons3.size()); ++idxcurve) + { + for (int k=0; k; + using PointType = primal::Point< CoordType, DIM >; + using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + + SLIC_INFO("Test finding area of intersection two linear triangular CurvedPolygons (single region)."); + + CurvedPolygonType bPolygon; + EXPECT_EQ(0, bPolygon.numEdges()); + + PointType controlPoints[order+1] = { + PointType::make_point(0.6, 1.2), + PointType::make_point(0.3, 2.0) + }; + + PointType controlPoints2[order+1] = { + PointType::make_point( 0.3, 2.0), + PointType::make_point( 0.0 , 1.6) + }; + + PointType controlPoints3[order+1] = { + PointType::make_point( 0.0 , 1.6), + PointType::make_point( 0.6, 1.2) + }; + + BezierCurveType bCurve(controlPoints,order); + bPolygon.addEdge(bCurve); + + BezierCurveType bCurve2(controlPoints2,order); + bPolygon.addEdge(bCurve2); + + BezierCurveType bCurve3(controlPoints3,order); + bPolygon.addEdge(bCurve3); + CurvedPolygonType bPolygon2=bPolygon; + for (int i=0; i bPolygons3; + bool didIntersect=intersect(bPolygon,bPolygon2,bPolygons3); + EXPECT_TRUE(didIntersect); + + + CoordType A=0.0; + for (int i=0; i(bPolygons3.size()); ++i) + { + A+=bPolygons3[i].area(1e-14); + } + CoordType expA=-0.0793347222222222222; + EXPECT_NEAR(A,expA,1e-10); +} + +//---------------------------------------------------------------------------------- +TEST( primal_curvedpolygon, area_intersection_triangle_quadratic) +{ + const int DIM = 2; + const int order = 2; + using CoordType = double; + using CurvedPolygonType = primal::CurvedPolygon< CoordType, DIM>; + using PointType = primal::Point< CoordType, DIM >; + using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + + SLIC_INFO("Test intersecting two quadratic triangular CurvedPolygons (single region)."); + + CurvedPolygonType bPolygon; + EXPECT_EQ(0, bPolygon.numEdges()); + + PointType controlPoints[order+1] = { + PointType::make_point(0.6, 1.2), + PointType::make_point(0.4, 1.3), + PointType::make_point(0.3, 2.0) + }; + + PointType controlPoints2[order+1] = { + PointType::make_point( 0.3, 2.0), + PointType::make_point( 0.27, 1.5), + PointType::make_point( 0.0 , 1.6) + }; + + PointType controlPoints3[order+1] = { + PointType::make_point( 0.0 , 1.6), + PointType::make_point( 0.1 , 1.5), + PointType::make_point( 0.6, 1.2) + }; + + BezierCurveType bCurve(controlPoints,order); + bPolygon.addEdge(bCurve); + + BezierCurveType bCurve2(controlPoints2,order); + bPolygon.addEdge(bCurve2); + + BezierCurveType bCurve3(controlPoints3,order); + bPolygon.addEdge(bCurve3); + CurvedPolygonType bPolygon2=bPolygon; + for (int i=0; i bPolygons3; + bool didIntersect=intersect(bPolygon,bPolygon2,bPolygons3,1e-14); + EXPECT_TRUE(didIntersect); + + CoordType A=0.0; + for (int i=0; i(bPolygons3.size()); ++i) + { + A+=bPolygons3[i].area(1e-8); + } + CoordType expA=-0.024649833203616; + EXPECT_NEAR(A,expA,1e-10); +} + +TEST( primal_curvedpolygon, area_intersection_triangle_quadratic_two_regions) +{ + const int DIM = 2; + const int order = 2; + using CoordType = double; + using CurvedPolygonType = primal::CurvedPolygon< CoordType, DIM>; + using PointType = primal::Point< CoordType, DIM >; + using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + + SLIC_INFO("Test intersecting two quadratic triangular CurvedPolygons (two regions)."); + + CurvedPolygonType bPolygon; + EXPECT_EQ(0, bPolygon.numEdges()); + + PointType controlPoints[order+1] = { + PointType::make_point(0.6 , 1.2), + PointType::make_point(0.4 , 1.3), + PointType::make_point(0.3 , 2.0) + }; + + PointType controlPoints2[order+1] = { + PointType::make_point(0.3 , 2.0), + PointType::make_point(0.27, 1.5), + PointType::make_point(0.0 , 1.6) + }; + + PointType controlPoints3[order+1] = { + PointType::make_point(0.0 , 1.6), + PointType::make_point(0.1 , 1.5), + PointType::make_point(0.6 , 1.2) + }; + + PointType controlPoints4[order+1] = { + PointType::make_point(1.0205, 1.6699), + PointType::make_point(0.8339, 1.5467), + PointType::make_point(0.1777, 1.8101) + }; + + PointType controlPoints5[order+1] = { + PointType::make_point(0.1777, 1.8101), + PointType::make_point(0.5957, 1.5341), + PointType::make_point(0.3741, 1.3503) + }; + + PointType controlPoints6[order+1] = { + PointType::make_point(0.3741, 1.3503), + PointType::make_point(0.5107, 1.3869), + PointType::make_point(1.0205, 1.6699) + }; + CurvedPolygonType bPolygon2; + + BezierCurveType bCurve(controlPoints,order); + BezierCurveType bCurve4(controlPoints4,order); + bPolygon2.addEdge(bCurve4); + bPolygon.addEdge(bCurve); + + BezierCurveType bCurve2(controlPoints2,order); + BezierCurveType bCurve5(controlPoints5,order); + bPolygon2.addEdge(bCurve5); + bPolygon.addEdge(bCurve2); + + BezierCurveType bCurve3(controlPoints3,order); + BezierCurveType bCurve6(controlPoints6,order); + bPolygon2.addEdge(bCurve6); + bPolygon.addEdge(bCurve3); + + std::vector bPolygons3; + bool didIntersect=intersect(bPolygon,bPolygon2,bPolygons3); + EXPECT_TRUE(didIntersect); + EXPECT_EQ(bPolygons3.size(),2); +} + +TEST( primal_curvedpolygon, area_intersection_triangle_inclusion) +{ + const int DIM = 2; + const int order = 2; + using CoordType = double; + using CurvedPolygonType = primal::CurvedPolygon< CoordType, DIM>; + using PointType = primal::Point< CoordType, DIM >; + using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + + SLIC_INFO("Test intersecting two quadratic triangular CurvedPolygons (inclusion)."); + + CurvedPolygonType bPolygon; + EXPECT_EQ(0, bPolygon.numEdges()); + + PointType controlPoints[order+1] = { + PointType::make_point(0.0 , 0.0), + PointType::make_point(0.5 , 0.0), + PointType::make_point(1.0 , 0.0) + }; + + PointType controlPoints2[order+1] = { + PointType::make_point(1.0 , 0.0), + PointType::make_point(0.5, 0.5), + PointType::make_point(0.0 , 1.0) + }; + + PointType controlPoints3[order+1] = { + PointType::make_point(0.0 , 1.0), + PointType::make_point(0.0 , 0.5), + PointType::make_point(0.0 , 0.0) + }; + + BezierCurveType bCurve(controlPoints,order); + bPolygon.addEdge(bCurve); + + BezierCurveType bCurve2(controlPoints2,order); + bPolygon.addEdge(bCurve2); + + BezierCurveType bCurve3(controlPoints3,order); + bPolygon.addEdge(bCurve3); + + CurvedPolygonType bPolygon2=bPolygon; + for (int i=0; i bPolygons3; + bool didIntersect=intersect(bPolygon,bPolygon2,bPolygons3); + bPolygons3.clear(); + bool didIntersect2=intersect(bPolygon2,bPolygon,bPolygons3); + EXPECT_TRUE(didIntersect); + EXPECT_TRUE(didIntersect2); + EXPECT_EQ(bPolygons3.size(),1); +} +//---------------------------------------------------------------------------------- +int main(int argc, char* argv[]) +{ + int result = 0; + + ::testing::InitGoogleTest(&argc, argv); + + axom::slic::UnitTestLogger logger; // create & initialize test logger, + + result = RUN_ALL_TESTS(); + + return result; +} From 199c2ad6fd07622998758ece8e5c0c98a0b1f9af Mon Sep 17 00:00:00 2001 From: David Gunderman Date: Wed, 2 Oct 2019 09:04:27 -0700 Subject: [PATCH 36/44] Refactored CurvedPolygon tests, refactored CurvedPolygon --- .../detail/intersect_curved_poly_impl.hpp | 39 +- src/axom/primal/operators/intersect.hpp | 4 +- .../primal/tests/primal_curved_polygon.cpp | 483 ++++++++++++++++- .../tests/primal_curved_polygon_intersect.cpp | 505 +++++------------- 4 files changed, 607 insertions(+), 424 deletions(-) diff --git a/src/axom/primal/operators/detail/intersect_curved_poly_impl.hpp b/src/axom/primal/operators/detail/intersect_curved_poly_impl.hpp index 1aa299a24c..001ada1fb6 100644 --- a/src/axom/primal/operators/detail/intersect_curved_poly_impl.hpp +++ b/src/axom/primal/operators/detail/intersect_curved_poly_impl.hpp @@ -56,8 +56,8 @@ int isContained( const CurvedPolygon p1, * \param [out] pnew vector of type CurvedPolygon holding CurvedPolygon objects representing boundaries of intersection regions. */ template < typename T, int NDIMS> -bool intersect_polygon( CurvedPolygon< T, NDIMS>& p1, - CurvedPolygon< T, NDIMS>& p2, +bool intersect_polygon( const CurvedPolygon< T, NDIMS>& p1, + const CurvedPolygon< T, NDIMS>& p2, std::vector>& pnew, double sq_tol ) @@ -80,7 +80,6 @@ bool intersect_polygon( CurvedPolygon< T, NDIMS>& p1, { E1IntData[i].push_back({p1times[k],i,p2times[k],j,numinters+k+1}); E2IntData[j].push_back({p2times[k],j,p1times[k],i,numinters+k+1}); - // std::cout << p1times[k] << ',' << p2times[k] << std::endl; if (numinters==0) { firstinter={p1times[0],i,p2times[0],j,1}; @@ -105,43 +104,9 @@ bool intersect_polygon( CurvedPolygon< T, NDIMS>& p1, CurvedPolygon psplit[2]; // The two completely split polygons will be stored in this array psplit[0]=p1; psplit[1]=p2; - - //split polygon 1 at all the intersection points and store as psplit[0] - /*int addedints=0; - for (int i = 0; i < p1.numEdges(); ++i) - { - edgelabels[0].push_back(0); - for (int j = 0; j < static_cast(E1IntData[i].size()); ++j) - { - psplit[0].splitEdge(i+addedints,E1IntData[i][j].myTime); - edgelabels[0].insert(edgelabels[0].begin()+i+addedints,E1IntData[i][j].numinter); - addedints+=1; - for (int k = j+1; k < static_cast(E1IntData[i].size()); ++k) - { - E1IntData[i][k].myTime=(E1IntData[i][k].myTime-E1IntData[i][j].myTime)/(1-E1IntData[i][j].myTime); - } - } - } - addedints=0; - for (int i = 0; i < p2.numEdges(); ++i) - { - edgelabels[1].push_back(0); - for (int j = 0; j < static_cast(E2IntData[i].size()); ++j) - { - psplit[1].splitEdge(i+addedints,E2IntData[i][j].myTime); - edgelabels[1].insert(edgelabels[1].begin()+i+addedints,E2IntData[i][j].numinter); - addedints+=1; - for (int k = j+1; k < static_cast(E2IntData[i].size()); ++k) - { - E2IntData[i][k].myTime=(E2IntData[i][k].myTime-E2IntData[i][j].myTime)/(1-E2IntData[i][j].myTime); - } - } - }*/ splitPolygon(psplit[0],E1IntData,edgelabels[0]); splitPolygon(psplit[1],E2IntData,edgelabels[1]); - //split polygon 2 at all the intersection points and store as psplit[1] - // This performs the directional walking method using the completely split polygons std::vector::iterator> usedlabels; diff --git a/src/axom/primal/operators/intersect.hpp b/src/axom/primal/operators/intersect.hpp index 02ca357ca4..af21ca9fd8 100644 --- a/src/axom/primal/operators/intersect.hpp +++ b/src/axom/primal/operators/intersect.hpp @@ -337,8 +337,8 @@ bool intersect(const OrientedBoundingBox< T, 3 >& b1, * \f$ t==1 \f$. */ template < typename T, int NDIMS> -bool intersect( CurvedPolygon< T, NDIMS>& p1, - CurvedPolygon< T, NDIMS>& p2, +bool intersect( const CurvedPolygon< T, NDIMS>& p1, + const CurvedPolygon< T, NDIMS>& p2, std::vector< CurvedPolygon< T, NDIMS>>& pnew, double tol=1E-8) { diff --git a/src/axom/primal/tests/primal_curved_polygon.cpp b/src/axom/primal/tests/primal_curved_polygon.cpp index e6750c0268..d9b2cfff2d 100644 --- a/src/axom/primal/tests/primal_curved_polygon.cpp +++ b/src/axom/primal/tests/primal_curved_polygon.cpp @@ -29,10 +29,12 @@ void checkMoments(const primal::CurvedPolygon& bPolygon, } } +/* Helper function to create a CurvedPolygon from a list of control points and a list of orders of component curves. Control points should be given as a list of Points in order of orientation with no duplicates except that the first control point should also be the last control point (if the polygon is closed). Orders should be given as a list of ints in order of orientation, representing the orders of the component curves. + */ template primal::CurvedPolygon createPolygon( - std::vector> ControlPoints, - std::vector orders) + const std::vector> ControlPoints, + const std::vector orders) { using PointType = primal::Point< CoordType, DIM>; using CurvedPolygonType = primal::CurvedPolygon< CoordType, DIM>; @@ -41,12 +43,10 @@ primal::CurvedPolygon createPolygon( const int num_edges = orders.size(); const int num_unique_control_points = ControlPoints.size(); - //std::cout << num_edges << ", " << num_unique_control_points << std::endl; - //std::cout << ControlPoints << std::endl; - - //checks if the orders and control points given will give a valid polygon + //checks if the orders and control points given will yield a valid curved polygon EXPECT_EQ(accumulate(orders.begin(),orders.end(), 0)+1,num_unique_control_points); - + + //Converts the control points to BezierCurves of specified orders and stores them in a CurvedPolygon object. CurvedPolygonType bPolygon; int iter=0; for (int j=0; j; + using PointType = primal::Point< CoordType, DIM >; + using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + + SLIC_INFO("Test intersecting two linear triangular CurvedPolygons (single region)."); + + CurvedPolygonType bPolygon; + EXPECT_EQ(0, bPolygon.numEdges()); + + PointType controlPoints[order+1] = { + PointType::make_point(0.6, 1.2), + PointType::make_point(0.3, 2.0) + }; + + PointType controlPoints2[order+1] = { + PointType::make_point( 0.3, 2.0), + PointType::make_point( 0.0 , 1.6) + }; + + PointType controlPoints3[order+1] = { + PointType::make_point( 0.0 , 1.6), + PointType::make_point( 0.6, 1.2) + }; + + BezierCurveType bCurve(controlPoints,order); + bPolygon.addEdge(bCurve); + + BezierCurveType bCurve2(controlPoints2,order); + bPolygon.addEdge(bCurve2); + + BezierCurveType bCurve3(controlPoints3,order); + bPolygon.addEdge(bCurve3); + CurvedPolygonType bPolygon2=bPolygon; + for (int i=0; i bPolygons3; + bool didIntersect=intersect(bPolygon,bPolygon2,bPolygons3); + EXPECT_TRUE(didIntersect); + std::cout << bPolygons3.size() << std::endl; + // int nEd= bPolygons3.size(); + for (int i=0; i< static_cast(bPolygons3.size()); ++i) + { + std::cout << bPolygons3[i] << std::endl; + } + for (int i=0; i(bPolygons3.size()); ++idxcurve) + { + for (int k=0; k; + using PointType = primal::Point< CoordType, DIM >; + using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + + SLIC_INFO("Test intersecting two quadratic triangular CurvedPolygons (single region)."); + + CurvedPolygonType bPolygon; + EXPECT_EQ(0, bPolygon.numEdges()); + + PointType controlPoints[order+1] = { + PointType::make_point(0.6, 1.2), + PointType::make_point(0.4, 1.3), + PointType::make_point(0.3, 2.0) + }; + + PointType controlPoints2[order+1] = { + PointType::make_point( 0.3, 2.0), + PointType::make_point( 0.27, 1.5), + PointType::make_point( 0.0 , 1.6) + }; + + PointType controlPoints3[order+1] = { + PointType::make_point( 0.0 , 1.6), + PointType::make_point( 0.1 , 1.5), + PointType::make_point( 0.6, 1.2) + }; + + BezierCurveType bCurve(controlPoints,order); + bPolygon.addEdge(bCurve); + + BezierCurveType bCurve2(controlPoints2,order); + bPolygon.addEdge(bCurve2); + + BezierCurveType bCurve3(controlPoints3,order); + bPolygon.addEdge(bCurve3); + CurvedPolygonType bPolygon2=bPolygon; + for (int i=0; i bPolygons3; + bool didIntersect=intersect(bPolygon,bPolygon2,bPolygons3); + EXPECT_TRUE(didIntersect); + + for (int i=0; i(bPolygons3.size()); ++idxcurve) + { + for (int k=0; k; + using PointType = primal::Point< CoordType, DIM >; + using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + + SLIC_INFO("Test finding area of intersection two linear triangular CurvedPolygons (single region)."); + + CurvedPolygonType bPolygon; + EXPECT_EQ(0, bPolygon.numEdges()); + + PointType controlPoints[order+1] = { + PointType::make_point(0.6, 1.2), + PointType::make_point(0.3, 2.0) + }; + + PointType controlPoints2[order+1] = { + PointType::make_point( 0.3, 2.0), + PointType::make_point( 0.0 , 1.6) + }; + + PointType controlPoints3[order+1] = { + PointType::make_point( 0.0 , 1.6), + PointType::make_point( 0.6, 1.2) + }; + + BezierCurveType bCurve(controlPoints,order); + bPolygon.addEdge(bCurve); + + BezierCurveType bCurve2(controlPoints2,order); + bPolygon.addEdge(bCurve2); + + BezierCurveType bCurve3(controlPoints3,order); + bPolygon.addEdge(bCurve3); + CurvedPolygonType bPolygon2=bPolygon; + for (int i=0; i bPolygons3; + bool didIntersect=intersect(bPolygon,bPolygon2,bPolygons3); + EXPECT_TRUE(didIntersect); + + + CoordType A=0.0; + for (int i=0; i(bPolygons3.size()); ++i) + { + A+=bPolygons3[i].area(1e-14); + } + CoordType expA=-0.0793347222222222222; + EXPECT_NEAR(A,expA,1e-10); +} + +//---------------------------------------------------------------------------------- +TEST( primal_curvedpolygon, area_intersection_triangle_quadratic) +{ + const int DIM = 2; + const int order = 2; + using CoordType = double; + using CurvedPolygonType = primal::CurvedPolygon< CoordType, DIM>; + using PointType = primal::Point< CoordType, DIM >; + using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + + SLIC_INFO("Test intersecting two quadratic triangular CurvedPolygons (single region)."); + + CurvedPolygonType bPolygon; + EXPECT_EQ(0, bPolygon.numEdges()); + + PointType controlPoints[order+1] = { + PointType::make_point(0.6, 1.2), + PointType::make_point(0.4, 1.3), + PointType::make_point(0.3, 2.0) + }; + + PointType controlPoints2[order+1] = { + PointType::make_point( 0.3, 2.0), + PointType::make_point( 0.27, 1.5), + PointType::make_point( 0.0 , 1.6) + }; + + PointType controlPoints3[order+1] = { + PointType::make_point( 0.0 , 1.6), + PointType::make_point( 0.1 , 1.5), + PointType::make_point( 0.6, 1.2) + }; + + BezierCurveType bCurve(controlPoints,order); + bPolygon.addEdge(bCurve); + + BezierCurveType bCurve2(controlPoints2,order); + bPolygon.addEdge(bCurve2); + + BezierCurveType bCurve3(controlPoints3,order); + bPolygon.addEdge(bCurve3); + CurvedPolygonType bPolygon2=bPolygon; + for (int i=0; i bPolygons3; + bool didIntersect=intersect(bPolygon,bPolygon2,bPolygons3,1e-14); + EXPECT_TRUE(didIntersect); + + CoordType A=0.0; + for (int i=0; i(bPolygons3.size()); ++i) + { + A+=bPolygons3[i].area(1e-8); + } + CoordType expA=-0.024649833203616; + EXPECT_NEAR(A,expA,1e-10); +} + +TEST( primal_curvedpolygon, area_intersection_triangle_quadratic_two_regions) +{ + const int DIM = 2; + const int order = 2; + using CoordType = double; + using CurvedPolygonType = primal::CurvedPolygon< CoordType, DIM>; + using PointType = primal::Point< CoordType, DIM >; + using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + + SLIC_INFO("Test intersecting two quadratic triangular CurvedPolygons (two regions)."); + + CurvedPolygonType bPolygon; + EXPECT_EQ(0, bPolygon.numEdges()); + + PointType controlPoints[order+1] = { + PointType::make_point(0.6 , 1.2), + PointType::make_point(0.4 , 1.3), + PointType::make_point(0.3 , 2.0) + }; + + PointType controlPoints2[order+1] = { + PointType::make_point(0.3 , 2.0), + PointType::make_point(0.27, 1.5), + PointType::make_point(0.0 , 1.6) + }; + + PointType controlPoints3[order+1] = { + PointType::make_point(0.0 , 1.6), + PointType::make_point(0.1 , 1.5), + PointType::make_point(0.6 , 1.2) + }; + + PointType controlPoints4[order+1] = { + PointType::make_point(1.0205, 1.6699), + PointType::make_point(0.8339, 1.5467), + PointType::make_point(0.1777, 1.8101) + }; + + PointType controlPoints5[order+1] = { + PointType::make_point(0.1777, 1.8101), + PointType::make_point(0.5957, 1.5341), + PointType::make_point(0.3741, 1.3503) + }; + + PointType controlPoints6[order+1] = { + PointType::make_point(0.3741, 1.3503), + PointType::make_point(0.5107, 1.3869), + PointType::make_point(1.0205, 1.6699) + }; + CurvedPolygonType bPolygon2; + + BezierCurveType bCurve(controlPoints,order); + BezierCurveType bCurve4(controlPoints4,order); + bPolygon2.addEdge(bCurve4); + bPolygon.addEdge(bCurve); + + BezierCurveType bCurve2(controlPoints2,order); + BezierCurveType bCurve5(controlPoints5,order); + bPolygon2.addEdge(bCurve5); + bPolygon.addEdge(bCurve2); + + BezierCurveType bCurve3(controlPoints3,order); + BezierCurveType bCurve6(controlPoints6,order); + bPolygon2.addEdge(bCurve6); + bPolygon.addEdge(bCurve3); + + std::vector bPolygons3; + bool didIntersect=intersect(bPolygon,bPolygon2,bPolygons3); + EXPECT_TRUE(didIntersect); + EXPECT_EQ(bPolygons3.size(),2); +} + +TEST( primal_curvedpolygon, area_intersection_triangle_inclusion) +{ + const int DIM = 2; + const int order = 2; + using CoordType = double; + using CurvedPolygonType = primal::CurvedPolygon< CoordType, DIM>; + using PointType = primal::Point< CoordType, DIM >; + using BezierCurveType = primal::BezierCurve< CoordType, DIM >; + + SLIC_INFO("Test intersecting two quadratic triangular CurvedPolygons (inclusion)."); + + CurvedPolygonType bPolygon; + EXPECT_EQ(0, bPolygon.numEdges()); + + PointType controlPoints[order+1] = { + PointType::make_point(0.0 , 0.0), + PointType::make_point(0.5 , 0.0), + PointType::make_point(1.0 , 0.0) + }; + + PointType controlPoints2[order+1] = { + PointType::make_point(1.0 , 0.0), + PointType::make_point(0.5, 0.5), + PointType::make_point(0.0 , 1.0) + }; + + PointType controlPoints3[order+1] = { + PointType::make_point(0.0 , 1.0), + PointType::make_point(0.0 , 0.5), + PointType::make_point(0.0 , 0.0) + }; + + BezierCurveType bCurve(controlPoints,order); + bPolygon.addEdge(bCurve); + + BezierCurveType bCurve2(controlPoints2,order); + bPolygon.addEdge(bCurve2); + + BezierCurveType bCurve3(controlPoints3,order); + bPolygon.addEdge(bCurve3); + + CurvedPolygonType bPolygon2=bPolygon; + for (int i=0; i bPolygons3; + bool didIntersect=intersect(bPolygon,bPolygon2,bPolygons3); + bPolygons3.clear(); + bool didIntersect2=intersect(bPolygon2,bPolygon,bPolygons3); + EXPECT_TRUE(didIntersect); + EXPECT_TRUE(didIntersect2); + EXPECT_EQ(bPolygons3.size(),1); } //---------------------------------------------------------------------------------- int main(int argc, char* argv[]) diff --git a/src/axom/primal/tests/primal_curved_polygon_intersect.cpp b/src/axom/primal/tests/primal_curved_polygon_intersect.cpp index e56f1ed0e3..c67629b080 100644 --- a/src/axom/primal/tests/primal_curved_polygon_intersect.cpp +++ b/src/axom/primal/tests/primal_curved_polygon_intersect.cpp @@ -15,24 +15,50 @@ namespace primal = axom::primal; /** - * Helper function to compute the area and centroid of a curved polygon and to check that they match expectations, stored in \a expArea and \a expCentroid. Areas and Moments are computed within tolerance \a eps and checks use \a test_eps. + * Helper function to compute the set of intersection polygons given two input polygons and to check that they match expectations, stored in \a expbPolygon. Intersection polygon is computed to within tolerance \a eps and checks use \a test_eps. */ + template -void checkMoments(const primal::CurvedPolygon& bPolygon, - const CoordType expArea, const primal::Point& expMoment, - double eps, double test_eps) +void checkIntersection( + const primal::CurvedPolygon& bPolygon1, + const primal::CurvedPolygon& bPolygon2, + const std::vector> expbPolygon, + const double eps = 1e-15, const double test_eps = 1e-13) { - EXPECT_NEAR(expArea, bPolygon.area(eps),test_eps); - for (int i= 0; i< DIM; ++i) + using CurvedPolygonType = primal::CurvedPolygon< CoordType, DIM>; + + std::vector intersectionPolys; + + //Compute intersection using algorithm with tolerance of eps + intersect(bPolygon1,bPolygon2,intersectionPolys,eps); + //Check that expected number of intersection regions are found + EXPECT_EQ(expbPolygon.size(),intersectionPolys.size()); + + //Check that expected intersection curves are found to within test_eps + for (int i=0; i primal::CurvedPolygon createPolygon( - std::vector> ControlPoints, - std::vector orders) + const std::vector> ControlPoints, + const std::vector orders) { using PointType = primal::Point< CoordType, DIM>; using CurvedPolygonType = primal::CurvedPolygon< CoordType, DIM>; @@ -41,14 +67,10 @@ primal::CurvedPolygon createPolygon( const int num_edges = orders.size(); const int num_unique_control_points = ControlPoints.size(); - //std::cout << num_edges << ", " << num_unique_control_points << std::endl; - //std::cout << ControlPoints << std::endl; - for (int i=0; i createPolygon( bPolygon.addEdge(addCurve); iter+=(orders[j]); } - std::cout << bPolygon << std::endl; return bPolygon; } TEST( primal_curvedpolygon, intersection_triangle_linear) { const int DIM = 2; - const int order = 1; using CoordType = double; using CurvedPolygonType = primal::CurvedPolygon< CoordType, DIM>; using PointType = primal::Point< CoordType, DIM >; - using BezierCurveType = primal::BezierCurve< CoordType, DIM >; SLIC_INFO("Test intersecting two linear triangular CurvedPolygons (single region)."); - CurvedPolygonType bPolygon; - EXPECT_EQ(0, bPolygon.numEdges()); - - PointType controlPoints[order+1] = { + std::vector CP = { PointType::make_point(0.6, 1.2), - PointType::make_point(0.3, 2.0) + PointType::make_point(0.3, 2.0), + PointType::make_point( 0.0 , 1.6), + PointType::make_point(0.6, 1.2) }; + std::vector orders = {1,1,1}; - PointType controlPoints2[order+1] = { - PointType::make_point( 0.3, 2.0), - PointType::make_point( 0.0 , 1.6) - }; + CurvedPolygonType bPolygon1 = createPolygon(CP,orders); - PointType controlPoints3[order+1] = { - PointType::make_point( 0.0 , 1.6), - PointType::make_point( 0.6, 1.2) + std::vector CP2 = { + PointType::make_point(0.71, 1.31), + PointType::make_point(0.41, 2.11), + PointType::make_point( 0.11 , 1.71), + PointType::make_point(0.71, 1.31) }; - - BezierCurveType bCurve(controlPoints,order); - bPolygon.addEdge(bCurve); - BezierCurveType bCurve2(controlPoints2,order); - bPolygon.addEdge(bCurve2); - - BezierCurveType bCurve3(controlPoints3,order); - bPolygon.addEdge(bCurve3); - CurvedPolygonType bPolygon2=bPolygon; - for (int i=0; i expCP = { PointType::make_point(0.3091666666666666666666, 1.9755555555555555555), - PointType::make_point(0.11, 1.71) - }; - - PointType expcontrolPoints2[order+1] = { - PointType::make_point( 0.11, 1.71), - PointType::make_point( 0.5083333333333333333 , 1.44444444444444444444) - }; - - PointType expcontrolPoints3[order+1] = { + PointType::make_point(0.11, 1.71), PointType::make_point( 0.5083333333333333333 , 1.44444444444444444444), PointType::make_point(0.3091666666666666666666, 1.9755555555555555555) }; - - CurvedPolygonType expbPolygon; - - BezierCurveType expbCurve(expcontrolPoints,order); - expbPolygon.addEdge(expbCurve); - BezierCurveType expbCurve2(expcontrolPoints2,order); - expbPolygon.addEdge(expbCurve2); - BezierCurveType expbCurve3(expcontrolPoints3,order); - expbPolygon.addEdge(expbCurve3); + std::vector exporders={1,1,1}; + CurvedPolygonType expbPolygon = createPolygon(expCP,exporders); - std::vector bPolygons3; - bool didIntersect=intersect(bPolygon,bPolygon2,bPolygons3); - EXPECT_TRUE(didIntersect); - std::cout << bPolygons3.size() << std::endl; - // int nEd= bPolygons3.size(); - for (int i=0; i< static_cast(bPolygons3.size()); ++i) - { - std::cout << bPolygons3[i] << std::endl; - } - for (int i=0; i(bPolygons3.size()); ++idxcurve) - { - for (int k=0; k expbPolygons {expbPolygon}; + checkIntersection(bPolygon1,bPolygon2,expbPolygons); } //---------------------------------------------------------------------------------- TEST( primal_curvedpolygon, intersection_triangle_quadratic) { const int DIM = 2; - const int order = 2; using CoordType = double; using CurvedPolygonType = primal::CurvedPolygon< CoordType, DIM>; using PointType = primal::Point< CoordType, DIM >; - using BezierCurveType = primal::BezierCurve< CoordType, DIM >; SLIC_INFO("Test intersecting two quadratic triangular CurvedPolygons (single region)."); CurvedPolygonType bPolygon; EXPECT_EQ(0, bPolygon.numEdges()); - PointType controlPoints[order+1] = { + std::vector CP= { PointType::make_point(0.6, 1.2), PointType::make_point(0.4, 1.3), - PointType::make_point(0.3, 2.0) - }; - - PointType controlPoints2[order+1] = { PointType::make_point( 0.3, 2.0), PointType::make_point( 0.27, 1.5), - PointType::make_point( 0.0 , 1.6) - }; - - PointType controlPoints3[order+1] = { PointType::make_point( 0.0 , 1.6), PointType::make_point( 0.1 , 1.5), PointType::make_point( 0.6, 1.2) }; - - BezierCurveType bCurve(controlPoints,order); - bPolygon.addEdge(bCurve); + std::vector orders = {2,2,2}; + CurvedPolygonType bPolygon1 = createPolygon(CP,orders); - BezierCurveType bCurve2(controlPoints2,order); - bPolygon.addEdge(bCurve2); - - BezierCurveType bCurve3(controlPoints3,order); - bPolygon.addEdge(bCurve3); - CurvedPolygonType bPolygon2=bPolygon; - for (int i=0; i CP2= { + PointType::make_point(0.71, 1.31), + PointType::make_point(0.51, 1.41), + PointType::make_point( 0.41, 2.11), + PointType::make_point( 0.38, 1.61), + PointType::make_point( 0.11 , 1.71), + PointType::make_point( 0.21 , 1.61), + PointType::make_point( 0.71, 1.31) + }; + CurvedPolygonType bPolygon2 = createPolygon(CP2,orders); - PointType expcontrolPoints[order+1] = { + std::vector expCP = { PointType::make_point(0.335956890729522, 1.784126953773395), PointType::make_point(0.297344765794753, 1.718171485335525), - PointType::make_point(0.239567753301698, 1.700128235793372) - }; - - PointType expcontrolPoints2[order+1] = { PointType::make_point( 0.2395677533016981, 1.700128235793371), PointType::make_point( 0.221884203146682, 1.662410644580941 ), - PointType::make_point( 0.199328465398189, 1.636873522352205 ) - }; - - PointType expcontrolPoints3[order+1] = { - PointType::make_point(0.199328465398188,1.636873522352206 ), + PointType::make_point( 0.199328465398189, 1.636873522352205 ), PointType::make_point(0.277429214338182,1.579562422716502 ), - PointType::make_point(0.408882616650578,1.495574996394597 ) - }; - - PointType expcontrolPoints4[order+1] = { - PointType::make_point(0.408882616650588,1.495574996394586 ), + PointType::make_point(0.408882616650578,1.495574996394597 ), PointType::make_point(0.368520120719339,1.616453177259694), PointType::make_point(0.335956890729522,1.784126953773394 ) }; + std::vector exporders = {2,2,2,2}; + CurvedPolygonType expbPolygon = createPolygon(expCP,exporders); + std::vector expbPolygons = {expbPolygon}; - CurvedPolygonType expbPolygon; - - BezierCurveType expbCurve(expcontrolPoints,order); - expbPolygon.addEdge(expbCurve); - BezierCurveType expbCurve2(expcontrolPoints2,order); - expbPolygon.addEdge(expbCurve2); - BezierCurveType expbCurve3(expcontrolPoints3,order); - expbPolygon.addEdge(expbCurve3); - BezierCurveType expbCurve4(expcontrolPoints4,order); - expbPolygon.addEdge(expbCurve4); - - std::vector bPolygons3; - bool didIntersect=intersect(bPolygon,bPolygon2,bPolygons3); - EXPECT_TRUE(didIntersect); - - for (int i=0; i(bPolygons3.size()); ++idxcurve) - { - for (int k=0; k; - using PointType = primal::Point< CoordType, DIM >; - using BezierCurveType = primal::BezierCurve< CoordType, DIM >; - - SLIC_INFO("Test finding area of intersection two linear triangular CurvedPolygons (single region)."); - - CurvedPolygonType bPolygon; - EXPECT_EQ(0, bPolygon.numEdges()); - - PointType controlPoints[order+1] = { - PointType::make_point(0.6, 1.2), - PointType::make_point(0.3, 2.0) - }; - - PointType controlPoints2[order+1] = { - PointType::make_point( 0.3, 2.0), - PointType::make_point( 0.0 , 1.6) - }; - - PointType controlPoints3[order+1] = { - PointType::make_point( 0.0 , 1.6), - PointType::make_point( 0.6, 1.2) - }; - - BezierCurveType bCurve(controlPoints,order); - bPolygon.addEdge(bCurve); - - BezierCurveType bCurve2(controlPoints2,order); - bPolygon.addEdge(bCurve2); - - BezierCurveType bCurve3(controlPoints3,order); - bPolygon.addEdge(bCurve3); - CurvedPolygonType bPolygon2=bPolygon; - for (int i=0; i bPolygons3; - bool didIntersect=intersect(bPolygon,bPolygon2,bPolygons3); - EXPECT_TRUE(didIntersect); - - - CoordType A=0.0; - for (int i=0; i(bPolygons3.size()); ++i) - { - A+=bPolygons3[i].area(1e-14); - } - CoordType expA=-0.0793347222222222222; - EXPECT_NEAR(A,expA,1e-10); + checkIntersection(bPolygon1,bPolygon2,expbPolygons, 1e-15,1e-13); } -//---------------------------------------------------------------------------------- -TEST( primal_curvedpolygon, area_intersection_triangle_quadratic) -{ - const int DIM = 2; - const int order = 2; - using CoordType = double; - using CurvedPolygonType = primal::CurvedPolygon< CoordType, DIM>; - using PointType = primal::Point< CoordType, DIM >; - using BezierCurveType = primal::BezierCurve< CoordType, DIM >; - - SLIC_INFO("Test intersecting two quadratic triangular CurvedPolygons (single region)."); - - CurvedPolygonType bPolygon; - EXPECT_EQ(0, bPolygon.numEdges()); - - PointType controlPoints[order+1] = { - PointType::make_point(0.6, 1.2), - PointType::make_point(0.4, 1.3), - PointType::make_point(0.3, 2.0) - }; - - PointType controlPoints2[order+1] = { - PointType::make_point( 0.3, 2.0), - PointType::make_point( 0.27, 1.5), - PointType::make_point( 0.0 , 1.6) - }; - - PointType controlPoints3[order+1] = { - PointType::make_point( 0.0 , 1.6), - PointType::make_point( 0.1 , 1.5), - PointType::make_point( 0.6, 1.2) - }; - - BezierCurveType bCurve(controlPoints,order); - bPolygon.addEdge(bCurve); - - BezierCurveType bCurve2(controlPoints2,order); - bPolygon.addEdge(bCurve2); - BezierCurveType bCurve3(controlPoints3,order); - bPolygon.addEdge(bCurve3); - CurvedPolygonType bPolygon2=bPolygon; - for (int i=0; i bPolygons3; - bool didIntersect=intersect(bPolygon,bPolygon2,bPolygons3,1e-14); - EXPECT_TRUE(didIntersect); - - CoordType A=0.0; - for (int i=0; i(bPolygons3.size()); ++i) - { - A+=bPolygons3[i].area(1e-8); - } - CoordType expA=-0.024649833203616; - EXPECT_NEAR(A,expA,1e-10); -} - -TEST( primal_curvedpolygon, area_intersection_triangle_quadratic_two_regions) +TEST( primal_curvedpolygon, intersection_triangle_quadratic_two_regions) { const int DIM = 2; - const int order = 2; using CoordType = double; using CurvedPolygonType = primal::CurvedPolygon< CoordType, DIM>; using PointType = primal::Point< CoordType, DIM >; - using BezierCurveType = primal::BezierCurve< CoordType, DIM >; SLIC_INFO("Test intersecting two quadratic triangular CurvedPolygons (two regions)."); - CurvedPolygonType bPolygon; - EXPECT_EQ(0, bPolygon.numEdges()); - - PointType controlPoints[order+1] = { + std::vector CP1 = { PointType::make_point(0.6 , 1.2), PointType::make_point(0.4 , 1.3), - PointType::make_point(0.3 , 2.0) - }; - - PointType controlPoints2[order+1] = { PointType::make_point(0.3 , 2.0), PointType::make_point(0.27, 1.5), - PointType::make_point(0.0 , 1.6) - }; - - PointType controlPoints3[order+1] = { PointType::make_point(0.0 , 1.6), PointType::make_point(0.1 , 1.5), PointType::make_point(0.6 , 1.2) }; - PointType controlPoints4[order+1] = { + std::vector CP2 = { PointType::make_point(1.0205, 1.6699), PointType::make_point(0.8339, 1.5467), - PointType::make_point(0.1777, 1.8101) - }; - - PointType controlPoints5[order+1] = { PointType::make_point(0.1777, 1.8101), PointType::make_point(0.5957, 1.5341), - PointType::make_point(0.3741, 1.3503) - }; - - PointType controlPoints6[order+1] = { PointType::make_point(0.3741, 1.3503), PointType::make_point(0.5107, 1.3869), PointType::make_point(1.0205, 1.6699) }; - CurvedPolygonType bPolygon2; + std::vector orders = {2,2,2}; + + std::vector expCP1 = { + PointType::make_point(0.343364196589264, 1.747080669655736), + PointType::make_point(0.305984025190458, 1.760433098612141), + PointType::make_point(0.266743999290327, 1.775316659915674), + PointType::make_point(0.263419346128088, 1.763343410502168), + PointType::make_point(0.259796003065908, 1.752116885838515), + PointType::make_point(0.320641367919239, 1.705796408318085), + PointType::make_point(0.362111919147859, 1.662268860466508), + PointType::make_point(0.352450139541348, 1.702947255097842), + PointType::make_point(0.343364196589264, 1.747080669655736), + }; - BezierCurveType bCurve(controlPoints,order); - BezierCurveType bCurve4(controlPoints4,order); - bPolygon2.addEdge(bCurve4); - bPolygon.addEdge(bCurve); - - BezierCurveType bCurve2(controlPoints2,order); - BezierCurveType bCurve5(controlPoints5,order); - bPolygon2.addEdge(bCurve5); - bPolygon.addEdge(bCurve2); - - BezierCurveType bCurve3(controlPoints3,order); - BezierCurveType bCurve6(controlPoints6,order); - bPolygon2.addEdge(bCurve6); - bPolygon.addEdge(bCurve3); + std::vector expCP2 = { + PointType::make_point(0.454478985809487, 1.379250566393211 ), + PointType::make_point(0.444689566319939, 1.400290430035245 ), + PointType::make_point(0.435276730907216, 1.423589798138227 ), + PointType::make_point(0.416268597450954, 1.385275578571685 ), + PointType::make_point(0.374100000000000, 1.350300000000000 ), + PointType::make_point(0.404839872482010, 1.358536305511285 ), + PointType::make_point(0.454478985809487, 1.379250566393211 ) + }; + + std::vector exporder1 = {2,2,2,2}; + std::vector exporder2 = {2,2,2}; + CurvedPolygonType bPolygon1 = createPolygon(CP1,orders); + CurvedPolygonType bPolygon2 = createPolygon(CP2,orders); + CurvedPolygonType expbPolygon1 = createPolygon(expCP1,exporder1); + CurvedPolygonType expbPolygon2 = createPolygon(expCP2,exporder2); + std::vector expIntersections= {expbPolygon1,expbPolygon2}; - std::vector bPolygons3; - bool didIntersect=intersect(bPolygon,bPolygon2,bPolygons3); - EXPECT_TRUE(didIntersect); - EXPECT_EQ(bPolygons3.size(),2); + checkIntersection(bPolygon1,bPolygon2,expIntersections); } TEST( primal_curvedpolygon, area_intersection_triangle_inclusion) { const int DIM = 2; - const int order = 2; using CoordType = double; using CurvedPolygonType = primal::CurvedPolygon< CoordType, DIM>; using PointType = primal::Point< CoordType, DIM >; - using BezierCurveType = primal::BezierCurve< CoordType, DIM >; SLIC_INFO("Test intersecting two quadratic triangular CurvedPolygons (inclusion)."); - CurvedPolygonType bPolygon; - EXPECT_EQ(0, bPolygon.numEdges()); - - PointType controlPoints[order+1] = { + std::vector CP1 = { PointType::make_point(0.0 , 0.0), PointType::make_point(0.5 , 0.0), - PointType::make_point(1.0 , 0.0) - }; - - PointType controlPoints2[order+1] = { PointType::make_point(1.0 , 0.0), PointType::make_point(0.5, 0.5), - PointType::make_point(0.0 , 1.0) - }; - - PointType controlPoints3[order+1] = { PointType::make_point(0.0 , 1.0), PointType::make_point(0.0 , 0.5), PointType::make_point(0.0 , 0.0) }; - BezierCurveType bCurve(controlPoints,order); - bPolygon.addEdge(bCurve); - - BezierCurveType bCurve2(controlPoints2,order); - bPolygon.addEdge(bCurve2); + std::vector CP2 = { + PointType::make_point(0.05 , 0.05), + PointType::make_point(0.30 , 0.05), + PointType::make_point(0.55 , 0.05), + PointType::make_point(0.30, 0.30), + PointType::make_point(0.05 , 0.55), + PointType::make_point(0.05 , 0.30), + PointType::make_point(0.05 , 0.05) + }; + std::vector orders = {2,2,2}; + CurvedPolygonType bPolygon1 = createPolygon(CP1,orders); + CurvedPolygonType bPolygon2 = createPolygon(CP2,orders); + std::vector expIntersections = {bPolygon2}; - BezierCurveType bCurve3(controlPoints3,order); - bPolygon.addEdge(bCurve3); - - CurvedPolygonType bPolygon2=bPolygon; - for (int i=0; i bPolygons3; - bool didIntersect=intersect(bPolygon,bPolygon2,bPolygons3); - bPolygons3.clear(); - bool didIntersect2=intersect(bPolygon2,bPolygon,bPolygons3); - EXPECT_TRUE(didIntersect); - EXPECT_TRUE(didIntersect2); - EXPECT_EQ(bPolygons3.size(),1); + checkIntersection(bPolygon1,bPolygon2,expIntersections); + checkIntersection(bPolygon2,bPolygon1,expIntersections); } //---------------------------------------------------------------------------------- int main(int argc, char* argv[]) From e2974e880c19cb7adb0ab50a7b46593b261ff8a4 Mon Sep 17 00:00:00 2001 From: Kenny Weiss Date: Tue, 8 Oct 2019 10:32:33 -0700 Subject: [PATCH 37/44] Bugfixes for curved polygon tests in MSVC --- src/axom/primal/geometry/BezierCurve.hpp | 7 +------ src/axom/primal/tests/primal_curved_polygon.cpp | 8 ++++++-- .../primal/tests/primal_curved_polygon_intersect.cpp | 9 +++++++-- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/axom/primal/geometry/BezierCurve.hpp b/src/axom/primal/geometry/BezierCurve.hpp index 917f5cfb0f..5abb28660b 100644 --- a/src/axom/primal/geometry/BezierCurve.hpp +++ b/src/axom/primal/geometry/BezierCurve.hpp @@ -183,18 +183,13 @@ class BezierCurve * */ - BezierCurve(std::vector pts, int ord) + BezierCurve(const std::vector& pts, int ord) { - SLIC_ASSERT(pts != nullptr); SLIC_ASSERT(ord >= 0); const int sz = utilities::max(0, ord+1); m_controlPoints.resize(sz); m_controlPoints = pts; - /*for (int p = 0 ; p <= ord ; ++p) - { - m_controlPoints[p] = pts[p]; - }*/ } /*! Sets the order of the Bezier Curve*/ diff --git a/src/axom/primal/tests/primal_curved_polygon.cpp b/src/axom/primal/tests/primal_curved_polygon.cpp index d9b2cfff2d..ab2807f0eb 100644 --- a/src/axom/primal/tests/primal_curved_polygon.cpp +++ b/src/axom/primal/tests/primal_curved_polygon.cpp @@ -12,6 +12,8 @@ #include "axom/primal/geometry/CurvedPolygon.hpp" #include "axom/primal/operators/intersect.hpp" +#include + namespace primal = axom::primal; /** @@ -44,8 +46,10 @@ primal::CurvedPolygon createPolygon( const int num_unique_control_points = ControlPoints.size(); //checks if the orders and control points given will yield a valid curved polygon - EXPECT_EQ(accumulate(orders.begin(),orders.end(), 0)+1,num_unique_control_points); - + { + const int sum_of_orders = std::accumulate(orders.begin(), orders.end(), 0); + EXPECT_EQ(sum_of_orders + 1, num_unique_control_points); + } //Converts the control points to BezierCurves of specified orders and stores them in a CurvedPolygon object. CurvedPolygonType bPolygon; int iter=0; diff --git a/src/axom/primal/tests/primal_curved_polygon_intersect.cpp b/src/axom/primal/tests/primal_curved_polygon_intersect.cpp index c67629b080..6eb12e93c6 100644 --- a/src/axom/primal/tests/primal_curved_polygon_intersect.cpp +++ b/src/axom/primal/tests/primal_curved_polygon_intersect.cpp @@ -12,6 +12,8 @@ #include "axom/primal/geometry/CurvedPolygon.hpp" #include "axom/primal/operators/intersect.hpp" +#include + namespace primal = axom::primal; /** @@ -68,8 +70,11 @@ primal::CurvedPolygon createPolygon( const int num_unique_control_points = ControlPoints.size(); //checks if the orders and control points given will yield a valid curved polygon - EXPECT_EQ(accumulate(orders.begin(),orders.end(), 0)+1,num_unique_control_points); - + { + const int sum_of_orders = std::accumulate(orders.begin(), orders.end(), 0); + EXPECT_EQ(sum_of_orders + 1, num_unique_control_points); + } + //Converts the control points to BezierCurves of specified orders and stores them in a CurvedPolygon object. CurvedPolygonType bPolygon; int iter=0; From ffbc7f37db213d9801b7ff92370275f1cca4ce86 Mon Sep 17 00:00:00 2001 From: Kenny Weiss Date: Tue, 8 Oct 2019 10:59:39 -0700 Subject: [PATCH 38/44] Bugfix for API change in primal::intersect --- src/axom/quest/examples/quest_high_order_remap.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/axom/quest/examples/quest_high_order_remap.cpp b/src/axom/quest/examples/quest_high_order_remap.cpp index 19b6963f6a..20d9f6c49f 100644 --- a/src/axom/quest/examples/quest_high_order_remap.cpp +++ b/src/axom/quest/examples/quest_high_order_remap.cpp @@ -460,7 +460,7 @@ struct Remapper std::vector> pnew; tgtPoly.reverseOrientation(); srcPoly.reverseOrientation(); - if (primal::intersect_polygon(tgtPoly,srcPoly,pnew)) + if (primal::intersect(tgtPoly,srcPoly,pnew)) { for (int i=0; i< static_cast(pnew.size()); ++i) { From e5165351f0e20ea185882f955410b1dcb1f8f6d5 Mon Sep 17 00:00:00 2001 From: Kenny Weiss Date: Tue, 8 Oct 2019 11:00:55 -0700 Subject: [PATCH 39/44] Rely on Axom data submodule for mesh files in quest high order remap example --- src/axom/quest/examples/quest_high_order_remap.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/axom/quest/examples/quest_high_order_remap.cpp b/src/axom/quest/examples/quest_high_order_remap.cpp index 20d9f6c49f..06ed7f8edd 100644 --- a/src/axom/quest/examples/quest_high_order_remap.cpp +++ b/src/axom/quest/examples/quest_high_order_remap.cpp @@ -377,7 +377,11 @@ struct Remapper } // create the target mesh { - auto* mesh = new mfem::Mesh("./disc-nurbs-80.mesh",1,1); + // NOTE (KW): For now, assume we have AXOM_DATA_DIR + namespace fs = axom::utilities::filesystem; + std::string fname = fs::joinPath(AXOM_DATA_DIR, "mfem/disc-nurbs.mesh"); + + auto* mesh = new mfem::Mesh(fname.c_str(),1,1); if (mesh->NURBSext) { int order =tgt_ord; From 7aac19dd2af0f520b8e200ea3a572be477bebea0 Mon Sep 17 00:00:00 2001 From: Kenny Weiss Date: Tue, 8 Oct 2019 11:39:03 -0700 Subject: [PATCH 40/44] primal's BezierCurve::dt should return a Vector rather than a Point --- src/axom/primal/geometry/BezierCurve.hpp | 10 ++--- .../detail/intersect_curved_poly_impl.hpp | 41 +++++++++---------- src/axom/primal/tests/primal_bezier_curve.cpp | 13 +++--- 3 files changed, 32 insertions(+), 32 deletions(-) diff --git a/src/axom/primal/geometry/BezierCurve.hpp b/src/axom/primal/geometry/BezierCurve.hpp index 5abb28660b..d2eeca7459 100644 --- a/src/axom/primal/geometry/BezierCurve.hpp +++ b/src/axom/primal/geometry/BezierCurve.hpp @@ -24,7 +24,6 @@ #include "axom/primal/operators/squared_distance.hpp" -#include "fmt/fmt.hpp" #include #include #include @@ -304,9 +303,9 @@ class BezierCurve * \note We typically find the tangent of the curve at \a t between 0 and 1 */ - PointType dt(T t) const + VectorType dt(T t) const { - PointType ptval; + VectorType val; const int ord = getOrder(); std::vector dCarray(ord+1); @@ -319,6 +318,7 @@ class BezierCurve dCarray[p] = m_controlPoints[p][i]; } + // stop one step early and take difference of last two values for ( int p=1 ; p <= ord-1 ; ++p) { const int end = ord-p; @@ -327,10 +327,10 @@ class BezierCurve dCarray[k]=(1-t)*dCarray[k] + t*dCarray[k+1]; } } - ptval[i]=ord*(dCarray[1]-dCarray[0]); + val[i]=ord*(dCarray[1]-dCarray[0]); } - return ptval; + return val; } /*! diff --git a/src/axom/primal/operators/detail/intersect_curved_poly_impl.hpp b/src/axom/primal/operators/detail/intersect_curved_poly_impl.hpp index 001ada1fb6..508575bb4b 100644 --- a/src/axom/primal/operators/detail/intersect_curved_poly_impl.hpp +++ b/src/axom/primal/operators/detail/intersect_curved_poly_impl.hpp @@ -36,15 +36,15 @@ namespace detail template < typename T > class IntersectionInfo; -template -bool orient( const BezierCurve c1, - const BezierCurve c2, +template +bool orient( const BezierCurve& c1, + const BezierCurve& c2, T s, T t); template -int isContained( const CurvedPolygon p1, - const CurvedPolygon p2, +int isContained( const CurvedPolygon& p1, + const CurvedPolygon& p2, double sq_tol = 1e-10); /*! @@ -203,19 +203,19 @@ bool intersect_polygon( const CurvedPolygon< T, NDIMS>& p1, * \return 0 if mutually exclusive, 1 if p1 is in p2, 2 if p2 is in p1 */ template -int isContained(const CurvedPolygon p1, const CurvedPolygon p2,double sq_tol) +int isContained(const CurvedPolygon& p1, const CurvedPolygon& p2,double sq_tol) { const int NDIMS=2; - using PointType = primal::Point< T, NDIMS >; using BCurve = BezierCurve< T, NDIMS >; + using PointType = typename BCurve::PointType; + using VectorType = typename BCurve::VectorType; + int p1c = 0; int p2c = 0; T p1t = .5; T p2t = .5; - PointType controlPoints[2] = { - p1[p1c].evaluate(p1t), - p2[p2c].evaluate(p2t) - }; + PointType controlPoints[2] = { p1[p1c].evaluate(p1t), + p2[p2c].evaluate(p2t)}; BCurve LineGuess = BCurve(controlPoints,1); T line1s=0.0; T line2s=1.0; @@ -223,7 +223,8 @@ int isContained(const CurvedPolygon p1, const CurvedPolygon p2,double { std::vector temps; std::vector tempt; - intersect_bezier_curves(LineGuess,p1[j],temps,tempt,sq_tol,1,p1[j].getOrder(),1.,0.,1.,0.); + intersect_bezier_curves(LineGuess,p1[j],temps,tempt,sq_tol,1,p1[j].getOrder(),1.,0.,1.,0.); + for (int i=0 ; i(temps.size()) ; ++i) { if (temps[i]>line1s) @@ -251,9 +252,8 @@ int isContained(const CurvedPolygon p1, const CurvedPolygon p2,double } } - PointType origin = PointType::make_point(0.0, 0.0); - bool E1inE2 = (detail::twoDcross(p1[p1c].dt(p1t),LineGuess.dt(line1s),origin)<0); - bool E2inE1 = (detail::twoDcross(p2[p2c].dt(p2t),LineGuess.dt(line2s),origin)<0); + bool E1inE2 = VectorType::cross_product(p1[p1c].dt(p1t),LineGuess.dt(line1s))[2]<0; + bool E2inE1 = VectorType::cross_product(p2[p2c].dt(p2t),LineGuess.dt(line2s))[2]<0; if (E1inE2 && E2inE1) {return 1;} else if (!E1inE2 && !E2inE1) {return 2;} else {return 0;} @@ -294,13 +294,12 @@ void splitPolygon( CurvedPolygon< T, NDIMS>& p1, * \param [in] t the parameter value of intersection on c2 * \return True if the c1's positive direction is counterclockwise from c2's positive direction */ -template -bool orient(const BezierCurve c1, const BezierCurve c2, T s, T t) +template +bool orient(const BezierCurve& c1, const BezierCurve& c2, T s, T t) { - Point dc1s = c1.dt(s); - Point dc2t = c2.dt(t); - Point origin = primal::Point< T, NDIMS >::make_point(0.0, 0.0); - auto orientation = detail::twoDcross(dc1s,dc2t, origin); + using VectorType = primal::Vector; + + auto orientation = VectorType::cross_product(c1.dt(s), c2.dt(t))[2]; return (orientation>0); } diff --git a/src/axom/primal/tests/primal_bezier_curve.cpp b/src/axom/primal/tests/primal_bezier_curve.cpp index 48dbefbe4b..923f2f45e1 100644 --- a/src/axom/primal/tests/primal_bezier_curve.cpp +++ b/src/axom/primal/tests/primal_bezier_curve.cpp @@ -175,6 +175,7 @@ TEST( primal_beziercurve_, tangent) const int DIM = 3; using CoordType = double; using PointType = primal::Point< CoordType, DIM >; + using VectorType = primal::Vector< CoordType, DIM >; using BezierCurveType = primal::BezierCurve< CoordType, DIM >; const int order = 3; @@ -185,15 +186,15 @@ TEST( primal_beziercurve_, tangent) BezierCurveType b2Curve(data, order); - PointType midtval = PointType::make_point(3.15,2.325,1.875); - PointType starttval = PointType::make_point(2.1,1.2,2.4); - PointType endtval = PointType::make_point(.9,3.3,2.1); + VectorType midtval = VectorType::make_vector(3.15,2.325,1.875); + VectorType starttval = VectorType::make_vector(2.1,1.2,2.4); + VectorType endtval = VectorType::make_vector(.9,3.3,2.1); // Evaluate the curve at several parameter values // Curve should be tangent to control net at endpoints - PointType eval0 = b2Curve.dt(0.0); - PointType eval1 = b2Curve.dt(1.0); - PointType evalMid = b2Curve.dt(0.5); + VectorType eval0 = b2Curve.dt(0.0); + VectorType eval1 = b2Curve.dt(1.0); + VectorType evalMid = b2Curve.dt(0.5); for ( int i=0 ; i Date: Tue, 8 Oct 2019 16:26:01 -0700 Subject: [PATCH 41/44] Updates axom_data submodule --- data | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data b/data index c434d07238..865651b308 160000 --- a/data +++ b/data @@ -1 +1 @@ -Subproject commit c434d0723843b4013a922771ef2cf3cc73fb32d8 +Subproject commit 865651b308a71a3735741650466f32830238e6b6 From 3c9d92968a3a284ad9d61abbdfeabc454b4c8e98 Mon Sep 17 00:00:00 2001 From: Kenny Weiss Date: Tue, 8 Oct 2019 16:26:41 -0700 Subject: [PATCH 42/44] Minor updates to CurvedPolygon --- src/axom/primal/geometry/CurvedPolygon.hpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/axom/primal/geometry/CurvedPolygon.hpp b/src/axom/primal/geometry/CurvedPolygon.hpp index 1173df323e..7a4385d28b 100644 --- a/src/axom/primal/geometry/CurvedPolygon.hpp +++ b/src/axom/primal/geometry/CurvedPolygon.hpp @@ -6,7 +6,7 @@ /*! * \file CurvedPolygon.hpp * - * \brief A CurvedPolygon primitive for primal based on Bezier Curves + * \brief A CurvedPolygon primitive whose edges are Bezier Curves */ #ifndef PRIMAL_CURVEDPOLYGON_HPP_ @@ -19,7 +19,6 @@ #include "axom/primal/geometry/NumericArray.hpp" #include "axom/primal/geometry/BezierCurve.hpp" -#include "fmt/fmt.hpp" #include #include // for std::ostream @@ -39,11 +38,11 @@ std::ostream& operator<<(std::ostream & os, const CurvedPolygon< T,NDIMS > & pol /*! * \class CurvedPolygon * - * \brief Represents a curved polygon defined by a vector of BezierCurves + * \brief Represents a polygon with curved edges defined by BezierCurves * \tparam T the coordinate type, e.g., double, float, etc. * \tparam NDIMS the number of dimensions * \note The component curves should be ordered in a counter clockwise - * orientation with respect to the polygon's desired normal vector + * orientation with respect to the polygon's normal vector */ template < typename T,int NDIMS > class CurvedPolygon @@ -118,7 +117,7 @@ class CurvedPolygon { SLIC_ASSERT(idx csplit=m_edges[idx]; + auto& csplit=m_edges[idx]; csplit.split(t,m_edges[idx],m_edges[idx+1]); } From 349753a08578acc488d8f95e3c7ce39a8b0ebef9 Mon Sep 17 00:00:00 2001 From: David Gunderman Date: Tue, 22 Oct 2019 12:52:28 -0700 Subject: [PATCH 43/44] Attempting to fix macos error --- data | 2 +- src/axom/primal/operators/detail/intersect_curved_poly_impl.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/data b/data index 865651b308..c434d07238 160000 --- a/data +++ b/data @@ -1 +1 @@ -Subproject commit 865651b308a71a3735741650466f32830238e6b6 +Subproject commit c434d0723843b4013a922771ef2cf3cc73fb32d8 diff --git a/src/axom/primal/operators/detail/intersect_curved_poly_impl.hpp b/src/axom/primal/operators/detail/intersect_curved_poly_impl.hpp index 508575bb4b..be8700cbb4 100644 --- a/src/axom/primal/operators/detail/intersect_curved_poly_impl.hpp +++ b/src/axom/primal/operators/detail/intersect_curved_poly_impl.hpp @@ -320,7 +320,7 @@ class IntersectionInfo /*! * \brief Comparison operator for sorting by parameter value */ - bool operator<(IntersectionInfo other) + bool operator<(const IntersectionInfo& other) const { return myTime Date: Tue, 29 Oct 2019 15:34:18 -0700 Subject: [PATCH 44/44] Added conversion to positive basis, transform for high order mesh. Changed source mesh to high order mesh. --- .../detail/intersect_curved_poly_impl.hpp | 2 +- .../quest/examples/quest_high_order_remap.cpp | 258 ++++++++++++------ 2 files changed, 174 insertions(+), 86 deletions(-) diff --git a/src/axom/primal/operators/detail/intersect_curved_poly_impl.hpp b/src/axom/primal/operators/detail/intersect_curved_poly_impl.hpp index be8700cbb4..bcde15cedf 100644 --- a/src/axom/primal/operators/detail/intersect_curved_poly_impl.hpp +++ b/src/axom/primal/operators/detail/intersect_curved_poly_impl.hpp @@ -223,7 +223,7 @@ int isContained(const CurvedPolygon& p1, const CurvedPolygon& p2,doubl { std::vector temps; std::vector tempt; - intersect_bezier_curves(LineGuess,p1[j],temps,tempt,sq_tol,1,p1[j].getOrder(),1.,0.,1.,0.); + intersect_bezier_curves(LineGuess,p1[j],temps,tempt,sq_tol,1,p1[j].getOrder(),0.,1.,0.,1.); for (int i=0 ; i(temps.size()) ; ++i) { diff --git a/src/axom/quest/examples/quest_high_order_remap.cpp b/src/axom/quest/examples/quest_high_order_remap.cpp index 06ed7f8edd..109b0abb32 100644 --- a/src/axom/quest/examples/quest_high_order_remap.cpp +++ b/src/axom/quest/examples/quest_high_order_remap.cpp @@ -273,6 +273,79 @@ struct Remapper } + mfem::GridFunction * project_to_pos_basis(const mfem::GridFunction *gf, bool &is_new) + { + mfem::GridFunction * out_pos_gf = nullptr; + is_new = false; + + SLIC_ASSERT(gf != nullptr); + + const mfem::FiniteElementSpace *nodal_fe_space = gf->FESpace(); + if (nodal_fe_space == nullptr) { std::cerr << "project_to_pos_basis(): nodal_fe_space is NULL!" << std::endl; } + + const mfem::FiniteElementCollection *nodal_fe_coll = nodal_fe_space->FEColl(); + if (nodal_fe_coll == nullptr) { std::cerr << "project_to_pos_basis(): nodal_fe_coll is NULL!" << std::endl; } + + mfem::Mesh *gf_mesh = nodal_fe_space->GetMesh(); + if (gf_mesh == nullptr) { std::cerr << "project_to_pos_basis(): gf_mesh is NULL!" << std::endl; } + + int order = nodal_fe_space->GetOrder(0); + int dim = gf_mesh->Dimension(); + mfem::Geometry::Type geom_type = gf_mesh->GetElementBaseGeometry(0); + int map_type = + (nodal_fe_coll != nullptr) + ? nodal_fe_coll->FiniteElementForGeometry(geom_type)->GetMapType() + : static_cast(mfem::FiniteElement::VALUE); + + auto* pos_fe_coll = new mfem::H1_FECollection(5, dim, + mfem::BasisType::Positive); + // fecMap.Register("src_fec", fec, true); + // mfem::FiniteElementCollection pos_fe_coll = *nodal_fe_coll; + // detail::get_pos_fec(nodal_fe_coll, + // order, + // dim, + // map_type); + + //SLIC_ASSERT_MSG( + // pos_fe_coll != AXOM_NULLPTR, + // "Problem generating a positive finite element collection " + // << "corresponding to the mesh's '"<< nodal_fe_coll->Name() + // << "' finite element collection."); + + if(pos_fe_coll != nullptr) + { + //DEBUG + //std::cerr << "Good so far... pos_fe_coll is not null. Making FESpace and GridFunction." << std::endl; + const int dims = nodal_fe_space->GetVDim(); + // Create a positive (Bernstein) grid function for the nodes + mfem::FiniteElementSpace* pos_fe_space = + new mfem::FiniteElementSpace(gf_mesh, pos_fe_coll, dims); + mfem::GridFunction *pos_nodes = new mfem::GridFunction(pos_fe_space); + + // m_pos_nodes takes ownership of pos_fe_coll's memory (and pos_fe_space's memory) + pos_nodes->MakeOwner(pos_fe_coll); + + // Project the nodal grid function onto this + pos_nodes->ProjectGridFunction(*gf); + + out_pos_gf = pos_nodes; + is_new = true; + } + //DEBUG + else std::cerr << "BAD... pos_fe_coll is NULL. Could not make FESpace or GridFunction." << std::endl; + + //DEBUG + if (!out_pos_gf) + { + std::cerr << "project_to_pos_basis(): Construction failed; out_pos_gf is NULL!" << std::endl; + } + + // } + + return out_pos_gf; + } + + /*! Set up the source and target meshes */ void setupMeshes(int res1, int res2, int order) { @@ -287,7 +360,8 @@ struct Remapper const int tgt_res = res1; const int tgt_ord = order; const double tgt_scale = .712378102150; - const double tgt_trans = .1345747181586; + const double tgt_trans1 = .1345747181586; + const double tgt_trans2 = .1345747181586; // create the source mesh { @@ -317,7 +391,7 @@ struct Remapper // create the target mesh { auto* mesh = new mfem::Mesh(tgt_res, tgt_res, quadType, true); - xformMesh(mesh, tgt_scale, tgt_trans); + xformMesh(mesh, tgt_scale, tgt_trans1, tgt_trans2); auto* fec = new mfem::H1_FECollection(tgt_ord, dim, mfem::BasisType::Positive); @@ -345,62 +419,94 @@ struct Remapper // paramters for target mesh -- quad mesh covering unit square const int src_res = res2; const int src_ord = order; - const double src_scale = 6.0; - const double src_trans = -3.001; + const double src_scale = 1.5; + const double src_trans1 = .01517288412347; + const double src_trans2 = .02571238506182; // paramters for target mesh -- quad mesh covering (part of) unit square const int tgt_ord = 2; { - // create mfem mesh - auto* mesh = new mfem::Mesh(src_res, src_res, quadType, true); - xformMesh(mesh, src_scale, src_trans); - - // create finite element collection for nodes - auto* fec = new mfem::H1_FECollection(src_ord, dim, - mfem::BasisType::Positive); - fecMap.Register("src_fec", fec, true); - - // create finite element space for nodes - auto* fes = new mfem::FiniteElementSpace(mesh, fec, dim); - fesMap.Register("src_fes", fes, true); - mesh->SetNodalFESpace(fes); + // NOTE (KW): For now, assume we have AXOM_DATA_DIR + namespace fs = axom::utilities::filesystem; + std::string fname = fs::joinPath(AXOM_DATA_DIR, "mfem/disc-nurbs-80.mesh"); - // SLIC_INFO("Writing to: " << axom::utilities::filesystem::getCWD()); + auto* mesh = new mfem::Mesh(fname.c_str(),1,1); + xformMesh(mesh, src_scale, src_trans1, src_trans2); + if (mesh->NURBSext) + { + int order =src_ord; + mesh->SetCurvature(5); + } + // xformMesh(mesh, tgt_scale, tgt_trans); { std::ofstream file; - file.open("source_mesh.mfem"); + file.open("src_mesh_orig.mfem"); mesh->Print(file); } - - srcMesh.setMesh(mesh); + + bool is_mesh_gf_new; + mfem::GridFunction *mesh_nodes = mesh->GetNodes(); + mfem::GridFunction *pos_mesh_nodes_ptr = project_to_pos_basis(mesh_nodes, is_mesh_gf_new); + mfem::GridFunction & pos_mesh_nodes = (is_mesh_gf_new ? *pos_mesh_nodes_ptr : *mesh_nodes); + mesh->NewNodes(pos_mesh_nodes, true); + + //auto* fec = new mfem::H1_FECollection(tgt_ord, dim, + // mfem::BasisType::Positive); + //fecMap.Register("tgt_fec", fec, true); + + //auto* fes = new mfem::FiniteElementSpace(mesh, fec, dim); + //fesMap.Register("tgt_fes", fes, true); + //mesh->SetNodalFESpace(fes); + std::cout << mesh->GetNV() << std::endl; + { + std::ofstream file; + file.open("src_mesh_set.mfem"); + mesh->Print(file); + } + //std::cout << "Got here!" << std::endl; + srcMesh.setMesh(mesh); } + // create the target mesh + { // NOTE (KW): For now, assume we have AXOM_DATA_DIR namespace fs = axom::utilities::filesystem; - std::string fname = fs::joinPath(AXOM_DATA_DIR, "mfem/disc-nurbs.mesh"); + std::string fname = fs::joinPath(AXOM_DATA_DIR, "mfem/disc-nurbs-80.mesh"); auto* mesh = new mfem::Mesh(fname.c_str(),1,1); if (mesh->NURBSext) { int order =tgt_ord; - mesh->SetCurvature(order); + mesh->SetCurvature(5); } + // xformMesh(mesh, tgt_scale, tgt_trans); + const double tgt_scale = 1.0; + const double tgt_trans1 = .001237586; + const double tgt_trans2 = -.06172376; + xformMesh(mesh, tgt_scale, tgt_trans1, tgt_trans2); + { std::ofstream file; file.open("target_mesh_orig.mfem"); mesh->Print(file); } - auto* fec = new mfem::H1_FECollection(tgt_ord, dim, - mfem::BasisType::Positive); - fecMap.Register("tgt_fec", fec, true); + bool is_mesh_gf_new; + mfem::GridFunction *mesh_nodes = mesh->GetNodes(); + mfem::GridFunction *pos_mesh_nodes_ptr = project_to_pos_basis(mesh_nodes, is_mesh_gf_new); + mfem::GridFunction & pos_mesh_nodes = (is_mesh_gf_new ? *pos_mesh_nodes_ptr : *mesh_nodes); + mesh->NewNodes(pos_mesh_nodes, true); + + //auto* fec = new mfem::H1_FECollection(tgt_ord, dim, + // mfem::BasisType::Positive); + //fecMap.Register("tgt_fec", fec, true); - auto* fes = new mfem::FiniteElementSpace(mesh, fec, dim); - fesMap.Register("tgt_fes", fes, true); - mesh->SetNodalFESpace(fes); + //auto* fes = new mfem::FiniteElementSpace(mesh, fec, dim); + //fesMap.Register("tgt_fes", fes, true); + //mesh->SetNodalFESpace(fes); { std::ofstream file; @@ -433,7 +539,7 @@ struct Remapper double totalArea=0.0; double correctArea=0.0; const int nTargetElems = tgtMesh.numElements(); - // SLIC_INFO("Number of Target Elements: " << nTargetElems); + // SLIC_INFO("Number of Target Elements: " << nTargetElems); double calcE=0.0; for (int i = 0 ; i < nTargetElems ; ++i) { @@ -444,45 +550,45 @@ struct Remapper break; auto tgtPoly = tgtMesh.elemAsCurvedPolygon(i); - // SLIC_INFO("Target Element: " << tgtPoly); + //SLIC_INFO("Target Element: " << tgtPoly); correctArea+=tgtPoly.area(); - //j SLIC_INFO("Target elem " << i - //j << " -- area " << tgtPoly.area() - //j //<< " -- bbox " << tgtMesh.elementBoundingBox(i) - //j ); + //SLIC_INFO("Target elem " << i + // << " -- area " << tgtPoly.area() + // //<< " -- bbox " << tgtMesh.elementBoundingBox(i) + // ); double A=0.0; for (int srcElem : candidates) { auto srcPoly = srcMesh.elemAsCurvedPolygon(srcElem); - // SLIC_INFO("*Source Element: " << srcPoly); - // SLIC_INFO("* Source elem " << srcElem - // << " -- area " << srcPoly.area() - //// //<< " -- bbox " << srcMesh.elementBoundingBox(srcElem) - // ); + // SLIC_INFO("*Source Element: " << srcPoly); + //SLIC_INFO("* Source elem " << srcElem + // << " -- area " << srcPoly.area() + // //// //<< " -- bbox " << srcMesh.elementBoundingBox(srcElem) + // ); std::vector> pnew; tgtPoly.reverseOrientation(); srcPoly.reverseOrientation(); - if (primal::intersect(tgtPoly,srcPoly,pnew)) + if (primal::intersect(tgtPoly,srcPoly,pnew,1e-8)) { for (int i=0; i< static_cast(pnew.size()); ++i) { A-=pnew[i].area(); - // SLIC_INFO("** Intersection area :" << -pnew[i].area() - // ); + //SLIC_INFO("** Intersection area :" << -pnew[i].area() + // ); } } srcPoly.reverseOrientation(); tgtPoly.reverseOrientation(); - // SLIC_INFO("* Calculated area: " << srcElem - // << " -- area " << A - // //<< " -- bbox " << srcMesh.elementBoundingBox(srcElem) - // ); + //SLIC_INFO("* Calculated area: " << srcElem + // << " -- area " << A + //// //<< " -- bbox " << srcMesh.elementBoundingBox(srcElem) + // ); } calcE+=abs(tgtPoly.area()-A); totalArea+=A; - // SLIC_INFO("Calculated Area :" << A); + //SLIC_INFO("Calculated Area :" << A); } // std::cout << inclusion << ", "; // std::cout << calcE << ", "; @@ -494,8 +600,8 @@ struct Remapper // const double tgt_trans = .000000000001345747181586; // double trueError = (tgt_scale*tgt_scale-totalArea); // std::cout << trueError << ", "; - std:: cout << totalArea << std::endl; - std::cout << correctArea << std::endl; + std:: cout << "Calculated area (supermesh): " << std::fixed<< totalArea << std::endl; + std::cout << "Calculated area (target mesh): " << correctArea << std::endl; return (totalArea-correctArea); } @@ -538,43 +644,23 @@ struct Remapper private: // scale and translate the vertices of the given mesh - void xformMesh(mfem::Mesh* mesh, double sc, double off) + void xformMesh(mfem::Mesh* mesh, double sc, double off1, double off2) { - - std::cout << "Transforming Mesh now" << std::endl; - for (int v = 0 ; v < mesh->GetNV() ; ++v) + // std::cout << "Transforming Mesh now" << std::endl; + //for (int v = 0 ; v < NumVerts ; ++v) + //{ + // double pt* = mesh->GetVertex(v); + // pt[0] = sc*pt[0] + off1; + // pt[1] = sc*pt[1] + off2; + //} + mfem::GridFunction *mesh_nodes = mesh->GetNodes(); + //std::cout << *mesh_nodes[0] << std::endl; + int NumDofs= mesh_nodes->Size(); + for (int e =0 ; e < (NumDofs/2) ; ++e) { - double* pt = mesh->GetVertex(v); - // if (v==0) - // { - // std::cout << pt[0] << " , " << pt[1] << std::endl; - // } - // pt[0] = sc*pt[0] + off; - pt[0] = sc*pt[0] + off + .000147582957; - pt[1] = sc*pt[1] + off; - - /* if (v%3==0) - { - pt[0] = pt[0]-.01748; - } - if (v%5==0) - { - pt[0] = pt[0]+.004; - } - if (v%7==0) - { - pt[1] = pt[1]+.0243; - }*/ - // if (v==0) - // { - // std::cout << pt[0] << " , " << pt[1] << std::endl; - // } + (*mesh_nodes)[2*e] = sc*(*mesh_nodes)[2*e]+off1; + (*mesh_nodes)[2*e+1] = sc*(*mesh_nodes)[2*e+1]+off2; } - // for (int e =0 ; e < mesh->GetNEdges() ; ++e) - // { - // mfem::array dofs(); - // GetEdgeDofs(e, dofs); - // } } }; @@ -582,6 +668,7 @@ struct Remapper + //------------------------------------------------------------------------------ int main( int argc, char** argv ) { @@ -606,9 +693,10 @@ int main( int argc, char** argv ) { Remapper remap; + std::cout.precision(16); // res1= res1*2; - res1=25; + res1=i; int res2= res1+2; // Setup the two meshes in the Bernstein basis // The current implementation hard-codes the two meshes