mirror of
https://github.com/copyrighttxt/watrbx-game-engine.git
synced 2026-09-04 20:57:49 +00:00
216 lines
5.9 KiB
C++
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
|