segment_ratio.hpp 10 KB

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