imops 0.8.8__cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. _build_utils.py +113 -0
  2. imops/__init__.py +10 -0
  3. imops/__version__.py +1 -0
  4. imops/_configs.py +29 -0
  5. imops/backend.py +95 -0
  6. imops/box.py +74 -0
  7. imops/cpp/cpp_modules.cpython-312-x86_64-linux-gnu.so +0 -0
  8. imops/cpp/interp2d/delaunator/delaunator-header-only.hpp +33 -0
  9. imops/cpp/interp2d/delaunator/delaunator.cpp +645 -0
  10. imops/cpp/interp2d/delaunator/delaunator.hpp +170 -0
  11. imops/cpp/interp2d/interpolator.h +52 -0
  12. imops/cpp/interp2d/triangulator.h +198 -0
  13. imops/cpp/interp2d/utils.h +63 -0
  14. imops/cpp/main.cpp +13 -0
  15. imops/crop.py +120 -0
  16. imops/interp1d.py +207 -0
  17. imops/interp2d.py +120 -0
  18. imops/measure.py +228 -0
  19. imops/morphology.py +525 -0
  20. imops/numeric.py +384 -0
  21. imops/pad.py +253 -0
  22. imops/py.typed +0 -0
  23. imops/radon.py +247 -0
  24. imops/src/__init__.py +0 -0
  25. imops/src/_backprojection.c +27339 -0
  26. imops/src/_backprojection.cpython-312-x86_64-linux-gnu.so +0 -0
  27. imops/src/_fast_backprojection.c +27374 -0
  28. imops/src/_fast_backprojection.cpython-312-x86_64-linux-gnu.so +0 -0
  29. imops/src/_fast_measure.c +33845 -0
  30. imops/src/_fast_measure.cpython-312-x86_64-linux-gnu.so +0 -0
  31. imops/src/_fast_morphology.c +26124 -0
  32. imops/src/_fast_morphology.cpython-312-x86_64-linux-gnu.so +0 -0
  33. imops/src/_fast_numeric.c +48686 -0
  34. imops/src/_fast_numeric.cpython-312-x86_64-linux-gnu.so +0 -0
  35. imops/src/_fast_radon.c +30749 -0
  36. imops/src/_fast_radon.cpython-312-x86_64-linux-gnu.so +0 -0
  37. imops/src/_fast_zoom.c +57238 -0
  38. imops/src/_fast_zoom.cpython-312-x86_64-linux-gnu.so +0 -0
  39. imops/src/_measure.c +33810 -0
  40. imops/src/_measure.cpython-312-x86_64-linux-gnu.so +0 -0
  41. imops/src/_morphology.c +26089 -0
  42. imops/src/_morphology.cpython-312-x86_64-linux-gnu.so +0 -0
  43. imops/src/_numba_zoom.py +503 -0
  44. imops/src/_numeric.c +48651 -0
  45. imops/src/_numeric.cpython-312-x86_64-linux-gnu.so +0 -0
  46. imops/src/_radon.c +30714 -0
  47. imops/src/_radon.cpython-312-x86_64-linux-gnu.so +0 -0
  48. imops/src/_zoom.c +57203 -0
  49. imops/src/_zoom.cpython-312-x86_64-linux-gnu.so +0 -0
  50. imops/testing.py +57 -0
  51. imops/utils.py +205 -0
  52. imops/zoom.py +297 -0
  53. imops-0.8.8.dist-info/LICENSE +21 -0
  54. imops-0.8.8.dist-info/METADATA +218 -0
  55. imops-0.8.8.dist-info/RECORD +58 -0
  56. imops-0.8.8.dist-info/WHEEL +6 -0
  57. imops-0.8.8.dist-info/top_level.txt +2 -0
  58. imops.libs/libgomp-a34b3233.so.1.0.0 +0 -0
@@ -0,0 +1,645 @@
1
+ // MIT License
2
+
3
+ // Copyright (c) 2018 Volodymyr Bilonenko
4
+
5
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ // of this software and associated documentation files (the "Software"), to deal
7
+ // in the Software without restriction, including without limitation the rights
8
+ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ // copies of the Software, and to permit persons to whom the Software is
10
+ // furnished to do so, subject to the following conditions:
11
+
12
+ // The above copyright notice and this permission notice shall be included in all
13
+ // copies or substantial portions of the Software.
14
+
15
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ // SOFTWARE.
22
+
23
+
24
+
25
+ #include "delaunator.hpp"
26
+
27
+ #include <iostream>
28
+
29
+ #include <algorithm>
30
+ #include <cmath>
31
+ #include <numeric>
32
+ #include <limits>
33
+ #include <stdexcept>
34
+ #include <tuple>
35
+ #include <vector>
36
+
37
+ namespace delaunator {
38
+
39
+ //@see https://stackoverflow.com/questions/33333363/built-in-mod-vs-custom-mod-function-improve-the-performance-of-modulus-op/33333636#33333636
40
+ inline size_t fast_mod(const size_t i, const size_t c) {
41
+ return i >= c ? i % c : i;
42
+ }
43
+
44
+ // Kahan and Babuska summation, Neumaier variant; accumulates less FP error
45
+ inline double sum(const std::vector<double>& x) {
46
+ double sum = x[0];
47
+ double err = 0.0;
48
+
49
+ for (size_t i = 1; i < x.size(); i++) {
50
+ const double k = x[i];
51
+ const double m = sum + k;
52
+ err += std::fabs(sum) >= std::fabs(k) ? sum - m + k : k - m + sum;
53
+ sum = m;
54
+ }
55
+ return sum + err;
56
+ }
57
+
58
+ inline double dist(
59
+ const double ax,
60
+ const double ay,
61
+ const double bx,
62
+ const double by) {
63
+ const double dx = ax - bx;
64
+ const double dy = ay - by;
65
+ return dx * dx + dy * dy;
66
+ }
67
+
68
+ inline double circumradius(const Point& p1, const Point& p2, const Point& p3)
69
+ {
70
+ Point d = Point::vector(p1, p2);
71
+ Point e = Point::vector(p1, p3);
72
+
73
+ const double bl = d.magnitude2();
74
+ const double cl = e.magnitude2();
75
+ const double det = Point::determinant(d, e);
76
+
77
+ Point radius((e.y() * bl - d.y() * cl) * 0.5 / det,
78
+ (d.x() * cl - e.x() * bl) * 0.5 / det);
79
+
80
+ if ((bl > 0.0 || bl < 0.0) &&
81
+ (cl > 0.0 || cl < 0.0) &&
82
+ (det > 0.0 || det < 0.0))
83
+ return radius.magnitude2();
84
+ return (std::numeric_limits<double>::max)();
85
+ }
86
+
87
+ inline bool clockwise(const Point& p0, const Point& p1, const Point& p2)
88
+ {
89
+ Point v0 = Point::vector(p0, p1);
90
+ Point v1 = Point::vector(p0, p2);
91
+ double det = Point::determinant(v0, v1);
92
+ if (det == 0)
93
+ {
94
+ return false;
95
+ }
96
+ double dist = v0.magnitude2() + v1.magnitude2();
97
+ double reldet = std::abs(dist / det);
98
+ if (reldet > 1e14)
99
+ return false;
100
+ return det < 0;
101
+ }
102
+
103
+ inline bool clockwise(double px, double py, double qx, double qy,
104
+ double rx, double ry)
105
+ {
106
+ Point p0(px, py);
107
+ Point p1(qx, qy);
108
+ Point p2(rx, ry);
109
+ return clockwise(p0, p1, p2);
110
+ }
111
+
112
+ inline bool counterclockwise(const Point& p0, const Point& p1, const Point& p2)
113
+ {
114
+ Point v0 = Point::vector(p0, p1);
115
+ Point v1 = Point::vector(p0, p2);
116
+ double det = Point::determinant(v0, v1);
117
+ if (det == 0)
118
+ return false;
119
+ double dist = v0.magnitude2() + v1.magnitude2();
120
+ double reldet = std::abs(dist / det);
121
+ if (reldet > 1e14)
122
+ return false;
123
+ return det > 0;
124
+ }
125
+
126
+ inline bool counterclockwise(double px, double py, double qx, double qy,
127
+ double rx, double ry)
128
+ {
129
+ Point p0(px, py);
130
+ Point p1(qx, qy);
131
+ Point p2(rx, ry);
132
+ return counterclockwise(p0, p1, p2);
133
+ }
134
+
135
+
136
+ inline Point circumcenter(
137
+ const double ax,
138
+ const double ay,
139
+ const double bx,
140
+ const double by,
141
+ const double cx,
142
+ const double cy) {
143
+ const double dx = bx - ax;
144
+ const double dy = by - ay;
145
+ const double ex = cx - ax;
146
+ const double ey = cy - ay;
147
+
148
+ const double bl = dx * dx + dy * dy;
149
+ const double cl = ex * ex + ey * ey;
150
+ //ABELL - This is suspect for div-by-0.
151
+ const double d = dx * ey - dy * ex;
152
+
153
+ if (d == 0.0) {
154
+ throw std::runtime_error("Division by zero");
155
+ }
156
+
157
+ const double x = ax + (ey * bl - dy * cl) * 0.5 / d;
158
+ const double y = ay + (dx * cl - ex * bl) * 0.5 / d;
159
+
160
+ return Point(x, y);
161
+ }
162
+
163
+ inline bool in_circle(
164
+ const double ax,
165
+ const double ay,
166
+ const double bx,
167
+ const double by,
168
+ const double cx,
169
+ const double cy,
170
+ const double px,
171
+ const double py) {
172
+ const double dx = ax - px;
173
+ const double dy = ay - py;
174
+ const double ex = bx - px;
175
+ const double ey = by - py;
176
+ const double fx = cx - px;
177
+ const double fy = cy - py;
178
+
179
+ const double ap = dx * dx + dy * dy;
180
+ const double bp = ex * ex + ey * ey;
181
+ const double cp = fx * fx + fy * fy;
182
+
183
+ return (dx * (ey * cp - bp * fy) -
184
+ dy * (ex * cp - bp * fx) +
185
+ ap * (ex * fy - ey * fx)) < 0.0;
186
+ }
187
+
188
+ constexpr double EPSILON = std::numeric_limits<double>::epsilon();
189
+
190
+ inline bool check_pts_equal(double x1, double y1, double x2, double y2) {
191
+ return std::fabs(x1 - x2) <= EPSILON &&
192
+ std::fabs(y1 - y2) <= EPSILON;
193
+ }
194
+
195
+ // monotonically increases with real angle, but doesn't need expensive trigonometry
196
+ inline double pseudo_angle(const double dx, const double dy) {
197
+ const double p = dx / (std::abs(dx) + std::abs(dy));
198
+ return (dy > 0.0 ? 3.0 - p : 1.0 + p) / 4.0; // [0..1)
199
+ }
200
+
201
+
202
+ Delaunator::Delaunator(std::vector<double> const& in_coords)
203
+ : coords(in_coords), m_points(in_coords)
204
+ {
205
+ std::size_t n = coords.size() >> 1;
206
+
207
+ std::vector<std::size_t> ids(n);
208
+ std::iota(ids.begin(), ids.end(), 0);
209
+
210
+ double max_x = std::numeric_limits<double>::lowest();
211
+ double max_y = std::numeric_limits<double>::lowest();
212
+ double min_x = std::numeric_limits<double>::max();
213
+ double min_y = std::numeric_limits<double>::max();
214
+ for (const Point& p : m_points)
215
+ {
216
+ min_x = std::min(p.x(), min_x);
217
+ min_y = std::min(p.y(), min_y);
218
+ max_x = std::max(p.x(), max_x);
219
+ max_y = std::max(p.y(), max_y);
220
+ }
221
+ double width = max_x - min_x;
222
+ double height = max_y - min_y;
223
+ double span = width * width + height * height; // Everything is square dist.
224
+
225
+ Point center((min_x + max_x) / 2, (min_y + max_y) / 2);
226
+
227
+ std::size_t i0 = INVALID_INDEX;
228
+ std::size_t i1 = INVALID_INDEX;
229
+ std::size_t i2 = INVALID_INDEX;
230
+
231
+ // pick a seed point close to the centroid
232
+ double min_dist = std::numeric_limits<double>::max();
233
+ for (size_t i = 0; i < m_points.size(); ++i)
234
+ {
235
+ const double d = Point::dist2(center, m_points[i]);
236
+ if (d < min_dist) {
237
+ i0 = i;
238
+ min_dist = d;
239
+ }
240
+ }
241
+
242
+ const Point& p0 = m_points[i0];
243
+
244
+ min_dist = std::numeric_limits<double>::max();
245
+
246
+ // find the point closest to the seed
247
+ for (std::size_t i = 0; i < n; i++) {
248
+ if (i == i0) continue;
249
+ const double d = Point::dist2(p0, m_points[i]);
250
+ if (d < min_dist && d > 0.0) {
251
+ i1 = i;
252
+ min_dist = d;
253
+ }
254
+ }
255
+
256
+ const Point& p1 = m_points[i1];
257
+
258
+ double min_radius = std::numeric_limits<double>::max();
259
+
260
+ // find the third point which forms the smallest circumcircle
261
+ // with the first two
262
+ for (std::size_t i = 0; i < n; i++) {
263
+ if (i == i0 || i == i1) continue;
264
+
265
+ const double r = circumradius(p0, p1, m_points[i]);
266
+ if (r < min_radius) {
267
+ i2 = i;
268
+ min_radius = r;
269
+ }
270
+ }
271
+
272
+ if (!(min_radius < std::numeric_limits<double>::max())) {
273
+ throw std::runtime_error("not triangulation");
274
+ }
275
+
276
+ const Point& p2 = m_points[i2];
277
+
278
+ if (counterclockwise(p0, p1, p2))
279
+ std::swap(i1, i2);
280
+
281
+ double i0x = p0.x();
282
+ double i0y = p0.y();
283
+ double i1x = m_points[i1].x();
284
+ double i1y = m_points[i1].y();
285
+ double i2x = m_points[i2].x();
286
+ double i2y = m_points[i2].y();
287
+
288
+ m_center = circumcenter(i0x, i0y, i1x, i1y, i2x, i2y);
289
+
290
+ // Calculate the distances from the center once to avoid having to
291
+ // calculate for each compare. This used to be done in the comparator,
292
+ // but GCC 7.5+ would copy the comparator to iterators used in the
293
+ // sort, and this was excruciatingly slow when there were many points
294
+ // because you had to copy the vector of distances.
295
+ std::vector<double> dists;
296
+ dists.reserve(m_points.size());
297
+ for (const Point& p : m_points)
298
+ dists.push_back(dist(p.x(), p.y(), m_center.x(), m_center.y()));
299
+
300
+ // sort the points by distance from the seed triangle circumcenter
301
+ std::sort(ids.begin(), ids.end(),
302
+ [&dists](std::size_t i, std::size_t j)
303
+ { return dists[i] < dists[j]; });
304
+
305
+ // initialize a hash table for storing edges of the advancing convex hull
306
+ m_hash_size = static_cast<std::size_t>(std::ceil(std::sqrt(n)));
307
+ m_hash.resize(m_hash_size);
308
+ std::fill(m_hash.begin(), m_hash.end(), INVALID_INDEX);
309
+
310
+ // initialize arrays for tracking the edges of the advancing convex hull
311
+ hull_prev.resize(n);
312
+ hull_next.resize(n);
313
+ hull_tri.resize(n);
314
+
315
+ hull_start = i0;
316
+
317
+ size_t hull_size = 3;
318
+
319
+ hull_next[i0] = hull_prev[i2] = i1;
320
+ hull_next[i1] = hull_prev[i0] = i2;
321
+ hull_next[i2] = hull_prev[i1] = i0;
322
+
323
+ hull_tri[i0] = 0;
324
+ hull_tri[i1] = 1;
325
+ hull_tri[i2] = 2;
326
+
327
+ m_hash[hash_key(i0x, i0y)] = i0;
328
+ m_hash[hash_key(i1x, i1y)] = i1;
329
+ m_hash[hash_key(i2x, i2y)] = i2;
330
+
331
+ // ABELL - Why are we doing this is n < 3? There is no triangulation if
332
+ // there is no triangle.
333
+
334
+ std::size_t max_triangles = n < 3 ? 1 : 2 * n - 5;
335
+ triangles.reserve(max_triangles * 3);
336
+ halfedges.reserve(max_triangles * 3);
337
+ add_triangle(i0, i1, i2, INVALID_INDEX, INVALID_INDEX, INVALID_INDEX);
338
+ double xp = std::numeric_limits<double>::quiet_NaN();
339
+ double yp = std::numeric_limits<double>::quiet_NaN();
340
+
341
+ // Go through points based on distance from the center.
342
+ for (std::size_t k = 0; k < n; k++) {
343
+ const std::size_t i = ids[k];
344
+ const double x = coords[2 * i];
345
+ const double y = coords[2 * i + 1];
346
+
347
+ // skip near-duplicate points
348
+ if (k > 0 && check_pts_equal(x, y, xp, yp))
349
+ continue;
350
+ xp = x;
351
+ yp = y;
352
+
353
+ //ABELL - This is dumb. We have the indices. Use them.
354
+ // skip seed triangle points
355
+ if (check_pts_equal(x, y, i0x, i0y) ||
356
+ check_pts_equal(x, y, i1x, i1y) ||
357
+ check_pts_equal(x, y, i2x, i2y)) continue;
358
+
359
+ // find a visible edge on the convex hull using edge hash
360
+ std::size_t start = 0;
361
+
362
+ size_t key = hash_key(x, y);
363
+ for (size_t j = 0; j < m_hash_size; j++) {
364
+ start = m_hash[fast_mod(key + j, m_hash_size)];
365
+
366
+ // ABELL - Not sure how hull_next[start] could ever equal start
367
+ // I *think* hull_next is just a representation of the hull in one
368
+ // direction.
369
+ if (start != INVALID_INDEX && start != hull_next[start])
370
+ break;
371
+ }
372
+
373
+ //ABELL
374
+ // Make sure what we found is on the hull.
375
+ assert(hull_prev[start] != start);
376
+ assert(hull_prev[start] != INVALID_INDEX);
377
+
378
+ start = hull_prev[start];
379
+ size_t e = start;
380
+ size_t q;
381
+
382
+ // Advance until we find a place in the hull where our current point
383
+ // can be added.
384
+ while (true)
385
+ {
386
+ q = hull_next[e];
387
+ if (Point::equal(m_points[i], m_points[e], span) ||
388
+ Point::equal(m_points[i], m_points[q], span))
389
+ {
390
+ e = INVALID_INDEX;
391
+ break;
392
+ }
393
+ if (counterclockwise(x, y, coords[2 * e], coords[2 * e + 1],
394
+ coords[2 * q], coords[2 * q + 1]))
395
+ break;
396
+ e = q;
397
+ if (e == start) {
398
+ e = INVALID_INDEX;
399
+ break;
400
+ }
401
+ }
402
+
403
+ // ABELL
404
+ // This seems wrong. Perhaps we should check what's going on?
405
+ if (e == INVALID_INDEX) // likely a near-duplicate point; skip it
406
+ continue;
407
+
408
+ // add the first triangle from the point
409
+ std::size_t t = add_triangle(
410
+ e,
411
+ i,
412
+ hull_next[e],
413
+ INVALID_INDEX,
414
+ INVALID_INDEX,
415
+ hull_tri[e]);
416
+
417
+ hull_tri[i] = legalize(t + 2); // Legalize the triangle we just added.
418
+ hull_tri[e] = t;
419
+ hull_size++;
420
+
421
+ // walk forward through the hull, adding more triangles and
422
+ // flipping recursively
423
+ std::size_t next = hull_next[e];
424
+ while (true)
425
+ {
426
+ q = hull_next[next];
427
+ if (!counterclockwise(x, y, coords[2 * next], coords[2 * next + 1],
428
+ coords[2 * q], coords[2 * q + 1]))
429
+ break;
430
+ t = add_triangle(next, i, q,
431
+ hull_tri[i], INVALID_INDEX, hull_tri[next]);
432
+ hull_tri[i] = legalize(t + 2);
433
+ hull_next[next] = next; // mark as removed
434
+ hull_size--;
435
+ next = q;
436
+ }
437
+
438
+ // walk backward from the other side, adding more triangles and flipping
439
+ if (e == start) {
440
+ while (true)
441
+ {
442
+ q = hull_prev[e];
443
+ if (!counterclockwise(x, y, coords[2 * q], coords[2 * q + 1],
444
+ coords[2 * e], coords[2 * e + 1]))
445
+ break;
446
+ t = add_triangle(q, i, e,
447
+ INVALID_INDEX, hull_tri[e], hull_tri[q]);
448
+ legalize(t + 2);
449
+ hull_tri[q] = t;
450
+ hull_next[e] = e; // mark as removed
451
+ hull_size--;
452
+ e = q;
453
+ }
454
+ }
455
+
456
+ // update the hull indices
457
+ hull_prev[i] = e;
458
+ hull_start = e;
459
+ hull_prev[next] = i;
460
+ hull_next[e] = i;
461
+ hull_next[i] = next;
462
+
463
+ m_hash[hash_key(x, y)] = i;
464
+ m_hash[hash_key(coords[2 * e], coords[2 * e + 1])] = e;
465
+ }
466
+ }
467
+
468
+ double Delaunator::get_hull_area()
469
+ {
470
+ std::vector<double> hull_area;
471
+ size_t e = hull_start;
472
+ size_t cnt = 1;
473
+ do {
474
+ hull_area.push_back((coords[2 * e] - coords[2 * hull_prev[e]]) *
475
+ (coords[2 * e + 1] + coords[2 * hull_prev[e] + 1]));
476
+ cnt++;
477
+ e = hull_next[e];
478
+ } while (e != hull_start);
479
+ return sum(hull_area);
480
+ }
481
+
482
+ double Delaunator::get_triangle_area()
483
+ {
484
+ std::vector<double> vals;
485
+ for (size_t i = 0; i < triangles.size(); i += 3)
486
+ {
487
+ const double ax = coords[2 * triangles[i]];
488
+ const double ay = coords[2 * triangles[i] + 1];
489
+ const double bx = coords[2 * triangles[i + 1]];
490
+ const double by = coords[2 * triangles[i + 1] + 1];
491
+ const double cx = coords[2 * triangles[i + 2]];
492
+ const double cy = coords[2 * triangles[i + 2] + 1];
493
+ double val = std::fabs((by - ay) * (cx - bx) - (bx - ax) * (cy - by));
494
+ vals.push_back(val);
495
+ }
496
+ return sum(vals);
497
+ }
498
+
499
+ std::size_t Delaunator::legalize(std::size_t a) {
500
+ std::size_t i = 0;
501
+ std::size_t ar = 0;
502
+ m_edge_stack.clear();
503
+
504
+ // recursion eliminated with a fixed-size stack
505
+ while (true) {
506
+ const size_t b = halfedges[a];
507
+
508
+ /* if the pair of triangles doesn't satisfy the Delaunay condition
509
+ * (p1 is inside the circumcircle of [p0, pl, pr]), flip them,
510
+ * then do the same check/flip recursively for the new pair of triangles
511
+ *
512
+ * pl pl
513
+ * /||\ / \
514
+ * al/ || \bl al/ \a
515
+ * / || \ / \
516
+ * / a||b \ flip /___ar___\
517
+ * p0\ || /p1 => p0\---bl---/p1
518
+ * \ || / \ /
519
+ * ar\ || /br b\ /br
520
+ * \||/ \ /
521
+ * pr pr
522
+ */
523
+ const size_t a0 = 3 * (a / 3);
524
+ ar = a0 + (a + 2) % 3;
525
+
526
+ if (b == INVALID_INDEX) {
527
+ if (i > 0) {
528
+ i--;
529
+ a = m_edge_stack[i];
530
+ continue;
531
+ } else {
532
+ //i = INVALID_INDEX;
533
+ break;
534
+ }
535
+ }
536
+
537
+ const size_t b0 = 3 * (b / 3);
538
+ const size_t al = a0 + (a + 1) % 3;
539
+ const size_t bl = b0 + (b + 2) % 3;
540
+
541
+ const std::size_t p0 = triangles[ar];
542
+ const std::size_t pr = triangles[a];
543
+ const std::size_t pl = triangles[al];
544
+ const std::size_t p1 = triangles[bl];
545
+
546
+ const bool illegal = in_circle(
547
+ coords[2 * p0],
548
+ coords[2 * p0 + 1],
549
+ coords[2 * pr],
550
+ coords[2 * pr + 1],
551
+ coords[2 * pl],
552
+ coords[2 * pl + 1],
553
+ coords[2 * p1],
554
+ coords[2 * p1 + 1]);
555
+
556
+ if (illegal) {
557
+ triangles[a] = p1;
558
+ triangles[b] = p0;
559
+
560
+ auto hbl = halfedges[bl];
561
+
562
+ // Edge swapped on the other side of the hull (rare).
563
+ // Fix the halfedge reference
564
+ if (hbl == INVALID_INDEX) {
565
+ std::size_t e = hull_start;
566
+ do {
567
+ if (hull_tri[e] == bl) {
568
+ hull_tri[e] = a;
569
+ break;
570
+ }
571
+ e = hull_prev[e];
572
+ } while (e != hull_start);
573
+ }
574
+ link(a, hbl);
575
+ link(b, halfedges[ar]);
576
+ link(ar, bl);
577
+ std::size_t br = b0 + (b + 1) % 3;
578
+
579
+ if (i < m_edge_stack.size()) {
580
+ m_edge_stack[i] = br;
581
+ } else {
582
+ m_edge_stack.push_back(br);
583
+ }
584
+ i++;
585
+
586
+ } else {
587
+ if (i > 0) {
588
+ i--;
589
+ a = m_edge_stack[i];
590
+ continue;
591
+ } else {
592
+ break;
593
+ }
594
+ }
595
+ }
596
+ return ar;
597
+ }
598
+
599
+ std::size_t Delaunator::hash_key(const double x, const double y) const {
600
+ const double dx = x - m_center.x();
601
+ const double dy = y - m_center.y();
602
+ return fast_mod(
603
+ static_cast<std::size_t>(std::llround(std::floor(pseudo_angle(dx, dy) * static_cast<double>(m_hash_size)))),
604
+ m_hash_size);
605
+ }
606
+
607
+ std::size_t Delaunator::add_triangle(
608
+ std::size_t i0,
609
+ std::size_t i1,
610
+ std::size_t i2,
611
+ std::size_t a,
612
+ std::size_t b,
613
+ std::size_t c) {
614
+ std::size_t t = triangles.size();
615
+ triangles.push_back(i0);
616
+ triangles.push_back(i1);
617
+ triangles.push_back(i2);
618
+ link(t, a);
619
+ link(t + 1, b);
620
+ link(t + 2, c);
621
+ return t;
622
+ }
623
+
624
+ void Delaunator::link(const std::size_t a, const std::size_t b) {
625
+ std::size_t s = halfedges.size();
626
+ if (a == s) {
627
+ halfedges.push_back(b);
628
+ } else if (a < s) {
629
+ halfedges[a] = b;
630
+ } else {
631
+ throw std::runtime_error("Cannot link edge");
632
+ }
633
+ if (b != INVALID_INDEX) {
634
+ std::size_t s2 = halfedges.size();
635
+ if (b == s2) {
636
+ halfedges.push_back(a);
637
+ } else if (b < s2) {
638
+ halfedges[b] = a;
639
+ } else {
640
+ throw std::runtime_error("Cannot link edge");
641
+ }
642
+ }
643
+ }
644
+
645
+ } //namespace delaunator