get_turn_info_la.hpp 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881
  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_LA_HPP
  11. #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_LA_HPP
  12. #include <boost/throw_exception.hpp>
  13. #include <boost/geometry/core/assert.hpp>
  14. #include <boost/geometry/util/condition.hpp>
  15. #include <boost/geometry/algorithms/detail/overlay/get_turn_info.hpp>
  16. #include <boost/geometry/algorithms/detail/overlay/get_turn_info_for_endpoint.hpp>
  17. // TEMP, for spikes detector
  18. //#include <boost/geometry/algorithms/detail/overlay/get_turn_info_ll.hpp>
  19. namespace boost { namespace geometry {
  20. #ifndef DOXYGEN_NO_DETAIL
  21. namespace detail { namespace overlay {
  22. template<typename AssignPolicy>
  23. struct get_turn_info_linear_areal
  24. {
  25. // Currently only Linear spikes are handled
  26. // Areal spikes are ignored
  27. static const bool handle_spikes = true;
  28. template
  29. <
  30. typename UniqueSubRange1,
  31. typename UniqueSubRange2,
  32. typename TurnInfo,
  33. typename UmbrellaStrategy,
  34. typename RobustPolicy,
  35. typename OutputIterator
  36. >
  37. static inline OutputIterator apply(
  38. UniqueSubRange1 const& range_p,
  39. UniqueSubRange2 const& range_q,
  40. TurnInfo const& tp_model,
  41. UmbrellaStrategy const& umbrella_strategy,
  42. RobustPolicy const& robust_policy,
  43. OutputIterator out)
  44. {
  45. typedef intersection_info
  46. <
  47. UniqueSubRange1, UniqueSubRange2,
  48. typename TurnInfo::point_type,
  49. UmbrellaStrategy,
  50. RobustPolicy
  51. > inters_info;
  52. inters_info inters(range_p, range_q, umbrella_strategy, robust_policy);
  53. char const method = inters.d_info().how;
  54. // Copy, to copy possibly extended fields
  55. TurnInfo tp = tp_model;
  56. // Select method and apply
  57. switch(method)
  58. {
  59. case 'a' : // collinear, "at"
  60. case 'f' : // collinear, "from"
  61. case 's' : // starts from the middle
  62. get_turn_info_for_endpoint<true, true>(range_p, range_q,
  63. tp_model, inters, method_none, out,
  64. umbrella_strategy.get_point_in_point_strategy());
  65. break;
  66. case 'd' : // disjoint: never do anything
  67. break;
  68. case 'm' :
  69. {
  70. if ( get_turn_info_for_endpoint<false, true>(range_p, range_q,
  71. tp_model, inters, method_touch_interior, out,
  72. umbrella_strategy.get_point_in_point_strategy()) )
  73. {
  74. // do nothing
  75. }
  76. else
  77. {
  78. typedef touch_interior<TurnInfo> handler;
  79. // If Q (1) arrives (1)
  80. if ( inters.d_info().arrival[1] == 1 )
  81. {
  82. handler::template apply<0>(range_p, range_q, tp,
  83. inters.i_info(), inters.d_info(),
  84. inters.sides(), umbrella_strategy);
  85. }
  86. else
  87. {
  88. // Swap p/q
  89. handler::template apply<1>(range_q, range_p,
  90. tp, inters.i_info(), inters.d_info(),
  91. inters.get_swapped_sides(), umbrella_strategy);
  92. }
  93. if ( tp.operations[1].operation == operation_blocked )
  94. {
  95. tp.operations[0].is_collinear = true;
  96. }
  97. replace_method_and_operations_tm(tp.method,
  98. tp.operations[0].operation,
  99. tp.operations[1].operation);
  100. // this function assumes that 'u' must be set for a spike
  101. calculate_spike_operation(tp.operations[0].operation,
  102. inters,
  103. umbrella_strategy.get_point_in_point_strategy());
  104. *out++ = tp;
  105. }
  106. }
  107. break;
  108. case 'i' :
  109. {
  110. crosses<TurnInfo>::apply(tp, inters.i_info(), inters.d_info());
  111. replace_operations_i(tp.operations[0].operation, tp.operations[1].operation);
  112. *out++ = tp;
  113. }
  114. break;
  115. case 't' :
  116. {
  117. // Both touch (both arrive there)
  118. if ( get_turn_info_for_endpoint<false, true>(range_p, range_q,
  119. tp_model, inters, method_touch, out,
  120. umbrella_strategy.get_point_in_point_strategy()) )
  121. {
  122. // do nothing
  123. }
  124. else
  125. {
  126. touch<TurnInfo>::apply(range_p, range_q, tp,
  127. inters.i_info(), inters.d_info(), inters.sides(),
  128. umbrella_strategy);
  129. if ( tp.operations[1].operation == operation_blocked )
  130. {
  131. tp.operations[0].is_collinear = true;
  132. }
  133. // workarounds for touch<> not taking spikes into account starts here
  134. // those was discovered empirically
  135. // touch<> is not symmetrical!
  136. // P spikes and Q spikes may produce various operations!
  137. // Only P spikes are valid for L/A
  138. // TODO: this is not optimal solution - think about rewriting touch<>
  139. if ( tp.operations[0].operation == operation_blocked )
  140. {
  141. // a spike on P on the same line with Q1
  142. if ( inters.is_spike_p() )
  143. {
  144. if ( inters.sides().qk_wrt_p1() == 0 )
  145. {
  146. tp.operations[0].is_collinear = true;
  147. }
  148. else
  149. {
  150. tp.operations[0].operation = operation_union;
  151. }
  152. }
  153. }
  154. else if ( tp.operations[0].operation == operation_continue
  155. && tp.operations[1].operation == operation_continue )
  156. {
  157. // P spike on the same line with Q2 (opposite)
  158. if ( inters.sides().pk_wrt_q1() == -inters.sides().qk_wrt_q1()
  159. && inters.is_spike_p() )
  160. {
  161. tp.operations[0].operation = operation_union;
  162. tp.operations[1].operation = operation_union;
  163. }
  164. }
  165. else if ( tp.operations[0].operation == operation_none
  166. && tp.operations[1].operation == operation_none )
  167. {
  168. // spike not handled by touch<>
  169. if ( inters.is_spike_p() )
  170. {
  171. tp.operations[0].operation = operation_intersection;
  172. tp.operations[1].operation = operation_union;
  173. if ( inters.sides().pk_wrt_q2() == 0 )
  174. {
  175. tp.operations[0].operation = operation_continue; // will be converted to i
  176. tp.operations[0].is_collinear = true;
  177. }
  178. }
  179. }
  180. // workarounds for touch<> not taking spikes into account ends here
  181. replace_method_and_operations_tm(tp.method,
  182. tp.operations[0].operation,
  183. tp.operations[1].operation);
  184. bool ignore_spike
  185. = calculate_spike_operation(tp.operations[0].operation,
  186. inters,
  187. umbrella_strategy.get_point_in_point_strategy());
  188. if ( ! BOOST_GEOMETRY_CONDITION(handle_spikes)
  189. || ignore_spike
  190. || ! append_opposite_spikes<append_touches>( // for 'i' or 'c' i???
  191. tp, inters, out) )
  192. {
  193. *out++ = tp;
  194. }
  195. }
  196. }
  197. break;
  198. case 'e':
  199. {
  200. if ( get_turn_info_for_endpoint<true, true>(range_p, range_q,
  201. tp_model, inters, method_equal, out,
  202. umbrella_strategy.get_point_in_point_strategy()) )
  203. {
  204. // do nothing
  205. }
  206. else
  207. {
  208. tp.operations[0].is_collinear = true;
  209. if ( ! inters.d_info().opposite )
  210. {
  211. // Both equal
  212. // or collinear-and-ending at intersection point
  213. equal<TurnInfo>::apply(range_p, range_q, tp,
  214. inters.i_info(), inters.d_info(), inters.sides(),
  215. umbrella_strategy);
  216. turn_transformer_ec<false> transformer(method_touch);
  217. transformer(tp);
  218. // conditionally handle spikes
  219. if ( ! BOOST_GEOMETRY_CONDITION(handle_spikes)
  220. || ! append_collinear_spikes(tp, inters,
  221. method_touch, append_equal, out) )
  222. {
  223. *out++ = tp; // no spikes
  224. }
  225. }
  226. else
  227. {
  228. equal_opposite
  229. <
  230. TurnInfo,
  231. AssignPolicy
  232. >::apply(range_p, range_q,
  233. tp, out, inters);
  234. }
  235. }
  236. }
  237. break;
  238. case 'c' :
  239. {
  240. // Collinear
  241. if ( get_turn_info_for_endpoint<true, true>(
  242. range_p, range_q,
  243. tp_model, inters, method_collinear, out,
  244. umbrella_strategy.get_point_in_point_strategy()) )
  245. {
  246. // do nothing
  247. }
  248. else
  249. {
  250. tp.operations[0].is_collinear = true;
  251. if ( ! inters.d_info().opposite )
  252. {
  253. method_type method_replace = method_touch_interior;
  254. append_version_c version = append_collinear;
  255. if ( inters.d_info().arrival[0] == 0 )
  256. {
  257. // Collinear, but similar thus handled as equal
  258. equal<TurnInfo>::apply(range_p, range_q, tp,
  259. inters.i_info(), inters.d_info(), inters.sides(),
  260. umbrella_strategy);
  261. method_replace = method_touch;
  262. version = append_equal;
  263. }
  264. else
  265. {
  266. collinear<TurnInfo>::apply(range_p, range_q, tp,
  267. inters.i_info(), inters.d_info(), inters.sides());
  268. //method_replace = method_touch_interior;
  269. //version = append_collinear;
  270. }
  271. turn_transformer_ec<false> transformer(method_replace);
  272. transformer(tp);
  273. // conditionally handle spikes
  274. if ( ! BOOST_GEOMETRY_CONDITION(handle_spikes)
  275. || ! append_collinear_spikes(tp, inters,
  276. method_replace, version, out) )
  277. {
  278. // no spikes
  279. *out++ = tp;
  280. }
  281. }
  282. else
  283. {
  284. // Is this always 'm' ?
  285. turn_transformer_ec<false> transformer(method_touch_interior);
  286. // conditionally handle spikes
  287. if ( BOOST_GEOMETRY_CONDITION(handle_spikes) )
  288. {
  289. append_opposite_spikes<append_collinear_opposite>(
  290. tp, inters, out);
  291. }
  292. // TODO: ignore for spikes?
  293. // E.g. pass is_p_valid = !is_p_last && !is_pj_spike,
  294. // the same with is_q_valid
  295. collinear_opposite
  296. <
  297. TurnInfo,
  298. AssignPolicy
  299. >::apply(range_p, range_q,
  300. tp, out, inters,
  301. inters.sides(), transformer);
  302. }
  303. }
  304. }
  305. break;
  306. case '0' :
  307. {
  308. // degenerate points
  309. if ( BOOST_GEOMETRY_CONDITION(AssignPolicy::include_degenerate) )
  310. {
  311. only_convert::apply(tp, inters.i_info());
  312. if ( range_p.is_first_segment()
  313. && equals::equals_point_point(range_p.at(0), tp.point,
  314. umbrella_strategy.get_point_in_point_strategy()) )
  315. {
  316. tp.operations[0].position = position_front;
  317. }
  318. else if ( range_p.is_last_segment()
  319. && equals::equals_point_point(range_p.at(1), tp.point,
  320. umbrella_strategy.get_point_in_point_strategy()) )
  321. {
  322. tp.operations[0].position = position_back;
  323. }
  324. // tp.operations[1].position = position_middle;
  325. *out++ = tp;
  326. }
  327. }
  328. break;
  329. default :
  330. {
  331. #if defined(BOOST_GEOMETRY_DEBUG_ROBUSTNESS)
  332. std::cout << "TURN: Unknown method: " << method << std::endl;
  333. #endif
  334. #if ! defined(BOOST_GEOMETRY_OVERLAY_NO_THROW)
  335. BOOST_THROW_EXCEPTION(turn_info_exception(method));
  336. #endif
  337. }
  338. break;
  339. }
  340. return out;
  341. }
  342. template <typename Operation,
  343. typename IntersectionInfo,
  344. typename EqPPStrategy>
  345. static inline bool calculate_spike_operation(Operation & op,
  346. IntersectionInfo const& inters,
  347. EqPPStrategy const& strategy)
  348. {
  349. bool is_p_spike = ( op == operation_union || op == operation_intersection )
  350. && inters.is_spike_p();
  351. if ( is_p_spike )
  352. {
  353. int const pk_q1 = inters.sides().pk_wrt_q1();
  354. bool going_in = pk_q1 < 0; // Pk on the right
  355. bool going_out = pk_q1 > 0; // Pk on the left
  356. int const qk_q1 = inters.sides().qk_wrt_q1();
  357. // special cases
  358. if ( qk_q1 < 0 ) // Q turning R
  359. {
  360. // spike on the edge point
  361. // if it's already known that the spike is going out this musn't be checked
  362. if ( ! going_out
  363. && detail::equals::equals_point_point(inters.rpj(), inters.rqj(), strategy) )
  364. {
  365. int const pk_q2 = inters.sides().pk_wrt_q2();
  366. going_in = pk_q1 < 0 && pk_q2 < 0; // Pk on the right of both
  367. going_out = pk_q1 > 0 || pk_q2 > 0; // Pk on the left of one of them
  368. }
  369. }
  370. else if ( qk_q1 > 0 ) // Q turning L
  371. {
  372. // spike on the edge point
  373. // if it's already known that the spike is going in this musn't be checked
  374. if ( ! going_in
  375. && detail::equals::equals_point_point(inters.rpj(), inters.rqj(), strategy) )
  376. {
  377. int const pk_q2 = inters.sides().pk_wrt_q2();
  378. going_in = pk_q1 < 0 || pk_q2 < 0; // Pk on the right of one of them
  379. going_out = pk_q1 > 0 && pk_q2 > 0; // Pk on the left of both
  380. }
  381. }
  382. if ( going_in )
  383. {
  384. op = operation_intersection;
  385. return true;
  386. }
  387. else if ( going_out )
  388. {
  389. op = operation_union;
  390. return true;
  391. }
  392. }
  393. return false;
  394. }
  395. enum append_version_c { append_equal, append_collinear };
  396. template <typename TurnInfo,
  397. typename IntersectionInfo,
  398. typename OutIt>
  399. static inline bool append_collinear_spikes(TurnInfo & tp,
  400. IntersectionInfo const& inters,
  401. method_type method, append_version_c version,
  402. OutIt out)
  403. {
  404. // method == touch || touch_interior
  405. // both position == middle
  406. bool is_p_spike = ( version == append_equal ?
  407. ( tp.operations[0].operation == operation_union
  408. || tp.operations[0].operation == operation_intersection ) :
  409. tp.operations[0].operation == operation_continue )
  410. && inters.is_spike_p();
  411. // TODO: throw an exception for spike in Areal?
  412. /*bool is_q_spike = tp.operations[1].operation == operation_continue
  413. && inters.is_spike_q();
  414. // both are collinear spikes on the same IP, we can just follow both
  415. if ( is_p_spike && is_q_spike )
  416. {
  417. return false;
  418. }
  419. // spike on Linear - it's turning back on the boundary of Areal
  420. else*/
  421. if ( is_p_spike )
  422. {
  423. tp.method = method;
  424. tp.operations[0].operation = operation_blocked;
  425. tp.operations[1].operation = operation_union;
  426. *out++ = tp;
  427. tp.operations[0].operation = operation_continue; // boundary
  428. //tp.operations[1].operation = operation_union;
  429. *out++ = tp;
  430. return true;
  431. }
  432. // spike on Areal - Linear is going outside
  433. /*else if ( is_q_spike )
  434. {
  435. tp.method = method;
  436. tp.operations[0].operation = operation_union;
  437. tp.operations[1].operation = operation_continue;
  438. *out++ = tp;
  439. *out++ = tp;
  440. return true;
  441. }*/
  442. return false;
  443. }
  444. enum append_version_o { append_touches, append_collinear_opposite };
  445. template <append_version_o Version,
  446. typename TurnInfo,
  447. typename IntersectionInfo,
  448. typename OutIt>
  449. static inline bool append_opposite_spikes(TurnInfo & tp,
  450. IntersectionInfo const& inters,
  451. OutIt out)
  452. {
  453. static const bool is_version_touches = (Version == append_touches);
  454. bool is_p_spike = ( is_version_touches ?
  455. ( tp.operations[0].operation == operation_continue
  456. || tp.operations[0].operation == operation_intersection ) : // i ???
  457. true )
  458. && inters.is_spike_p();
  459. // TODO: throw an exception for spike in Areal?
  460. /*bool is_q_spike = ( ( Version == append_touches
  461. && tp.operations[1].operation == operation_continue )
  462. || ( Version == append_collinear_opposite
  463. && tp.operations[1].operation == operation_none ) )
  464. && inters.is_spike_q();
  465. if ( is_p_spike && is_q_spike )
  466. {
  467. // u/u or nothing?
  468. return false;
  469. }
  470. else*/
  471. if ( is_p_spike )
  472. {
  473. if ( BOOST_GEOMETRY_CONDITION(is_version_touches)
  474. || inters.d_info().arrival[0] == 1 )
  475. {
  476. if ( BOOST_GEOMETRY_CONDITION(is_version_touches) )
  477. {
  478. tp.operations[0].is_collinear = true;
  479. //tp.operations[1].is_collinear = false;
  480. tp.method = method_touch;
  481. }
  482. else
  483. {
  484. tp.operations[0].is_collinear = true;
  485. //tp.operations[1].is_collinear = false;
  486. BOOST_GEOMETRY_ASSERT(inters.i_info().count > 1);
  487. base_turn_handler::assign_point(tp, method_touch_interior, inters.i_info(), 1);
  488. }
  489. tp.operations[0].operation = operation_blocked;
  490. tp.operations[1].operation = operation_continue; // boundary
  491. *out++ = tp;
  492. tp.operations[0].operation = operation_continue; // boundary
  493. //tp.operations[1].operation = operation_continue; // boundary
  494. *out++ = tp;
  495. return true;
  496. }
  497. }
  498. /*else if ( is_q_spike )
  499. {
  500. tp.operations[0].is_collinear = true;
  501. tp.method = is_version_touches ? method_touch : method_touch_interior;
  502. tp.operations[0].operation = operation_continue;
  503. tp.operations[1].operation = operation_continue; // boundary
  504. *out++ = tp;
  505. *out++ = tp;
  506. return true;
  507. }*/
  508. return false;
  509. }
  510. static inline void replace_method_and_operations_tm(method_type & method,
  511. operation_type & op0,
  512. operation_type & op1)
  513. {
  514. if ( op0 == operation_blocked && op1 == operation_blocked )
  515. {
  516. // NOTE: probably only if methods are WRT IPs, not segments!
  517. method = (method == method_touch ? method_equal : method_collinear);
  518. }
  519. // Assuming G1 is always Linear
  520. if ( op0 == operation_blocked )
  521. {
  522. op0 = operation_continue;
  523. }
  524. if ( op1 == operation_blocked )
  525. {
  526. op1 = operation_continue;
  527. }
  528. else if ( op1 == operation_intersection )
  529. {
  530. op1 = operation_union;
  531. }
  532. // spikes in 'm'
  533. if ( method == method_error )
  534. {
  535. method = method_touch_interior;
  536. op0 = operation_union;
  537. op1 = operation_union;
  538. }
  539. }
  540. template <bool IsFront>
  541. class turn_transformer_ec
  542. {
  543. public:
  544. explicit turn_transformer_ec(method_type method_t_or_m)
  545. : m_method(method_t_or_m)
  546. {}
  547. template <typename Turn>
  548. void operator()(Turn & turn) const
  549. {
  550. operation_type & op0 = turn.operations[0].operation;
  551. operation_type & op1 = turn.operations[1].operation;
  552. // NOTE: probably only if methods are WRT IPs, not segments!
  553. if ( BOOST_GEOMETRY_CONDITION(IsFront)
  554. || op0 == operation_intersection || op0 == operation_union
  555. || op1 == operation_intersection || op1 == operation_union )
  556. {
  557. turn.method = m_method;
  558. }
  559. turn.operations[0].is_collinear = op0 != operation_blocked;
  560. // Assuming G1 is always Linear
  561. if ( op0 == operation_blocked )
  562. {
  563. op0 = operation_continue;
  564. }
  565. if ( op1 == operation_blocked )
  566. {
  567. op1 = operation_continue;
  568. }
  569. else if ( op1 == operation_intersection )
  570. {
  571. op1 = operation_union;
  572. }
  573. }
  574. private:
  575. method_type m_method;
  576. };
  577. static inline void replace_operations_i(operation_type & /*op0*/, operation_type & op1)
  578. {
  579. // assuming Linear is always the first one
  580. op1 = operation_union;
  581. }
  582. // NOTE: Spikes may NOT be handled for Linear endpoints because it's not
  583. // possible to define a spike on an endpoint. Areal geometries must
  584. // NOT have spikes at all. One thing that could be done is to throw
  585. // an exception when spike is detected in Areal geometry.
  586. template <bool EnableFirst,
  587. bool EnableLast,
  588. typename UniqueSubRange1,
  589. typename UniqueSubRange2,
  590. typename TurnInfo,
  591. typename IntersectionInfo,
  592. typename OutputIterator,
  593. typename EqPPStrategy>
  594. static inline bool get_turn_info_for_endpoint(
  595. UniqueSubRange1 const& range_p,
  596. UniqueSubRange2 const& range_q,
  597. TurnInfo const& tp_model,
  598. IntersectionInfo const& inters,
  599. method_type /*method*/,
  600. OutputIterator out,
  601. EqPPStrategy const& strategy)
  602. {
  603. namespace ov = overlay;
  604. typedef ov::get_turn_info_for_endpoint<EnableFirst, EnableLast> get_info_e;
  605. const std::size_t ip_count = inters.i_info().count;
  606. // no intersection points
  607. if (ip_count == 0)
  608. {
  609. return false;
  610. }
  611. if (! range_p.is_first_segment() && ! range_p.is_last_segment())
  612. {
  613. // P sub-range has no end-points
  614. return false;
  615. }
  616. typename IntersectionInfo::side_strategy_type const& sides
  617. = inters.get_side_strategy();
  618. linear_intersections intersections(range_p.at(0),
  619. range_q.at(0),
  620. inters.result(),
  621. range_p.is_last_segment(),
  622. range_q.is_last_segment(),
  623. strategy);
  624. linear_intersections::ip_info const& ip0 = intersections.template get<0>();
  625. linear_intersections::ip_info const& ip1 = intersections.template get<1>();
  626. const bool opposite = inters.d_info().opposite;
  627. // ANALYSE AND ASSIGN FIRST
  628. // IP on the first point of Linear Geometry
  629. bool was_first_point_handled = false;
  630. if ( BOOST_GEOMETRY_CONDITION(EnableFirst)
  631. && range_p.is_first_segment() && ip0.is_pi && !ip0.is_qi ) // !q0i prevents duplication
  632. {
  633. TurnInfo tp = tp_model;
  634. tp.operations[0].position = position_front;
  635. tp.operations[1].position = position_middle;
  636. if ( opposite ) // opposite -> collinear
  637. {
  638. tp.operations[0].operation = operation_continue;
  639. tp.operations[1].operation = operation_union;
  640. tp.method = ip0.is_qj ? method_touch : method_touch_interior;
  641. }
  642. else
  643. {
  644. // pi is the intersection point at qj or in the middle of q1
  645. // so consider segments
  646. // 1. pi at qj: qi-qj-pj and qi-qj-qk
  647. // x: qi-qj, y: qj-qk, qz: qk
  648. // 2. pi in the middle of q1: qi-pi-pj and qi-pi-qj
  649. // x: qi-pi, y: pi-qj, qz: qj
  650. // qi-pi, side the same as WRT q1
  651. // pi-qj, side the same as WRT q1
  652. // qj WRT q1 is 0
  653. method_type replaced_method = method_none;
  654. int side_pj_y = 0, side_pj_x = 0, side_qz_x = 0;
  655. // 1. ip0 or pi at qj
  656. if ( ip0.is_qj )
  657. {
  658. replaced_method = method_touch;
  659. side_pj_y = sides.apply(range_q.at(1), range_q.at(2), range_p.at(1)); // pj wrt q2
  660. side_pj_x = sides.apply(range_q.at(0), range_q.at(1), range_p.at(1)); // pj wrt q1
  661. side_qz_x = sides.apply(range_q.at(0), range_q.at(1), range_q.at(2)); // qk wrt q1
  662. }
  663. // 2. ip0 or pi in the middle of q1
  664. else
  665. {
  666. replaced_method = method_touch_interior;
  667. side_pj_y = sides.apply(range_q.at(0), range_q.at(1), range_p.at(1)); // pj wrt q1
  668. side_pj_x = side_pj_y; // pj wrt q1
  669. side_qz_x = 0; // qj wrt q1
  670. }
  671. std::pair<operation_type, operation_type> operations
  672. = get_info_e::operations_of_equal(side_pj_y, side_pj_x, side_qz_x);
  673. tp.operations[0].operation = operations.first;
  674. tp.operations[1].operation = operations.second;
  675. turn_transformer_ec<true> transformer(replaced_method);
  676. transformer(tp);
  677. }
  678. // equals<> or collinear<> will assign the second point,
  679. // we'd like to assign the first one
  680. base_turn_handler::assign_point(tp, tp.method, inters.i_info(), 0);
  681. // NOTE: is_collinear is not set for the first endpoint of L
  682. // for which there is no preceding segment
  683. // here is_p_first_ip == true
  684. tp.operations[0].is_collinear = false;
  685. *out++ = tp;
  686. was_first_point_handled = true;
  687. }
  688. // ANALYSE AND ASSIGN LAST
  689. // IP on the last point of Linear Geometry
  690. if ( BOOST_GEOMETRY_CONDITION(EnableLast)
  691. && range_p.is_last_segment()
  692. && ( ip_count > 1 ? (ip1.is_pj && !ip1.is_qi) : (ip0.is_pj && !ip0.is_qi) ) ) // prevents duplication
  693. {
  694. TurnInfo tp = tp_model;
  695. if ( inters.i_info().count > 1 )
  696. {
  697. //BOOST_GEOMETRY_ASSERT( result.template get<1>().dir_a == 0 && result.template get<1>().dir_b == 0 );
  698. tp.operations[0].is_collinear = true;
  699. tp.operations[1].operation = opposite ? operation_continue : operation_union;
  700. }
  701. else //if ( result.template get<0>().count == 1 )
  702. {
  703. // pj is the intersection point at qj or in the middle of q1
  704. // so consider segments
  705. // 1. pj at qj: qi-qj-pi and qi-qj-qk
  706. // x: qi-qj, y: qj-qk, qz: qk
  707. // 2. pj in the middle of q1: qi-pj-pi and qi-pj-qj
  708. // x: qi-pj, y: pj-qj, qz: qj
  709. // qi-pj, the side is the same as WRT q1
  710. // pj-qj, the side is the same as WRT q1
  711. // side of qj WRT q1 is 0
  712. int side_pi_y = 0, side_pi_x = 0, side_qz_x = 0;
  713. // 1. ip0 or pj at qj
  714. if ( ip0.is_qj )
  715. {
  716. side_pi_y = sides.apply(range_q.at(1), range_q.at(2), range_p.at(0)); // pi wrt q2
  717. side_pi_x = sides.apply(range_q.at(0), range_q.at(1), range_p.at(0)); // pi wrt q1
  718. side_qz_x = sides.apply(range_q.at(0), range_q.at(1), range_q.at(2)); // qk wrt q1
  719. }
  720. // 2. ip0 or pj in the middle of q1
  721. else
  722. {
  723. side_pi_y = sides.apply(range_q.at(0), range_q.at(1), range_p.at(0)); // pi wrt q1
  724. side_pi_x = side_pi_y; // pi wrt q1
  725. side_qz_x = 0; // qj wrt q1
  726. }
  727. std::pair<operation_type, operation_type> operations
  728. = get_info_e::operations_of_equal(side_pi_y, side_pi_x, side_qz_x);
  729. tp.operations[0].operation = operations.first;
  730. tp.operations[1].operation = operations.second;
  731. turn_transformer_ec<false> transformer(method_none);
  732. transformer(tp);
  733. tp.operations[0].is_collinear = tp.both(operation_continue);
  734. }
  735. tp.method = ( ip_count > 1 ? ip1.is_qj : ip0.is_qj ) ? method_touch : method_touch_interior;
  736. tp.operations[0].operation = operation_blocked;
  737. tp.operations[0].position = position_back;
  738. tp.operations[1].position = position_middle;
  739. // equals<> or collinear<> will assign the second point,
  740. // we'd like to assign the first one
  741. unsigned int ip_index = ip_count > 1 ? 1 : 0;
  742. base_turn_handler::assign_point(tp, tp.method, inters.i_info(), ip_index);
  743. *out++ = tp;
  744. // don't ignore the first IP if the segment is opposite
  745. return !( opposite && ip_count > 1 ) || was_first_point_handled;
  746. }
  747. // don't ignore anything for now
  748. return false;
  749. }
  750. template <typename Point1, typename Point2, typename IntersectionStrategy>
  751. static inline bool equals_point_point(Point1 const& point1, Point2 const& point2,
  752. IntersectionStrategy const& strategy)
  753. {
  754. return detail::equals::equals_point_point(point1, point2,
  755. strategy.get_point_in_point_strategy());
  756. }
  757. };
  758. }} // namespace detail::overlay
  759. #endif // DOXYGEN_NO_DETAIL
  760. }} // namespace boost::geometry
  761. #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_LA_HPP