get_turn_info_ll.hpp 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727
  1. // Boost.Geometry (aka GGL, Generic Geometry Library)
  2. // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
  3. // Copyright (c) 2017 Adam Wulkiewicz, Lodz, Poland.
  4. // This file was modified by Oracle on 2013, 2014, 2015, 2017, 2018.
  5. // Modifications copyright (c) 2013-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_OVERLAY_GET_TURN_INFO_LL_HPP
  11. #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_LL_HPP
  12. #include <boost/throw_exception.hpp>
  13. #include <boost/geometry/core/assert.hpp>
  14. #include <boost/geometry/algorithms/detail/overlay/get_turn_info.hpp>
  15. #include <boost/geometry/algorithms/detail/overlay/get_turn_info_for_endpoint.hpp>
  16. #include <boost/geometry/util/condition.hpp>
  17. namespace boost { namespace geometry {
  18. #ifndef DOXYGEN_NO_DETAIL
  19. namespace detail { namespace overlay {
  20. template<typename AssignPolicy>
  21. struct get_turn_info_linear_linear
  22. {
  23. static const bool handle_spikes = true;
  24. template
  25. <
  26. typename UniqueSubRange1,
  27. typename UniqueSubRange2,
  28. typename TurnInfo,
  29. typename UmbrellaStrategy,
  30. typename RobustPolicy,
  31. typename OutputIterator
  32. >
  33. static inline OutputIterator apply(
  34. UniqueSubRange1 const& range_p,
  35. UniqueSubRange2 const& range_q,
  36. TurnInfo const& tp_model,
  37. UmbrellaStrategy const& umbrella_strategy,
  38. RobustPolicy const& robust_policy,
  39. OutputIterator out)
  40. {
  41. typedef intersection_info
  42. <
  43. UniqueSubRange1, UniqueSubRange2,
  44. typename TurnInfo::point_type,
  45. UmbrellaStrategy,
  46. RobustPolicy
  47. > inters_info;
  48. inters_info inters(range_p, range_q, umbrella_strategy, robust_policy);
  49. char const method = inters.d_info().how;
  50. // Copy, to copy possibly extended fields
  51. TurnInfo tp = tp_model;
  52. // Select method and apply
  53. switch(method)
  54. {
  55. case 'a' : // collinear, "at"
  56. case 'f' : // collinear, "from"
  57. case 's' : // starts from the middle
  58. get_turn_info_for_endpoint<true, true>
  59. ::apply(range_p, range_q,
  60. tp_model, inters, method_none, out,
  61. umbrella_strategy.get_point_in_point_strategy());
  62. break;
  63. case 'd' : // disjoint: never do anything
  64. break;
  65. case 'm' :
  66. {
  67. if ( get_turn_info_for_endpoint<false, true>
  68. ::apply(range_p, range_q,
  69. tp_model, inters, method_touch_interior, out,
  70. umbrella_strategy.get_point_in_point_strategy()) )
  71. {
  72. // do nothing
  73. }
  74. else
  75. {
  76. typedef touch_interior
  77. <
  78. TurnInfo
  79. > policy;
  80. // If Q (1) arrives (1)
  81. if ( inters.d_info().arrival[1] == 1)
  82. {
  83. policy::template apply<0>(range_p, range_q, tp,
  84. inters.i_info(), inters.d_info(),
  85. inters.sides(),
  86. umbrella_strategy);
  87. }
  88. else
  89. {
  90. // Swap p/q
  91. policy::template apply<1>(range_q, range_p, tp,
  92. inters.i_info(), inters.d_info(),
  93. inters.get_swapped_sides(),
  94. umbrella_strategy);
  95. }
  96. if ( tp.operations[0].operation == operation_blocked )
  97. {
  98. tp.operations[1].is_collinear = true;
  99. }
  100. if ( tp.operations[1].operation == operation_blocked )
  101. {
  102. tp.operations[0].is_collinear = true;
  103. }
  104. replace_method_and_operations_tm(tp.method,
  105. tp.operations[0].operation,
  106. tp.operations[1].operation);
  107. *out++ = tp;
  108. }
  109. }
  110. break;
  111. case 'i' :
  112. {
  113. crosses<TurnInfo>::apply(tp, inters.i_info(), inters.d_info());
  114. replace_operations_i(tp.operations[0].operation, tp.operations[1].operation);
  115. *out++ = tp;
  116. }
  117. break;
  118. case 't' :
  119. {
  120. // Both touch (both arrive there)
  121. if ( get_turn_info_for_endpoint<false, true>
  122. ::apply(range_p, range_q,
  123. tp_model, inters, method_touch, out,
  124. umbrella_strategy.get_point_in_point_strategy()) )
  125. {
  126. // do nothing
  127. }
  128. else
  129. {
  130. touch<TurnInfo>::apply(range_p, range_q, tp,
  131. inters.i_info(), inters.d_info(),
  132. inters.sides(),
  133. umbrella_strategy);
  134. // workarounds for touch<> not taking spikes into account starts here
  135. // those was discovered empirically
  136. // touch<> is not symmetrical!
  137. // P spikes and Q spikes may produce various operations!
  138. // TODO: this is not optimal solution - think about rewriting touch<>
  139. if ( tp.operations[0].operation == operation_blocked
  140. && tp.operations[1].operation == operation_blocked )
  141. {
  142. // two touching spikes on the same line
  143. if ( inters.is_spike_p() && inters.is_spike_q() )
  144. {
  145. tp.operations[0].operation = operation_union;
  146. tp.operations[1].operation = operation_union;
  147. }
  148. else
  149. {
  150. tp.operations[0].is_collinear = true;
  151. tp.operations[1].is_collinear = true;
  152. }
  153. }
  154. else if ( tp.operations[0].operation == operation_blocked )
  155. {
  156. // a spike on P on the same line with Q1
  157. if ( inters.is_spike_p() )
  158. {
  159. if ( inters.sides().qk_wrt_p1() == 0 )
  160. {
  161. tp.operations[0].is_collinear = true;
  162. }
  163. else
  164. {
  165. tp.operations[0].operation = operation_union;
  166. }
  167. }
  168. else
  169. {
  170. tp.operations[1].is_collinear = true;
  171. }
  172. }
  173. else if ( tp.operations[1].operation == operation_blocked )
  174. {
  175. // a spike on Q on the same line with P1
  176. if ( inters.is_spike_q() )
  177. {
  178. if ( inters.sides().pk_wrt_q1() == 0 )
  179. {
  180. tp.operations[1].is_collinear = true;
  181. }
  182. else
  183. {
  184. tp.operations[1].operation = operation_union;
  185. }
  186. }
  187. else
  188. {
  189. tp.operations[0].is_collinear = true;
  190. }
  191. }
  192. else if ( tp.operations[0].operation == operation_continue
  193. && tp.operations[1].operation == operation_continue )
  194. {
  195. // P spike on the same line with Q2 (opposite)
  196. if ( inters.sides().pk_wrt_q1() == -inters.sides().qk_wrt_q1()
  197. && inters.is_spike_p() )
  198. {
  199. tp.operations[0].operation = operation_union;
  200. tp.operations[1].operation = operation_union;
  201. }
  202. }
  203. else if ( tp.operations[0].operation == operation_none
  204. && tp.operations[1].operation == operation_none )
  205. {
  206. // spike not handled by touch<>
  207. bool const is_p = inters.is_spike_p();
  208. bool const is_q = inters.is_spike_q();
  209. if ( is_p || is_q )
  210. {
  211. tp.operations[0].operation = operation_union;
  212. tp.operations[1].operation = operation_union;
  213. if ( inters.sides().pk_wrt_q2() == 0 )
  214. {
  215. tp.operations[0].operation = operation_continue; // will be converted to i
  216. if ( is_p )
  217. {
  218. tp.operations[0].is_collinear = true;
  219. }
  220. }
  221. if ( inters.sides().qk_wrt_p2() == 0 )
  222. {
  223. tp.operations[1].operation = operation_continue; // will be converted to i
  224. if ( is_q )
  225. {
  226. tp.operations[1].is_collinear = true;
  227. }
  228. }
  229. }
  230. }
  231. // workarounds for touch<> not taking spikes into account ends here
  232. replace_method_and_operations_tm(tp.method,
  233. tp.operations[0].operation,
  234. tp.operations[1].operation);
  235. if ( ! BOOST_GEOMETRY_CONDITION(handle_spikes)
  236. || ! append_opposite_spikes<append_touches>(tp, inters, out) )
  237. {
  238. *out++ = tp;
  239. }
  240. }
  241. }
  242. break;
  243. case 'e':
  244. {
  245. if ( get_turn_info_for_endpoint<true, true>
  246. ::apply(range_p, range_q,
  247. tp_model, inters, method_equal, out,
  248. umbrella_strategy.get_point_in_point_strategy()) )
  249. {
  250. // do nothing
  251. }
  252. else
  253. {
  254. tp.operations[0].is_collinear = true;
  255. tp.operations[1].is_collinear = true;
  256. if ( ! inters.d_info().opposite )
  257. {
  258. // Both equal
  259. // or collinear-and-ending at intersection point
  260. equal<TurnInfo>::apply(range_p, range_q, tp,
  261. inters.i_info(), inters.d_info(), inters.sides(),
  262. umbrella_strategy);
  263. operation_type spike_op
  264. = ( tp.operations[0].operation != operation_continue
  265. || tp.operations[1].operation != operation_continue ) ?
  266. operation_union :
  267. operation_continue;
  268. // transform turn
  269. turn_transformer_ec transformer(method_touch);
  270. transformer(tp);
  271. // conditionally handle spikes
  272. if ( ! BOOST_GEOMETRY_CONDITION(handle_spikes)
  273. || ! append_collinear_spikes(tp, inters,
  274. method_touch, spike_op,
  275. out) )
  276. {
  277. *out++ = tp; // no spikes
  278. }
  279. }
  280. else
  281. {
  282. // TODO: ignore for spikes or generate something else than opposite?
  283. equal_opposite
  284. <
  285. TurnInfo,
  286. AssignPolicy
  287. >::apply(range_p, range_q, tp, out, inters);
  288. }
  289. }
  290. }
  291. break;
  292. case 'c' :
  293. {
  294. // Collinear
  295. if ( get_turn_info_for_endpoint<true, true>
  296. ::apply(range_p, range_q,
  297. tp_model, inters, method_collinear, out,
  298. umbrella_strategy.get_point_in_point_strategy()) )
  299. {
  300. // do nothing
  301. }
  302. else
  303. {
  304. // NOTE: this is for spikes since those are set in the turn_transformer_ec
  305. tp.operations[0].is_collinear = true;
  306. tp.operations[1].is_collinear = true;
  307. if ( ! inters.d_info().opposite )
  308. {
  309. method_type method_replace = method_touch_interior;
  310. operation_type spike_op = operation_continue;
  311. if ( inters.d_info().arrival[0] == 0 )
  312. {
  313. // Collinear, but similar thus handled as equal
  314. equal<TurnInfo>::apply(range_p, range_q, tp,
  315. inters.i_info(), inters.d_info(), inters.sides(),
  316. umbrella_strategy);
  317. method_replace = method_touch;
  318. if ( tp.operations[0].operation != operation_continue
  319. || tp.operations[1].operation != operation_continue )
  320. {
  321. spike_op = operation_union;
  322. }
  323. }
  324. else
  325. {
  326. collinear<TurnInfo>::apply(range_p, range_q,
  327. tp, inters.i_info(), inters.d_info(), inters.sides());
  328. //method_replace = method_touch_interior;
  329. //spike_op = operation_continue;
  330. }
  331. // transform turn
  332. turn_transformer_ec transformer(method_replace);
  333. transformer(tp);
  334. // conditionally handle spikes
  335. if ( ! BOOST_GEOMETRY_CONDITION(handle_spikes)
  336. || ! append_collinear_spikes(tp, inters,
  337. method_replace, spike_op,
  338. out) )
  339. {
  340. // no spikes
  341. *out++ = tp;
  342. }
  343. }
  344. else
  345. {
  346. // If this always 'm' ?
  347. turn_transformer_ec transformer(method_touch_interior);
  348. // conditionally handle spikes
  349. if ( BOOST_GEOMETRY_CONDITION(handle_spikes) )
  350. {
  351. append_opposite_spikes<append_collinear_opposite>(tp, inters, out);
  352. }
  353. // TODO: ignore for spikes?
  354. // E.g. pass is_p_valid = !is_p_last && !is_pj_spike,
  355. // the same with is_q_valid
  356. collinear_opposite
  357. <
  358. TurnInfo,
  359. AssignPolicy
  360. >::apply(range_p, range_q,
  361. tp, out, inters, inters.sides(),
  362. transformer);
  363. }
  364. }
  365. }
  366. break;
  367. case '0' :
  368. {
  369. // degenerate points
  370. if ( BOOST_GEOMETRY_CONDITION(AssignPolicy::include_degenerate) )
  371. {
  372. typedef typename UmbrellaStrategy::point_in_point_strategy_type
  373. equals_strategy_type;
  374. only_convert::apply(tp, inters.i_info());
  375. // if any, only one of those should be true
  376. if ( range_p.is_first_segment()
  377. && equals::equals_point_point(range_p.at(0), tp.point, equals_strategy_type()) )
  378. {
  379. tp.operations[0].position = position_front;
  380. }
  381. else if ( range_p.is_last_segment()
  382. && equals::equals_point_point(range_p.at(1), tp.point, equals_strategy_type()) )
  383. {
  384. tp.operations[0].position = position_back;
  385. }
  386. else if ( range_q.is_first_segment()
  387. && equals::equals_point_point(range_q.at(0), tp.point, equals_strategy_type()) )
  388. {
  389. tp.operations[1].position = position_front;
  390. }
  391. else if ( range_q.is_last_segment()
  392. && equals::equals_point_point(range_q.at(1), tp.point, equals_strategy_type()) )
  393. {
  394. tp.operations[1].position = position_back;
  395. }
  396. *out++ = tp;
  397. }
  398. }
  399. break;
  400. default :
  401. {
  402. #if defined(BOOST_GEOMETRY_DEBUG_ROBUSTNESS)
  403. std::cout << "TURN: Unknown method: " << method << std::endl;
  404. #endif
  405. #if ! defined(BOOST_GEOMETRY_OVERLAY_NO_THROW)
  406. BOOST_THROW_EXCEPTION(turn_info_exception(method));
  407. #endif
  408. }
  409. break;
  410. }
  411. return out;
  412. }
  413. template <typename TurnInfo,
  414. typename IntersectionInfo,
  415. typename OutIt>
  416. static inline bool append_collinear_spikes(TurnInfo & tp,
  417. IntersectionInfo const& inters_info,
  418. method_type method, operation_type spike_op,
  419. OutIt out)
  420. {
  421. // method == touch || touch_interior
  422. // both position == middle
  423. bool is_p_spike = tp.operations[0].operation == spike_op
  424. && inters_info.is_spike_p();
  425. bool is_q_spike = tp.operations[1].operation == spike_op
  426. && inters_info.is_spike_q();
  427. if ( is_p_spike && is_q_spike )
  428. {
  429. if ( tp.method == method_equal
  430. && tp.operations[0].operation == operation_continue
  431. && tp.operations[1].operation == operation_continue )
  432. {
  433. // treat both non-opposite collinear spikes as no-spikes
  434. return false;
  435. }
  436. tp.method = method;
  437. tp.operations[0].operation = operation_blocked;
  438. tp.operations[1].operation = operation_blocked;
  439. *out++ = tp;
  440. tp.operations[0].operation = operation_intersection;
  441. tp.operations[1].operation = operation_intersection;
  442. *out++ = tp;
  443. return true;
  444. }
  445. else if ( is_p_spike )
  446. {
  447. tp.method = method;
  448. tp.operations[0].operation = operation_blocked;
  449. tp.operations[1].operation = operation_union;
  450. *out++ = tp;
  451. tp.operations[0].operation = operation_intersection;
  452. //tp.operations[1].operation = operation_union;
  453. *out++ = tp;
  454. return true;
  455. }
  456. else if ( is_q_spike )
  457. {
  458. tp.method = method;
  459. tp.operations[0].operation = operation_union;
  460. tp.operations[1].operation = operation_blocked;
  461. *out++ = tp;
  462. //tp.operations[0].operation = operation_union;
  463. tp.operations[1].operation = operation_intersection;
  464. *out++ = tp;
  465. return true;
  466. }
  467. return false;
  468. }
  469. enum append_version { append_touches, append_collinear_opposite };
  470. template <append_version Version,
  471. typename TurnInfo,
  472. typename IntersectionInfo,
  473. typename OutIt>
  474. static inline bool append_opposite_spikes(TurnInfo & tp,
  475. IntersectionInfo const& inters,
  476. OutIt out)
  477. {
  478. static const bool is_version_touches = (Version == append_touches);
  479. bool is_p_spike = ( is_version_touches ?
  480. ( tp.operations[0].operation == operation_continue
  481. || tp.operations[0].operation == operation_intersection ) :
  482. true )
  483. && inters.is_spike_p();
  484. bool is_q_spike = ( is_version_touches ?
  485. ( tp.operations[1].operation == operation_continue
  486. || tp.operations[1].operation == operation_intersection ) :
  487. true )
  488. && inters.is_spike_q();
  489. bool res = false;
  490. if ( is_p_spike
  491. && ( BOOST_GEOMETRY_CONDITION(is_version_touches)
  492. || inters.d_info().arrival[0] == 1 ) )
  493. {
  494. if ( BOOST_GEOMETRY_CONDITION(is_version_touches) )
  495. {
  496. tp.operations[0].is_collinear = true;
  497. tp.operations[1].is_collinear = false;
  498. tp.method = method_touch;
  499. }
  500. else // Version == append_collinear_opposite
  501. {
  502. tp.operations[0].is_collinear = true;
  503. tp.operations[1].is_collinear = false;
  504. BOOST_GEOMETRY_ASSERT(inters.i_info().count > 1);
  505. base_turn_handler::assign_point(tp, method_touch_interior,
  506. inters.i_info(), 1);
  507. }
  508. tp.operations[0].operation = operation_blocked;
  509. tp.operations[1].operation = operation_intersection;
  510. *out++ = tp;
  511. tp.operations[0].operation = operation_intersection;
  512. //tp.operations[1].operation = operation_intersection;
  513. *out++ = tp;
  514. res = true;
  515. }
  516. if ( is_q_spike
  517. && ( BOOST_GEOMETRY_CONDITION(is_version_touches)
  518. || inters.d_info().arrival[1] == 1 ) )
  519. {
  520. if ( BOOST_GEOMETRY_CONDITION(is_version_touches) )
  521. {
  522. tp.operations[0].is_collinear = false;
  523. tp.operations[1].is_collinear = true;
  524. tp.method = method_touch;
  525. }
  526. else // Version == append_collinear_opposite
  527. {
  528. tp.operations[0].is_collinear = false;
  529. tp.operations[1].is_collinear = true;
  530. BOOST_GEOMETRY_ASSERT(inters.i_info().count > 0);
  531. base_turn_handler::assign_point(tp, method_touch_interior, inters.i_info(), 0);
  532. }
  533. tp.operations[0].operation = operation_intersection;
  534. tp.operations[1].operation = operation_blocked;
  535. *out++ = tp;
  536. //tp.operations[0].operation = operation_intersection;
  537. tp.operations[1].operation = operation_intersection;
  538. *out++ = tp;
  539. res = true;
  540. }
  541. return res;
  542. }
  543. static inline void replace_method_and_operations_tm(method_type & method,
  544. operation_type & op0,
  545. operation_type & op1)
  546. {
  547. if ( op0 == operation_blocked && op1 == operation_blocked )
  548. {
  549. // NOTE: probably only if methods are WRT IPs, not segments!
  550. method = (method == method_touch ? method_equal : method_collinear);
  551. op0 = operation_continue;
  552. op1 = operation_continue;
  553. }
  554. else
  555. {
  556. if ( op0 == operation_continue || op0 == operation_blocked )
  557. {
  558. op0 = operation_intersection;
  559. }
  560. else if ( op0 == operation_intersection )
  561. {
  562. op0 = operation_union;
  563. }
  564. if ( op1 == operation_continue || op1 == operation_blocked )
  565. {
  566. op1 = operation_intersection;
  567. }
  568. else if ( op1 == operation_intersection )
  569. {
  570. op1 = operation_union;
  571. }
  572. // spikes in 'm'
  573. if ( method == method_error )
  574. {
  575. method = method_touch_interior;
  576. op0 = operation_union;
  577. op1 = operation_union;
  578. }
  579. }
  580. }
  581. class turn_transformer_ec
  582. {
  583. public:
  584. explicit turn_transformer_ec(method_type method_t_or_m)
  585. : m_method(method_t_or_m)
  586. {}
  587. template <typename Turn>
  588. void operator()(Turn & turn) const
  589. {
  590. operation_type & op0 = turn.operations[0].operation;
  591. operation_type & op1 = turn.operations[1].operation;
  592. BOOST_GEOMETRY_ASSERT(op0 != operation_blocked || op1 != operation_blocked );
  593. if ( op0 == operation_blocked )
  594. {
  595. op0 = operation_intersection;
  596. }
  597. else if ( op0 == operation_intersection )
  598. {
  599. op0 = operation_union;
  600. }
  601. if ( op1 == operation_blocked )
  602. {
  603. op1 = operation_intersection;
  604. }
  605. else if ( op1 == operation_intersection )
  606. {
  607. op1 = operation_union;
  608. }
  609. if ( op0 == operation_intersection || op0 == operation_union
  610. || op1 == operation_intersection || op1 == operation_union )
  611. {
  612. turn.method = m_method;
  613. }
  614. // TODO: is this correct?
  615. // it's equivalent to comparing to operation_blocked at the beginning of the function
  616. turn.operations[0].is_collinear = op0 != operation_intersection;
  617. turn.operations[1].is_collinear = op1 != operation_intersection;
  618. }
  619. private:
  620. method_type m_method;
  621. };
  622. static inline void replace_operations_i(operation_type & op0, operation_type & op1)
  623. {
  624. if ( op0 == operation_intersection )
  625. {
  626. op0 = operation_union;
  627. }
  628. if ( op1 == operation_intersection )
  629. {
  630. op1 = operation_union;
  631. }
  632. }
  633. };
  634. }} // namespace detail::overlay
  635. #endif // DOXYGEN_NO_DETAIL
  636. }} // namespace boost::geometry
  637. #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_LL_HPP