11
11
12
12
namespace delaunator {
13
13
14
+ // Kahan and Babuska summation, Neumaier variant; accumulates less FP error
15
+ inline double sum (const std::vector<double >& x) {
16
+ double sum = x[0 ];
17
+ double err = 0.0 ;
18
+
19
+ for (size_t i = 1 ; i < x.size (); i++) {
20
+ const double k = x[i];
21
+ const double m = sum + k;
22
+ err += std::fabs (sum) >= std::fabs (k) ? sum - m + k : k - m + sum;
23
+ sum = m;
24
+ }
25
+ return sum + err;
26
+ }
27
+
14
28
inline double dist (
15
29
const double ax,
16
30
const double ay,
@@ -47,14 +61,14 @@ inline double circumradius(
47
61
}
48
62
}
49
63
50
- inline double area (
64
+ inline bool orient (
51
65
const double px,
52
66
const double py,
53
67
const double qx,
54
68
const double qy,
55
69
const double rx,
56
70
const double ry) {
57
- return (qy - py) * (rx - qx) - (qx - px) * (ry - qy);
71
+ return (qy - py) * (rx - qx) - (qx - px) * (ry - qy) < 0.0 ;
58
72
}
59
73
60
74
inline std::pair<double , double > circumcenter (
@@ -136,9 +150,12 @@ inline bool in_circle(
136
150
ap * (ex * fy - ey * fx)) < 0.0 ;
137
151
}
138
152
153
+ constexpr double EPSILON = std::numeric_limits<double >::epsilon();
154
+ constexpr std::size_t INVALID_INDEX = std::numeric_limits<std::size_t >::max();
155
+
139
156
inline bool check_pts_equal (double x1, double y1, double x2, double y2) {
140
- return std::fabs (x1 - x2) < std::numeric_limits< double >:: epsilon () &&
141
- std::fabs (y1 - y2) < std::numeric_limits< double >:: epsilon () ;
157
+ return std::fabs (x1 - x2) < EPSILON &&
158
+ std::fabs (y1 - y2) < EPSILON ;
142
159
}
143
160
144
161
// monotonically increases with real angle, but doesn't need expensive trigonometry
@@ -147,8 +164,6 @@ inline double pseudo_angle(double dx, double dy) {
147
164
return (dy > 0.0 ? 3.0 - p : 1.0 + p) / 4.0 ; // [0..1)
148
165
}
149
166
150
- constexpr std::size_t INVALID_INDEX = std::numeric_limits<std::size_t >::max();
151
-
152
167
struct DelaunatorPoint {
153
168
std::size_t i;
154
169
double x;
@@ -270,12 +285,15 @@ Delaunator::Delaunator(std::vector<double> const& in_coords)
270
285
271
286
if (!(min_radius < std::numeric_limits<double >::max ())) {
272
287
throw std::runtime_error (" not triangulation" );
273
- ;
274
288
}
275
289
276
- bool coord_area = area (
277
- coords[2 * i0], coords[2 * i0 + 1 ], coords[2 * i1], coords[2 * i1 + 1 ], coords[2 * i2], coords[2 * i2 + 1 ]) < 0.0 ;
278
- if (coord_area) {
290
+ if (orient (
291
+ coords[2 * i0], coords[2 * i0 + 1 ], //
292
+ coords[2 * i1],
293
+ coords[2 * i1 + 1 ], //
294
+ coords[2 * i2],
295
+ coords[2 * i2 + 1 ]) //
296
+ ) {
279
297
std::swap (i1, i2);
280
298
}
281
299
@@ -325,14 +343,19 @@ Delaunator::Delaunator(std::vector<double> const& in_coords)
325
343
const std::size_t i = ids[k];
326
344
const double x = coords[2 * i];
327
345
const double y = coords[2 * i + 1 ];
346
+
347
+ // skip duplicate points
328
348
if (check_pts_equal (x, y, xp, yp)) continue ;
329
349
xp = x;
330
350
yp = y;
351
+
352
+ // skip seed triangle points
331
353
if (
332
354
check_pts_equal (x, y, i0x, i0y) ||
333
355
check_pts_equal (x, y, i1x, i1y) ||
334
356
check_pts_equal (x, y, i2x, i2y)) continue ;
335
357
358
+ // find a visible edge on the convex hull using edge hash
336
359
const std::size_t start_key = hash_key (x, y);
337
360
std::size_t key = start_key;
338
361
std::size_t start = INVALID_INDEX;
@@ -345,17 +368,24 @@ Delaunator::Delaunator(std::vector<double> const& in_coords)
345
368
346
369
start = m_hull[start].prev ;
347
370
e = start;
348
-
349
- while (
350
- area (
351
- x, y, m_hull[e].x , m_hull[e].y , m_hull[m_hull[e].next ].x , m_hull[m_hull[e].next ].y ) >= 0.0 ) {
371
+ while (!orient (
372
+ x, y, //
373
+ m_hull[e].x ,
374
+ m_hull[e].y , //
375
+ m_hull[m_hull[e].next ].x , //
376
+ m_hull[m_hull[e].next ].y ) //
377
+ ) {
352
378
e = m_hull[e].next ;
353
379
354
380
if (e == start) {
355
- throw std::runtime_error (" Something is wrong with the input points." );
381
+ e = INVALID_INDEX; // TODO: will it work correct?
382
+ break ;
356
383
}
357
384
}
358
385
386
+ // likely a near-duplicate point; skip it
387
+ if (e == INVALID_INDEX) continue ;
388
+
359
389
const bool walk_back = e == start;
360
390
361
391
// add the first triangle from the point
@@ -375,9 +405,13 @@ Delaunator::Delaunator(std::vector<double> const& in_coords)
375
405
376
406
// walk forward through the hull, adding more triangles and flipping recursively
377
407
std::size_t q = m_hull[e].next ;
378
- while (
379
- area (
380
- x, y, m_hull[q].x , m_hull[q].y , m_hull[m_hull[q].next ].x , m_hull[m_hull[q].next ].y ) < 0.0 ) {
408
+ while (orient (
409
+ x, y, //
410
+ m_hull[q].x ,
411
+ m_hull[q].y , //
412
+ m_hull[m_hull[q].next ].x ,
413
+ m_hull[m_hull[q].next ].y //
414
+ )) {
381
415
t = add_triangle (
382
416
m_hull[q].i , i, m_hull[m_hull[q].next ].i , m_hull[m_hull[q].prev ].t , INVALID_INDEX, m_hull[q].t );
383
417
m_hull[m_hull[q].prev ].t = legalize (t + 2 );
@@ -387,9 +421,13 @@ Delaunator::Delaunator(std::vector<double> const& in_coords)
387
421
if (walk_back) {
388
422
// walk backward from the other side, adding more triangles and flipping
389
423
q = m_hull[e].prev ;
390
- while (
391
- area (
392
- x, y, m_hull[m_hull[q].prev ].x , m_hull[m_hull[q].prev ].y , m_hull[q].x , m_hull[q].y ) < 0.0 ) {
424
+ while (orient (
425
+ x, y, //
426
+ m_hull[m_hull[q].prev ].x ,
427
+ m_hull[m_hull[q].prev ].y , //
428
+ m_hull[q].x ,
429
+ m_hull[q].y //
430
+ )) {
393
431
t = add_triangle (
394
432
m_hull[m_hull[q].prev ].i , i, m_hull[q].i , INVALID_INDEX, m_hull[q].t , m_hull[m_hull[q].prev ].t );
395
433
legalize (t + 2 );
@@ -404,13 +442,13 @@ Delaunator::Delaunator(std::vector<double> const& in_coords)
404
442
}
405
443
406
444
double Delaunator::get_hull_area () {
407
- double hull_area = 0 ;
445
+ std::vector< double > hull_area;
408
446
size_t e = m_hull_entry;
409
447
do {
410
- hull_area += ( m_hull[e].x - m_hull[m_hull[e].prev ].x ) * (m_hull[e].y + m_hull[m_hull[e].prev ].y );
448
+ hull_area. push_back (( m_hull[e].x - m_hull[m_hull[e].prev ].x ) * (m_hull[e].y + m_hull[m_hull[e].prev ].y ) );
411
449
e = m_hull[e].next ;
412
450
} while (e != m_hull_entry);
413
- return hull_area;
451
+ return sum ( hull_area) ;
414
452
}
415
453
416
454
std::size_t Delaunator::remove_node (std::size_t node) {
0 commit comments