piece_border.hpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512
  1. // Boost.Geometry (aka GGL, Generic Geometry Library)
  2. // Copyright (c) 2020-2021 Barend Gehrels, Amsterdam, the Netherlands.
  3. // Copyright (c) 2023 Adam Wulkiewicz, Lodz, Poland.
  4. // This file was modified by Oracle on 2020-2022.
  5. // Modifications copyright (c) 2020-2022, Oracle and/or its affiliates.
  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_ALGORITHMS_DETAIL_BUFFER_PIECE_BORDER_HPP
  11. #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_PIECE_BORDER_HPP
  12. #include <array>
  13. #include <boost/core/addressof.hpp>
  14. #include <boost/range/size.hpp>
  15. #include <boost/geometry/core/assert.hpp>
  16. #include <boost/geometry/core/config.hpp>
  17. #include <boost/geometry/algorithms/assign.hpp>
  18. #include <boost/geometry/algorithms/comparable_distance.hpp>
  19. #include <boost/geometry/algorithms/equals.hpp>
  20. #include <boost/geometry/algorithms/expand.hpp>
  21. #include <boost/geometry/algorithms/detail/buffer/buffer_policies.hpp>
  22. #include <boost/geometry/algorithms/detail/expand_by_epsilon.hpp>
  23. #include <boost/geometry/strategies/cartesian/turn_in_ring_winding.hpp>
  24. #include <boost/geometry/geometries/box.hpp>
  25. #include <boost/geometry/geometries/segment.hpp>
  26. namespace boost { namespace geometry
  27. {
  28. #ifndef DOXYGEN_NO_DETAIL
  29. namespace detail
  30. {
  31. template <typename It, typename T, typename Compare>
  32. inline bool get_range_around(It begin, It end, T const& value, Compare const& compare, It& lower, It& upper)
  33. {
  34. lower = end;
  35. upper = end;
  36. // Get first element not smaller than value
  37. if (begin == end)
  38. {
  39. return false;
  40. }
  41. if (compare(value, *begin))
  42. {
  43. // The value is smaller than the first item, therefore not in range
  44. return false;
  45. }
  46. // *(begin + std::distance(begin, end) - 1))
  47. if (compare(*(end - 1), value))
  48. {
  49. // The last item is larger than the value, therefore not in range
  50. return false;
  51. }
  52. // Assign the iterators.
  53. // lower >= begin and lower < end
  54. // upper > lower and upper <= end
  55. // lower_bound points to first element NOT LESS than value - but because
  56. // we want the first value LESS than value, we decrease it
  57. lower = std::lower_bound(begin, end, value, compare);
  58. // upper_bound points to first element of which value is LESS
  59. upper = std::upper_bound(begin, end, value, compare);
  60. if (lower != begin)
  61. {
  62. --lower;
  63. }
  64. if (upper != end)
  65. {
  66. ++upper;
  67. }
  68. return true;
  69. }
  70. }
  71. namespace detail { namespace buffer
  72. {
  73. //! Contains the border of the piece, consisting of 4 parts:
  74. //! 1: the part of the offsetted ring (referenced, not copied)
  75. //! 2: the part of the original (one or two points)
  76. //! 3: the left part (from original to offsetted)
  77. //! 4: the right part (from offsetted to original)
  78. //! Besides that, it contains some properties of the piece(border);
  79. //! - convexity
  80. //! - envelope
  81. //! - monotonicity of the offsetted ring
  82. //! - min/max radius of a point buffer
  83. //! - if it is a "reversed" piece (linear features with partly negative buffers)
  84. template <typename Ring, typename Point>
  85. struct piece_border
  86. {
  87. typedef typename geometry::coordinate_type<Point>::type coordinate_type;
  88. typedef typename default_comparable_distance_result<Point>::type radius_type;
  89. typedef typename geometry::strategy::buffer::turn_in_ring_winding<coordinate_type>::state_type state_type;
  90. bool m_reversed;
  91. // Points from the offsetted ring. They are not copied, this structure
  92. // refers to those points
  93. Ring const* m_ring;
  94. std::size_t m_begin;
  95. std::size_t m_end;
  96. // Points from the original (one or two, depending on piece shape)
  97. // Note, if there are 2 points, they are REVERSED w.r.t. the original
  98. // Therefore here we can walk in its order.
  99. std::array<Point, 2> m_originals;
  100. std::size_t m_original_size;
  101. geometry::model::box<Point> m_envelope;
  102. bool m_has_envelope;
  103. // True if piece is determined as "convex"
  104. bool m_is_convex;
  105. // True if offsetted part is monotonically changing in x-direction
  106. bool m_is_monotonic_increasing;
  107. bool m_is_monotonic_decreasing;
  108. radius_type m_min_comparable_radius;
  109. radius_type m_max_comparable_radius;
  110. piece_border()
  111. : m_reversed(false)
  112. , m_ring(NULL)
  113. , m_begin(0)
  114. , m_end(0)
  115. , m_original_size(0)
  116. , m_has_envelope(false)
  117. , m_is_convex(false)
  118. , m_is_monotonic_increasing(false)
  119. , m_is_monotonic_decreasing(false)
  120. , m_min_comparable_radius(0)
  121. , m_max_comparable_radius(0)
  122. {
  123. }
  124. // Only used for debugging (SVG)
  125. Ring get_full_ring() const
  126. {
  127. Ring result;
  128. if (ring_or_original_empty())
  129. {
  130. return result;
  131. }
  132. std::copy(m_ring->begin() + m_begin,
  133. m_ring->begin() + m_end,
  134. std::back_inserter(result));
  135. std::copy(m_originals.begin(),
  136. m_originals.begin() + m_original_size,
  137. std::back_inserter(result));
  138. // Add the closing point
  139. result.push_back(*(m_ring->begin() + m_begin));
  140. return result;
  141. }
  142. template <typename Strategy>
  143. void get_properties_of_border(bool is_point_buffer, Point const& center,
  144. Strategy const& strategy)
  145. {
  146. m_has_envelope = calculate_envelope(m_envelope, strategy);
  147. if (m_has_envelope)
  148. {
  149. // Take roundings into account, enlarge box
  150. geometry::detail::expand_by_epsilon(m_envelope);
  151. }
  152. if (! ring_or_original_empty() && is_point_buffer)
  153. {
  154. // Determine min/max radius
  155. calculate_radii(center, m_ring->begin() + m_begin, m_ring->begin() + m_end);
  156. }
  157. }
  158. template <typename Strategy>
  159. void get_properties_of_offsetted_ring_part(Strategy const& strategy)
  160. {
  161. if (! ring_or_original_empty())
  162. {
  163. m_is_convex = is_convex(strategy);
  164. check_monotonicity(m_ring->begin() + m_begin, m_ring->begin() + m_end);
  165. }
  166. }
  167. void set_offsetted(Ring const& ring, std::size_t begin, std::size_t end)
  168. {
  169. BOOST_GEOMETRY_ASSERT(begin <= end);
  170. BOOST_GEOMETRY_ASSERT(begin < boost::size(ring));
  171. BOOST_GEOMETRY_ASSERT(end <= boost::size(ring));
  172. m_ring = boost::addressof(ring);
  173. m_begin = begin;
  174. m_end = end;
  175. }
  176. void add_original_point(Point const& point)
  177. {
  178. BOOST_GEOMETRY_ASSERT(m_original_size < 2);
  179. m_originals[m_original_size++] = point;
  180. }
  181. template <typename Box, typename Strategy>
  182. bool calculate_envelope(Box& envelope, Strategy const& strategy) const
  183. {
  184. geometry::assign_inverse(envelope);
  185. if (ring_or_original_empty())
  186. {
  187. return false;
  188. }
  189. expand_envelope(envelope, m_ring->begin() + m_begin, m_ring->begin() + m_end, strategy);
  190. expand_envelope(envelope, m_originals.begin(), m_originals.begin() + m_original_size, strategy);
  191. return true;
  192. }
  193. // Whatever the return value, the state should be checked.
  194. template <typename TurnPoint, typename State>
  195. bool point_on_piece(TurnPoint const& point,
  196. bool one_sided, bool is_linear_end_point,
  197. State& state) const
  198. {
  199. if (ring_or_original_empty())
  200. {
  201. return false;
  202. }
  203. // Walk over the different parts of the ring, in clockwise order
  204. // For performance reasons: start with the helper part (one segment)
  205. // then the original part (one segment, if any), then the other helper
  206. // part (one segment), and only then the offsetted part
  207. // (probably more segments, check monotonicity)
  208. geometry::strategy::buffer::turn_in_ring_winding<coordinate_type> tir;
  209. Point const offsetted_front = *(m_ring->begin() + m_begin);
  210. Point const offsetted_back = *(m_ring->begin() + m_end - 1);
  211. // For onesided buffers, or turns colocated with linear end points,
  212. // the place on the ring is changed to offsetted (because of colocation)
  213. geometry::strategy::buffer::place_on_ring_type const por_original
  214. = adapted_place_on_ring(geometry::strategy::buffer::place_on_ring_original,
  215. one_sided, is_linear_end_point);
  216. geometry::strategy::buffer::place_on_ring_type const por_from_offsetted
  217. = adapted_place_on_ring(geometry::strategy::buffer::place_on_ring_from_offsetted,
  218. one_sided, is_linear_end_point);
  219. geometry::strategy::buffer::place_on_ring_type const por_to_offsetted
  220. = adapted_place_on_ring(geometry::strategy::buffer::place_on_ring_to_offsetted,
  221. one_sided, is_linear_end_point);
  222. bool continue_processing = true;
  223. if (m_original_size == 1)
  224. {
  225. // One point. Walk from last offsetted to point, and from point to first offsetted
  226. continue_processing = step(point, offsetted_back, m_originals[0],
  227. tir, por_from_offsetted, state)
  228. && step(point, m_originals[0], offsetted_front,
  229. tir, por_to_offsetted, state);
  230. }
  231. else if (m_original_size == 2)
  232. {
  233. // Two original points. Walk from last offsetted point to first original point,
  234. // then along original, then from second oginal to first offsetted point
  235. continue_processing = step(point, offsetted_back, m_originals[0],
  236. tir, por_from_offsetted, state)
  237. && step(point, m_originals[0], m_originals[1],
  238. tir, por_original, state)
  239. && step(point, m_originals[1], offsetted_front,
  240. tir, por_to_offsetted, state);
  241. }
  242. if (continue_processing)
  243. {
  244. // Check the offsetted ring (in rounded joins, these might be
  245. // several segments)
  246. walk_offsetted(point, m_ring->begin() + m_begin, m_ring->begin() + m_end,
  247. tir, state);
  248. }
  249. return true;
  250. }
  251. //! Returns true if empty (no ring, or no points, or no original)
  252. bool ring_or_original_empty() const
  253. {
  254. return m_ring == NULL || m_begin >= m_end || m_original_size == 0;
  255. }
  256. private :
  257. static geometry::strategy::buffer::place_on_ring_type
  258. adapted_place_on_ring(geometry::strategy::buffer::place_on_ring_type target,
  259. bool one_sided, bool is_linear_end_point)
  260. {
  261. return one_sided || is_linear_end_point
  262. ? geometry::strategy::buffer::place_on_ring_offsetted
  263. : target;
  264. }
  265. template
  266. <
  267. typename TurnPoint, typename Iterator,
  268. typename TiRStrategy,
  269. typename State
  270. >
  271. bool walk_offsetted(TurnPoint const& point, Iterator begin, Iterator end,
  272. TiRStrategy const & strategy,
  273. State& state) const
  274. {
  275. Iterator it = begin;
  276. Iterator beyond = end;
  277. // Move iterators if the offsetted ring is monotonic increasing or decreasing
  278. if (m_is_monotonic_increasing)
  279. {
  280. if (! get_range_around(begin, end, point, geometry::less<Point, 0>(), it, beyond))
  281. {
  282. return true;
  283. }
  284. }
  285. else if (m_is_monotonic_decreasing)
  286. {
  287. if (! get_range_around(begin, end, point, geometry::greater<Point, 0>(), it, beyond))
  288. {
  289. return true;
  290. }
  291. }
  292. for (Iterator previous = it++ ; it != beyond ; ++previous, ++it )
  293. {
  294. if (! step(point, *previous, *it, strategy,
  295. geometry::strategy::buffer::place_on_ring_offsetted, state))
  296. {
  297. return false;
  298. }
  299. }
  300. return true;
  301. }
  302. template <typename TurnPoint, typename TiRStrategy, typename State>
  303. bool step(TurnPoint const& point, Point const& p1, Point const& p2,
  304. TiRStrategy const& strategy,
  305. geometry::strategy::buffer::place_on_ring_type place_on_ring, State& state) const
  306. {
  307. return strategy.apply(point, p1, p2, place_on_ring, m_is_convex, state);
  308. }
  309. template <typename It, typename Box, typename Strategy>
  310. void expand_envelope(Box& envelope, It begin, It end, Strategy const& strategy) const
  311. {
  312. for (It it = begin; it != end; ++it)
  313. {
  314. geometry::expand(envelope, *it, strategy);
  315. }
  316. }
  317. template <typename Strategy>
  318. bool is_convex(Strategy const& strategy) const
  319. {
  320. if (ring_or_original_empty())
  321. {
  322. // Convexity is undetermined, and for this case it does not matter,
  323. // because it is only used for optimization in point_on_piece,
  324. // but that is not called if the piece border is not valid
  325. return false;
  326. }
  327. if (m_end - m_begin <= 2)
  328. {
  329. // The offsetted ring part of this piece has only two points.
  330. // If this is true, and the original ring part has only one point,
  331. // a triangle and it is convex. If the original ring part has two
  332. // points, it is a rectangle and theoretically could be concave,
  333. // but because of the way the buffer is generated, that is never
  334. // the case.
  335. return true;
  336. }
  337. // The offsetted ring part of thie piece has at least three points
  338. // (this is often the case in a piece marked as "join")
  339. // We can assume all points of the offset ring are different, and also
  340. // that all points on the original are different, and that the offsetted
  341. // ring is different from the original(s)
  342. Point const offsetted_front = *(m_ring->begin() + m_begin);
  343. Point const offsetted_second = *(m_ring->begin() + m_begin + 1);
  344. // These two points will be reassigned in every is_convex call
  345. Point previous = offsetted_front;
  346. Point current = offsetted_second;
  347. // Verify the offsetted range (from the second point on), the original,
  348. // and loop through the first two points of the offsetted range
  349. bool const result = is_convex(previous, current, m_ring->begin() + m_begin + 2, m_ring->begin() + m_end, strategy)
  350. && is_convex(previous, current, m_originals.begin(), m_originals.begin() + m_original_size, strategy)
  351. && is_convex(previous, current, offsetted_front, strategy)
  352. && is_convex(previous, current, offsetted_second, strategy);
  353. return result;
  354. }
  355. template <typename It, typename Strategy>
  356. bool is_convex(Point& previous, Point& current, It begin, It end, Strategy const& strategy) const
  357. {
  358. for (It it = begin; it != end; ++it)
  359. {
  360. if (! is_convex(previous, current, *it, strategy))
  361. {
  362. return false;
  363. }
  364. }
  365. return true;
  366. }
  367. template <typename Strategy>
  368. bool is_convex(Point& previous, Point& current, Point const& next, Strategy const& strategy) const
  369. {
  370. int const side = strategy.side().apply(previous, current, next);
  371. if (side == 1)
  372. {
  373. // Next is on the left side of clockwise ring: piece is not convex
  374. return false;
  375. }
  376. if (! equals::equals_point_point(current, next, strategy))
  377. {
  378. previous = current;
  379. current = next;
  380. }
  381. return true;
  382. }
  383. template <int Direction>
  384. inline void step_for_monotonicity(Point const& current, Point const& next)
  385. {
  386. if (geometry::get<Direction>(current) >= geometry::get<Direction>(next))
  387. {
  388. m_is_monotonic_increasing = false;
  389. }
  390. if (geometry::get<Direction>(current) <= geometry::get<Direction>(next))
  391. {
  392. m_is_monotonic_decreasing = false;
  393. }
  394. }
  395. template <typename It>
  396. void check_monotonicity(It begin, It end)
  397. {
  398. m_is_monotonic_increasing = true;
  399. m_is_monotonic_decreasing = true;
  400. if (begin == end || begin + 1 == end)
  401. {
  402. return;
  403. }
  404. It it = begin;
  405. for (It previous = it++; it != end; ++previous, ++it)
  406. {
  407. step_for_monotonicity<0>(*previous, *it);
  408. }
  409. }
  410. template <typename It>
  411. inline void calculate_radii(Point const& center, It begin, It end)
  412. {
  413. typedef geometry::model::referring_segment<Point const> segment_type;
  414. bool first = true;
  415. // An offsetted point-buffer ring around a point is supposed to be closed,
  416. // therefore walking from start to end is fine.
  417. It it = begin;
  418. for (It previous = it++; it != end; ++previous, ++it)
  419. {
  420. Point const& p0 = *previous;
  421. Point const& p1 = *it;
  422. segment_type const s(p0, p1);
  423. radius_type const d = geometry::comparable_distance(center, s);
  424. if (first || d < m_min_comparable_radius)
  425. {
  426. m_min_comparable_radius = d;
  427. }
  428. if (first || d > m_max_comparable_radius)
  429. {
  430. m_max_comparable_radius = d;
  431. }
  432. first = false;
  433. }
  434. }
  435. };
  436. }} // namespace detail::buffer
  437. #endif // DOXYGEN_NO_DETAIL
  438. }} // namespace boost::geometry
  439. #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_PIECE_BORDER_HPP