turn_in_piece_visitor.hpp 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974
  1. // Boost.Geometry (aka GGL, Generic Geometry Library)
  2. // Copyright (c) 2012-2014 Barend Gehrels, Amsterdam, the Netherlands.
  3. // Copyright (c) 2017 Adam Wulkiewicz, Lodz, Poland.
  4. // This file was modified by Oracle on 2016, 2018.
  5. // Modifications copyright (c) 2016-2018 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_TURN_IN_PIECE_VISITOR
  11. #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_TURN_IN_PIECE_VISITOR
  12. #include <boost/core/ignore_unused.hpp>
  13. #include <boost/range.hpp>
  14. #include <boost/geometry/core/assert.hpp>
  15. #include <boost/geometry/core/config.hpp>
  16. #include <boost/geometry/arithmetic/dot_product.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/disjoint/point_box.hpp>
  22. #include <boost/geometry/algorithms/detail/disjoint/box_box.hpp>
  23. #include <boost/geometry/algorithms/detail/overlay/segment_identifier.hpp>
  24. #include <boost/geometry/algorithms/detail/overlay/get_turn_info.hpp>
  25. #include <boost/geometry/policies/compare.hpp>
  26. #include <boost/geometry/strategies/buffer.hpp>
  27. #include <boost/geometry/algorithms/detail/buffer/buffer_policies.hpp>
  28. #include <boost/geometry/strategies/cartesian/side_of_intersection.hpp>
  29. namespace boost { namespace geometry
  30. {
  31. #ifndef DOXYGEN_NO_DETAIL
  32. namespace detail { namespace buffer
  33. {
  34. struct piece_get_box
  35. {
  36. template <typename Box, typename Piece>
  37. static inline void apply(Box& total, Piece const& piece)
  38. {
  39. geometry::expand(total, piece.robust_envelope);
  40. }
  41. };
  42. template <typename DisjointBoxBoxStrategy>
  43. struct piece_ovelaps_box
  44. {
  45. template <typename Box, typename Piece>
  46. static inline bool apply(Box const& box, Piece const& piece)
  47. {
  48. if (piece.type == strategy::buffer::buffered_flat_end
  49. || piece.type == strategy::buffer::buffered_concave)
  50. {
  51. // Turns cannot be inside a flat end (though they can be on border)
  52. // Neither we need to check if they are inside concave helper pieces
  53. // Skip all pieces not used as soon as possible
  54. return false;
  55. }
  56. return ! geometry::detail::disjoint::disjoint_box_box(box, piece.robust_envelope,
  57. DisjointBoxBoxStrategy());
  58. }
  59. };
  60. struct turn_get_box
  61. {
  62. template <typename Box, typename Turn>
  63. static inline void apply(Box& total, Turn const& turn)
  64. {
  65. geometry::expand(total, turn.robust_point);
  66. }
  67. };
  68. template <typename DisjointPointBoxStrategy>
  69. struct turn_ovelaps_box
  70. {
  71. template <typename Box, typename Turn>
  72. static inline bool apply(Box const& box, Turn const& turn)
  73. {
  74. return ! geometry::detail::disjoint::disjoint_point_box(turn.robust_point, box,
  75. DisjointPointBoxStrategy());
  76. }
  77. };
  78. enum analyse_result
  79. {
  80. analyse_unknown,
  81. analyse_continue,
  82. analyse_disjoint,
  83. analyse_within,
  84. analyse_on_original_boundary,
  85. analyse_on_offsetted,
  86. analyse_near_offsetted
  87. };
  88. template <typename Point>
  89. inline bool in_box(Point const& previous,
  90. Point const& current, Point const& point)
  91. {
  92. // Get its box (TODO: this can be prepared-on-demand later)
  93. typedef geometry::model::box<Point> box_type;
  94. box_type box;
  95. geometry::assign_inverse(box);
  96. geometry::expand(box, previous);
  97. geometry::expand(box, current);
  98. return geometry::covered_by(point, box);
  99. }
  100. template <typename NumericType>
  101. inline bool is_one_sided(NumericType const& left, NumericType const& right)
  102. {
  103. static NumericType const zero = 0;
  104. return geometry::math::equals(left, zero)
  105. || geometry::math::equals(right, zero);
  106. }
  107. template <typename Point, typename DistanceStrategy>
  108. inline bool has_zero_distance_at(Point const& point,
  109. DistanceStrategy const& distance_strategy)
  110. {
  111. return is_one_sided(distance_strategy.apply(point, point,
  112. strategy::buffer::buffer_side_left),
  113. distance_strategy.apply(point, point,
  114. strategy::buffer::buffer_side_right));
  115. }
  116. // meta-programming-structure defining if to use side-of-intersection
  117. // (only for cartesian / only necessary with rescaling)
  118. template <typename Tag>
  119. struct use_side_of_intersection {};
  120. #if defined(BOOST_GEOMETRY_USE_RESCALING)
  121. // With rescaling, let Cartesian use side-of-intersection
  122. template <>
  123. struct use_side_of_intersection<cartesian_tag> { static bool const value = true; };
  124. #else
  125. template <>
  126. struct use_side_of_intersection<cartesian_tag> { static bool const value = false; };
  127. #endif
  128. template <>
  129. struct use_side_of_intersection<spherical_tag> { static bool const value = false; };
  130. template <>
  131. struct use_side_of_intersection<geographic_tag> { static bool const value = false; };
  132. template <bool UseSideOfIntersection>
  133. struct check_segment {};
  134. // Implementation using side-of-intersection
  135. template <>
  136. struct check_segment<true>
  137. {
  138. template <typename Point, typename Turn, typename SideStrategy>
  139. static inline analyse_result apply(Point const& previous,
  140. Point const& current, Turn const& turn,
  141. bool from_monotonic,
  142. SideStrategy const& )
  143. {
  144. typedef geometry::model::referring_segment<Point const> segment_type;
  145. segment_type const p(turn.rob_pi, turn.rob_pj);
  146. segment_type const q(turn.rob_qi, turn.rob_qj);
  147. segment_type const r(previous, current);
  148. int const side = strategy::side::side_of_intersection::apply(p, q, r,
  149. turn.robust_point);
  150. if (side == 0)
  151. {
  152. return analyse_on_offsetted;
  153. }
  154. if (side == -1 && from_monotonic)
  155. {
  156. return analyse_within;
  157. }
  158. if (side == 1 && from_monotonic)
  159. {
  160. return analyse_disjoint;
  161. }
  162. return analyse_continue;
  163. }
  164. };
  165. template <>
  166. struct check_segment<false>
  167. {
  168. template <typename Point, typename Turn, typename SideStrategy>
  169. static inline analyse_result apply(Point const& previous,
  170. Point const& current, Turn const& turn,
  171. bool from_monotonic,
  172. SideStrategy const& side_strategy)
  173. {
  174. int const side = side_strategy.apply(previous, current, turn.robust_point);
  175. if (side == 0)
  176. {
  177. // Collinear, only on segment if it is covered by its bbox
  178. if (in_box(previous, current, turn.robust_point))
  179. {
  180. return analyse_on_offsetted;
  181. }
  182. }
  183. else if (side == -1)
  184. {
  185. // It is in the triangle right-of the segment where the
  186. // segment is the hypothenusa. Check if it is close
  187. // (within rounding-area)
  188. if (in_box(previous, current, turn.robust_point))
  189. {
  190. return analyse_near_offsetted;
  191. }
  192. else if (from_monotonic)
  193. {
  194. return analyse_within;
  195. }
  196. }
  197. else if (from_monotonic)
  198. {
  199. // Left of segment
  200. return analyse_disjoint;
  201. }
  202. // Not monotonic, on left or right side: continue analysing
  203. return analyse_continue;
  204. }
  205. };
  206. template <bool UseSideOfIntersection>
  207. class analyse_turn_wrt_point_piece {};
  208. template <>
  209. class analyse_turn_wrt_point_piece<true>
  210. {
  211. public :
  212. template <typename Turn, typename Piece, typename PointInGeometryStrategy, typename SideStrategy>
  213. static inline analyse_result apply(Turn const& turn, Piece const& piece,
  214. PointInGeometryStrategy const& ,
  215. SideStrategy const& )
  216. {
  217. typedef typename Piece::section_type section_type;
  218. typedef typename Turn::robust_point_type point_type;
  219. typedef typename geometry::coordinate_type<point_type>::type coordinate_type;
  220. typedef geometry::model::referring_segment<point_type const> segment_type;
  221. segment_type const p(turn.rob_pi, turn.rob_pj);
  222. segment_type const q(turn.rob_qi, turn.rob_qj);
  223. BOOST_GEOMETRY_ASSERT(! piece.sections.empty());
  224. coordinate_type const point_x = geometry::get<0>(turn.robust_point);
  225. for (std::size_t s = 0; s < piece.sections.size(); s++)
  226. {
  227. section_type const& section = piece.sections[s];
  228. // If point within horizontal range of monotonic section:
  229. if (! section.duplicate
  230. && section.begin_index < section.end_index
  231. && point_x >= geometry::get<min_corner, 0>(section.bounding_box) - 1
  232. && point_x <= geometry::get<max_corner, 0>(section.bounding_box) + 1)
  233. {
  234. for (signed_size_type i = section.begin_index + 1; i <= section.end_index; i++)
  235. {
  236. point_type const& previous = piece.robust_ring[i - 1];
  237. point_type const& current = piece.robust_ring[i];
  238. // First check if it is in range - if it is not, the
  239. // expensive side_of_intersection does not need to be
  240. // applied
  241. coordinate_type x1 = geometry::get<0>(previous);
  242. coordinate_type x2 = geometry::get<0>(current);
  243. if (x1 > x2)
  244. {
  245. std::swap(x1, x2);
  246. }
  247. if (point_x >= x1 - 1 && point_x <= x2 + 1)
  248. {
  249. segment_type const r(previous, current);
  250. int const side = strategy::side::side_of_intersection::apply(p, q, r,
  251. turn.robust_point);
  252. // Sections are monotonic in x-dimension
  253. if (side == 1)
  254. {
  255. // Left on segment
  256. return analyse_disjoint;
  257. }
  258. else if (side == 0)
  259. {
  260. // Collinear - TODO: check if really on segment
  261. return analyse_on_offsetted;
  262. }
  263. }
  264. }
  265. }
  266. }
  267. // It is nowhere outside, and not on segment, so it is within
  268. return analyse_within;
  269. }
  270. };
  271. template <>
  272. class analyse_turn_wrt_point_piece<false>
  273. {
  274. public :
  275. template <typename Turn, typename Piece, typename PointInGeometryStrategy, typename SideStrategy>
  276. static inline analyse_result apply(Turn const& turn, Piece const& piece,
  277. PointInGeometryStrategy const& point_in_geometry_strategy,
  278. SideStrategy const& side_strategy)
  279. {
  280. typedef typename Piece::section_type section_type;
  281. typedef typename Turn::robust_point_type point_type;
  282. typedef typename geometry::coordinate_type<point_type>::type coordinate_type;
  283. typename PointInGeometryStrategy::state_type state;
  284. BOOST_GEOMETRY_ASSERT(! piece.sections.empty());
  285. coordinate_type const point_x = geometry::get<0>(turn.robust_point);
  286. for (std::size_t s = 0; s < piece.sections.size(); s++)
  287. {
  288. section_type const& section = piece.sections[s];
  289. // If point within horizontal range of monotonic section:
  290. if (! section.duplicate
  291. && section.begin_index < section.end_index
  292. && point_x >= geometry::get<min_corner, 0>(section.bounding_box) - 1
  293. && point_x <= geometry::get<max_corner, 0>(section.bounding_box) + 1)
  294. {
  295. for (signed_size_type i = section.begin_index + 1; i <= section.end_index; i++)
  296. {
  297. point_type const& previous = piece.robust_ring[i - 1];
  298. point_type const& current = piece.robust_ring[i];
  299. analyse_result code = check_segment<false>::apply(previous, current, turn, false, side_strategy);
  300. if (code != analyse_continue)
  301. {
  302. return code;
  303. }
  304. // Get the state (to determine it is within), we don't have
  305. // to cover the on-segment case (covered above)
  306. point_in_geometry_strategy.apply(turn.robust_point, previous, current, state);
  307. }
  308. }
  309. }
  310. int const code = point_in_geometry_strategy.result(state);
  311. if (code == 1)
  312. {
  313. return analyse_within;
  314. }
  315. else if (code == -1)
  316. {
  317. return analyse_disjoint;
  318. }
  319. // Should normally not occur - on-segment is covered
  320. return analyse_unknown;
  321. }
  322. };
  323. template <bool UseSideOfIntersection>
  324. struct check_helper_segment {};
  325. template <>
  326. struct check_helper_segment<true>
  327. {
  328. template <typename Point, typename Turn, typename SideStrategy>
  329. static inline analyse_result apply(Point const& s1,
  330. Point const& s2, Turn const& turn,
  331. bool is_original,
  332. analyse_result result_for_original,
  333. Point const& offsetted,
  334. SideStrategy const& )
  335. {
  336. boost::ignore_unused(offsetted, is_original);
  337. typedef geometry::model::referring_segment<Point const> segment_type;
  338. segment_type const p(turn.rob_pi, turn.rob_pj);
  339. segment_type const q(turn.rob_qi, turn.rob_qj);
  340. segment_type const r(s1, s2);
  341. int const side = strategy::side::side_of_intersection::apply(p, q, r,
  342. turn.robust_point);
  343. if (side == 1)
  344. {
  345. // left of segment
  346. return analyse_disjoint;
  347. }
  348. else if (side == 0)
  349. {
  350. // If is collinear, either on segment or before/after
  351. typedef geometry::model::box<Point> box_type;
  352. box_type box;
  353. geometry::assign_inverse(box);
  354. geometry::expand(box, s1);
  355. geometry::expand(box, s2);
  356. if (geometry::covered_by(turn.robust_point, box))
  357. {
  358. return result_for_original;
  359. }
  360. // It is collinear but not on the segment. Because these
  361. // segments are convex, it is outside
  362. // Unless the offsetted ring is collinear or concave w.r.t.
  363. // helper-segment but that scenario is not yet supported
  364. return analyse_disjoint;
  365. }
  366. // right of segment
  367. return analyse_continue;
  368. }
  369. };
  370. template <>
  371. struct check_helper_segment<false>
  372. {
  373. template <typename Point, typename Turn, typename SideStrategy>
  374. static inline analyse_result apply(Point const& s1,
  375. Point const& s2, Turn const& turn,
  376. bool is_original,
  377. analyse_result result_for_original,
  378. Point const& offsetted,
  379. SideStrategy const& side_strategy)
  380. {
  381. switch(side_strategy.apply(s1, s2, turn.robust_point))
  382. {
  383. case 1 :
  384. return analyse_disjoint; // left of segment
  385. case 0 :
  386. {
  387. // If is collinear, either on segment or before/after
  388. typedef geometry::model::box<Point> box_type;
  389. box_type box;
  390. geometry::assign_inverse(box);
  391. geometry::expand(box, s1);
  392. geometry::expand(box, s2);
  393. if (geometry::covered_by(turn.robust_point, box))
  394. {
  395. // It is on the segment
  396. if (! is_original
  397. && geometry::comparable_distance(turn.robust_point, offsetted) <= 1)
  398. {
  399. // It is within, and close to the offsetted-boundary,
  400. // take any rounding-issues into account
  401. return analyse_near_offsetted;
  402. }
  403. // Points on helper-segments are considered as within
  404. // Points on original boundary are processed differently
  405. return result_for_original;
  406. }
  407. // It is collinear but not on the segment. Because these
  408. // segments are convex, it is outside
  409. // Unless the offsetted ring is collinear or concave w.r.t.
  410. // helper-segment but that scenario is not yet supported
  411. return analyse_disjoint;
  412. }
  413. break;
  414. }
  415. // right of segment
  416. return analyse_continue;
  417. }
  418. };
  419. template <bool UseSideOfIntersection>
  420. class analyse_turn_wrt_piece
  421. {
  422. template
  423. <
  424. typename Turn,
  425. typename Piece,
  426. typename DistanceStrategy,
  427. typename SideStrategy
  428. >
  429. static inline analyse_result
  430. check_helper_segments(Turn const& turn, Piece const& piece,
  431. DistanceStrategy const& distance_strategy,
  432. SideStrategy const& side_strategy)
  433. {
  434. typedef typename Turn::robust_point_type point_type;
  435. geometry::equal_to<point_type> comparator;
  436. point_type points[4];
  437. signed_size_type helper_count = static_cast<signed_size_type>(piece.robust_ring.size())
  438. - piece.offsetted_count;
  439. if (helper_count == 4)
  440. {
  441. for (int i = 0; i < 4; i++)
  442. {
  443. points[i] = piece.robust_ring[piece.offsetted_count + i];
  444. }
  445. // 3--offsetted outline--0
  446. // | |
  447. // left | | right
  448. // | |
  449. // 2===>==original===>===1
  450. }
  451. else if (helper_count == 3)
  452. {
  453. // Triangular piece, assign points but assign second twice
  454. for (int i = 0; i < 4; i++)
  455. {
  456. int index = i < 2 ? i : i - 1;
  457. points[i] = piece.robust_ring[piece.offsetted_count + index];
  458. }
  459. }
  460. else
  461. {
  462. // Some pieces (e.g. around points) do not have helper segments.
  463. // Others should have 3 (join) or 4 (side)
  464. return analyse_continue;
  465. }
  466. // If a turn is located on the original, it is considered as within,
  467. // unless it is at a flat start or end, or the buffer (at that point)
  468. // is one-sided (zero-distance)
  469. bool const one_sided = has_zero_distance_at(turn.point, distance_strategy);
  470. analyse_result const result_for_original
  471. = one_sided || piece.is_flat_end || piece.is_flat_start
  472. ? analyse_on_original_boundary
  473. : analyse_within;
  474. // First check point-equality
  475. point_type const& point = turn.robust_point;
  476. if (comparator(point, points[0]) || comparator(point, points[3]))
  477. {
  478. return analyse_on_offsetted;
  479. }
  480. if (comparator(point, points[1]))
  481. {
  482. // On original, with right corner of piece
  483. return result_for_original;
  484. }
  485. if (comparator(point, points[2]))
  486. {
  487. // On original, with left corner of piece
  488. return result_for_original;
  489. }
  490. // Right side of the piece (never an original)
  491. analyse_result result
  492. = check_helper_segment<UseSideOfIntersection>::apply(points[0], points[1], turn,
  493. false, analyse_within, points[0], side_strategy);
  494. if (result != analyse_continue)
  495. {
  496. return result;
  497. }
  498. // Left side of the piece (never an original)
  499. result = check_helper_segment<UseSideOfIntersection>::apply(points[2], points[3], turn,
  500. false, analyse_within, points[3], side_strategy);
  501. if (result != analyse_continue)
  502. {
  503. return result;
  504. }
  505. // Side of the piece at side of original geometry
  506. // (here flat start/end will result in within)
  507. if (! comparator(points[1], points[2]))
  508. {
  509. result = check_helper_segment<UseSideOfIntersection>::apply(points[1],
  510. points[2], turn, true,
  511. one_sided ? analyse_on_original_boundary : analyse_within,
  512. point, side_strategy);
  513. if (result != analyse_continue)
  514. {
  515. return result;
  516. }
  517. }
  518. // We are within the \/ or |_| shaped piece, where the top is the
  519. // offsetted ring.
  520. if (! geometry::covered_by(point, piece.robust_offsetted_envelope))
  521. {
  522. // Not in offsetted-area. This makes a cheap check possible
  523. switch(side_strategy.apply(points[3], points[0], point))
  524. {
  525. case 1 : return analyse_disjoint;
  526. case -1 : return analyse_within;
  527. case 0 : return analyse_disjoint;
  528. }
  529. }
  530. return analyse_continue;
  531. }
  532. template <typename Turn, typename Piece, typename Compare, typename SideStrategy>
  533. static inline analyse_result check_monotonic(Turn const& turn, Piece const& piece, Compare const& compare, SideStrategy const& side_strategy)
  534. {
  535. typedef typename Piece::piece_robust_ring_type ring_type;
  536. typedef typename ring_type::const_iterator it_type;
  537. it_type end = piece.robust_ring.begin() + piece.offsetted_count;
  538. it_type it = std::lower_bound(piece.robust_ring.begin(),
  539. end,
  540. turn.robust_point,
  541. compare);
  542. if (it != end
  543. && it != piece.robust_ring.begin())
  544. {
  545. // iterator points to point larger than point
  546. // w.r.t. specified direction, and prev points to a point smaller
  547. // We now know if it is inside/outside
  548. it_type prev = it - 1;
  549. return check_segment<UseSideOfIntersection>::apply(*prev, *it, turn, true, side_strategy);
  550. }
  551. return analyse_continue;
  552. }
  553. public :
  554. template
  555. <
  556. typename Turn,
  557. typename Piece,
  558. typename DistanceStrategy,
  559. typename SideStrategy
  560. >
  561. static inline analyse_result apply(Turn const& turn, Piece const& piece,
  562. DistanceStrategy const& distance_strategy,
  563. SideStrategy const& side_strategy)
  564. {
  565. typedef typename Turn::robust_point_type point_type;
  566. analyse_result code = check_helper_segments(turn, piece, distance_strategy, side_strategy);
  567. if (code != analyse_continue)
  568. {
  569. return code;
  570. }
  571. geometry::equal_to<point_type> comparator;
  572. if (piece.offsetted_count > 8)
  573. {
  574. // If the offset contains some points and is monotonic, we try
  575. // to avoid walking all points linearly.
  576. // We try it only once.
  577. if (piece.is_monotonic_increasing[0])
  578. {
  579. code = check_monotonic(turn, piece, geometry::less<point_type, 0>(), side_strategy);
  580. if (code != analyse_continue) return code;
  581. }
  582. else if (piece.is_monotonic_increasing[1])
  583. {
  584. code = check_monotonic(turn, piece, geometry::less<point_type, 1>(), side_strategy);
  585. if (code != analyse_continue) return code;
  586. }
  587. else if (piece.is_monotonic_decreasing[0])
  588. {
  589. code = check_monotonic(turn, piece, geometry::greater<point_type, 0>(), side_strategy);
  590. if (code != analyse_continue) return code;
  591. }
  592. else if (piece.is_monotonic_decreasing[1])
  593. {
  594. code = check_monotonic(turn, piece, geometry::greater<point_type, 1>(), side_strategy);
  595. if (code != analyse_continue) return code;
  596. }
  597. }
  598. // It is small or not monotonic, walk linearly through offset
  599. // TODO: this will be combined with winding strategy
  600. for (signed_size_type i = 1; i < piece.offsetted_count; i++)
  601. {
  602. point_type const& previous = piece.robust_ring[i - 1];
  603. point_type const& current = piece.robust_ring[i];
  604. // The robust ring can contain duplicates
  605. // (on which any side or side-value would return 0)
  606. if (! comparator(previous, current))
  607. {
  608. code = check_segment<UseSideOfIntersection>::apply(previous, current, turn, false, side_strategy);
  609. if (code != analyse_continue)
  610. {
  611. return code;
  612. }
  613. }
  614. }
  615. return analyse_unknown;
  616. }
  617. };
  618. // Helper Structure, of which the apply method returns a side value in {-1, 0, 1}
  619. template <bool UseSideOfIntersection>
  620. struct turn_in_piece {};
  621. template <>
  622. struct turn_in_piece<true>
  623. {
  624. private :
  625. template <typename Turn, typename Piece>
  626. static inline int in_convex_piece(Turn const& turn, Piece const& piece)
  627. {
  628. typedef typename Turn::robust_point_type point_type;
  629. typedef typename Piece::piece_robust_ring_type ring_type;
  630. typedef geometry::model::referring_segment<point_type const> segment;
  631. segment const p(turn.rob_pi, turn.rob_pj);
  632. segment const q(turn.rob_qi, turn.rob_qj);
  633. typedef typename boost::range_iterator<ring_type const>::type iterator_type;
  634. iterator_type it = boost::begin(piece.robust_ring);
  635. iterator_type end = boost::end(piece.robust_ring);
  636. // A robust ring is always closed, and always clockwise
  637. for (iterator_type previous = it++; it != end; ++previous, ++it)
  638. {
  639. geometry::equal_to<point_type> comparator;
  640. if (comparator(*previous, *it))
  641. {
  642. // Points are the same
  643. continue;
  644. }
  645. segment r(*previous, *it);
  646. int const side = strategy::side::side_of_intersection::apply(p, q, r,
  647. turn.robust_point);
  648. if (side == 1)
  649. {
  650. // IP is left of segment, so it is outside
  651. return -1; // outside
  652. }
  653. else if (side == 0)
  654. {
  655. // IP is collinear with segment. TODO: we should analyze this further
  656. // For now we use the fallback point
  657. if (in_box(*previous, *it, turn.robust_point))
  658. {
  659. return 0;
  660. }
  661. else
  662. {
  663. return -1; // outside
  664. }
  665. }
  666. }
  667. return 1; // inside
  668. }
  669. public :
  670. template <typename Turn, typename Piece, typename Strategy>
  671. static inline int apply(Turn const& turn, Piece const& piece,
  672. Strategy const& strategy)
  673. {
  674. if (piece.is_convex)
  675. {
  676. return in_convex_piece(turn, piece);
  677. }
  678. else
  679. {
  680. // side-of-intersection only supported for convex pieces
  681. // Call point_in_geometry, a performance-bottleneck
  682. // TODO: might be replaced by extending analysing piece
  683. return detail::within::point_in_geometry(turn.robust_point,
  684. piece.robust_ring, strategy);
  685. }
  686. }
  687. };
  688. template <>
  689. struct turn_in_piece<false>
  690. {
  691. public :
  692. template <typename Turn, typename Piece, typename Strategy>
  693. static inline int apply(Turn const& turn, Piece const& piece,
  694. Strategy const& strategy)
  695. {
  696. return detail::within::point_in_geometry(turn.robust_point,
  697. piece.robust_ring, strategy);
  698. }
  699. };
  700. template
  701. <
  702. typename CsTag,
  703. typename Turns,
  704. typename Pieces,
  705. typename DistanceStrategy,
  706. typename PointInGeometryStrategy,
  707. typename SideStrategy
  708. >
  709. class turn_in_piece_visitor
  710. {
  711. Turns& m_turns; // because partition is currently operating on const input only
  712. Pieces const& m_pieces; // to check for piece-type
  713. DistanceStrategy const& m_distance_strategy; // to check if point is on original
  714. PointInGeometryStrategy const& m_point_in_geometry_strategy;
  715. SideStrategy const& m_side_strategy;
  716. template <typename Operation, typename Piece>
  717. inline bool skip(Operation const& op, Piece const& piece) const
  718. {
  719. if (op.piece_index == piece.index)
  720. {
  721. return true;
  722. }
  723. Piece const& pc = m_pieces[op.piece_index];
  724. if (pc.left_index == piece.index || pc.right_index == piece.index)
  725. {
  726. if (pc.type == strategy::buffer::buffered_flat_end)
  727. {
  728. // If it is a flat end, don't compare against its neighbor:
  729. // it will always be located on one of the helper segments
  730. return true;
  731. }
  732. if (pc.type == strategy::buffer::buffered_concave)
  733. {
  734. // If it is concave, the same applies: the IP will be
  735. // located on one of the helper segments
  736. return true;
  737. }
  738. }
  739. return false;
  740. }
  741. public:
  742. inline turn_in_piece_visitor(Turns& turns, Pieces const& pieces,
  743. DistanceStrategy const& distance_strategy,
  744. PointInGeometryStrategy const& strategy,
  745. SideStrategy const& side_strategy)
  746. : m_turns(turns)
  747. , m_pieces(pieces)
  748. , m_distance_strategy(distance_strategy)
  749. , m_point_in_geometry_strategy(strategy)
  750. , m_side_strategy(side_strategy)
  751. {}
  752. template <typename Turn, typename Piece>
  753. inline bool apply(Turn const& turn, Piece const& piece, bool first = true)
  754. {
  755. boost::ignore_unused(first);
  756. if (turn.count_within > 0)
  757. {
  758. // Already inside - no need to check again
  759. return true;
  760. }
  761. if (piece.type == strategy::buffer::buffered_flat_end
  762. || piece.type == strategy::buffer::buffered_concave)
  763. {
  764. // Turns cannot be located within flat-end or concave pieces
  765. return true;
  766. }
  767. if (! geometry::covered_by(turn.robust_point, piece.robust_envelope))
  768. {
  769. // Easy check: if the turn is not in the envelope, we can safely return
  770. return true;
  771. }
  772. if (skip(turn.operations[0], piece) || skip(turn.operations[1], piece))
  773. {
  774. return true;
  775. }
  776. // TODO: mutable_piece to make some on-demand preparations in analyse
  777. Turn& mutable_turn = m_turns[turn.turn_index];
  778. if (piece.type == geometry::strategy::buffer::buffered_point)
  779. {
  780. // Optimization for buffer around points: if distance from center
  781. // is not between min/max radius, the result is clear
  782. typedef typename default_comparable_distance_result
  783. <
  784. typename Turn::robust_point_type
  785. >::type distance_type;
  786. distance_type const cd
  787. = geometry::comparable_distance(piece.robust_center,
  788. turn.robust_point);
  789. if (cd < piece.robust_min_comparable_radius)
  790. {
  791. mutable_turn.count_within++;
  792. return true;
  793. }
  794. if (cd > piece.robust_max_comparable_radius)
  795. {
  796. return true;
  797. }
  798. }
  799. static const bool use_soi = use_side_of_intersection<CsTag>::value;
  800. boost::ignore_unused(use_soi);
  801. analyse_result const analyse_code =
  802. piece.type == geometry::strategy::buffer::buffered_point
  803. ? analyse_turn_wrt_point_piece<use_soi>::apply(turn, piece, m_point_in_geometry_strategy, m_side_strategy)
  804. : analyse_turn_wrt_piece<use_soi>::apply(turn, piece, m_distance_strategy, m_side_strategy);
  805. switch(analyse_code)
  806. {
  807. case analyse_disjoint :
  808. return true;
  809. case analyse_on_offsetted :
  810. mutable_turn.count_on_offsetted++; // value is not used anymore
  811. return true;
  812. case analyse_on_original_boundary :
  813. mutable_turn.count_on_original_boundary++;
  814. return true;
  815. case analyse_within :
  816. mutable_turn.count_within++;
  817. return true;
  818. case analyse_near_offsetted :
  819. mutable_turn.count_within_near_offsetted++;
  820. return true;
  821. default :
  822. break;
  823. }
  824. int const geometry_code = turn_in_piece<use_soi>::apply(turn, piece,
  825. m_point_in_geometry_strategy);
  826. if (geometry_code == 1)
  827. {
  828. mutable_turn.count_within++;
  829. }
  830. return true;
  831. }
  832. };
  833. }} // namespace detail::buffer
  834. #endif // DOXYGEN_NO_DETAIL
  835. }} // namespace boost::geometry
  836. #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_TURN_IN_PIECE_VISITOR