Files
2025-09-18 17:55:52 -04:00

216 lines
5.9 KiB
C++

#include "stdafx.h"
#include "Util/quadedge.h"
namespace GEMS {
unsigned int QuadEdge::globalVisitId = 1;
Edge* MakeEdge()
{
QuadEdge *ql = new QuadEdge;
return ql->e;
}
void Splice(Edge* a, Edge* b)
// This operator affects the two edge rings around the origins of a and b,
// and, independently, the two edge rings around the left faces of a and b.
// In each case, (i) if the two rings are distinct, Splice will combine
// them into one; (ii) if the two are the same ring, Splice will break it
// into two separate pieces.
// Thus, Splice can be used both to attach the two edges together, and
// to break them apart. See Guibas and Stolfi (1985) p.96 for more details
// and illustrations.
{
Edge* alpha = a->Onext()->Rot();
Edge* beta = b->Onext()->Rot();
Edge* t1 = b->Onext();
Edge* t2 = a->Onext();
Edge* t3 = beta->Onext();
Edge* t4 = alpha->Onext();
a->next = t1;
b->next = t2;
alpha->next = t3;
beta->next = t4;
}
void DeleteEdge(Edge* e)
{
Splice(e, e->Oprev());
Splice(e->Sym(), e->Sym()->Oprev());
delete e->Qedge();
}
/************* Topological Operations for Delaunay Diagrams *****************/
Subdivision::Subdivision(const Point2d& a, const Point2d& b, const Point2d& c)
// Initialize a subdivision to the triangle defined by the points a, b, c.
{
Point2d *da, *db, *dc;
da = new Point2d(a), db = new Point2d(b), dc = new Point2d(c);
Edge* ea = MakeEdge();
ea->EndPoints(da, db);
Edge* eb = MakeEdge();
Splice(ea->Sym(), eb);
eb->EndPoints(db, dc);
Edge* ec = MakeEdge();
Splice(eb->Sym(), ec);
ec->EndPoints(dc, da);
Splice(ec->Sym(), ea);
startingEdge = ea;
}
Edge* Connect(Edge* a, Edge* b)
// Add a new edge e connecting the destination of a to the
// origin of b, in such a way that all three have the same
// left face after the connection is complete.
// Additionally, the data pointers of the new edge are set.
{
Edge* e = MakeEdge();
Splice(e, a->Lnext());
Splice(e->Sym(), b);
e->EndPoints(a->Dest(), b->Org());
return e;
}
void Swap(Edge* e)
// Essentially turns edge e counterclockwise inside its enclosing
// quadrilateral. The data pointers are modified accordingly.
{
Edge* a = e->Oprev();
Edge* b = e->Sym()->Oprev();
Splice(e, a);
Splice(e->Sym(), b);
Splice(e, a->Lnext());
Splice(e->Sym(), b->Lnext());
e->EndPoints(a->Dest(), b->Dest());
}
/*************** Geometric Predicates for Delaunay Diagrams *****************/
inline Real TriArea(const Point2d& a, const Point2d& b, const Point2d& c)
// Returns twice the area of the oriented triangle (a, b, c), i.e., the
// area is positive if the triangle is oriented counterclockwise.
{
return (b.x - a.x)*(c.y - a.y) - (b.y - a.y)*(c.x - a.x);
}
int InCircle(const Point2d& a, const Point2d& b,
const Point2d& c, const Point2d& d)
// Returns TRUE if the point d is inside the circle defined by the
// points a, b, c. See Guibas and Stolfi (1985) p.107.
{
return (a.x*a.x + a.y*a.y) * TriArea(b, c, d) -
(b.x*b.x + b.y*b.y) * TriArea(a, c, d) +
(c.x*c.x + c.y*c.y) * TriArea(a, b, d) -
(d.x*d.x + d.y*d.y) * TriArea(a, b, c) > 0;
}
int ccw(const Point2d& a, const Point2d& b, const Point2d& c)
// Returns TRUE if the points a, b, c are in a counterclockwise order
{
return (TriArea(a, b, c) > 0);
}
int RightOf(const Point2d& x, Edge* e)
{
return ccw(x, e->Dest2d(), e->Org2d());
}
int LeftOf(const Point2d& x, Edge* e)
{
return ccw(x, e->Org2d(), e->Dest2d());
}
bool OnEdge(const Point2d& x, Edge* e)
// A predicate that determines if the point x is on the edge e.
// The point is considered on if it is in the EPS-neighborhood
// of the edge.
{
Real t1, t2, t3;
t1 = (x - e->Org2d()).norm();
t2 = (x - e->Dest2d()).norm();
if (t1 < GEMS_EPS || t2 < GEMS_EPS)
return true;
t3 = (e->Org2d() - e->Dest2d()).norm();
if (t1 > t3 || t2 > t3)
return false;
Line line(e->Org2d(), e->Dest2d());
return (fabs(line.eval(x)) < GEMS_EPS);
}
/************* An Incremental Algorithm for the Construction of *************/
/************************ Delaunay Diagrams *********************************/
Edge* Subdivision::Locate(const Point2d& x)
// Returns an edge e, s.t. either x is on e, or e is an edge of
// a triangle containing x. The search starts from startingEdge
// and proceeds in the general direction of x. Based on the
// pseudocode in Guibas and Stolfi (1985) p.121.
{
Edge* e = startingEdge;
while (true) {
if (x == e->Org2d() || x == e->Dest2d())
return e;
else if (RightOf(x, e))
e = e->Sym();
else if (!RightOf(x, e->Onext()))
e = e->Onext();
else if (!RightOf(x, e->Dprev()))
e = e->Dprev();
else
return e;
}
}
void Subdivision::InsertSite(const Point2d& x)
// Inserts a new point into a subdivision representing a Delaunay
// triangulation, and fixes the affected edges so that the result
// is still a Delaunay triangulation. This is based on the
// pseudocode from Guibas and Stolfi (1985) p.120, with slight
// modifications and a bug fix.
{
Edge* e = Locate(x);
if ((x == e->Org2d()) || (x == e->Dest2d())) // point is already in
return;
else if (OnEdge(x, e)) {
e = e->Oprev();
DeleteEdge(e->Onext());
}
// Connect the new point to the vertices of the containing
// triangle (or quadrilateral, if the new point fell on an
// existing edge.)
Edge* base = MakeEdge();
base->EndPoints(e->Org(), new Point2d(x));
Splice(base, e);
startingEdge = base;
do {
base = Connect(e, base->Sym());
e = base->Oprev();
} while (e->Lnext() != startingEdge);
// Examine suspect edges to ensure that the Delaunay condition
// is satisfied.
do {
Edge* t = e->Oprev();
if (RightOf(t->Dest2d(), e) &&
InCircle(e->Org2d(), t->Dest2d(), e->Dest2d(), x)) {
Swap(e);
e = e->Oprev();
}
else if (e->Onext() == startingEdge) // no more suspect edges
return;
else // pop a suspect edge
e = e->Onext()->Lprev();
} while (true);
}
/*****************************************************************************/
} // namespace