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