segment_ratio.hpp 10 KB

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