diff --git a/poly2tri/sweep/cdt.h b/poly2tri/sweep/cdt.h index c428430..baf6ca7 100644 --- a/poly2tri/sweep/cdt.h +++ b/poly2tri/sweep/cdt.h @@ -36,31 +36,67 @@ #include "sweep_context.h" #include "sweep.h" +/** + * + * @author Mason Green + * + */ + namespace p2t { class CDT { public: -/// Constructor -CDT(std::vector polyline); -/// Destructor -~CDT(); -/// Add a hole -void AddHole(std::vector polyline); -/// Add a single point -void AddPoint(Point* point); -/// Triangulate points -void Triangulate(); -/// Get Delaunay triangles -std::vector GetTriangles(); -/// Get triangle map -std::list GetMap(); + /** + * Constructor - add polyline with non repeating points + * + * @param polyline + */ + CDT(std::vector polyline); + + /** + * Destructor - clean up memory + */ + ~CDT(); + + /** + * Add a hole + * + * @param polyline + */ + void AddHole(std::vector polyline); + + /** + * Add a steiner point + * + * @param point + */ + void AddPoint(Point* point); + + /** + * Triangulate - do this AFTER you've added the polyline, holes, and Steiner points + */ + void Triangulate(); + + /** + * Get CDT triangles + */ + std::vector GetTriangles(); + + /** + * Get triangle map + */ + std::list GetMap(); -private: + private: -SweepContext* sweep_context_; -Sweep* sweep_; + /** + * Internals + */ + + SweepContext* sweep_context_; + Sweep* sweep_; }; diff --git a/poly2tri/sweep/sweep.cc b/poly2tri/sweep/sweep.cc index 66bd099..12a402f 100644 --- a/poly2tri/sweep/sweep.cc +++ b/poly2tri/sweep/sweep.cc @@ -71,15 +71,6 @@ void Sweep::FinalizationPolygon(SweepContext& tcx) tcx.MeshClean(*t); } -/** - * Find closes node to the left of the new point and - * create a new triangle. If needed new holes and basins - * will be filled to. - * - * @param tcx - * @param point - * @return - */ Node& Sweep::PointEvent(SweepContext& tcx, Point& point) { Node& node = tcx.LocateNode(point); @@ -205,11 +196,6 @@ Node& Sweep::NewFrontTriangle(SweepContext& tcx, Point& point, Node& node) return *new_node; } -/** - * Adds a triangle to the advancing front to fill a hole. - * @param tcx - * @param node - middle node, that is the bottom of the hole - */ void Sweep::Fill(SweepContext& tcx, Node& node) { Triangle* triangle = new Triangle(*node.prev->point, *node.point, *node.next->point); @@ -232,13 +218,6 @@ void Sweep::Fill(SweepContext& tcx, Node& node) } -/** - * Fills holes in the Advancing Front - * - * - * @param tcx - * @param n - */ void Sweep::FillAdvancingFront(SweepContext& tcx, Node& n) { @@ -278,11 +257,6 @@ double Sweep::BasinAngle(Node& node) return atan2(ay, ax); } -/** - * - * @param node - middle node - * @return the angle between 3 front nodes - */ double Sweep::HoleAngle(Node& node) { /* Complex plane @@ -300,9 +274,6 @@ double Sweep::HoleAngle(Node& node) return atan2(ax * by - ay * bx, ax * bx + ay * by); } -/** - * Returns true if triangle was legalized - */ bool Sweep::Legalize(SweepContext& tcx, Triangle& t) { // To legalize a triangle we start by finding if any of the three edges @@ -364,30 +335,6 @@ bool Sweep::Legalize(SweepContext& tcx, Triangle& t) return false; } -/** - * Requirement:
- * 1. a,b and c form a triangle.
- * 2. a and d is know to be on opposite side of bc
- *
- *                a
- *                +
- *               / \
- *              /   \
- *            b/     \c
- *            +-------+
- *           /    d    \
- *          /           \
- * 
- * Fact: d has to be in area B to have a chance to be inside the circle formed by - * a,b and c
- * d is outside B if orient2d(a,b,d) or orient2d(c,a,d) is CW
- * This preknowledge gives us a way to optimize the incircle test - * @param a - triangle point, opposite d - * @param b - triangle point - * @param c - triangle point - * @param d - point opposite a - * @return true if d is inside circle, false if on circle edge - */ bool Sweep::Incircle(Point& pa, Point& pb, Point& pc, Point& pd) { double adx = pa.x - pd.x; @@ -424,20 +371,6 @@ bool Sweep::Incircle(Point& pa, Point& pb, Point& pc, Point& pd) return det > 0; } -/** - * Rotates a triangle pair one vertex CW - *
- *       n2                    n2
- *  P +-----+             P +-----+
- *    | t  /|               |\  t |
- *    |   / |               | \   |
- *  n1|  /  |n3           n1|  \  |n3
- *    | /   |    after CW   |   \ |
- *    |/ oT |               | oT \|
- *    +-----+ oP            +-----+
- *       n4                    n4
- * 
- */ void Sweep::RotateTrianglePair(Triangle& t, Point& p, Triangle& ot, Point& op) { Triangle* n1, *n2, *n3, *n4; @@ -487,15 +420,6 @@ void Sweep::RotateTrianglePair(Triangle& t, Point& p, Triangle& ot, Point& op) t.MarkNeighbor(ot); } -/** - * Fills a basin that has formed on the Advancing Front to the right - * of given node.
- * First we decide a left,bottom and right node that forms the - * boundaries of the basin. Then we do a reqursive fill. - * - * @param tcx - * @param node - starting node, this or next node will be left node - */ void Sweep::FillBasin(SweepContext& tcx, Node& node) { if (Orient2d(*node.point, *node.next->point, *node.next->next->point) == CCW) { @@ -531,13 +455,6 @@ void Sweep::FillBasin(SweepContext& tcx, Node& node) FillBasinReq(tcx, tcx.basin.bottom_node); } -/** - * Recursive algorithm to fill a Basin with triangles - * - * @param tcx - * @param node - bottom_node - * @param cnt - counter used to alternate on even and odd numbers - */ void Sweep::FillBasinReq(SweepContext& tcx, Node* node) { // if shallow stop filling diff --git a/poly2tri/sweep/sweep.h b/poly2tri/sweep/sweep.h index 5cb8ef4..bd98adf 100644 --- a/poly2tri/sweep/sweep.h +++ b/poly2tri/sweep/sweep.h @@ -49,75 +49,227 @@ struct Point; struct Edge; class Triangle; -class Sweep { +class Sweep +{ public: -void Triangulate(SweepContext& tcx); -~Sweep(); + /** + * Triangulate + * + * @param tcx + */ + void Triangulate(SweepContext& tcx); + + /** + * Destructor - clean up memory + */ + ~Sweep(); private: -void SweepPoints(SweepContext& tcx); + /** + * Start sweeping the Y-sorted point set from bottom to top + * + * @param tcx + */ + void SweepPoints(SweepContext& tcx); -Node& PointEvent(SweepContext& tcx, Point& point); + /** + * Find closes node to the left of the new point and + * create a new triangle. If needed new holes and basins + * will be filled to. + * + * @param tcx + * @param point + * @return + */ + Node& PointEvent(SweepContext& tcx, Point& point); -void EdgeEvent(SweepContext& tcx, Edge* edge, Node* node); + /** + * + * + * @param tcx + * @param edge + * @param node + */ + void EdgeEvent(SweepContext& tcx, Edge* edge, Node* node); -void EdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* triangle, Point& point); + void EdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* triangle, Point& point); -Node& NewFrontTriangle(SweepContext& tcx, Point& point, Node& node); + /** + * Creates a new front triangle and legalize it + * + * @param tcx + * @param point + * @param node + * @return + */ + Node& NewFrontTriangle(SweepContext& tcx, Point& point, Node& node); -void Fill(SweepContext& tcx, Node& node); + /** + * Adds a triangle to the advancing front to fill a hole. + * @param tcx + * @param node - middle node, that is the bottom of the hole + */ + void Fill(SweepContext& tcx, Node& node); -bool Legalize(SweepContext& tcx, Triangle& t); + /** + * Returns true if triangle was legalized + */ + bool Legalize(SweepContext& tcx, Triangle& t); -bool Incircle(Point& pa, Point& pb, Point& pc, Point& pd); + /** + * Requirement:
+ * 1. a,b and c form a triangle.
+ * 2. a and d is know to be on opposite side of bc
+ *
+   *                a
+   *                +
+   *               / \
+   *              /   \
+   *            b/     \c
+   *            +-------+
+   *           /    d    \
+   *          /           \
+   * 
+ * Fact: d has to be in area B to have a chance to be inside the circle formed by + * a,b and c
+ * d is outside B if orient2d(a,b,d) or orient2d(c,a,d) is CW
+ * This preknowledge gives us a way to optimize the incircle test + * @param a - triangle point, opposite d + * @param b - triangle point + * @param c - triangle point + * @param d - point opposite a + * @return true if d is inside circle, false if on circle edge + */ + bool Incircle(Point& pa, Point& pb, Point& pc, Point& pd); -void RotateTrianglePair(Triangle& t, Point& p, Triangle& ot, Point& op); + /** + * Rotates a triangle pair one vertex CW + *
+   *       n2                    n2
+   *  P +-----+             P +-----+
+   *    | t  /|               |\  t |
+   *    |   / |               | \   |
+   *  n1|  /  |n3           n1|  \  |n3
+   *    | /   |    after CW   |   \ |
+   *    |/ oT |               | oT \|
+   *    +-----+ oP            +-----+
+   *       n4                    n4
+   * 
+ */ + void RotateTrianglePair(Triangle& t, Point& p, Triangle& ot, Point& op); -void FillAdvancingFront(SweepContext& tcx, Node& n); + /** + * Fills holes in the Advancing Front + * + * + * @param tcx + * @param n + */ + void FillAdvancingFront(SweepContext& tcx, Node& n); -double HoleAngle(Node& node); + /** + * + * @param node - middle node + * @return the angle between 3 front nodes + */ + double HoleAngle(Node& node); -double BasinAngle(Node& node); + /** + * The basin angle is decided against the horizontal line [1,0] + */ + double BasinAngle(Node& node); -void FillBasin(SweepContext& tcx, Node& node); + /** + * Fills a basin that has formed on the Advancing Front to the right + * of given node.
+ * First we decide a left,bottom and right node that forms the + * boundaries of the basin. Then we do a reqursive fill. + * + * @param tcx + * @param node - starting node, this or next node will be left node + */ + void FillBasin(SweepContext& tcx, Node& node); -void FillBasinReq(SweepContext& tcx, Node* node); + /** + * Recursive algorithm to fill a Basin with triangles + * + * @param tcx + * @param node - bottom_node + * @param cnt - counter used to alternate on even and odd numbers + */ + void FillBasinReq(SweepContext& tcx, Node* node); -bool IsShallow(SweepContext& tcx, Node& node); + bool IsShallow(SweepContext& tcx, Node& node); -bool IsEdgeSideOfTriangle(Triangle& triangle, Point& ep, Point& eq); + bool IsEdgeSideOfTriangle(Triangle& triangle, Point& ep, Point& eq); -void FillEdgeEvent(SweepContext& tcx, Edge* edge, Node* node); + void FillEdgeEvent(SweepContext& tcx, Edge* edge, Node* node); -void FillRightAboveEdgeEvent(SweepContext& tcx, Edge* edge, Node* node); + void FillRightAboveEdgeEvent(SweepContext& tcx, Edge* edge, Node* node); -void FillRightBelowEdgeEvent(SweepContext& tcx, Edge* edge, Node& node); + void FillRightBelowEdgeEvent(SweepContext& tcx, Edge* edge, Node& node); -void FillRightConcaveEdgeEvent(SweepContext& tcx, Edge* edge, Node& node); + void FillRightConcaveEdgeEvent(SweepContext& tcx, Edge* edge, Node& node); -void FillRightConvexEdgeEvent(SweepContext& tcx, Edge* edge, Node& node); + void FillRightConvexEdgeEvent(SweepContext& tcx, Edge* edge, Node& node); -void FillLeftAboveEdgeEvent(SweepContext& tcx, Edge* edge, Node* node); + void FillLeftAboveEdgeEvent(SweepContext& tcx, Edge* edge, Node* node); -void FillLeftBelowEdgeEvent(SweepContext& tcx, Edge* edge, Node& node); + void FillLeftBelowEdgeEvent(SweepContext& tcx, Edge* edge, Node& node); -void FillLeftConcaveEdgeEvent(SweepContext& tcx, Edge* edge, Node& node); + void FillLeftConcaveEdgeEvent(SweepContext& tcx, Edge* edge, Node& node); -void FillLeftConvexEdgeEvent(SweepContext& tcx, Edge* edge, Node& node); + void FillLeftConvexEdgeEvent(SweepContext& tcx, Edge* edge, Node& node); -void FlipEdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* t, Point& p); + void FlipEdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* t, Point& p); -Triangle& NextFlipTriangle(SweepContext& tcx, int o, Triangle& t, Triangle& ot, Point& p, Point& op); + /** + * After a flip we have two triangles and know that only one will still be + * intersecting the edge. So decide which to contiune with and legalize the other + * + * @param tcx + * @param o - should be the result of an orient2d( eq, op, ep ) + * @param t - triangle 1 + * @param ot - triangle 2 + * @param p - a point shared by both triangles + * @param op - another point shared by both triangles + * @return returns the triangle still intersecting the edge + */ + Triangle& NextFlipTriangle(SweepContext& tcx, int o, Triangle& t, Triangle& ot, Point& p, Point& op); -Point& NextFlipPoint(Point& ep, Point& eq, Triangle& ot, Point& op); + /** + * When we need to traverse from one triangle to the next we need + * the point in current triangle that is the opposite point to the next + * triangle. + * + * @param ep + * @param eq + * @param ot + * @param op + * @return + */ + Point& NextFlipPoint(Point& ep, Point& eq, Triangle& ot, Point& op); -void FlipScanEdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle& flip_triangle, Triangle& t, Point& p); + /** + * Scan part of the FlipScan algorithm
+ * When a triangle pair isn't flippable we will scan for the next + * point that is inside the flip triangle scan area. When found + * we generate a new flipEdgeEvent + * + * @param tcx + * @param ep - last point on the edge we are traversing + * @param eq - first point on the edge we are traversing + * @param flipTriangle - the current triangle sharing the point eq with edge + * @param t + * @param p + */ + void FlipScanEdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle& flip_triangle, Triangle& t, Point& p); -void FinalizationPolygon(SweepContext& tcx); + void FinalizationPolygon(SweepContext& tcx); -std::vector nodes_; + std::vector nodes_; };