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