15inline size_t fast_mod(
const size_t i,
const size_t c) {
16 return i >= c ? i % c : i;
20inline double sum(
const std::vector<double>& x) {
24 for (
size_t i = 1; i < x.size(); i++) {
25 const double k = x[i];
26 const double m = sum + k;
27 err += std::fabs(sum) >= std::fabs(k) ? sum - m + k : k - m + sum;
38 const double dx = ax - bx;
39 const double dy = ay - by;
40 return dx * dx + dy * dy;
43inline double circumradius(
50 const double dx = bx - ax;
51 const double dy = by - ay;
52 const double ex = cx - ax;
53 const double ey = cy - ay;
55 const double bl = dx * dx + dy * dy;
56 const double cl = ex * ex + ey * ey;
57 const double d = dx * ey - dy * ex;
59 const double x = (ey * bl - dy * cl) * 0.5 / d;
60 const double y = (dx * cl - ex * bl) * 0.5 / d;
62 if ((bl > 0.0 || bl < 0.0) && (cl > 0.0 || cl < 0.0) && (d > 0.0 || d < 0.0)) {
65 return std::numeric_limits<double>::max();
76 return (qy - py) * (rx - qx) - (qx - px) * (ry - qy) < 0.0;
79inline std::pair<double, double> circumcenter(
86 const double dx = bx - ax;
87 const double dy = by - ay;
88 const double ex = cx - ax;
89 const double ey = cy - ay;
91 const double bl = dx * dx + dy * dy;
92 const double cl = ex * ex + ey * ey;
93 const double d = dx * ey - dy * ex;
95 const double x = ax + (ey * bl - dy * cl) * 0.5 / d;
96 const double y = ay + (dx * cl - ex * bl) * 0.5 / d;
98 return std::make_pair(x, y);
103 std::vector<double>
const& coords;
107 bool operator()(std::size_t i, std::size_t j) {
108 const double d1 = dist(coords[2 * i], coords[2 * i + 1], cx, cy);
109 const double d2 = dist(coords[2 * j], coords[2 * j + 1], cx, cy);
110 const double diff1 = d1 - d2;
111 const double diff2 = coords[2 * i] - coords[2 * j];
112 const double diff3 = coords[2 * i + 1] - coords[2 * j + 1];
114 if (diff1 > 0.0 || diff1 < 0.0) {
116 }
else if (diff2 > 0.0 || diff2 < 0.0) {
124inline bool in_circle(
133 const double dx = ax - px;
134 const double dy = ay - py;
135 const double ex = bx - px;
136 const double ey = by - py;
137 const double fx = cx - px;
138 const double fy = cy - py;
140 const double ap = dx * dx + dy * dy;
141 const double bp = ex * ex + ey * ey;
142 const double cp = fx * fx + fy * fy;
144 return (dx * (ey * cp - bp * fy) -
145 dy * (ex * cp - bp * fx) +
146 ap * (ex * fy - ey * fx)) < 0.0;
149 double EPSILON = std::numeric_limits<double>::epsilon();
150 std::size_t INVALID_INDEX = std::numeric_limits<std::size_t>::max();
152inline bool check_pts_equal(
double x1,
double y1,
double x2,
double y2) {
153 return std::fabs(x1 - x2) <= EPSILON &&
154 std::fabs(y1 - y2) <= EPSILON;
158inline double pseudo_angle(
const double dx,
const double dy) {
159 const double p = dx / (std::abs(dx) + std::abs(dy));
160 return (dy > 0.0 ? 3.0 - p : 1.0 + p) / 4.0;
176 std::vector<double>
const& coords;
177 std::vector<std::size_t> triangles;
178 std::vector<std::size_t> halfedges;
179 std::vector<std::size_t> hull_prev;
180 std::vector<std::size_t> hull_next;
181 std::vector<std::size_t> hull_tri;
182 std::size_t hull_start;
184 Delaunator(std::vector<double>
const& in_coords);
186 double get_hull_area();
189 std::vector<std::size_t> m_hash;
192 std::size_t m_hash_size;
193 std::vector<std::size_t> m_edge_stack;
195 std::size_t legalize(std::size_t a);
196 std::size_t hash_key(
double x,
double y)
const;
197 std::size_t add_triangle(
204 void link(std::size_t a, std::size_t b);
207Delaunator::Delaunator(std::vector<double>
const& in_coords)
220 std::size_t n = coords.size() >> 1;
222 double max_x = std::numeric_limits<double>::min();
223 double max_y = std::numeric_limits<double>::min();
224 double min_x = std::numeric_limits<double>::max();
225 double min_y = std::numeric_limits<double>::max();
226 std::vector<std::size_t> ids;
229 for (std::size_t i = 0; i < n; i++) {
230 const double x = coords[2 * i];
231 const double y = coords[2 * i + 1];
233 if (x < min_x) min_x = x;
234 if (y < min_y) min_y = y;
235 if (x > max_x) max_x = x;
236 if (y > max_y) max_y = y;
240 const double cx = (min_x + max_x) / 2;
241 const double cy = (min_y + max_y) / 2;
242 double min_dist = std::numeric_limits<double>::max();
244 std::size_t i0 = INVALID_INDEX;
245 std::size_t i1 = INVALID_INDEX;
246 std::size_t i2 = INVALID_INDEX;
249 for (std::size_t i = 0; i < n; i++) {
250 const double d = dist(cx, cy, coords[2 * i], coords[2 * i + 1]);
257 const double i0x = coords[2 * i0];
258 const double i0y = coords[2 * i0 + 1];
260 min_dist = std::numeric_limits<double>::max();
263 for (std::size_t i = 0; i < n; i++) {
264 if (i == i0)
continue;
265 const double d = dist(i0x, i0y, coords[2 * i], coords[2 * i + 1]);
266 if (d < min_dist && d > 0.0) {
272 double i1x = coords[2 * i1];
273 double i1y = coords[2 * i1 + 1];
275 double min_radius = std::numeric_limits<double>::max();
278 for (std::size_t i = 0; i < n; i++) {
279 if (i == i0 || i == i1)
continue;
281 const double r = circumradius(
282 i0x, i0y, i1x, i1y, coords[2 * i], coords[2 * i + 1]);
284 if (r < min_radius) {
290 if (!(min_radius < std::numeric_limits<double>::max())) {
291 throw std::runtime_error(
"not triangulation");
294 double i2x = coords[2 * i2];
295 double i2y = coords[2 * i2 + 1];
297 if (orient(i0x, i0y, i1x, i1y, i2x, i2y)) {
303 std::tie(m_center_x, m_center_y) = circumcenter(i0x, i0y, i1x, i1y, i2x, i2y);
306 std::sort(ids.begin(), ids.end(), compare{ coords, m_center_x, m_center_y });
309 m_hash_size =
static_cast<std::size_t
>(std::llround(std::ceil(std::sqrt(n))));
310 m_hash.resize(m_hash_size);
311 std::fill(m_hash.begin(), m_hash.end(), INVALID_INDEX);
320 size_t hull_size = 3;
322 hull_next[i0] = hull_prev[i2] = i1;
323 hull_next[i1] = hull_prev[i0] = i2;
324 hull_next[i2] = hull_prev[i1] = i0;
330 m_hash[hash_key(i0x, i0y)] = i0;
331 m_hash[hash_key(i1x, i1y)] = i1;
332 m_hash[hash_key(i2x, i2y)] = i2;
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 for (std::size_t k = 0; k < n; k++) {
341 const std::size_t i = ids[k];
342 const double x = coords[2 * i];
343 const double y = coords[2 * i + 1];
346 if (k > 0 && check_pts_equal(x, y, xp, yp))
continue;
352 check_pts_equal(x, y, i0x, i0y) ||
353 check_pts_equal(x, y, i1x, i1y) ||
354 check_pts_equal(x, y, i2x, i2y))
continue;
357 std::size_t start = 0;
359 size_t key = hash_key(x, y);
360 for (
size_t j = 0; j < m_hash_size; j++) {
361 start = m_hash[fast_mod(key + j, m_hash_size)];
362 if (start != INVALID_INDEX && start != hull_next[start])
break;
365 start = hull_prev[start];
369 while (q = hull_next[e], !orient(x, y, coords[2 * e], coords[2 * e + 1], coords[2 * q], coords[2 * q + 1])) {
377 if (e == INVALID_INDEX)
continue;
380 std::size_t t = add_triangle(
388 hull_tri[i] = legalize(t + 2);
393 std::size_t next = hull_next[e];
396 orient(x, y, coords[2 * next], coords[2 * next + 1], coords[2 * q], coords[2 * q + 1])) {
397 t = add_triangle(next, i, q, hull_tri[i], INVALID_INDEX, hull_tri[next]);
398 hull_tri[i] = legalize(t + 2);
399 hull_next[next] = next;
408 orient(x, y, coords[2 * q], coords[2 * q + 1], coords[2 * e], coords[2 * e + 1])) {
409 t = add_triangle(q, i, e, INVALID_INDEX, hull_tri[e], hull_tri[q]);
425 m_hash[hash_key(x, y)] = i;
426 m_hash[hash_key(coords[2 * e], coords[2 * e + 1])] = e;
430double Delaunator::get_hull_area() {
431 std::vector<double> hull_area;
432 size_t e = hull_start;
434 hull_area.push_back((coords[2 * e] - coords[2 * hull_prev[e]]) * (coords[2 * e + 1] + coords[2 * hull_prev[e] + 1]));
436 }
while (e != hull_start);
437 return sum(hull_area);
440std::size_t Delaunator::legalize(std::size_t a) {
443 m_edge_stack.clear();
447 const size_t b = halfedges[a];
464 const size_t a0 = 3 * (a / 3);
465 ar = a0 + (a + 2) % 3;
467 if (b == INVALID_INDEX) {
478 const size_t b0 = 3 * (b / 3);
479 const size_t al = a0 + (a + 1) % 3;
480 const size_t bl = b0 + (b + 2) % 3;
482 const std::size_t p0 = triangles[ar];
483 const std::size_t pr = triangles[a];
484 const std::size_t pl = triangles[al];
485 const std::size_t p1 = triangles[bl];
487 const bool illegal = in_circle(
501 auto hbl = halfedges[bl];
504 if (hbl == INVALID_INDEX) {
505 std::size_t e = hull_start;
507 if (hull_tri[e] == bl) {
512 }
while (e != hull_start);
515 link(b, halfedges[ar]);
517 std::size_t br = b0 + (b + 1) % 3;
519 if (i < m_edge_stack.size()) {
520 m_edge_stack[i] = br;
522 m_edge_stack.push_back(br);
539inline std::size_t Delaunator::hash_key(
const double x,
const double y)
const {
540 const double dx = x - m_center_x;
541 const double dy = y - m_center_y;
543 static_cast<std::size_t
>(std::llround(std::floor(pseudo_angle(dx, dy) *
static_cast<double>(m_hash_size)))),
547std::size_t Delaunator::add_triangle(
554 std::size_t t = triangles.size();
555 triangles.push_back(i0);
556 triangles.push_back(i1);
557 triangles.push_back(i2);
564void Delaunator::link(
const std::size_t a,
const std::size_t b) {
565 std::size_t s = halfedges.size();
567 halfedges.push_back(b);
571 throw std::runtime_error(
"Cannot link edge");
573 if (b != INVALID_INDEX) {
574 std::size_t s2 = halfedges.size();
576 halfedges.push_back(a);
580 throw std::runtime_error(
"Cannot link edge");
Definition delaunator.hpp:173
Definition delaunator.hpp:163
Definition delaunator.hpp:101