/src/assimp/contrib/earcut-hpp/earcut.hpp
Line | Count | Source |
1 | | #pragma once |
2 | | |
3 | | #include <algorithm> |
4 | | #include <cassert> |
5 | | #include <cmath> |
6 | | #include <cstddef> |
7 | | #include <cstdint> |
8 | | #include <limits> |
9 | | #include <memory> |
10 | | #include <utility> |
11 | | #include <vector> |
12 | | |
13 | | namespace mapbox { |
14 | | |
15 | | namespace util { |
16 | | |
17 | | template <std::size_t I, typename T> struct nth { |
18 | | inline static typename std::tuple_element<I, T>::type |
19 | | get(const T& t) { return std::get<I>(t); }; |
20 | | }; |
21 | | |
22 | | } |
23 | | |
24 | | namespace detail { |
25 | | |
26 | | template <typename N = uint32_t> |
27 | | class Earcut { |
28 | | public: |
29 | | std::vector<N> indices; |
30 | | std::size_t vertices = 0; |
31 | | |
32 | | template <typename Polygon> |
33 | | void operator()(const Polygon& points); |
34 | | |
35 | | private: |
36 | | struct Node { |
37 | 29.7k | Node(N index, double x_, double y_) : i(index), x(x_), y(y_) {} |
38 | | Node(const Node&) = delete; |
39 | | Node& operator=(const Node&) = delete; |
40 | | Node(Node&&) = delete; |
41 | | Node& operator=(Node&&) = delete; |
42 | | |
43 | | const N i; |
44 | | const double x; |
45 | | const double y; |
46 | | |
47 | | // previous and next vertice nodes in a polygon ring |
48 | | Node* prev = nullptr; |
49 | | Node* next = nullptr; |
50 | | |
51 | | // z-order curve value |
52 | | int32_t z = 0; |
53 | | |
54 | | // previous and next nodes in z-order |
55 | | Node* prevZ = nullptr; |
56 | | Node* nextZ = nullptr; |
57 | | |
58 | | // indicates whether this is a steiner point |
59 | | bool steiner = false; |
60 | | }; |
61 | | |
62 | | template <typename Ring> Node* linkedList(const Ring& points, const bool clockwise); |
63 | | Node* filterPoints(Node* start, Node* end = nullptr); |
64 | | void earcutLinked(Node* ear, int pass = 0); |
65 | | bool isEar(Node* ear); |
66 | | bool isEarHashed(Node* ear); |
67 | | Node* cureLocalIntersections(Node* start); |
68 | | void splitEarcut(Node* start); |
69 | | template <typename Polygon> Node* eliminateHoles(const Polygon& points, Node* outerNode); |
70 | | Node* eliminateHole(Node* hole, Node* outerNode); |
71 | | Node* findHoleBridge(Node* hole, Node* outerNode); |
72 | | bool sectorContainsSector(const Node* m, const Node* p); |
73 | | void indexCurve(Node* start); |
74 | | Node* sortLinked(Node* list); |
75 | | int32_t zOrder(const double x_, const double y_); |
76 | | Node* getLeftmost(Node* start); |
77 | | bool pointInTriangle(double ax, double ay, double bx, double by, double cx, double cy, double px, double py) const; |
78 | | bool isValidDiagonal(Node* a, Node* b); |
79 | | double area(const Node* p, const Node* q, const Node* r) const; |
80 | | bool equals(const Node* p1, const Node* p2); |
81 | | bool intersects(const Node* p1, const Node* q1, const Node* p2, const Node* q2); |
82 | | bool onSegment(const Node* p, const Node* q, const Node* r); |
83 | | int sign(double val); |
84 | | bool intersectsPolygon(const Node* a, const Node* b); |
85 | | bool locallyInside(const Node* a, const Node* b); |
86 | | bool middleInside(const Node* a, const Node* b); |
87 | | Node* splitPolygon(Node* a, Node* b); |
88 | | template <typename Point> Node* insertNode(std::size_t i, const Point& p, Node* last); |
89 | | void removeNode(Node* p); |
90 | | |
91 | | bool hashing; |
92 | | double minX, maxX; |
93 | | double minY, maxY; |
94 | | double inv_size = 0; |
95 | | |
96 | | template <typename T, typename Alloc = std::allocator<T>> |
97 | | class ObjectPool { |
98 | | public: |
99 | 992 | ObjectPool() { } |
100 | | ObjectPool(std::size_t blockSize_) { |
101 | | reset(blockSize_); |
102 | | } |
103 | 992 | ~ObjectPool() { |
104 | 992 | clear(); |
105 | 992 | } |
106 | | template <typename... Args> |
107 | 29.7k | T* construct(Args&&... args) { |
108 | 29.7k | if (currentIndex >= blockSize) { |
109 | 992 | currentBlock = alloc_traits::allocate(alloc, blockSize); |
110 | 992 | allocations.emplace_back(currentBlock); |
111 | 992 | currentIndex = 0; |
112 | 992 | } |
113 | 29.7k | T* object = ¤tBlock[currentIndex++]; |
114 | 29.7k | alloc_traits::construct(alloc, object, std::forward<Args>(args)...); |
115 | 29.7k | return object; |
116 | 29.7k | } mapbox::detail::Earcut<unsigned int>::Node* mapbox::detail::Earcut<unsigned int>::ObjectPool<mapbox::detail::Earcut<unsigned int>::Node, std::__1::allocator<mapbox::detail::Earcut<unsigned int>::Node> >::construct<unsigned int, float, float>(unsigned int&&, float&&, float&&) Line | Count | Source | 107 | 29.7k | T* construct(Args&&... args) { | 108 | 29.7k | if (currentIndex >= blockSize) { | 109 | 992 | currentBlock = alloc_traits::allocate(alloc, blockSize); | 110 | 992 | allocations.emplace_back(currentBlock); | 111 | 992 | currentIndex = 0; | 112 | 992 | } | 113 | 29.7k | T* object = ¤tBlock[currentIndex++]; | 114 | 29.7k | alloc_traits::construct(alloc, object, std::forward<Args>(args)...); | 115 | 29.7k | return object; | 116 | 29.7k | } |
mapbox::detail::Earcut<unsigned int>::Node* mapbox::detail::Earcut<unsigned int>::ObjectPool<mapbox::detail::Earcut<unsigned int>::Node, std::__1::allocator<mapbox::detail::Earcut<unsigned int>::Node> >::construct<unsigned int const&, double const&, double const&>(unsigned int const&, double const&, double const&) Line | Count | Source | 107 | 26 | T* construct(Args&&... args) { | 108 | 26 | if (currentIndex >= blockSize) { | 109 | 0 | currentBlock = alloc_traits::allocate(alloc, blockSize); | 110 | 0 | allocations.emplace_back(currentBlock); | 111 | 0 | currentIndex = 0; | 112 | 0 | } | 113 | 26 | T* object = ¤tBlock[currentIndex++]; | 114 | 26 | alloc_traits::construct(alloc, object, std::forward<Args>(args)...); | 115 | 26 | return object; | 116 | 26 | } |
|
117 | 2.97k | void reset(std::size_t newBlockSize) { |
118 | 2.97k | for (auto allocation : allocations) { |
119 | 992 | alloc_traits::deallocate(alloc, allocation, blockSize); |
120 | 992 | } |
121 | 2.97k | allocations.clear(); |
122 | 2.97k | blockSize = std::max<std::size_t>(1, newBlockSize); |
123 | 2.97k | currentBlock = nullptr; |
124 | 2.97k | currentIndex = blockSize; |
125 | 2.97k | } |
126 | 1.98k | void clear() { reset(blockSize); } |
127 | | private: |
128 | | T* currentBlock = nullptr; |
129 | | std::size_t currentIndex = 1; |
130 | | std::size_t blockSize = 1; |
131 | | std::vector<T*> allocations; |
132 | | Alloc alloc; |
133 | | typedef typename std::allocator_traits<Alloc> alloc_traits; |
134 | | }; |
135 | | ObjectPool<Node> nodes; |
136 | | }; |
137 | | |
138 | | template <typename N> template <typename Polygon> |
139 | 992 | void Earcut<N>::operator()(const Polygon& points) { |
140 | | // reset |
141 | 992 | indices.clear(); |
142 | 992 | vertices = 0; |
143 | | |
144 | 992 | if (points.empty()) return; |
145 | | |
146 | 992 | double x; |
147 | 992 | double y; |
148 | 992 | int threshold = 80; |
149 | 992 | std::size_t len = 0; |
150 | | |
151 | 1.98k | for (size_t i = 0; threshold >= 0 && i < points.size(); i++) { |
152 | 992 | threshold -= static_cast<int>(points[i].size()); |
153 | 992 | len += points[i].size(); |
154 | 992 | } |
155 | | |
156 | | //estimate size of nodes and indices |
157 | 992 | nodes.reset(len * 3 / 2); |
158 | 992 | indices.reserve(len + points[0].size()); |
159 | | |
160 | 992 | Node* outerNode = linkedList(points[0], true); |
161 | 992 | if (!outerNode || outerNode->prev == outerNode->next) return; |
162 | | |
163 | 992 | if (points.size() > 1) outerNode = eliminateHoles(points, outerNode); |
164 | | |
165 | | // if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox |
166 | 992 | hashing = threshold < 0; |
167 | 992 | if (hashing) { |
168 | 184 | Node* p = outerNode->next; |
169 | 184 | minX = maxX = outerNode->x; |
170 | 184 | minY = maxY = outerNode->y; |
171 | 16.4k | do { |
172 | 16.4k | x = p->x; |
173 | 16.4k | y = p->y; |
174 | 16.4k | minX = std::min<double>(minX, x); |
175 | 16.4k | minY = std::min<double>(minY, y); |
176 | 16.4k | maxX = std::max<double>(maxX, x); |
177 | 16.4k | maxY = std::max<double>(maxY, y); |
178 | 16.4k | p = p->next; |
179 | 16.4k | } while (p != outerNode); |
180 | | |
181 | | // minX, minY and inv_size are later used to transform coords into integers for z-order calculation |
182 | 184 | inv_size = std::max<double>(maxX - minX, maxY - minY); |
183 | 184 | inv_size = inv_size != .0 ? (32767. / inv_size) : .0; |
184 | 184 | } |
185 | | |
186 | 992 | earcutLinked(outerNode); |
187 | | |
188 | 992 | nodes.clear(); |
189 | 992 | } |
190 | | |
191 | | // create a circular doubly linked list from polygon points in the specified winding order |
192 | | template <typename N> template <typename Ring> |
193 | | typename Earcut<N>::Node* |
194 | 992 | Earcut<N>::linkedList(const Ring& points, const bool clockwise) { |
195 | 992 | using Point = typename Ring::value_type; |
196 | 992 | double sum = 0; |
197 | 992 | const std::size_t len = points.size(); |
198 | 992 | std::size_t i, j; |
199 | 992 | Node* last = nullptr; |
200 | | |
201 | | // calculate original winding order of a polygon ring |
202 | 30.7k | for (i = 0, j = len > 0 ? len - 1 : 0; i < len; j = i++) { |
203 | 29.7k | const auto& p1 = points[i]; |
204 | 29.7k | const auto& p2 = points[j]; |
205 | 29.7k | const double p20 = util::nth<0, Point>::get(p2); |
206 | 29.7k | const double p10 = util::nth<0, Point>::get(p1); |
207 | 29.7k | const double p11 = util::nth<1, Point>::get(p1); |
208 | 29.7k | const double p21 = util::nth<1, Point>::get(p2); |
209 | 29.7k | sum += (p20 - p10) * (p11 + p21); |
210 | 29.7k | } |
211 | | |
212 | | // link points into circular doubly-linked list in the specified winding order |
213 | 992 | if (clockwise == (sum > 0)) { |
214 | 19.2k | for (i = 0; i < len; i++) last = insertNode(vertices + i, points[i], last); |
215 | 552 | } else { |
216 | 11.5k | for (i = len; i-- > 0;) last = insertNode(vertices + i, points[i], last); |
217 | 440 | } |
218 | | |
219 | 992 | if (last && equals(last, last->next)) { |
220 | 282 | removeNode(last); |
221 | 282 | last = last->next; |
222 | 282 | } |
223 | | |
224 | 992 | vertices += len; |
225 | | |
226 | 992 | return last; |
227 | 992 | } |
228 | | |
229 | | // eliminate colinear or duplicate points |
230 | | template <typename N> |
231 | | typename Earcut<N>::Node* |
232 | 1.01k | Earcut<N>::filterPoints(Node* start, Node* end) { |
233 | 1.01k | if (!end) end = start; |
234 | | |
235 | 1.01k | Node* p = start; |
236 | 1.01k | bool again; |
237 | 39.3k | do { |
238 | 39.3k | again = false; |
239 | | |
240 | 39.3k | if (!p->steiner && (equals(p, p->next) || area(p->prev, p, p->next) == 0)) { |
241 | 22.2k | removeNode(p); |
242 | 22.2k | p = end = p->prev; |
243 | | |
244 | 22.2k | if (p == p->next) break; |
245 | 21.7k | again = true; |
246 | | |
247 | 21.7k | } else { |
248 | 17.0k | p = p->next; |
249 | 17.0k | } |
250 | 39.3k | } while (again || p != end); |
251 | | |
252 | 1.01k | return end; |
253 | 1.01k | } |
254 | | |
255 | | // main ear slicing loop which triangulates a polygon (given as a linked list) |
256 | | template <typename N> |
257 | 1.91k | void Earcut<N>::earcutLinked(Node* ear, int pass) { |
258 | 1.91k | if (!ear) return; |
259 | | |
260 | | // interlink polygon nodes in z-order |
261 | 1.91k | if (!pass && hashing) indexCurve(ear); |
262 | | |
263 | 1.91k | Node* stop = ear; |
264 | 1.91k | Node* prev; |
265 | 1.91k | Node* next; |
266 | | |
267 | | // iterate through ears, slicing them one by one |
268 | 96.8k | while (ear->prev != ear->next) { |
269 | 95.8k | prev = ear->prev; |
270 | 95.8k | next = ear->next; |
271 | | |
272 | 95.8k | if (hashing ? isEarHashed(ear) : isEar(ear)) { |
273 | | // cut off the triangle |
274 | 5.65k | indices.emplace_back(prev->i); |
275 | 5.65k | indices.emplace_back(ear->i); |
276 | 5.65k | indices.emplace_back(next->i); |
277 | | |
278 | 5.65k | removeNode(ear); |
279 | | |
280 | | // skipping the next vertice leads to less sliver triangles |
281 | 5.65k | ear = next->next; |
282 | 5.65k | stop = next->next; |
283 | | |
284 | 5.65k | continue; |
285 | 5.65k | } |
286 | | |
287 | 90.2k | ear = next; |
288 | | |
289 | | // if we looped through the whole remaining polygon and can't find any more ears |
290 | 90.2k | if (ear == stop) { |
291 | | // try filtering points and slicing again |
292 | 948 | if (!pass) earcutLinked(filterPoints(ear), 1); |
293 | | |
294 | | // if this didn't work, try curing all small self-intersections locally |
295 | 148 | else if (pass == 1) { |
296 | 94 | ear = cureLocalIntersections(filterPoints(ear)); |
297 | 94 | earcutLinked(ear, 2); |
298 | | |
299 | | // as a last resort, try splitting the remaining polygon into two |
300 | 94 | } else if (pass == 2) splitEarcut(ear); |
301 | | |
302 | 948 | break; |
303 | 948 | } |
304 | 90.2k | } |
305 | 1.91k | } |
306 | | |
307 | | // check whether a polygon node forms a valid ear with adjacent nodes |
308 | | template <typename N> |
309 | 45.3k | bool Earcut<N>::isEar(Node* ear) { |
310 | 45.3k | const Node* a = ear->prev; |
311 | 45.3k | const Node* b = ear; |
312 | 45.3k | const Node* c = ear->next; |
313 | | |
314 | 45.3k | if (area(a, b, c) >= 0) return false; // reflex, can't be an ear |
315 | | |
316 | | // now make sure we don't have other points inside the potential ear |
317 | 17.2k | Node* p = ear->next->next; |
318 | | |
319 | 265k | while (p != ear->prev) { |
320 | 261k | if (pointInTriangle(a->x, a->y, b->x, b->y, c->x, c->y, p->x, p->y) && |
321 | 15.6k | area(p->prev, p, p->next) >= 0) return false; |
322 | 248k | p = p->next; |
323 | 248k | } |
324 | | |
325 | 4.20k | return true; |
326 | 17.2k | } |
327 | | |
328 | | template <typename N> |
329 | 50.5k | bool Earcut<N>::isEarHashed(Node* ear) { |
330 | 50.5k | const Node* a = ear->prev; |
331 | 50.5k | const Node* b = ear; |
332 | 50.5k | const Node* c = ear->next; |
333 | | |
334 | 50.5k | if (area(a, b, c) >= 0) return false; // reflex, can't be an ear |
335 | | |
336 | | // triangle bbox; min & max are calculated like this for speed |
337 | 10.0k | const double minTX = std::min<double>(a->x, std::min<double>(b->x, c->x)); |
338 | 10.0k | const double minTY = std::min<double>(a->y, std::min<double>(b->y, c->y)); |
339 | 10.0k | const double maxTX = std::max<double>(a->x, std::max<double>(b->x, c->x)); |
340 | 10.0k | const double maxTY = std::max<double>(a->y, std::max<double>(b->y, c->y)); |
341 | | |
342 | | // z-order range for the current triangle bbox; |
343 | 10.0k | const int32_t minZ = zOrder(minTX, minTY); |
344 | 10.0k | const int32_t maxZ = zOrder(maxTX, maxTY); |
345 | | |
346 | | // first look for points inside the triangle in increasing z-order |
347 | 10.0k | Node* p = ear->nextZ; |
348 | | |
349 | 173k | while (p && p->z <= maxZ) { |
350 | 172k | if (p != ear->prev && p != ear->next && |
351 | 162k | pointInTriangle(a->x, a->y, b->x, b->y, c->x, c->y, p->x, p->y) && |
352 | 8.51k | area(p->prev, p, p->next) >= 0) return false; |
353 | 163k | p = p->nextZ; |
354 | 163k | } |
355 | | |
356 | | // then look for points in decreasing z-order |
357 | 1.73k | p = ear->prevZ; |
358 | | |
359 | 29.1k | while (p && p->z >= minZ) { |
360 | 27.7k | if (p != ear->prev && p != ear->next && |
361 | 25.9k | pointInTriangle(a->x, a->y, b->x, b->y, c->x, c->y, p->x, p->y) && |
362 | 367 | area(p->prev, p, p->next) >= 0) return false; |
363 | 27.4k | p = p->prevZ; |
364 | 27.4k | } |
365 | | |
366 | 1.44k | return true; |
367 | 1.73k | } |
368 | | |
369 | | // go through all polygon nodes and cure small local self-intersections |
370 | | template <typename N> |
371 | | typename Earcut<N>::Node* |
372 | 94 | Earcut<N>::cureLocalIntersections(Node* start) { |
373 | 94 | Node* p = start; |
374 | 359 | do { |
375 | 359 | Node* a = p->prev; |
376 | 359 | Node* b = p->next->next; |
377 | | |
378 | | // a self-intersection where edge (v[i-1],v[i]) intersects (v[i+1],v[i+2]) |
379 | 359 | if (!equals(a, b) && intersects(a, p, p->next, b) && locallyInside(a, b) && locallyInside(b, a)) { |
380 | 3 | indices.emplace_back(a->i); |
381 | 3 | indices.emplace_back(p->i); |
382 | 3 | indices.emplace_back(b->i); |
383 | | |
384 | | // remove two nodes involved |
385 | 3 | removeNode(p); |
386 | 3 | removeNode(p->next); |
387 | | |
388 | 3 | p = start = b; |
389 | 3 | } |
390 | 359 | p = p->next; |
391 | 359 | } while (p != start); |
392 | | |
393 | 94 | return filterPoints(p); |
394 | 94 | } |
395 | | |
396 | | // try splitting polygon into two and triangulate them independently |
397 | | template <typename N> |
398 | 54 | void Earcut<N>::splitEarcut(Node* start) { |
399 | | // look for a valid diagonal that divides the polygon into two |
400 | 54 | Node* a = start; |
401 | 198 | do { |
402 | 198 | Node* b = a->next->next; |
403 | 723 | while (b != a->prev) { |
404 | 538 | if (a->i != b->i && isValidDiagonal(a, b)) { |
405 | | // split the polygon in two by the diagonal |
406 | 13 | Node* c = splitPolygon(a, b); |
407 | | |
408 | | // filter colinear points around the cuts |
409 | 13 | a = filterPoints(a, a->next); |
410 | 13 | c = filterPoints(c, c->next); |
411 | | |
412 | | // run earcut on each half |
413 | 13 | earcutLinked(a); |
414 | 13 | earcutLinked(c); |
415 | 13 | return; |
416 | 13 | } |
417 | 525 | b = b->next; |
418 | 525 | } |
419 | 185 | a = a->next; |
420 | 185 | } while (a != start); |
421 | 54 | } |
422 | | |
423 | | // link every hole into the outer loop, producing a single-ring polygon without holes |
424 | | template <typename N> template <typename Polygon> |
425 | | typename Earcut<N>::Node* |
426 | 0 | Earcut<N>::eliminateHoles(const Polygon& points, Node* outerNode) { |
427 | 0 | const size_t len = points.size(); |
428 | |
|
429 | 0 | std::vector<Node*> queue; |
430 | 0 | for (size_t i = 1; i < len; i++) { |
431 | 0 | Node* list = linkedList(points[i], false); |
432 | 0 | if (list) { |
433 | 0 | if (list == list->next) list->steiner = true; |
434 | 0 | queue.push_back(getLeftmost(list)); |
435 | 0 | } |
436 | 0 | } |
437 | 0 | std::sort(queue.begin(), queue.end(), [](const Node* a, const Node* b) { |
438 | 0 | return a->x < b->x; |
439 | 0 | }); |
440 | | |
441 | | // process holes from left to right |
442 | 0 | for (size_t i = 0; i < queue.size(); i++) { |
443 | 0 | outerNode = eliminateHole(queue[i], outerNode); |
444 | 0 | } |
445 | |
|
446 | 0 | return outerNode; |
447 | 0 | } |
448 | | |
449 | | // find a bridge between vertices that connects hole with an outer ring and and link it |
450 | | template <typename N> |
451 | | typename Earcut<N>::Node* |
452 | 0 | Earcut<N>::eliminateHole(Node* hole, Node* outerNode) { |
453 | 0 | Node* bridge = findHoleBridge(hole, outerNode); |
454 | 0 | if (!bridge) { |
455 | 0 | return outerNode; |
456 | 0 | } |
457 | | |
458 | 0 | Node* bridgeReverse = splitPolygon(bridge, hole); |
459 | | |
460 | | // filter collinear points around the cuts |
461 | 0 | filterPoints(bridgeReverse, bridgeReverse->next); |
462 | | |
463 | | // Check if input node was removed by the filtering |
464 | 0 | return filterPoints(bridge, bridge->next); |
465 | 0 | } |
466 | | |
467 | | // David Eberly's algorithm for finding a bridge between hole and outer polygon |
468 | | template <typename N> |
469 | | typename Earcut<N>::Node* |
470 | 0 | Earcut<N>::findHoleBridge(Node* hole, Node* outerNode) { |
471 | 0 | Node* p = outerNode; |
472 | 0 | double hx = hole->x; |
473 | 0 | double hy = hole->y; |
474 | 0 | double qx = -std::numeric_limits<double>::infinity(); |
475 | 0 | Node* m = nullptr; |
476 | | |
477 | | // find a segment intersected by a ray from the hole's leftmost Vertex to the left; |
478 | | // segment's endpoint with lesser x will be potential connection Vertex |
479 | 0 | do { |
480 | 0 | if (hy <= p->y && hy >= p->next->y && p->next->y != p->y) { |
481 | 0 | double x = p->x + (hy - p->y) * (p->next->x - p->x) / (p->next->y - p->y); |
482 | 0 | if (x <= hx && x > qx) { |
483 | 0 | qx = x; |
484 | 0 | m = p->x < p->next->x ? p : p->next; |
485 | 0 | if (x == hx) return m; // hole touches outer segment; pick leftmost endpoint |
486 | 0 | } |
487 | 0 | } |
488 | 0 | p = p->next; |
489 | 0 | } while (p != outerNode); |
490 | | |
491 | 0 | if (!m) return 0; |
492 | | |
493 | | // look for points inside the triangle of hole Vertex, segment intersection and endpoint; |
494 | | // if there are no points found, we have a valid connection; |
495 | | // otherwise choose the Vertex of the minimum angle with the ray as connection Vertex |
496 | | |
497 | 0 | const Node* stop = m; |
498 | 0 | double tanMin = std::numeric_limits<double>::infinity(); |
499 | 0 | double tanCur = 0; |
500 | |
|
501 | 0 | p = m; |
502 | 0 | double mx = m->x; |
503 | 0 | double my = m->y; |
504 | |
|
505 | 0 | do { |
506 | 0 | if (hx >= p->x && p->x >= mx && hx != p->x && |
507 | 0 | pointInTriangle(hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p->x, p->y)) { |
508 | |
|
509 | 0 | tanCur = std::abs(hy - p->y) / (hx - p->x); // tangential |
510 | |
|
511 | 0 | if (locallyInside(p, hole) && |
512 | 0 | (tanCur < tanMin || (tanCur == tanMin && (p->x > m->x || sectorContainsSector(m, p))))) { |
513 | 0 | m = p; |
514 | 0 | tanMin = tanCur; |
515 | 0 | } |
516 | 0 | } |
517 | |
|
518 | 0 | p = p->next; |
519 | 0 | } while (p != stop); |
520 | |
|
521 | 0 | return m; |
522 | 0 | } |
523 | | |
524 | | // whether sector in vertex m contains sector in vertex p in the same coordinates |
525 | | template <typename N> |
526 | 0 | bool Earcut<N>::sectorContainsSector(const Node* m, const Node* p) { |
527 | 0 | return area(m->prev, m, p->prev) < 0 && area(p->next, m, m->next) < 0; |
528 | 0 | } |
529 | | |
530 | | // interlink polygon nodes in z-order |
531 | | template <typename N> |
532 | 200 | void Earcut<N>::indexCurve(Node* start) { |
533 | 200 | assert(start); |
534 | 200 | Node* p = start; |
535 | | |
536 | 16.6k | do { |
537 | 16.6k | p->z = p->z ? p->z : zOrder(p->x, p->y); |
538 | 16.6k | p->prevZ = p->prev; |
539 | 16.6k | p->nextZ = p->next; |
540 | 16.6k | p = p->next; |
541 | 16.6k | } while (p != start); |
542 | | |
543 | 200 | p->prevZ->nextZ = nullptr; |
544 | 200 | p->prevZ = nullptr; |
545 | | |
546 | 200 | sortLinked(p); |
547 | 200 | } |
548 | | |
549 | | // Simon Tatham's linked list merge sort algorithm |
550 | | // http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html |
551 | | template <typename N> |
552 | | typename Earcut<N>::Node* |
553 | 200 | Earcut<N>::sortLinked(Node* list) { |
554 | 200 | assert(list); |
555 | 200 | Node* p; |
556 | 200 | Node* q; |
557 | 200 | Node* e; |
558 | 200 | Node* tail; |
559 | 200 | int i, numMerges, pSize, qSize; |
560 | 200 | int inSize = 1; |
561 | | |
562 | 1.32k | for (;;) { |
563 | 1.32k | p = list; |
564 | 1.32k | list = nullptr; |
565 | 1.32k | tail = nullptr; |
566 | 1.32k | numMerges = 0; |
567 | | |
568 | 18.3k | while (p) { |
569 | 17.0k | numMerges++; |
570 | 17.0k | q = p; |
571 | 17.0k | pSize = 0; |
572 | 81.5k | for (i = 0; i < inSize; i++) { |
573 | 65.0k | pSize++; |
574 | 65.0k | q = q->nextZ; |
575 | 65.0k | if (!q) break; |
576 | 65.0k | } |
577 | | |
578 | 17.0k | qSize = inSize; |
579 | | |
580 | 133k | while (pSize > 0 || (qSize > 0 && q)) { |
581 | | |
582 | 116k | if (pSize == 0) { |
583 | 38.1k | e = q; |
584 | 38.1k | q = q->nextZ; |
585 | 38.1k | qSize--; |
586 | 78.2k | } else if (qSize == 0 || !q) { |
587 | 8.09k | e = p; |
588 | 8.09k | p = p->nextZ; |
589 | 8.09k | pSize--; |
590 | 70.1k | } else if (p->z <= q->z) { |
591 | 56.9k | e = p; |
592 | 56.9k | p = p->nextZ; |
593 | 56.9k | pSize--; |
594 | 56.9k | } else { |
595 | 13.2k | e = q; |
596 | 13.2k | q = q->nextZ; |
597 | 13.2k | qSize--; |
598 | 13.2k | } |
599 | | |
600 | 116k | if (tail) tail->nextZ = e; |
601 | 1.32k | else list = e; |
602 | | |
603 | 116k | e->prevZ = tail; |
604 | 116k | tail = e; |
605 | 116k | } |
606 | | |
607 | 17.0k | p = q; |
608 | 17.0k | } |
609 | | |
610 | 1.32k | tail->nextZ = nullptr; |
611 | | |
612 | 1.32k | if (numMerges <= 1) return list; |
613 | | |
614 | 1.12k | inSize *= 2; |
615 | 1.12k | } |
616 | 200 | } |
617 | | |
618 | | // z-order of a Vertex given coords and size of the data bounding box |
619 | | template <typename N> |
620 | 36.7k | int32_t Earcut<N>::zOrder(const double x_, const double y_) { |
621 | | // coords are transformed into non-negative 15-bit integer range |
622 | 36.7k | int32_t x = static_cast<int32_t>((x_ - minX) * inv_size); |
623 | 36.7k | int32_t y = static_cast<int32_t>((y_ - minY) * inv_size); |
624 | | |
625 | 36.7k | x = (x | (x << 8)) & 0x00FF00FF; |
626 | 36.7k | x = (x | (x << 4)) & 0x0F0F0F0F; |
627 | 36.7k | x = (x | (x << 2)) & 0x33333333; |
628 | 36.7k | x = (x | (x << 1)) & 0x55555555; |
629 | | |
630 | 36.7k | y = (y | (y << 8)) & 0x00FF00FF; |
631 | 36.7k | y = (y | (y << 4)) & 0x0F0F0F0F; |
632 | 36.7k | y = (y | (y << 2)) & 0x33333333; |
633 | 36.7k | y = (y | (y << 1)) & 0x55555555; |
634 | | |
635 | 36.7k | return x | (y << 1); |
636 | 36.7k | } |
637 | | |
638 | | // find the leftmost node of a polygon ring |
639 | | template <typename N> |
640 | | typename Earcut<N>::Node* |
641 | 0 | Earcut<N>::getLeftmost(Node* start) { |
642 | 0 | Node* p = start; |
643 | 0 | Node* leftmost = start; |
644 | 0 | do { |
645 | 0 | if (p->x < leftmost->x || (p->x == leftmost->x && p->y < leftmost->y)) |
646 | 0 | leftmost = p; |
647 | 0 | p = p->next; |
648 | 0 | } while (p != start); |
649 | |
|
650 | 0 | return leftmost; |
651 | 0 | } |
652 | | |
653 | | // check if a point lies within a convex triangle |
654 | | template <typename N> |
655 | 449k | bool Earcut<N>::pointInTriangle(double ax, double ay, double bx, double by, double cx, double cy, double px, double py) const { |
656 | 449k | return (cx - px) * (ay - py) >= (ax - px) * (cy - py) && |
657 | 75.0k | (ax - px) * (by - py) >= (bx - px) * (ay - py) && |
658 | 58.1k | (bx - px) * (cy - py) >= (cx - px) * (by - py); |
659 | 449k | } |
660 | | |
661 | | // check if a diagonal between two polygon nodes is valid (lies in polygon interior) |
662 | | template <typename N> |
663 | 538 | bool Earcut<N>::isValidDiagonal(Node* a, Node* b) { |
664 | 538 | return a->next->i != b->i && a->prev->i != b->i && !intersectsPolygon(a, b) && // dones't intersect other edges |
665 | 85 | ((locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b) && // locally visible |
666 | 4 | (area(a->prev, a, b->prev) != 0.0 || area(a, b->prev, b) != 0.0)) || // does not create opposite-facing sectors |
667 | 81 | (equals(a, b) && area(a->prev, a, a->next) > 0 && area(b->prev, b, b->next) > 0)); // special zero-length case |
668 | 538 | } |
669 | | |
670 | | // signed area of a triangle |
671 | | template <typename N> |
672 | 146k | double Earcut<N>::area(const Node* p, const Node* q, const Node* r) const { |
673 | 146k | return (q->y - p->y) * (r->x - q->x) - (q->x - p->x) * (r->y - q->y); |
674 | 146k | } |
675 | | |
676 | | // check if two points are equal |
677 | | template <typename N> |
678 | 40.7k | bool Earcut<N>::equals(const Node* p1, const Node* p2) { |
679 | 40.7k | return p1->x == p2->x && p1->y == p2->y; |
680 | 40.7k | } |
681 | | |
682 | | // check if two segments intersect |
683 | | template <typename N> |
684 | 1.26k | bool Earcut<N>::intersects(const Node* p1, const Node* q1, const Node* p2, const Node* q2) { |
685 | 1.26k | int o1 = sign(area(p1, q1, p2)); |
686 | 1.26k | int o2 = sign(area(p1, q1, q2)); |
687 | 1.26k | int o3 = sign(area(p2, q2, p1)); |
688 | 1.26k | int o4 = sign(area(p2, q2, q1)); |
689 | | |
690 | 1.26k | if (o1 != o2 && o3 != o4) return true; // general case |
691 | | |
692 | 829 | if (o1 == 0 && onSegment(p1, p2, q1)) return true; // p1, q1 and p2 are collinear and p2 lies on p1q1 |
693 | 776 | if (o2 == 0 && onSegment(p1, q2, q1)) return true; // p1, q1 and q2 are collinear and q2 lies on p1q1 |
694 | 776 | if (o3 == 0 && onSegment(p2, p1, q2)) return true; // p2, q2 and p1 are collinear and p1 lies on p2q2 |
695 | 776 | if (o4 == 0 && onSegment(p2, q1, q2)) return true; // p2, q2 and q1 are collinear and q1 lies on p2q2 |
696 | | |
697 | 776 | return false; |
698 | 776 | } |
699 | | |
700 | | // for collinear points p, q, r, check if point q lies on segment pr |
701 | | template <typename N> |
702 | 689 | bool Earcut<N>::onSegment(const Node* p, const Node* q, const Node* r) { |
703 | 689 | return q->x <= std::max<double>(p->x, r->x) && |
704 | 458 | q->x >= std::min<double>(p->x, r->x) && |
705 | 207 | q->y <= std::max<double>(p->y, r->y) && |
706 | 128 | q->y >= std::min<double>(p->y, r->y); |
707 | 689 | } |
708 | | |
709 | | template <typename N> |
710 | 5.04k | int Earcut<N>::sign(double val) { |
711 | 5.04k | return (0.0 < val) - (val < 0.0); |
712 | 5.04k | } |
713 | | |
714 | | // check if a polygon diagonal intersects any polygon segments |
715 | | template <typename N> |
716 | 538 | bool Earcut<N>::intersectsPolygon(const Node* a, const Node* b) { |
717 | 538 | const Node* p = a; |
718 | 2.20k | do { |
719 | 2.20k | if (p->i != a->i && p->next->i != a->i && p->i != b->i && p->next->i != b->i && |
720 | 1.10k | intersects(p, p->next, a, b)) return true; |
721 | 1.75k | p = p->next; |
722 | 1.75k | } while (p != a); |
723 | | |
724 | 85 | return false; |
725 | 538 | } |
726 | | |
727 | | // check if a polygon diagonal is locally inside the polygon |
728 | | template <typename N> |
729 | 152 | bool Earcut<N>::locallyInside(const Node* a, const Node* b) { |
730 | 152 | return area(a->prev, a, a->next) < 0 ? |
731 | 54 | area(a, b, a->next) >= 0 && area(a, a->prev, b) >= 0 : |
732 | 152 | area(a, b, a->prev) < 0 || area(a, a->next, b) < 0; |
733 | 152 | } |
734 | | |
735 | | // check if the middle Vertex of a polygon diagonal is inside the polygon |
736 | | template <typename N> |
737 | 4 | bool Earcut<N>::middleInside(const Node* a, const Node* b) { |
738 | 4 | const Node* p = a; |
739 | 4 | bool inside = false; |
740 | 4 | double px = (a->x + b->x) / 2; |
741 | 4 | double py = (a->y + b->y) / 2; |
742 | 24 | do { |
743 | 24 | if (((p->y > py) != (p->next->y > py)) && p->next->y != p->y && |
744 | 16 | (px < (p->next->x - p->x) * (py - p->y) / (p->next->y - p->y) + p->x)) |
745 | 12 | inside = !inside; |
746 | 24 | p = p->next; |
747 | 24 | } while (p != a); |
748 | | |
749 | 4 | return inside; |
750 | 4 | } |
751 | | |
752 | | // link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits |
753 | | // polygon into two; if one belongs to the outer ring and another to a hole, it merges it into a |
754 | | // single ring |
755 | | template <typename N> |
756 | | typename Earcut<N>::Node* |
757 | 13 | Earcut<N>::splitPolygon(Node* a, Node* b) { |
758 | 13 | Node* a2 = nodes.construct(a->i, a->x, a->y); |
759 | 13 | Node* b2 = nodes.construct(b->i, b->x, b->y); |
760 | 13 | Node* an = a->next; |
761 | 13 | Node* bp = b->prev; |
762 | | |
763 | 13 | a->next = b; |
764 | 13 | b->prev = a; |
765 | | |
766 | 13 | a2->next = an; |
767 | 13 | an->prev = a2; |
768 | | |
769 | 13 | b2->next = a2; |
770 | 13 | a2->prev = b2; |
771 | | |
772 | 13 | bp->next = b2; |
773 | 13 | b2->prev = bp; |
774 | | |
775 | 13 | return b2; |
776 | 13 | } |
777 | | |
778 | | // create a node and util::optionally link it with previous one (in a circular doubly linked list) |
779 | | template <typename N> template <typename Point> |
780 | | typename Earcut<N>::Node* |
781 | 29.7k | Earcut<N>::insertNode(std::size_t i, const Point& pt, Node* last) { |
782 | 29.7k | Node* p = nodes.construct(static_cast<N>(i), util::nth<0, Point>::get(pt), util::nth<1, Point>::get(pt)); |
783 | | |
784 | 29.7k | if (!last) { |
785 | 992 | p->prev = p; |
786 | 992 | p->next = p; |
787 | | |
788 | 28.7k | } else { |
789 | 28.7k | assert(last); |
790 | 28.7k | p->next = last->next; |
791 | 28.7k | p->prev = last; |
792 | 28.7k | last->next->prev = p; |
793 | 28.7k | last->next = p; |
794 | 28.7k | } |
795 | 29.7k | return p; |
796 | 29.7k | } |
797 | | |
798 | | template <typename N> |
799 | 28.1k | void Earcut<N>::removeNode(Node* p) { |
800 | 28.1k | p->next->prev = p->prev; |
801 | 28.1k | p->prev->next = p->next; |
802 | | |
803 | 28.1k | if (p->prevZ) p->prevZ->nextZ = p->nextZ; |
804 | 28.1k | if (p->nextZ) p->nextZ->prevZ = p->prevZ; |
805 | 28.1k | } |
806 | | } |
807 | | |
808 | | template <typename N = uint32_t, typename Polygon> |
809 | 992 | std::vector<N> earcut(const Polygon& poly) { |
810 | 992 | mapbox::detail::Earcut<N> earcut; |
811 | 992 | earcut(poly); |
812 | 992 | return std::move(earcut.indices); |
813 | 992 | } |
814 | | } |