segment_ratio.hpp 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. // Boost.Geometry (aka GGL, Generic Geometry Library)
  2. // Copyright (c) 2013 Barend Gehrels, Amsterdam, the Netherlands.
  3. // This file was modified by Oracle on 2016-2020.
  4. // Modifications copyright (c) 2016-2020 Oracle and/or its affiliates.
  5. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
  6. // Use, modification and distribution is subject to the Boost Software License,
  7. // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
  8. // http://www.boost.org/LICENSE_1_0.txt)
  9. #ifndef BOOST_GEOMETRY_POLICIES_ROBUSTNESS_SEGMENT_RATIO_HPP
  10. #define BOOST_GEOMETRY_POLICIES_ROBUSTNESS_SEGMENT_RATIO_HPP
  11. #include <type_traits>
  12. #include <boost/config.hpp>
  13. #include <boost/rational.hpp>
  14. #include <boost/geometry/core/assert.hpp>
  15. #include <boost/geometry/util/math.hpp>
  16. #include <boost/geometry/util/promote_floating_point.hpp>
  17. namespace boost { namespace geometry
  18. {
  19. namespace detail { namespace segment_ratio
  20. {
  21. template
  22. <
  23. typename Type,
  24. bool IsIntegral = std::is_integral<Type>::type::value
  25. >
  26. struct less {};
  27. template <typename Type>
  28. struct less<Type, true>
  29. {
  30. template <typename Ratio>
  31. static inline bool apply(Ratio const& lhs, Ratio const& rhs)
  32. {
  33. return boost::rational<Type>(lhs.numerator(), lhs.denominator())
  34. < boost::rational<Type>(rhs.numerator(), rhs.denominator());
  35. }
  36. };
  37. template <typename Type>
  38. struct less<Type, false>
  39. {
  40. template <typename Ratio>
  41. static inline bool apply(Ratio const& lhs, Ratio const& rhs)
  42. {
  43. BOOST_GEOMETRY_ASSERT(lhs.denominator() != 0);
  44. BOOST_GEOMETRY_ASSERT(rhs.denominator() != 0);
  45. Type const a = lhs.numerator() / lhs.denominator();
  46. Type const b = rhs.numerator() / rhs.denominator();
  47. return ! geometry::math::equals(a, b)
  48. && a < b;
  49. }
  50. };
  51. template
  52. <
  53. typename Type,
  54. bool IsIntegral = std::is_integral<Type>::type::value
  55. >
  56. struct equal {};
  57. template <typename Type>
  58. struct equal<Type, true>
  59. {
  60. template <typename Ratio>
  61. static inline bool apply(Ratio const& lhs, Ratio const& rhs)
  62. {
  63. return boost::rational<Type>(lhs.numerator(), lhs.denominator())
  64. == boost::rational<Type>(rhs.numerator(), rhs.denominator());
  65. }
  66. };
  67. template <typename Type>
  68. struct equal<Type, false>
  69. {
  70. template <typename Ratio>
  71. static inline bool apply(Ratio const& lhs, Ratio const& rhs)
  72. {
  73. BOOST_GEOMETRY_ASSERT(lhs.denominator() != 0);
  74. BOOST_GEOMETRY_ASSERT(rhs.denominator() != 0);
  75. Type const a = lhs.numerator() / lhs.denominator();
  76. Type const b = rhs.numerator() / rhs.denominator();
  77. return geometry::math::equals(a, b);
  78. }
  79. };
  80. }}
  81. //! Small class to keep a ratio (e.g. 1/4)
  82. //! Main purpose is intersections and checking on 0, 1, and smaller/larger
  83. //! The prototype used Boost.Rational. However, we also want to store FP ratios,
  84. //! (so numerator/denominator both in float)
  85. //! and Boost.Rational starts with GCD which we prefer to avoid if not necessary
  86. //! On a segment means: this ratio is between 0 and 1 (both inclusive)
  87. //!
  88. template <typename Type>
  89. class segment_ratio
  90. {
  91. public :
  92. typedef Type numeric_type;
  93. // Type-alias for the type itself
  94. typedef segment_ratio<Type> thistype;
  95. inline segment_ratio()
  96. : m_numerator(0)
  97. , m_denominator(1)
  98. , m_approximation(0)
  99. {}
  100. inline segment_ratio(const Type& numerator, const Type& denominator)
  101. : m_numerator(numerator)
  102. , m_denominator(denominator)
  103. {
  104. initialize();
  105. }
  106. segment_ratio(segment_ratio const&) = default;
  107. segment_ratio& operator=(segment_ratio const&) = default;
  108. segment_ratio(segment_ratio&&) = default;
  109. segment_ratio& operator=(segment_ratio&&) = default;
  110. // These are needed because in intersection strategies ratios are assigned
  111. // in fractions and if a user passes CalculationType then ratio Type in
  112. // turns is taken from geometry coordinate_type and the one used in
  113. // a strategy uses Type selected using CalculationType.
  114. // See: detail::overlay::intersection_info_base
  115. // and policies::relate::segments_intersection_points
  116. // in particular segments_collinear() where ratios are assigned.
  117. template<typename T> friend class segment_ratio;
  118. template <typename T>
  119. segment_ratio(segment_ratio<T> const& r)
  120. : m_numerator(r.m_numerator)
  121. , m_denominator(r.m_denominator)
  122. {
  123. initialize();
  124. }
  125. template <typename T>
  126. segment_ratio& operator=(segment_ratio<T> const& r)
  127. {
  128. m_numerator = r.m_numerator;
  129. m_denominator = r.m_denominator;
  130. initialize();
  131. return *this;
  132. }
  133. template <typename T>
  134. segment_ratio(segment_ratio<T> && r)
  135. : m_numerator(std::move(r.m_numerator))
  136. , m_denominator(std::move(r.m_denominator))
  137. {
  138. initialize();
  139. }
  140. template <typename T>
  141. segment_ratio& operator=(segment_ratio<T> && r)
  142. {
  143. m_numerator = std::move(r.m_numerator);
  144. m_denominator = std::move(r.m_denominator);
  145. initialize();
  146. return *this;
  147. }
  148. inline Type const& numerator() const { return m_numerator; }
  149. inline Type const& denominator() const { return m_denominator; }
  150. inline void assign(const Type& numerator, const Type& denominator)
  151. {
  152. m_numerator = numerator;
  153. m_denominator = denominator;
  154. initialize();
  155. }
  156. inline void initialize()
  157. {
  158. // Minimal normalization
  159. // 1/-4 => -1/4, -1/-4 => 1/4
  160. if (m_denominator < 0)
  161. {
  162. m_numerator = -m_numerator;
  163. m_denominator = -m_denominator;
  164. }
  165. m_approximation =
  166. m_denominator == 0 ? 0
  167. : (
  168. boost::numeric_cast<fp_type>(m_numerator) * scale()
  169. / boost::numeric_cast<fp_type>(m_denominator)
  170. );
  171. }
  172. inline bool is_zero() const { return math::equals(m_numerator, 0); }
  173. inline bool is_one() const { return math::equals(m_numerator, m_denominator); }
  174. inline bool on_segment() const
  175. {
  176. // e.g. 0/4 or 4/4 or 2/4
  177. return m_numerator >= 0 && m_numerator <= m_denominator;
  178. }
  179. inline bool in_segment() const
  180. {
  181. // e.g. 1/4
  182. return m_numerator > 0 && m_numerator < m_denominator;
  183. }
  184. inline bool on_end() const
  185. {
  186. // e.g. 0/4 or 4/4
  187. return is_zero() || is_one();
  188. }
  189. inline bool left() const
  190. {
  191. // e.g. -1/4
  192. return m_numerator < 0;
  193. }
  194. inline bool right() const
  195. {
  196. // e.g. 5/4
  197. return m_numerator > m_denominator;
  198. }
  199. inline bool near_end() const
  200. {
  201. if (left() || right())
  202. {
  203. return false;
  204. }
  205. static fp_type const small_part_of_scale = scale() / 100;
  206. return m_approximation < small_part_of_scale
  207. || m_approximation > scale() - small_part_of_scale;
  208. }
  209. inline bool close_to(thistype const& other) const
  210. {
  211. return geometry::math::abs(m_approximation - other.m_approximation) < 50;
  212. }
  213. inline bool operator< (thistype const& other) const
  214. {
  215. return close_to(other)
  216. ? detail::segment_ratio::less<Type>::apply(*this, other)
  217. : m_approximation < other.m_approximation;
  218. }
  219. inline bool operator== (thistype const& other) const
  220. {
  221. return close_to(other)
  222. && detail::segment_ratio::equal<Type>::apply(*this, other);
  223. }
  224. static inline thistype zero()
  225. {
  226. static thistype result(0, 1);
  227. return result;
  228. }
  229. static inline thistype one()
  230. {
  231. static thistype result(1, 1);
  232. return result;
  233. }
  234. #if defined(BOOST_GEOMETRY_DEFINE_STREAM_OPERATOR_SEGMENT_RATIO)
  235. friend std::ostream& operator<<(std::ostream &os, segment_ratio const& ratio)
  236. {
  237. os << ratio.m_numerator << "/" << ratio.m_denominator
  238. << " (" << (static_cast<double>(ratio.m_numerator)
  239. / static_cast<double>(ratio.m_denominator))
  240. << ")";
  241. return os;
  242. }
  243. #endif
  244. private :
  245. // NOTE: if this typedef is used then fp_type is non-fundamental type
  246. // if Type is non-fundamental type
  247. //typedef typename promote_floating_point<Type>::type fp_type;
  248. // TODO: What with user-defined numeric types?
  249. // Shouldn't here is_integral be checked?
  250. typedef std::conditional_t
  251. <
  252. std::is_floating_point<Type>::value, Type, double
  253. > fp_type;
  254. Type m_numerator;
  255. Type m_denominator;
  256. // Contains ratio on scale 0..1000000 (for 0..1)
  257. // This is an approximation for fast and rough comparisons
  258. // Boost.Rational is used if the approximations are close.
  259. // Reason: performance, Boost.Rational does a GCD by default and also the
  260. // comparisons contain while-loops.
  261. fp_type m_approximation;
  262. static inline fp_type scale()
  263. {
  264. return 1000000.0;
  265. }
  266. };
  267. }} // namespace boost::geometry
  268. #endif // BOOST_GEOMETRY_POLICIES_ROBUSTNESS_SEGMENT_RATIO_HPP