segment_ratio.hpp 10 KB

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