1111
1212namespace 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+
1428inline 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
6074inline 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+
139156inline 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-
152167struct 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
406444double 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
416454std::size_t Delaunator::remove_node (std::size_t node) {
0 commit comments