13#include "predicates.h"
15#include "libmesh/int_range.h"
23 "The exact predicates read a point as two adjacent doubles.");
43 mooseError(
"XYIncrementalDelaunay: vertex id ",
45 " does not exist; the triangulation has ",
54 mooseAssert(!
isBounding(v),
"A bounding triangle vertex has no caller vertex id");
67 return {std::min(v0, v1), std::max(v0, v1)};
87 for (
const auto i : make_range(3u))
90 mooseError(
"XYIncrementalDelaunay: triangle ", t,
" does not have vertex ", v,
".");
96 for (
const auto i : make_range(3u))
100 mooseError(
"XYIncrementalDelaunay: triangle ",
102 " does not have an edge between vertices ",
112 for (
const auto i : make_range(3u))
113 if (moose_orient2d(
xy(
_triangles[t].vertices[(i + 1) % 3]),
122 const std::size_t v_mid,
123 const std::size_t v_last)
const
125 mooseAssert(moose_orient2d(
xy(v_first),
xy(v_mid),
xy(v_last)) == 0.0,
126 "The three vertices have to be collinear for a coordinate comparison to order them");
132 if (first.x != last.x)
133 return (first.x < mid.x && mid.x < last.x) || (last.x < mid.x && mid.x < first.x);
134 return (first.y < mid.y && mid.y < last.y) || (last.y < mid.y && mid.y < first.y);
141 return "bounding vertex " + std::to_string(v);
142 return "point " + std::to_string(
toCaller(v));
154 while (steps_left > 0)
160 for (
const auto i : make_range(3u))
161 if (moose_orient2d(
xy(
_triangles[current].vertices[(i + 1) % 3]),
186 const std::size_t v_new,
187 std::set<std::size_t> & cavity)
const
190 std::vector<std::size_t> pending{seed};
192 while (!pending.empty())
194 const auto t = pending.back();
197 for (
const auto i : make_range(3u))
199 const auto v0 =
_triangles[t].vertices[(i + 1) % 3];
200 const auto v1 =
_triangles[t].vertices[(i + 2) % 3];
201 const auto side = moose_orient2d(
xy(v0),
xy(v1),
xy(v_new));
206 mooseError(
"XYIncrementalDelaunay: the point (",
210 ") is on or beyond constrained segment (",
214 "), so it lies outside the region that segment bounds.");
225 if (side <= 0.0 || moose_incircle(
xy(
_triangles[n].vertices[0]),
231 pending.push_back(n);
237std::vector<std::size_t>
239 const std::vector<std::array<std::size_t, 3>> & added)
241 mooseAssert(added.size() >= removed.size(),
242 "A region takes at least as many triangles to cover as it is emptied of");
244 const std::set<std::size_t> emptied(removed.begin(), removed.end());
247 std::map<Segment, std::size_t> outside;
248 for (
const auto t : emptied)
249 for (
const auto i : make_range(3u))
260 std::vector<std::size_t> slots(emptied.begin(), emptied.end());
261 while (slots.size() < added.size())
267 for (
const auto i : index_range(added))
270 triangle.vertices = added[i];
272 for (
const auto v : triangle.vertices)
278 std::map<Segment, std::pair<std::size_t, unsigned int>> open_edges;
279 for (
const auto i : index_range(added))
281 const auto t = slots[i];
282 for (
const auto e : make_range(3u))
286 const auto it = open_edges.find(edge);
287 if (it == open_edges.end())
288 open_edges.emplace(edge, std::make_pair(t, e));
291 const auto [other_t, other_e] = it->second;
294 open_edges.erase(it);
299 for (
const auto & [edge, entry] : open_edges)
301 const auto [t, e] = entry;
302 const auto it = outside.find(edge);
304 if (it == outside.end())
316 const std::size_t v_start,
317 const std::size_t v_end,
318 const std::vector<std::size_t> & chain,
319 const std::size_t first,
320 const std::size_t last,
321 std::vector<std::array<std::size_t, 3>> & triangles)
const
333 for (
const auto i : make_range(first + 1, last))
334 if (moose_incircle(
xy(v_start),
xy(v_end),
xy(chain[best]),
xy(chain[i])) > 0.0)
340 triangles.push_back({v_start, v_end, chain[best]});
347 const std::vector<Segment> & segments)
349 Moose::initPredicates();
352 mooseError(
"XYIncrementalDelaunay: initialize() needs at least one point.");
359 auto x_min = points.front().x;
361 auto y_min = points.front().y;
363 for (
const auto & p : points)
365 x_min = std::min(x_min, p.x);
366 x_max = std::max(x_max, p.x);
367 y_min = std::min(y_min, p.y);
368 y_max = std::max(y_max, p.y);
376 const auto reach =
_bounding_reach * std::max({x_max - x_min, y_max - y_min, 1.0});
377 const auto x_mid = 0.5 * (x_min + x_max);
378 const auto y_mid = 0.5 * (y_min + y_max);
379 _vertices.push_back({x_mid - reach, y_mid - reach});
380 _vertices.push_back({x_mid + reach, y_mid - reach});
381 _vertices.push_back({x_mid, y_mid + reach});
390 for (
const auto i : index_range(points))
398 " are the same point; initialize() needs the points to be distinct, because the "
399 "constrained segments refer to them by position.");
402 for (
const auto & [v0, v1] : segments)
409 Moose::initPredicates();
412 mooseError(
"XYIncrementalDelaunay: initialize() has to run before a point can be inserted.");
414 const auto seed =
locate(p);
416 mooseError(
"XYIncrementalDelaunay: the point (",
420 ") is outside the region initialize() enclosed, so it cannot be inserted.");
424 for (
const auto v :
_triangles[seed].vertices)
428 mooseError(
"XYIncrementalDelaunay: the point (",
432 ") is a vertex of the bounding triangle, so it is far outside the region "
433 "initialize() enclosed.");
440 for (
const auto i : make_range(3u))
442 const auto v0 =
_triangles[seed].vertices[(i + 1) % 3];
443 const auto v1 =
_triangles[seed].vertices[(i + 2) % 3];
456 std::set<std::size_t> cavity;
459 std::vector<std::array<std::size_t, 3>> added;
460 for (
const auto t : cavity)
461 for (
const auto i : make_range(3u))
469 mooseAssert(added.size() == cavity.size() + 2,
470 "The Delaunay cavity of an insertion is a disc, whose boundary has two more edges "
471 "than the disc has triangles");
473 const std::vector<std::size_t> removed(cavity.begin(), cavity.end());
474 _last_triangle = retriangulate(removed, added).front();
476 const auto id = toCaller(v_new);
477 if (split.first != invalid_index)
479 insertSegment(split.first,
id);
480 insertSegment(
id, split.second);
488 Moose::initPredicates();
492 "XYIncrementalDelaunay: a constrained segment needs two different vertices, but both "
493 "ends of this one are vertex ",
506 mooseAssert(start <
_triangles.size(),
"Every vertex records a triangle it belongs to");
508 auto current = start;
512 const auto next_v =
_triangles[current].vertices[(i + 1) % 3];
513 const auto far_v =
_triangles[current].vertices[(i + 2) % 3];
521 const auto next_side = moose_orient2d(
xy(v_from),
xy(next_v),
xy(v_to));
531 if (next_side > 0.0 && moose_orient2d(
xy(v_from),
xy(far_v),
xy(v_to)) < 0.0)
539 current =
_triangles[current].neighbors[(i + 1) % 3];
543 mooseError(
"XYIncrementalDelaunay: no triangle around vertex ",
545 " is entered by the segment to vertex ",
550 std::vector<std::size_t> crossed{entered};
551 std::vector<std::size_t> right_chain{right};
552 std::vector<std::size_t> left_chain{left};
557 mooseError(
"XYIncrementalDelaunay: the segment from vertex ",
561 " crosses constrained segment (",
565 "); constrained segments may only meet at their ends.");
570 mooseError(
"XYIncrementalDelaunay: the segment from vertex ",
574 " leaves the triangulation before it reaches its end.");
579 crossed.push_back(next_t);
583 const auto side = moose_orient2d(
xy(v_from),
xy(v_to),
xy(apex));
591 crossed.push_back(next_t);
594 left_chain.push_back(apex);
599 right_chain.push_back(apex);
606 std::vector<std::array<std::size_t, 3>> added;
607 const std::vector<std::size_t> left_polygon(left_chain.rbegin(), left_chain.rend());
615std::vector<XYIncrementalDelaunay::Triangle>
619 std::size_t
count = 0;
623 position[t] =
count++;
625 std::vector<Triangle> triangles(
count);
631 auto & out = triangles[position[t]];
632 for (
const auto i : make_range(3u))
642std::vector<std::string>
645 Moose::initPredicates();
647 std::vector<std::string> violations;
648 std::set<Segment> edges;
654 xy(triangle.vertices[0]),
xy(triangle.vertices[1]),
xy(triangle.vertices[2])) <= 0.0)
655 violations.push_back(
"triangle " + std::to_string(t) +
656 " is not counter-clockwise, so its area is zero or negative");
658 for (
const auto i : make_range(3u))
660 const auto v0 = triangle.vertices[(i + 1) % 3];
661 const auto v1 = triangle.vertices[(i + 2) % 3];
664 const auto n = triangle.neighbors[i];
669 violations.push_back(
"triangle " + std::to_string(t) +
" has neighbor " +
670 std::to_string(n) +
", which is not a triangle");
675 for (
const auto k : make_range(3u))
682 violations.push_back(
"triangles " + std::to_string(t) +
" and " + std::to_string(n) +
683 " are neighbors but share no edge");
687 violations.push_back(
"triangle " + std::to_string(t) +
" has neighbor " +
688 std::to_string(n) +
", which does not have it back");
691 xy(triangle.vertices[1]),
692 xy(triangle.vertices[2]),
695 " is not constrained and is not locally Delaunay, because " +
697 " is inside the circumcircle of triangle " + std::to_string(t));
703 violations.push_back(
"constrained segment (" + std::to_string(v0) +
", " +
704 std::to_string(v1) +
") is not an edge of the triangulation");
709std::vector<std::string>
712 Moose::initPredicates();
714 std::vector<std::string> violations;
718 for (
const auto v : index_range(
_vertices))
720 if (v == triangle.vertices[0] || v == triangle.vertices[1] || v == triangle.vertices[2])
723 xy(triangle.vertices[0]),
xy(triangle.vertices[1]),
xy(triangle.vertices[2]),
xy(v)) >
725 violations.push_back(
vertexName(v) +
" is inside the circumcircle of triangle " +
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application.
std::size_t _last_triangle
A triangle the last insertion produced, which is where the next point walk starts.
static constexpr std::size_t _num_bounding
The number of bounding triangle vertices padding the front of the vertex list.
void initialize(const std::vector< Point2D > &points, const std::vector< Segment > &segments)
Triangulates points and recovers every entry of segments as an edge of the result.
std::size_t numPoints() const
std::size_t insertPoint(const Point2D &p)
Inserts a point, restoring the constrained Delaunay property around it.
bool isConstrainedSegment(std::size_t v0, std::size_t v1) const
void triangulatePseudopolygon(std::size_t v_start, std::size_t v_end, const std::vector< std::size_t > &chain, std::size_t first, std::size_t last, std::vector< std::array< std::size_t, 3 > > &triangles) const
Triangulates a polygon whose vertices are all visible from one of its edges, which is the shape a rec...
static Segment makeSegment(std::size_t v0, std::size_t v1)
unsigned int localVertexIndex(std::size_t t, std::size_t v) const
std::pair< std::size_t, std::size_t > Segment
A constrained segment, held as a vertex id pair with the smaller id first.
static constexpr double _bounding_reach
How far the bounding triangle reaches beyond the points, as a multiple of their extent.
std::set< Segment > _constraints
The constrained segments, in caller vertex ids with the smaller id first.
std::vector< std::string > checkInvariants() const
Checks everything this class promises: that every triangle is counter-clockwise, that the neighbor en...
bool isStrictlyBetween(std::size_t v_first, std::size_t v_mid, std::size_t v_last) const
std::vector< Triangle > _triangles
The triangles, every one of them live and counter-clockwise.
std::vector< std::string > checkEmptyCircumcircle() const
Tests every vertex against the circumcircle of every triangle.
static bool isBounding(std::size_t v)
std::size_t locate(const Point2D &p) const
Finds the triangle a point falls in by walking from the triangle the last insertion produced,...
void growCavity(std::size_t seed, std::size_t v_new, std::set< std::size_t > &cavity) const
Collects the triangles of the triangulation built so far that have to make way for a new vertex,...
std::string vertexName(std::size_t v) const
std::vector< Point2D > _vertices
The bounding triangle vertices followed by the caller's points.
unsigned int localEdgeIndex(std::size_t t, const Segment &edge) const
std::size_t toInternal(std::size_t id) const
void insertSegment(std::size_t v0, std::size_t v1)
Makes the segment between two vertices an edge of the triangulation and records it as constrained,...
bool isConstrainedEdge(std::size_t v0, std::size_t v1) const
const Point2D & point(std::size_t id) const
const double * xy(std::size_t v) const
bool containsPoint(std::size_t t, const Point2D &p) const
std::vector< std::size_t > retriangulate(const std::vector< std::size_t > &removed, const std::vector< std::array< std::size_t, 3 > > &added)
Swaps one triangulation of a region for another, reusing the slots of the triangles it removes and re...
std::size_t toCaller(std::size_t v) const
static constexpr std::size_t invalid_index
Sentinel for a vertex, triangle or neighbor that does not exist.
std::vector< std::size_t > _vertex_triangle
One triangle touching each vertex of _vertices, which is where a walk around it starts.
std::vector< Triangle > getTriangles() const
A point of the triangulation, held as plain coordinates.
A triangle of the triangulation.
std::array< std::size_t, 3 > neighbors
std::array< std::size_t, 3 > vertices