turn_in_ring_winding.hpp 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. // Boost.Geometry (aka GGL, Generic Geometry Library)
  2. // Copyright (c) 2020 Barend Gehrels, Amsterdam, the Netherlands.
  3. // Copyright (c) 2023 Adam Wulkiewicz, Lodz, Poland.
  4. // This file was modified by Oracle on 2023.
  5. // Modifications copyright (c) 2023 Oracle and/or its affiliates.
  6. // Contributed and/or modified by Vissarion Fysikopoulos, 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_STRATEGIES_CARTESIAN_TURN_IN_RING_WINDING_HPP
  11. #define BOOST_GEOMETRY_STRATEGIES_CARTESIAN_TURN_IN_RING_WINDING_HPP
  12. #include <boost/geometry/arithmetic/infinite_line_functions.hpp>
  13. #include <boost/geometry/core/access.hpp>
  14. #include <boost/geometry/core/config.hpp>
  15. #include <boost/geometry/algorithms/detail/make/make.hpp>
  16. #include <boost/geometry/util/math.hpp>
  17. #include <boost/geometry/strategies/cartesian/side_rounded_input.hpp>
  18. namespace boost { namespace geometry
  19. {
  20. namespace strategy { namespace buffer
  21. {
  22. #ifndef DOXYGEN_NO_DETAIL
  23. enum place_on_ring_type
  24. {
  25. // +----offsetted----> (offsetted is considered as outside)
  26. // | |
  27. // | |
  28. // left right (first point outside, rest inside)
  29. // | |
  30. // | |
  31. // <-----original----+ (original is considered as inside)
  32. place_on_ring_offsetted,
  33. place_on_ring_original,
  34. place_on_ring_to_offsetted,
  35. place_on_ring_from_offsetted,
  36. };
  37. template <typename CalculationType>
  38. class turn_in_ring_winding
  39. {
  40. // Implements the winding rule.
  41. // Basic calculations (on a clockwise ring of 5 segments)
  42. // (as everywhere in BG, -1 = right, 0 = on segment, +1 = left)
  43. // +--------2--------+ // P : For 1/3, nothing happens, it returns
  44. // | | // For 2, side is right (-1), multiplier=2, -2
  45. // | P | // For 4, side is right (-1), multiplier=1, -1
  46. // 1 3 // For 5, side is right (-1), multiplier=1, -1, total -4
  47. // | Q | // Q : For 2: -2, for 4: -2, total -4
  48. // | | // R : For 2: -2, for 5: +2, total 0
  49. // +----5---*----4---+ // S : For 2: -1, 3: nothing, 4: +1, total 0
  50. //
  51. // R S
  52. //
  53. public:
  54. struct counter
  55. {
  56. //! Counter, is increased if point is left of a segment (outside),
  57. //! and decreased if point is right of a segment (inside)
  58. int count{0};
  59. int count_on_offsetted{0};
  60. int count_on_origin{0};
  61. int count_on_edge{0};
  62. CalculationType edge_min_fraction{(std::numeric_limits<CalculationType>::max)()};
  63. #if defined(BOOST_GEOMETRY_USE_RESCALING)
  64. CalculationType inside_min_measure{(std::numeric_limits<CalculationType>::max)()};
  65. #endif
  66. inline bool is_inside() const
  67. {
  68. return count < 0 || count_on_origin > 0;
  69. }
  70. inline bool is_on_boundary() const
  71. {
  72. return count_on_origin == 0
  73. && (count_on_offsetted > 0
  74. || (count_on_edge > 0 && edge_min_fraction < 1.0e-3)
  75. #if defined(BOOST_GEOMETRY_USE_RESCALING)
  76. || (count < 0 && inside_min_measure < 1.0e-5)
  77. #endif
  78. );
  79. }
  80. };
  81. using state_type = counter;
  82. template <typename Point, typename PointOfSegment>
  83. static inline bool is_in_vertical_range(Point const& point,
  84. PointOfSegment const& s1,
  85. PointOfSegment const& s2)
  86. {
  87. CalculationType const py = get<1>(point);
  88. CalculationType const s1y = get<1>(s1);
  89. CalculationType const s2y = get<1>(s2);
  90. return s1y < s2y ? (py >= s1y && py <= s2y) : (py >= s2y && py <= s1y);
  91. }
  92. template <typename Point, typename PointOfSegment>
  93. static inline void apply_on_boundary(Point const& point,
  94. PointOfSegment const& s1,
  95. PointOfSegment const& s2,
  96. place_on_ring_type place_on_ring,
  97. counter& the_state)
  98. {
  99. if (place_on_ring == place_on_ring_offsetted)
  100. {
  101. the_state.count_on_offsetted++;
  102. }
  103. else if (place_on_ring == place_on_ring_to_offsetted
  104. || place_on_ring == place_on_ring_from_offsetted)
  105. {
  106. the_state.count_on_edge++;
  107. auto const line1 = detail::make::make_perpendicular_line<CalculationType>(s1, s2, s1);
  108. auto const line2 = detail::make::make_perpendicular_line<CalculationType>(s2, s1, s2);
  109. auto const value1 = arithmetic::side_value(line1, point);
  110. auto const value2 = arithmetic::side_value(line2, point);
  111. if (value1 >= 0 && value2 >= 0)
  112. {
  113. auto const length_value = value1 + value2;
  114. if (length_value > 0)
  115. {
  116. // If it is to the utmost point s1 or s2, it is "outside"
  117. auto const fraction = (place_on_ring == place_on_ring_to_offsetted ? value2 : value1) / length_value;
  118. if (fraction < the_state.edge_min_fraction)
  119. {
  120. the_state.edge_min_fraction = fraction;
  121. }
  122. }
  123. }
  124. }
  125. else
  126. {
  127. the_state.count_on_origin++;
  128. }
  129. }
  130. template <typename Point, typename PointOfSegment>
  131. static inline bool apply(Point const& point,
  132. PointOfSegment const& s1,
  133. PointOfSegment const& s2,
  134. place_on_ring_type place_on_ring,
  135. bool is_convex,
  136. counter& the_state)
  137. {
  138. int const side = strategy::side::side_rounded_input<CalculationType>::apply(s1, s2, point);
  139. if (is_convex && side > 0)
  140. {
  141. // If the point is left of this segment of a convex piece, it can never be inside.
  142. // Stop further processing
  143. the_state.count = 1;
  144. return false;
  145. }
  146. CalculationType const px = get<0>(point);
  147. CalculationType const s1x = get<0>(s1);
  148. CalculationType const s2x = get<0>(s2);
  149. bool const in_horizontal_range = s1x < s2x ? (px >= s1x && px <= s2x) : (px >= s2x && px <= s1x);
  150. bool const vertical = s1x == s2x;
  151. if (in_horizontal_range || (vertical && is_in_vertical_range(point, s1, s2)))
  152. {
  153. if (side == 0)
  154. {
  155. apply_on_boundary(point, s1, s2, place_on_ring, the_state);
  156. }
  157. #if defined(BOOST_GEOMETRY_USE_RESCALING)
  158. else if (side == -1)
  159. {
  160. auto const line = detail::make::make_infinite_line<CalculationType>(s1, s2);
  161. auto const value = -arithmetic::side_value(line, point);
  162. if (value > 0 && value < the_state.inside_min_measure) { the_state.inside_min_measure = value; }
  163. }
  164. #endif
  165. }
  166. if (in_horizontal_range)
  167. {
  168. auto const on_boundary = the_state.count_on_offsetted + the_state.count_on_edge + the_state.count_on_origin;
  169. if (on_boundary == 0)
  170. {
  171. // Use only absolute comparisons, because the ring is continuous -
  172. // what was missed is there earlier or later, and turns should
  173. // not be counted twice (which can happen if an epsilon is used).
  174. bool const eq1 = s1x == px;
  175. bool const eq2 = s2x == px;
  176. // Account for 1 or 2 for left side (outside)
  177. // and for -1 or -2 for right side (inside)
  178. int const multiplier = eq1 || eq2 ? 1 : 2;
  179. the_state.count += side * multiplier;
  180. }
  181. }
  182. return true;
  183. }
  184. };
  185. #endif // DOXYGEN_NO_DETAIL
  186. }} // namespace strategy::buffer
  187. }} // namespace boost::geometry
  188. #endif // BOOST_GEOMETRY_STRATEGIES_CARTESIAN_TURN_IN_RING_WINDING_HPP