Skip to content

Commit a23f7cd

Browse files
committed
#2 parity with js: mapbox/delaunator@0cea584
1 parent 3a7595e commit a23f7cd

File tree

2 files changed

+121
-46
lines changed

2 files changed

+121
-46
lines changed

include/delaunator.hpp

+62-24
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,20 @@
1111

1212
namespace delaunator {
1313

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+
1428
inline double dist(
1529
const double ax,
1630
const double ay,
@@ -47,14 +61,14 @@ inline double circumradius(
4761
}
4862
}
4963

50-
inline double area(
64+
inline bool orient(
5165
const double px,
5266
const double py,
5367
const double qx,
5468
const double qy,
5569
const double rx,
5670
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;
5872
}
5973

6074
inline std::pair<double, double> circumcenter(
@@ -136,9 +150,12 @@ inline bool in_circle(
136150
ap * (ex * fy - ey * fx)) < 0.0;
137151
}
138152

153+
constexpr double EPSILON = std::numeric_limits<double>::epsilon();
154+
constexpr std::size_t INVALID_INDEX = std::numeric_limits<std::size_t>::max();
155+
139156
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;
142159
}
143160

144161
// monotonically increases with real angle, but doesn't need expensive trigonometry
@@ -147,8 +164,6 @@ inline double pseudo_angle(double dx, double dy) {
147164
return (dy > 0.0 ? 3.0 - p : 1.0 + p) / 4.0; // [0..1)
148165
}
149166

150-
constexpr std::size_t INVALID_INDEX = std::numeric_limits<std::size_t>::max();
151-
152167
struct DelaunatorPoint {
153168
std::size_t i;
154169
double x;
@@ -270,12 +285,15 @@ Delaunator::Delaunator(std::vector<double> const& in_coords)
270285

271286
if (!(min_radius < std::numeric_limits<double>::max())) {
272287
throw std::runtime_error("not triangulation");
273-
;
274288
}
275289

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+
) {
279297
std::swap(i1, i2);
280298
}
281299

@@ -325,14 +343,19 @@ Delaunator::Delaunator(std::vector<double> const& in_coords)
325343
const std::size_t i = ids[k];
326344
const double x = coords[2 * i];
327345
const double y = coords[2 * i + 1];
346+
347+
// skip duplicate points
328348
if (check_pts_equal(x, y, xp, yp)) continue;
329349
xp = x;
330350
yp = y;
351+
352+
// skip seed triangle points
331353
if (
332354
check_pts_equal(x, y, i0x, i0y) ||
333355
check_pts_equal(x, y, i1x, i1y) ||
334356
check_pts_equal(x, y, i2x, i2y)) continue;
335357

358+
// find a visible edge on the convex hull using edge hash
336359
const std::size_t start_key = hash_key(x, y);
337360
std::size_t key = start_key;
338361
std::size_t start = INVALID_INDEX;
@@ -345,17 +368,24 @@ Delaunator::Delaunator(std::vector<double> const& in_coords)
345368

346369
start = m_hull[start].prev;
347370
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+
) {
352378
e = m_hull[e].next;
353379

354380
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;
356383
}
357384
}
358385

386+
// likely a near-duplicate point; skip it
387+
if (e == INVALID_INDEX) continue;
388+
359389
const bool walk_back = e == start;
360390

361391
// add the first triangle from the point
@@ -375,9 +405,13 @@ Delaunator::Delaunator(std::vector<double> const& in_coords)
375405

376406
// walk forward through the hull, adding more triangles and flipping recursively
377407
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+
)) {
381415
t = add_triangle(
382416
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);
383417
m_hull[m_hull[q].prev].t = legalize(t + 2);
@@ -387,9 +421,13 @@ Delaunator::Delaunator(std::vector<double> const& in_coords)
387421
if (walk_back) {
388422
// walk backward from the other side, adding more triangles and flipping
389423
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+
)) {
393431
t = add_triangle(
394432
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);
395433
legalize(t + 2);
@@ -404,13 +442,13 @@ Delaunator::Delaunator(std::vector<double> const& in_coords)
404442
}
405443

406444
double Delaunator::get_hull_area() {
407-
double hull_area = 0;
445+
std::vector<double> hull_area;
408446
size_t e = m_hull_entry;
409447
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));
411449
e = m_hull[e].next;
412450
} while (e != m_hull_entry);
413-
return hull_area;
451+
return sum(hull_area);
414452
}
415453

416454
std::size_t Delaunator::remove_node(std::size_t node) {

0 commit comments

Comments
 (0)