1 #pragma once
2
3 #include <mapbox/geometry/wagyu/active_bound_list.hpp>
4 #include <mapbox/geometry/wagyu/bound.hpp>
5 #include <mapbox/geometry/wagyu/bubble_sort.hpp>
6 #include <mapbox/geometry/wagyu/config.hpp>
7 #include <mapbox/geometry/wagyu/intersect.hpp>
8 #include <mapbox/geometry/wagyu/ring_util.hpp>
9 #include <mapbox/geometry/wagyu/util.hpp>
10
11 #include <algorithm>
12
13 namespace mapbox {
14 namespace geometry {
15 namespace wagyu {
16
17 template <typename T>
18 struct intersect_list_sorter {
operator ()mapbox::geometry::wagyu::intersect_list_sorter19 inline bool operator()(intersect_node<T> const& node1, intersect_node<T> const& node2) {
20 if (!values_are_equal(node2.pt.y, node1.pt.y)) {
21 return node2.pt.y < node1.pt.y;
22 } else {
23 return (node2.bound1->winding_count2 + node2.bound2->winding_count2) >
24 (node1.bound1->winding_count2 + node1.bound2->winding_count2);
25 }
26 }
27 };
28
29 template <typename T>
round_point(mapbox::geometry::point<double> const & pt)30 inline mapbox::geometry::point<T> round_point(mapbox::geometry::point<double> const& pt) {
31 return mapbox::geometry::point<T>(round_towards_max<T>(pt.x), round_towards_max<T>(pt.y));
32 }
33
34 template <typename T>
swap_rings(bound<T> & b1,bound<T> & b2)35 inline void swap_rings(bound<T>& b1, bound<T>& b2) {
36 ring_ptr<T> ring = b1.ring;
37 b1.ring = b2.ring;
38 b2.ring = ring;
39 }
40
41 template <typename T>
swap_sides(bound<T> & b1,bound<T> & b2)42 inline void swap_sides(bound<T>& b1, bound<T>& b2) {
43 edge_side side = b1.side;
44 b1.side = b2.side;
45 b2.side = side;
46 }
47
48 template <typename T1, typename T2>
get_edge_intersection(edge<T1> const & e1,edge<T1> const & e2,mapbox::geometry::point<T2> & pt)49 bool get_edge_intersection(edge<T1> const& e1,
50 edge<T1> const& e2,
51 mapbox::geometry::point<T2>& pt) {
52 T2 p0_x = static_cast<T2>(e1.bot.x);
53 T2 p0_y = static_cast<T2>(e1.bot.y);
54 T2 p1_x = static_cast<T2>(e1.top.x);
55 T2 p1_y = static_cast<T2>(e1.top.y);
56 T2 p2_x = static_cast<T2>(e2.bot.x);
57 T2 p2_y = static_cast<T2>(e2.bot.y);
58 T2 p3_x = static_cast<T2>(e2.top.x);
59 T2 p3_y = static_cast<T2>(e2.top.y);
60 T2 s1_x, s1_y, s2_x, s2_y;
61 s1_x = p1_x - p0_x;
62 s1_y = p1_y - p0_y;
63 s2_x = p3_x - p2_x;
64 s2_y = p3_y - p2_y;
65
66 T2 s = (-s1_y * (p0_x - p2_x) + s1_x * (p0_y - p2_y)) / (-s2_x * s1_y + s1_x * s2_y);
67 T2 t = (s2_x * (p0_y - p2_y) - s2_y * (p0_x - p2_x)) / (-s2_x * s1_y + s1_x * s2_y);
68
69 if (s >= 0.0 && s <= 1.0 && t >= 0.0 && t <= 1.0) {
70 pt.x = p0_x + (t * s1_x);
71 pt.y = p0_y + (t * s1_y);
72 return true;
73 }
74 // LCOV_EXCL_START
75 return false;
76 // LCOV_EXCL_END
77 }
78
79 template <typename T>
80 struct intersection_compare {
operator ()mapbox::geometry::wagyu::intersection_compare81 bool operator()(bound_ptr<T> const& b1, bound_ptr<T> const& b2) {
82 return !(b1->current_x > b2->current_x &&
83 !slopes_equal(*(b1->current_edge), *(b2->current_edge)));
84 }
85 };
86
87 template <typename T>
88 struct on_intersection_swap {
89
90 intersect_list<T>& intersects;
91
on_intersection_swapmapbox::geometry::wagyu::on_intersection_swap92 on_intersection_swap(intersect_list<T>& i) : intersects(i) {
93 }
94
operator ()mapbox::geometry::wagyu::on_intersection_swap95 void operator()(bound_ptr<T> const& b1, bound_ptr<T> const& b2) {
96 mapbox::geometry::point<double> pt;
97 if (!get_edge_intersection<T, double>(*(b1->current_edge), *(b2->current_edge), pt)) {
98 // LCOV_EXCL_START
99 throw std::runtime_error("Trying to find intersection of lines that do not intersect");
100 // LCOV_EXCL_END
101 }
102 intersects.emplace_back(b1, b2, pt);
103 }
104 };
105
106 template <typename T>
build_intersect_list(active_bound_list<T> & active_bounds,intersect_list<T> & intersects)107 void build_intersect_list(active_bound_list<T>& active_bounds, intersect_list<T>& intersects) {
108 bubble_sort(active_bounds.begin(), active_bounds.end(), intersection_compare<T>(),
109 on_intersection_swap<T>(intersects));
110 }
111
112 template <typename T>
intersect_bounds(bound<T> & b1,bound<T> & b2,mapbox::geometry::point<T> const & pt,clip_type cliptype,fill_type subject_fill_type,fill_type clip_fill_type,ring_manager<T> & rings,active_bound_list<T> & active_bounds)113 void intersect_bounds(bound<T>& b1,
114 bound<T>& b2,
115 mapbox::geometry::point<T> const& pt,
116 clip_type cliptype,
117 fill_type subject_fill_type,
118 fill_type clip_fill_type,
119 ring_manager<T>& rings,
120 active_bound_list<T>& active_bounds) {
121 bool b1Contributing = (b1.ring != nullptr);
122 bool b2Contributing = (b2.ring != nullptr);
123
124 // update winding counts...
125 // assumes that b1 will be to the Right of b2 ABOVE the intersection
126 if (b1.poly_type == b2.poly_type) {
127 if (is_even_odd_fill_type(b1, subject_fill_type, clip_fill_type)) {
128 std::swap(b1.winding_count, b2.winding_count);
129 } else {
130 if (b1.winding_count + b2.winding_delta == 0) {
131 b1.winding_count = -b1.winding_count;
132 } else {
133 b1.winding_count += b2.winding_delta;
134 }
135 if (b2.winding_count - b1.winding_delta == 0) {
136 b2.winding_count = -b2.winding_count;
137 } else {
138 b2.winding_count -= b1.winding_delta;
139 }
140 }
141 } else {
142 if (!is_even_odd_fill_type(b2, subject_fill_type, clip_fill_type)) {
143 b1.winding_count2 += b2.winding_delta;
144 } else {
145 b1.winding_count2 = (b1.winding_count2 == 0) ? 1 : 0;
146 }
147 if (!is_even_odd_fill_type(b1, subject_fill_type, clip_fill_type)) {
148 b2.winding_count2 -= b1.winding_delta;
149 } else {
150 b2.winding_count2 = (b2.winding_count2 == 0) ? 1 : 0;
151 }
152 }
153
154 fill_type b1FillType, b2FillType, b1FillType2, b2FillType2;
155 if (b1.poly_type == polygon_type_subject) {
156 b1FillType = subject_fill_type;
157 b1FillType2 = clip_fill_type;
158 } else {
159 b1FillType = clip_fill_type;
160 b1FillType2 = subject_fill_type;
161 }
162 if (b2.poly_type == polygon_type_subject) {
163 b2FillType = subject_fill_type;
164 b2FillType2 = clip_fill_type;
165 } else {
166 b2FillType = clip_fill_type;
167 b2FillType2 = subject_fill_type;
168 }
169
170 std::int32_t b1Wc, b2Wc;
171 switch (b1FillType) {
172 case fill_type_positive:
173 b1Wc = b1.winding_count;
174 break;
175 case fill_type_negative:
176 b1Wc = -b1.winding_count;
177 break;
178 case fill_type_even_odd:
179 case fill_type_non_zero:
180 default:
181 b1Wc = std::abs(static_cast<int>(b1.winding_count));
182 }
183 switch (b2FillType) {
184 case fill_type_positive:
185 b2Wc = b2.winding_count;
186 break;
187 case fill_type_negative:
188 b2Wc = -b2.winding_count;
189 break;
190 case fill_type_even_odd:
191 case fill_type_non_zero:
192 default:
193 b2Wc = std::abs(static_cast<int>(b2.winding_count));
194 }
195 if (b1Contributing && b2Contributing) {
196 if ((b1Wc != 0 && b1Wc != 1) || (b2Wc != 0 && b2Wc != 1) ||
197 (b1.poly_type != b2.poly_type && cliptype != clip_type_x_or)) {
198 add_local_maximum_point(b1, b2, pt, rings, active_bounds);
199 } else {
200 add_point(b1, active_bounds, pt, rings);
201 add_point(b2, active_bounds, pt, rings);
202 swap_sides(b1, b2);
203 swap_rings(b1, b2);
204 }
205 } else if (b1Contributing) {
206 if (b2Wc == 0 || b2Wc == 1) {
207 add_point(b1, active_bounds, pt, rings);
208 b2.last_point = pt;
209 swap_sides(b1, b2);
210 swap_rings(b1, b2);
211 }
212 } else if (b2Contributing) {
213 if (b1Wc == 0 || b1Wc == 1) {
214 b1.last_point = pt;
215 add_point(b2, active_bounds, pt, rings);
216 swap_sides(b1, b2);
217 swap_rings(b1, b2);
218 }
219 } else if ((b1Wc == 0 || b1Wc == 1) && (b2Wc == 0 || b2Wc == 1)) {
220 // neither bound is currently contributing ...
221
222 std::int32_t b1Wc2, b2Wc2;
223 switch (b1FillType2) {
224 case fill_type_positive:
225 b1Wc2 = b1.winding_count2;
226 break;
227 case fill_type_negative:
228 b1Wc2 = -b1.winding_count2;
229 break;
230 case fill_type_even_odd:
231 case fill_type_non_zero:
232 default:
233 b1Wc2 = std::abs(static_cast<int>(b1.winding_count2));
234 }
235 switch (b2FillType2) {
236 case fill_type_positive:
237 b2Wc2 = b2.winding_count2;
238 break;
239 case fill_type_negative:
240 b2Wc2 = -b2.winding_count2;
241 break;
242 case fill_type_even_odd:
243 case fill_type_non_zero:
244 default:
245 b2Wc2 = std::abs(static_cast<int>(b2.winding_count2));
246 }
247
248 if (b1.poly_type != b2.poly_type) {
249 add_local_minimum_point(b1, b2, active_bounds, pt, rings);
250 } else if (b1Wc == 1 && b2Wc == 1) {
251 switch (cliptype) {
252 case clip_type_intersection:
253 if (b1Wc2 > 0 && b2Wc2 > 0) {
254 add_local_minimum_point(b1, b2, active_bounds, pt, rings);
255 }
256 break;
257 default:
258 case clip_type_union:
259 if (b1Wc2 <= 0 && b2Wc2 <= 0) {
260 add_local_minimum_point(b1, b2, active_bounds, pt, rings);
261 }
262 break;
263 case clip_type_difference:
264 if (((b1.poly_type == polygon_type_clip) && (b1Wc2 > 0) && (b2Wc2 > 0)) ||
265 ((b1.poly_type == polygon_type_subject) && (b1Wc2 <= 0) && (b2Wc2 <= 0))) {
266 add_local_minimum_point(b1, b2, active_bounds, pt, rings);
267 }
268 break;
269 case clip_type_x_or:
270 add_local_minimum_point(b1, b2, active_bounds, pt, rings);
271 }
272 } else {
273 swap_sides(b1, b2);
274 }
275 }
276 }
277
278 template <typename T>
bounds_adjacent(intersect_node<T> const & inode,bound_ptr<T> next)279 bool bounds_adjacent(intersect_node<T> const& inode, bound_ptr<T> next) {
280 return (next == inode.bound2) || (next == inode.bound1);
281 }
282
283 template <typename T>
284 struct find_first_bound {
285 bound_ptr<T> b1;
286 bound_ptr<T> b2;
287
find_first_boundmapbox::geometry::wagyu::find_first_bound288 find_first_bound(intersect_node<T> const& inode) : b1(inode.bound1), b2(inode.bound2) {
289 }
290
operator ()mapbox::geometry::wagyu::find_first_bound291 bool operator()(bound_ptr<T> const& b) {
292 return b == b1 || b == b2;
293 }
294 };
295
296 template <typename T>
process_intersect_list(intersect_list<T> & intersects,clip_type cliptype,fill_type subject_fill_type,fill_type clip_fill_type,ring_manager<T> & rings,active_bound_list<T> & active_bounds)297 void process_intersect_list(intersect_list<T>& intersects,
298 clip_type cliptype,
299 fill_type subject_fill_type,
300 fill_type clip_fill_type,
301 ring_manager<T>& rings,
302 active_bound_list<T>& active_bounds) {
303 for (auto node_itr = intersects.begin(); node_itr != intersects.end(); ++node_itr) {
304 auto b1 = std::find_if(active_bounds.begin(), active_bounds.end(),
305 find_first_bound<T>(*node_itr));
306 auto b2 = std::next(b1);
307 if (!bounds_adjacent(*node_itr, *b2)) {
308 auto next_itr = std::next(node_itr);
309 while (next_itr != intersects.end()) {
310 auto n1 = std::find_if(active_bounds.begin(), active_bounds.end(),
311 find_first_bound<T>(*next_itr));
312 auto n2 = std::next(n1);
313 if (bounds_adjacent(*next_itr, *n2)) {
314 b1 = n1;
315 b2 = n2;
316 break;
317 }
318 ++next_itr;
319 }
320 if (next_itr == intersects.end()) {
321 throw std::runtime_error("Could not properly correct intersection order.");
322 }
323 std::iter_swap(node_itr, next_itr);
324 }
325 mapbox::geometry::point<T> pt = round_point<T>(node_itr->pt);
326 intersect_bounds(*(node_itr->bound1), *(node_itr->bound2), pt, cliptype, subject_fill_type,
327 clip_fill_type, rings, active_bounds);
328 std::iter_swap(b1, b2);
329 }
330 }
331
332 template <typename T>
update_current_x(active_bound_list<T> & active_bounds,T top_y)333 void update_current_x(active_bound_list<T>& active_bounds, T top_y) {
334 std::size_t pos = 0;
335 for (auto& bnd : active_bounds) {
336 bnd->pos = pos++;
337 bnd->current_x = get_current_x(*bnd->current_edge, top_y);
338 }
339 }
340
341 template <typename T>
process_intersections(T top_y,active_bound_list<T> & active_bounds,clip_type cliptype,fill_type subject_fill_type,fill_type clip_fill_type,ring_manager<T> & rings)342 void process_intersections(T top_y,
343 active_bound_list<T>& active_bounds,
344 clip_type cliptype,
345 fill_type subject_fill_type,
346 fill_type clip_fill_type,
347 ring_manager<T>& rings) {
348 if (active_bounds.empty()) {
349 return;
350 }
351 update_current_x(active_bounds, top_y);
352 intersect_list<T> intersects;
353 build_intersect_list(active_bounds, intersects);
354
355 if (intersects.empty()) {
356 return;
357 }
358
359 // Restore order of active bounds list
360 std::stable_sort(
361 active_bounds.begin(), active_bounds.end(),
362 [](bound_ptr<T> const& b1, bound_ptr<T> const& b2) { return b1->pos < b2->pos; });
363
364 // Sort the intersection list
365 std::stable_sort(intersects.begin(), intersects.end(), intersect_list_sorter<T>());
366
367 process_intersect_list(intersects, cliptype, subject_fill_type, clip_fill_type, rings,
368 active_bounds);
369 }
370 }
371 }
372 }
373