Skip to content

Commit c12fcfc

Browse files
authored
Merge pull request abellgithub#12 from abellgithub/issue-8
Fix error in detecting collinear points.
2 parents 83dab62 + a1adac3 commit c12fcfc

File tree

4 files changed

+201
-83
lines changed

4 files changed

+201
-83
lines changed

include/delaunator.cpp

Lines changed: 133 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11

22
#include "delaunator.hpp"
33

4+
#include <iostream>
5+
46
#include <algorithm>
57
#include <cmath>
8+
#include <numeric>
69
#include <limits>
710
#include <stdexcept>
811
#include <tuple>
@@ -38,6 +41,25 @@ inline double dist(
3841
return dx * dx + dy * dy;
3942
}
4043

44+
inline double circumradius(const Point& p1, const Point& p2, const Point& p3)
45+
{
46+
Point d = Point::vector(p1, p2);
47+
Point e = Point::vector(p1, p3);
48+
49+
const double bl = d.magnitude2();
50+
const double cl = e.magnitude2();
51+
const double det = Point::determinant(d, e);
52+
53+
Point radius((e.y() * bl - d.y() * cl) * 0.5 / det,
54+
(d.x() * cl - e.x() * bl) * 0.5 / det);
55+
56+
if ((bl > 0.0 || bl < 0.0) &&
57+
(cl > 0.0 || cl < 0.0) &&
58+
(det > 0.0 || det < 0.0))
59+
return radius.magnitude2();
60+
return (std::numeric_limits<double>::max)();
61+
}
62+
4163
inline double circumradius(
4264
const double ax,
4365
const double ay,
@@ -64,16 +86,57 @@ inline double circumradius(
6486
}
6587
}
6688

67-
inline bool orient(
68-
const double px,
69-
const double py,
70-
const double qx,
71-
const double qy,
72-
const double rx,
73-
const double ry) {
74-
return (qy - py) * (rx - qx) - (qx - px) * (ry - qy) < 0.0;
89+
inline bool clockwise(const Point& p0, const Point& p1, const Point& p2)
90+
{
91+
Point v0 = Point::vector(p0, p1);
92+
Point v1 = Point::vector(p0, p2);
93+
double det = Point::determinant(v0, v1);
94+
double dist = v0.magnitude2() + v1.magnitude2();
95+
double dist2 = Point::dist2(v0, v1);
96+
if (det == 0)
97+
{
98+
return false;
99+
}
100+
double reldet = std::abs(dist / det);
101+
if (reldet > 1e14)
102+
return false;
103+
return det < 0;
104+
}
105+
106+
inline bool clockwise(double px, double py, double qx, double qy,
107+
double rx, double ry)
108+
{
109+
Point p0(px, py);
110+
Point p1(qx, qy);
111+
Point p2(rx, ry);
112+
return clockwise(p0, p1, p2);
113+
}
114+
115+
inline bool counterclockwise(const Point& p0, const Point& p1, const Point& p2)
116+
{
117+
Point v0 = Point::vector(p0, p1);
118+
Point v1 = Point::vector(p0, p2);
119+
double det = Point::determinant(v0, v1);
120+
double dist = v0.magnitude2() + v1.magnitude2();
121+
double dist2 = Point::dist2(v0, v1);
122+
if (det == 0)
123+
return false;
124+
double reldet = std::abs(dist / det);
125+
if (reldet > 1e14)
126+
return false;
127+
return det > 0;
128+
}
129+
130+
inline bool counterclockwise(double px, double py, double qx, double qy,
131+
double rx, double ry)
132+
{
133+
Point p0(px, py);
134+
Point p1(qx, qy);
135+
Point p2(rx, ry);
136+
return counterclockwise(p0, p1, p2);
75137
}
76138

139+
77140
inline Point circumcenter(
78141
const double ax,
79142
const double ay,
@@ -88,6 +151,7 @@ inline Point circumcenter(
88151

89152
const double bl = dx * dx + dy * dy;
90153
const double cl = ex * ex + ey * ey;
154+
//ABELL - This is suspect for div-by-0.
91155
const double d = dx * ey - dy * ex;
92156

93157
const double x = ax + (ey * bl - dy * cl) * 0.5 / d;
@@ -173,81 +237,63 @@ inline double pseudo_angle(const double dx, const double dy) {
173237
return (dy > 0.0 ? 3.0 - p : 1.0 + p) / 4.0; // [0..1)
174238
}
175239

176-
struct DelaunatorPoint {
177-
std::size_t i;
178-
double x;
179-
double y;
180-
std::size_t t;
181-
std::size_t prev;
182-
std::size_t next;
183-
bool removed;
184-
};
185240

186241
Delaunator::Delaunator(std::vector<double> const& in_coords)
187-
: coords(in_coords),
188-
triangles(),
189-
halfedges(),
190-
hull_prev(),
191-
hull_next(),
192-
hull_tri(),
193-
hull_start(),
194-
m_hash(),
195-
m_hash_size(),
196-
m_edge_stack() {
242+
: coords(in_coords), m_points(in_coords)
243+
{
197244
std::size_t n = coords.size() >> 1;
198245

246+
std::vector<std::size_t> ids(n);
247+
std::iota(ids.begin(), ids.end(), 0);
248+
199249
double max_x = std::numeric_limits<double>::lowest();
200250
double max_y = std::numeric_limits<double>::lowest();
201251
double min_x = (std::numeric_limits<double>::max)();
202252
double min_y = (std::numeric_limits<double>::max)();
203-
std::vector<std::size_t> ids;
204-
ids.reserve(n);
205-
206-
for (std::size_t i = 0; i < n; i++) {
207-
const double x = coords[2 * i];
208-
const double y = coords[2 * i + 1];
209-
210-
if (x < min_x) min_x = x;
211-
if (y < min_y) min_y = y;
212-
if (x > max_x) max_x = x;
213-
if (y > max_y) max_y = y;
214-
215-
ids.push_back(i);
253+
for (const Point& p : m_points)
254+
{
255+
min_x = std::min(p.x(), min_x);
256+
min_y = std::min(p.y(), min_y);
257+
max_x = std::max(p.x(), max_x);
258+
max_y = std::max(p.y(), max_y);
216259
}
217-
const double cx = (min_x + max_x) / 2;
218-
const double cy = (min_y + max_y) / 2;
219-
double min_dist = (std::numeric_limits<double>::max)();
260+
double width = max_x - min_x;
261+
double height = max_x - min_y;
262+
double span = width * width + height * height; // Everything is square dist.
263+
264+
Point center((min_x + max_x) / 2, (min_y + max_y) / 2);
220265

221266
std::size_t i0 = INVALID_INDEX;
222267
std::size_t i1 = INVALID_INDEX;
223268
std::size_t i2 = INVALID_INDEX;
224269

225270
// pick a seed point close to the centroid
226-
for (std::size_t i = 0; i < n; i++) {
227-
const double d = dist(cx, cy, coords[2 * i], coords[2 * i + 1]);
271+
double min_dist = (std::numeric_limits<double>::max)();
272+
for (size_t i = 0; i < m_points.size(); ++i)
273+
{
274+
const Point& p = m_points[i];
275+
const double d = Point::dist2(center, p);
228276
if (d < min_dist) {
229277
i0 = i;
230278
min_dist = d;
231279
}
232280
}
233281

234-
const double i0x = coords[2 * i0];
235-
const double i0y = coords[2 * i0 + 1];
282+
const Point& p0 = m_points[i0];
236283

237284
min_dist = (std::numeric_limits<double>::max)();
238285

239286
// find the point closest to the seed
240287
for (std::size_t i = 0; i < n; i++) {
241288
if (i == i0) continue;
242-
const double d = dist(i0x, i0y, coords[2 * i], coords[2 * i + 1]);
289+
const double d = Point::dist2(p0, m_points[i]);
243290
if (d < min_dist && d > 0.0) {
244291
i1 = i;
245292
min_dist = d;
246293
}
247294
}
248295

249-
double i1x = coords[2 * i1];
250-
double i1y = coords[2 * i1 + 1];
296+
const Point& p1 = m_points[i1];
251297

252298
double min_radius = (std::numeric_limits<double>::max)();
253299

@@ -256,9 +302,7 @@ Delaunator::Delaunator(std::vector<double> const& in_coords)
256302
for (std::size_t i = 0; i < n; i++) {
257303
if (i == i0 || i == i1) continue;
258304

259-
const double r = circumradius(
260-
i0x, i0y, i1x, i1y, coords[2 * i], coords[2 * i + 1]);
261-
305+
const double r = circumradius(p0, p1, m_points[i]);
262306
if (r < min_radius) {
263307
i2 = i;
264308
min_radius = r;
@@ -269,14 +313,17 @@ Delaunator::Delaunator(std::vector<double> const& in_coords)
269313
throw std::runtime_error("not triangulation");
270314
}
271315

272-
double i2x = coords[2 * i2];
273-
double i2y = coords[2 * i2 + 1];
316+
const Point& p2 = m_points[i2];
274317

275-
if (orient(i0x, i0y, i1x, i1y, i2x, i2y)) {
318+
if (counterclockwise(p0, p1, p2))
276319
std::swap(i1, i2);
277-
std::swap(i1x, i2x);
278-
std::swap(i1y, i2y);
279-
}
320+
321+
double i0x = p0.x();
322+
double i0y = p0.y();
323+
double i1x = m_points[i1].x();
324+
double i1y = m_points[i1].y();
325+
double i2x = m_points[i2].x();
326+
double i2y = m_points[i2].y();
280327

281328
m_center = circumcenter(i0x, i0y, i1x, i1y, i2x, i2y);
282329

@@ -360,14 +407,20 @@ Delaunator::Delaunator(std::vector<double> const& in_coords)
360407
size_t e = start;
361408
size_t q;
362409

363-
//ABELL - Make sure that start and e are linked.
364-
365-
//Advance until we find a place in the hull where our current point
410+
// Advance until we find a place in the hull where our current point
366411
// can be added.
367-
while (q = hull_next[e],
368-
!orient(x, y, coords[2 * e], coords[2 * e + 1],
369-
coords[2 * q], coords[2 * q + 1]))
412+
while (true)
370413
{
414+
q = hull_next[e];
415+
if (Point::equal(m_points[i], m_points[e], span) ||
416+
Point::equal(m_points[i], m_points[q], span))
417+
{
418+
e = INVALID_INDEX;
419+
break;
420+
}
421+
if (counterclockwise(x, y, coords[2 * e], coords[2 * e + 1],
422+
coords[2 * q], coords[2 * q + 1]))
423+
break;
371424
e = q;
372425
if (e == start) {
373426
e = INVALID_INDEX;
@@ -396,11 +449,12 @@ Delaunator::Delaunator(std::vector<double> const& in_coords)
396449
// walk forward through the hull, adding more triangles and
397450
// flipping recursively
398451
std::size_t next = hull_next[e];
399-
while (
400-
q = hull_next[next],
401-
orient(x, y, coords[2 * next], coords[2 * next + 1],
402-
coords[2 * q], coords[2 * q + 1]))
452+
while (true)
403453
{
454+
q = hull_next[next];
455+
if (!counterclockwise(x, y, coords[2 * next], coords[2 * next + 1],
456+
coords[2 * q], coords[2 * q + 1]))
457+
break;
404458
t = add_triangle(next, i, q,
405459
hull_tri[i], INVALID_INDEX, hull_tri[next]);
406460
hull_tri[i] = legalize(t + 2);
@@ -411,11 +465,12 @@ Delaunator::Delaunator(std::vector<double> const& in_coords)
411465

412466
// walk backward from the other side, adding more triangles and flipping
413467
if (e == start) {
414-
while (
415-
q = hull_prev[e],
416-
orient(x, y, coords[2 * q], coords[2 * q + 1], coords[2 * e],
417-
coords[2 * e + 1]))
468+
while (true)
418469
{
470+
q = hull_prev[e];
471+
if (!counterclockwise(x, y, coords[2 * q], coords[2 * q + 1],
472+
coords[2 * e], coords[2 * e + 1]))
473+
break;
419474
t = add_triangle(q, i, e,
420475
INVALID_INDEX, hull_tri[e], hull_tri[q]);
421476
legalize(t + 2);
@@ -438,12 +493,15 @@ Delaunator::Delaunator(std::vector<double> const& in_coords)
438493
}
439494
}
440495

441-
double Delaunator::get_hull_area() {
496+
double Delaunator::get_hull_area()
497+
{
442498
std::vector<double> hull_area;
443499
size_t e = hull_start;
500+
size_t cnt = 1;
444501
do {
445502
hull_area.push_back((coords[2 * e] - coords[2 * hull_prev[e]]) *
446-
(coords[2 * e + 1] + coords[2 * hull_prev[e] + 1]));
503+
(coords[2 * e + 1] + coords[2 * hull_prev[e] + 1]));
504+
cnt++;
447505
e = hull_next[e];
448506
} while (e != hull_start);
449507
return sum(hull_area);

0 commit comments

Comments
 (0)