diff --git a/poly2tri/common/shapes.cc b/poly2tri/common/shapes.cc index fce2bf6..50c1043 100644 --- a/poly2tri/common/shapes.cc +++ b/poly2tri/common/shapes.cc @@ -362,5 +362,4 @@ void Triangle::DebugPrint() cout << points_[2]->x << "," << points_[2]->y << endl; } -} - +} \ No newline at end of file diff --git a/poly2tri/common/shapes.h b/poly2tri/common/shapes.h index caff5ed..5631606 100644 --- a/poly2tri/common/shapes.h +++ b/poly2tri/common/shapes.h @@ -320,6 +320,4 @@ inline void Triangle::IsInterior(bool b) } -#endif - - +#endif \ No newline at end of file diff --git a/poly2tri/common/utils.h b/poly2tri/common/utils.h index ad27efa..dcfb812 100644 --- a/poly2tri/common/utils.h +++ b/poly2tri/common/utils.h @@ -1,123 +1,122 @@ -/* - * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors - * http://code.google.com/p/poly2tri/ - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * * Neither the name of Poly2Tri nor the names of its contributors may be - * used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef UTILS_H -#define UTILS_H - -// Otherwise #defines like M_PI are undeclared under Visual Studio -#define _USE_MATH_DEFINES - -#include -#include - -namespace p2t { - -const double PI_3div4 = 3 * M_PI / 4; -const double PI_div2 = 1.57079632679489661923; -const double EPSILON = 1e-12; - -enum Orientation { CW, CCW, COLLINEAR }; - -/** - * Forumla to calculate signed area
- * Positive if CCW
- * Negative if CW
- * 0 if collinear
- *
- * A[P1,P2,P3]  =  (x1*y2 - y1*x2) + (x2*y3 - y2*x3) + (x3*y1 - y3*x1)
- *              =  (x1-x3)*(y2-y3) - (y1-y3)*(x2-x3)
- * 
- */ -Orientation Orient2d(Point& pa, Point& pb, Point& pc) -{ - double detleft = (pa.x - pc.x) * (pb.y - pc.y); - double detright = (pa.y - pc.y) * (pb.x - pc.x); - double val = detleft - detright; - if (val > -EPSILON && val < EPSILON) { - return COLLINEAR; - } else if (val > 0) { - return CCW; - } - return CW; -} - -/* -bool InScanArea(Point& pa, Point& pb, Point& pc, Point& pd) -{ - double pdx = pd.x; - double pdy = pd.y; - double adx = pa.x - pdx; - double ady = pa.y - pdy; - double bdx = pb.x - pdx; - double bdy = pb.y - pdy; - - double adxbdy = adx * bdy; - double bdxady = bdx * ady; - double oabd = adxbdy - bdxady; - - if (oabd <= EPSILON) { - return false; - } - - double cdx = pc.x - pdx; - double cdy = pc.y - pdy; - - double cdxady = cdx * ady; - double adxcdy = adx * cdy; - double ocad = cdxady - adxcdy; - - if (ocad <= EPSILON) { - return false; - } - - return true; -} - -*/ - -bool InScanArea(Point& pa, Point& pb, Point& pc, Point& pd) -{ - double oadb = (pa.x - pb.x)*(pd.y - pb.y) - (pd.x - pb.x)*(pa.y - pb.y); - if (oadb >= -EPSILON) { - return false; - } - - double oadc = (pa.x - pc.x)*(pd.y - pc.y) - (pd.x - pc.x)*(pa.y - pc.y); - if (oadc <= EPSILON) { - return false; - } - return true; -} - -} - -#endif - +/* + * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors + * http://code.google.com/p/poly2tri/ + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Poly2Tri nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef UTILS_H +#define UTILS_H + +// Otherwise #defines like M_PI are undeclared under Visual Studio +#define _USE_MATH_DEFINES + +#include +#include + +namespace p2t { + +const double PI_3div4 = 3 * M_PI / 4; +const double PI_div2 = 1.57079632679489661923; +const double EPSILON = 1e-12; + +enum Orientation { CW, CCW, COLLINEAR }; + +/** + * Forumla to calculate signed area
+ * Positive if CCW
+ * Negative if CW
+ * 0 if collinear
+ *
+ * A[P1,P2,P3]  =  (x1*y2 - y1*x2) + (x2*y3 - y2*x3) + (x3*y1 - y3*x1)
+ *              =  (x1-x3)*(y2-y3) - (y1-y3)*(x2-x3)
+ * 
+ */ +Orientation Orient2d(Point& pa, Point& pb, Point& pc) +{ + double detleft = (pa.x - pc.x) * (pb.y - pc.y); + double detright = (pa.y - pc.y) * (pb.x - pc.x); + double val = detleft - detright; + if (val > -EPSILON && val < EPSILON) { + return COLLINEAR; + } else if (val > 0) { + return CCW; + } + return CW; +} + +/* +bool InScanArea(Point& pa, Point& pb, Point& pc, Point& pd) +{ + double pdx = pd.x; + double pdy = pd.y; + double adx = pa.x - pdx; + double ady = pa.y - pdy; + double bdx = pb.x - pdx; + double bdy = pb.y - pdy; + + double adxbdy = adx * bdy; + double bdxady = bdx * ady; + double oabd = adxbdy - bdxady; + + if (oabd <= EPSILON) { + return false; + } + + double cdx = pc.x - pdx; + double cdy = pc.y - pdy; + + double cdxady = cdx * ady; + double adxcdy = adx * cdy; + double ocad = cdxady - adxcdy; + + if (ocad <= EPSILON) { + return false; + } + + return true; +} + +*/ + +bool InScanArea(Point& pa, Point& pb, Point& pc, Point& pd) +{ + double oadb = (pa.x - pb.x)*(pd.y - pb.y) - (pd.x - pb.x)*(pa.y - pb.y); + if (oadb >= -EPSILON) { + return false; + } + + double oadc = (pa.x - pc.x)*(pd.y - pc.y) - (pd.x - pc.x)*(pa.y - pc.y); + if (oadc <= EPSILON) { + return false; + } + return true; +} + +} + +#endif \ No newline at end of file diff --git a/poly2tri/poly2tri.h b/poly2tri/poly2tri.h index 487755e..29a08d0 100644 --- a/poly2tri/poly2tri.h +++ b/poly2tri/poly2tri.h @@ -1,4 +1,4 @@ -/* +/* * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors * http://code.google.com/p/poly2tri/ * @@ -35,5 +35,4 @@ #include "common/shapes.h" #include "sweep/cdt.h" -#endif - +#endif \ No newline at end of file diff --git a/poly2tri/sweep/advancing_front.cc b/poly2tri/sweep/advancing_front.cc index 019df4a..d81a41c 100644 --- a/poly2tri/sweep/advancing_front.cc +++ b/poly2tri/sweep/advancing_front.cc @@ -105,5 +105,4 @@ AdvancingFront::~AdvancingFront() { } -} - +} \ No newline at end of file diff --git a/poly2tri/sweep/advancing_front.h b/poly2tri/sweep/advancing_front.h index bab73d4..9268395 100644 --- a/poly2tri/sweep/advancing_front.h +++ b/poly2tri/sweep/advancing_front.h @@ -115,4 +115,4 @@ inline void AdvancingFront::set_search(Node* node) } -#endif +#endif \ No newline at end of file diff --git a/poly2tri/sweep/cdt.cc b/poly2tri/sweep/cdt.cc index f013e47..f983858 100644 --- a/poly2tri/sweep/cdt.cc +++ b/poly2tri/sweep/cdt.cc @@ -1,72 +1,71 @@ -/* - * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors - * http://code.google.com/p/poly2tri/ - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * * Neither the name of Poly2Tri nor the names of its contributors may be - * used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#include "cdt.h" - -namespace p2t { - -CDT::CDT(std::vector polyline) -{ - sweep_context_ = new SweepContext(polyline); - sweep_ = new Sweep; -} - -void CDT::AddHole(std::vector polyline) -{ - sweep_context_->AddHole(polyline); -} - -void CDT::AddPoint(Point* point) { - sweep_context_->AddPoint(point); -} - -void CDT::Triangulate() -{ - sweep_->Triangulate(*sweep_context_); -} - -std::vector CDT::GetTriangles() -{ - return sweep_context_->GetTriangles(); -} - -std::list CDT::GetMap() -{ - return sweep_context_->GetMap(); -} - -CDT::~CDT() -{ - delete sweep_context_; - delete sweep_; -} - -} - +/* + * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors + * http://code.google.com/p/poly2tri/ + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Poly2Tri nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "cdt.h" + +namespace p2t { + +CDT::CDT(std::vector polyline) +{ + sweep_context_ = new SweepContext(polyline); + sweep_ = new Sweep; +} + +void CDT::AddHole(std::vector polyline) +{ + sweep_context_->AddHole(polyline); +} + +void CDT::AddPoint(Point* point) { + sweep_context_->AddPoint(point); +} + +void CDT::Triangulate() +{ + sweep_->Triangulate(*sweep_context_); +} + +std::vector CDT::GetTriangles() +{ + return sweep_context_->GetTriangles(); +} + +std::list CDT::GetMap() +{ + return sweep_context_->GetMap(); +} + +CDT::~CDT() +{ + delete sweep_context_; + delete sweep_; +} + +} \ No newline at end of file diff --git a/poly2tri/sweep/cdt.h b/poly2tri/sweep/cdt.h index baf6ca7..a77734b 100644 --- a/poly2tri/sweep/cdt.h +++ b/poly2tri/sweep/cdt.h @@ -1,105 +1,105 @@ -/* - * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors - * http://code.google.com/p/poly2tri/ - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * * Neither the name of Poly2Tri nor the names of its contributors may be - * used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef CDT_H -#define CDT_H - -#include "advancing_front.h" -#include "sweep_context.h" -#include "sweep.h" - -/** - * - * @author Mason Green - * - */ - -namespace p2t { - -class CDT -{ -public: - - /** - * 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: - - /** - * Internals - */ - - SweepContext* sweep_context_; - Sweep* sweep_; - -}; - -} - -#endif +/* + * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors + * http://code.google.com/p/poly2tri/ + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Poly2Tri nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CDT_H +#define CDT_H + +#include "advancing_front.h" +#include "sweep_context.h" +#include "sweep.h" + +/** + * + * @author Mason Green + * + */ + +namespace p2t { + +class CDT +{ +public: + + /** + * 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: + + /** + * Internals + */ + + SweepContext* sweep_context_; + Sweep* sweep_; + +}; + +} + +#endif \ No newline at end of file diff --git a/poly2tri/sweep/sweep.cc b/poly2tri/sweep/sweep.cc index 9d157db..cfd675c 100644 --- a/poly2tri/sweep/sweep.cc +++ b/poly2tri/sweep/sweep.cc @@ -1,801 +1,801 @@ -/* - * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors - * http://code.google.com/p/poly2tri/ - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * * Neither the name of Poly2Tri nor the names of its contributors may be - * used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#include -#include "sweep.h" -#include "sweep_context.h" -#include "advancing_front.h" -#include "../common/utils.h" - -namespace p2t { - -// Triangulate simple polygon with holes -void Sweep::Triangulate(SweepContext& tcx) -{ - tcx.InitTriangulation(); - tcx.CreateAdvancingFront(nodes_); - // Sweep points; build mesh - SweepPoints(tcx); - // Clean up - FinalizationPolygon(tcx); -} - -void Sweep::SweepPoints(SweepContext& tcx) -{ - for (size_t i = 1; i < tcx.point_count(); i++) { - Point& point = *tcx.GetPoint(i); - Node* node = &PointEvent(tcx, point); - for (unsigned int i = 0; i < point.edge_list.size(); i++) { - EdgeEvent(tcx, point.edge_list[i], node); - } - } -} - -void Sweep::FinalizationPolygon(SweepContext& tcx) -{ - // Get an Internal triangle to start with - Triangle* t = tcx.front()->head()->next->triangle; - Point* p = tcx.front()->head()->next->point; - while (!t->GetConstrainedEdgeCW(*p)) { - t = t->NeighborCCW(*p); - } - - // Collect interior triangles constrained by edges - tcx.MeshClean(*t); -} - -Node& Sweep::PointEvent(SweepContext& tcx, Point& point) -{ - Node& node = tcx.LocateNode(point); - Node& new_node = NewFrontTriangle(tcx, point, node); - - // Only need to check +epsilon since point never have smaller - // x value than node due to how we fetch nodes from the front - if (point.x <= node.point->x + EPSILON) { - Fill(tcx, node); - } - - //tcx.AddNode(new_node); - - FillAdvancingFront(tcx, new_node); - return new_node; -} - -void Sweep::EdgeEvent(SweepContext& tcx, Edge* edge, Node* node) -{ - tcx.edge_event.constrained_edge = edge; - tcx.edge_event.right = (edge->p->x > edge->q->x); - - if (IsEdgeSideOfTriangle(*node->triangle, *edge->p, *edge->q)) { - return; - } - - // For now we will do all needed filling - // TODO: integrate with flip process might give some better performance - // but for now this avoid the issue with cases that needs both flips and fills - FillEdgeEvent(tcx, edge, node); - EdgeEvent(tcx, *edge->p, *edge->q, node->triangle, *edge->q); -} - -void Sweep::EdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* triangle, Point& point) -{ - if (IsEdgeSideOfTriangle(*triangle, ep, eq)) { - return; - } - - Point* p1 = triangle->PointCCW(point); - Orientation o1 = Orient2d(eq, *p1, ep); - if (o1 == COLLINEAR) { - if( triangle->Contains(&eq, p1)) { - triangle->MarkConstrainedEdge(&eq, p1 ); - // We are modifying the constraint maybe it would be better to - // not change the given constraint and just keep a variable for the new constraint - tcx.edge_event.constrained_edge->q = p1; - triangle = &triangle->NeighborAcross(point); - EdgeEvent( tcx, ep, *p1, triangle, *p1 ); - } else { - std::runtime_error("EdgeEvent - collinear points not supported"); - assert(0); - } - return; - } - - Point* p2 = triangle->PointCW(point); - Orientation o2 = Orient2d(eq, *p2, ep); - if (o2 == COLLINEAR) { - if( triangle->Contains(&eq, p2)) { - triangle->MarkConstrainedEdge(&eq, p2 ); - // We are modifying the constraint maybe it would be better to - // not change the given constraint and just keep a variable for the new constraint - tcx.edge_event.constrained_edge->q = p2; - triangle = &triangle->NeighborAcross(point); - EdgeEvent( tcx, ep, *p2, triangle, *p2 ); - } else { - std::runtime_error("EdgeEvent - collinear points not supported"); - assert(0); - } - return; - } - - if (o1 == o2) { - // Need to decide if we are rotating CW or CCW to get to a triangle - // that will cross edge - if (o1 == CW) { - triangle = triangle->NeighborCCW(point); - } else{ - triangle = triangle->NeighborCW(point); - } - EdgeEvent(tcx, ep, eq, triangle, point); - } else { - // This triangle crosses constraint so lets flippin start! - FlipEdgeEvent(tcx, ep, eq, triangle, point); - } -} - -bool Sweep::IsEdgeSideOfTriangle(Triangle& triangle, Point& ep, Point& eq) -{ - int index = triangle.EdgeIndex(&ep, &eq); - - if (index != -1) { - triangle.MarkConstrainedEdge(index); - Triangle* t = triangle.GetNeighbor(index); - if (t) { - t->MarkConstrainedEdge(&ep, &eq); - } - return true; - } - return false; -} - -Node& Sweep::NewFrontTriangle(SweepContext& tcx, Point& point, Node& node) -{ - Triangle* triangle = new Triangle(point, *node.point, *node.next->point); - - triangle->MarkNeighbor(*node.triangle); - tcx.AddToMap(triangle); - - Node* new_node = new Node(point); - nodes_.push_back(new_node); - - new_node->next = node.next; - new_node->prev = &node; - node.next->prev = new_node; - node.next = new_node; - - if (!Legalize(tcx, *triangle)) { - tcx.MapTriangleToNodes(*triangle); - } - - return *new_node; -} - -void Sweep::Fill(SweepContext& tcx, Node& node) -{ - Triangle* triangle = new Triangle(*node.prev->point, *node.point, *node.next->point); - - // TODO: should copy the constrained_edge value from neighbor triangles - // for now constrained_edge values are copied during the legalize - triangle->MarkNeighbor(*node.prev->triangle); - triangle->MarkNeighbor(*node.triangle); - - tcx.AddToMap(triangle); - - // Update the advancing front - node.prev->next = node.next; - node.next->prev = node.prev; - - // If it was legalized the triangle has already been mapped - if (!Legalize(tcx, *triangle)) { - tcx.MapTriangleToNodes(*triangle); - } - -} - -void Sweep::FillAdvancingFront(SweepContext& tcx, Node& n) +/* + * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors + * http://code.google.com/p/poly2tri/ + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Poly2Tri nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include +#include "sweep.h" +#include "sweep_context.h" +#include "advancing_front.h" +#include "../common/utils.h" + +namespace p2t { + +// Triangulate simple polygon with holes +void Sweep::Triangulate(SweepContext& tcx) { - - // Fill right holes - Node* node = n.next; - - while (node->next) { - // if HoleAngle exceeds 90 degrees then break. - if (LargeHole_DontFill(node)) break; - Fill(tcx, *node); - node = node->next; - } - - // Fill left holes - node = n.prev; - - while (node->prev) { - // if HoleAngle exceeds 90 degrees then break. - if (LargeHole_DontFill(node)) break; - Fill(tcx, *node); - node = node->prev; - } - - // Fill right basins - if (n.next && n.next->next) { - double angle = BasinAngle(n); - if (angle < PI_3div4) { - FillBasin(tcx, n); - } - } -} - -// True if HoleAngle exceeds 90 degrees. -bool Sweep::LargeHole_DontFill(Node* node) { - - Node* nextNode = node->next; - Node* prevNode = node->prev; - if (!AngleExceeds90Degrees(node->point, nextNode->point, prevNode->point)) - return false; - - // Check additional points on front. - Node* next2Node = nextNode->next; - // "..Plus.." because only want angles on same side as point being added. - if ((next2Node != NULL) && !AngleExceedsPlus90DegreesOrIsNegative(node->point, next2Node->point, prevNode->point)) - return false; - - Node* prev2Node = prevNode->prev; - // "..Plus.." because only want angles on same side as point being added. - if ((prev2Node != NULL) && !AngleExceedsPlus90DegreesOrIsNegative(node->point, nextNode->point, prev2Node->point)) - return false; - - return true; -} - -bool Sweep::AngleExceeds90Degrees(Point* origin, Point* pa, Point* pb) { - double angle = Angle(*origin, *pa, *pb); - bool exceeds90Degrees = ((angle > PI_div2) || (angle < -PI_div2)); - return exceeds90Degrees; -} - -bool Sweep::AngleExceedsPlus90DegreesOrIsNegative(Point* origin, Point* pa, Point* pb) { - double angle = Angle(*origin, *pa, *pb); - bool exceedsPlus90DegreesOrIsNegative = (angle > PI_div2) || (angle < 0); - return exceedsPlus90DegreesOrIsNegative; -} - -double Sweep::Angle(Point& origin, Point& pa, Point& pb) { - /* Complex plane - * ab = cosA +i*sinA - * ab = (ax + ay*i)(bx + by*i) = (ax*bx + ay*by) + i(ax*by-ay*bx) - * atan2(y,x) computes the principal value of the argument function - * applied to the complex number x+iy - * Where x = ax*bx + ay*by - * y = ax*by - ay*bx - */ - double px = origin.x; - double py = origin.y; - double ax = pa.x- px; - double ay = pa.y - py; - double bx = pb.x - px; - double by = pb.y - py; - double x = ax * by - ay * bx; - double y = ax * bx + ay * by; - double angle = atan2(x, y); - return angle; -} - -double Sweep::BasinAngle(Node& node) -{ - double ax = node.point->x - node.next->next->point->x; - double ay = node.point->y - node.next->next->point->y; - return atan2(ay, ax); -} - -double Sweep::HoleAngle(Node& node) -{ - /* Complex plane - * ab = cosA +i*sinA - * ab = (ax + ay*i)(bx + by*i) = (ax*bx + ay*by) + i(ax*by-ay*bx) - * atan2(y,x) computes the principal value of the argument function - * applied to the complex number x+iy - * Where x = ax*bx + ay*by - * y = ax*by - ay*bx - */ - double ax = node.next->point->x - node.point->x; - double ay = node.next->point->y - node.point->y; - double bx = node.prev->point->x - node.point->x; - double by = node.prev->point->y - node.point->y; - return atan2(ax * by - ay * bx, ax * bx + ay * by); -} - -bool Sweep::Legalize(SweepContext& tcx, Triangle& t) -{ - // To legalize a triangle we start by finding if any of the three edges - // violate the Delaunay condition - for (int i = 0; i < 3; i++) { - if (t.delaunay_edge[i]) - continue; - - Triangle* ot = t.GetNeighbor(i); - - if (ot) { - Point* p = t.GetPoint(i); - Point* op = ot->OppositePoint(t, *p); - int oi = ot->Index(op); - - // If this is a Constrained Edge or a Delaunay Edge(only during recursive legalization) - // then we should not try to legalize - if (ot->constrained_edge[oi] || ot->delaunay_edge[oi]) { - t.constrained_edge[i] = ot->constrained_edge[oi]; - continue; - } - - bool inside = Incircle(*p, *t.PointCCW(*p), *t.PointCW(*p), *op); - - if (inside) { - // Lets mark this shared edge as Delaunay - t.delaunay_edge[i] = true; - ot->delaunay_edge[oi] = true; - - // Lets rotate shared edge one vertex CW to legalize it - RotateTrianglePair(t, *p, *ot, *op); - - // We now got one valid Delaunay Edge shared by two triangles - // This gives us 4 new edges to check for Delaunay - - // Make sure that triangle to node mapping is done only one time for a specific triangle - bool not_legalized = !Legalize(tcx, t); - if (not_legalized) { - tcx.MapTriangleToNodes(t); - } - - not_legalized = !Legalize(tcx, *ot); - if (not_legalized) - tcx.MapTriangleToNodes(*ot); - - // Reset the Delaunay edges, since they only are valid Delaunay edges - // until we add a new triangle or point. - // XXX: need to think about this. Can these edges be tried after we - // return to previous recursive level? - t.delaunay_edge[i] = false; - ot->delaunay_edge[oi] = false; - - // If triangle have been legalized no need to check the other edges since - // the recursive legalization will handles those so we can end here. - return true; - } - } - } - return false; -} - -bool Sweep::Incircle(Point& pa, Point& pb, Point& pc, Point& pd) -{ - double adx = pa.x - pd.x; - double ady = pa.y - pd.y; - double bdx = pb.x - pd.x; - double bdy = pb.y - pd.y; - - double adxbdy = adx * bdy; - double bdxady = bdx * ady; - double oabd = adxbdy - bdxady; - - if (oabd <= 0) - return false; - - double cdx = pc.x - pd.x; - double cdy = pc.y - pd.y; - - double cdxady = cdx * ady; - double adxcdy = adx * cdy; - double ocad = cdxady - adxcdy; - - if (ocad <= 0) - return false; - - double bdxcdy = bdx * cdy; - double cdxbdy = cdx * bdy; - - double alift = adx * adx + ady * ady; - double blift = bdx * bdx + bdy * bdy; - double clift = cdx * cdx + cdy * cdy; - - double det = alift * (bdxcdy - cdxbdy) + blift * ocad + clift * oabd; - - return det > 0; -} - -void Sweep::RotateTrianglePair(Triangle& t, Point& p, Triangle& ot, Point& op) -{ - Triangle* n1, *n2, *n3, *n4; - n1 = t.NeighborCCW(p); - n2 = t.NeighborCW(p); - n3 = ot.NeighborCCW(op); - n4 = ot.NeighborCW(op); - - bool ce1, ce2, ce3, ce4; - ce1 = t.GetConstrainedEdgeCCW(p); - ce2 = t.GetConstrainedEdgeCW(p); - ce3 = ot.GetConstrainedEdgeCCW(op); - ce4 = ot.GetConstrainedEdgeCW(op); - - bool de1, de2, de3, de4; - de1 = t.GetDelunayEdgeCCW(p); - de2 = t.GetDelunayEdgeCW(p); - de3 = ot.GetDelunayEdgeCCW(op); - de4 = ot.GetDelunayEdgeCW(op); - - t.Legalize(p, op); - ot.Legalize(op, p); - - // Remap delaunay_edge - ot.SetDelunayEdgeCCW(p, de1); - t.SetDelunayEdgeCW(p, de2); - t.SetDelunayEdgeCCW(op, de3); - ot.SetDelunayEdgeCW(op, de4); - - // Remap constrained_edge - ot.SetConstrainedEdgeCCW(p, ce1); - t.SetConstrainedEdgeCW(p, ce2); - t.SetConstrainedEdgeCCW(op, ce3); - ot.SetConstrainedEdgeCW(op, ce4); - - // Remap neighbors - // XXX: might optimize the markNeighbor by keeping track of - // what side should be assigned to what neighbor after the - // rotation. Now mark neighbor does lots of testing to find - // the right side. - t.ClearNeighbors(); - ot.ClearNeighbors(); - if (n1) ot.MarkNeighbor(*n1); - if (n2) t.MarkNeighbor(*n2); - if (n3) t.MarkNeighbor(*n3); - if (n4) ot.MarkNeighbor(*n4); - t.MarkNeighbor(ot); -} - -void Sweep::FillBasin(SweepContext& tcx, Node& node) -{ - if (Orient2d(*node.point, *node.next->point, *node.next->next->point) == CCW) { - tcx.basin.left_node = node.next->next; - } else { - tcx.basin.left_node = node.next; - } - - // Find the bottom and right node - tcx.basin.bottom_node = tcx.basin.left_node; - while (tcx.basin.bottom_node->next - && tcx.basin.bottom_node->point->y >= tcx.basin.bottom_node->next->point->y) { - tcx.basin.bottom_node = tcx.basin.bottom_node->next; - } - if (tcx.basin.bottom_node == tcx.basin.left_node) { - // No valid basin - return; - } - - tcx.basin.right_node = tcx.basin.bottom_node; - while (tcx.basin.right_node->next - && tcx.basin.right_node->point->y < tcx.basin.right_node->next->point->y) { - tcx.basin.right_node = tcx.basin.right_node->next; - } - if (tcx.basin.right_node == tcx.basin.bottom_node) { - // No valid basins - return; - } - - tcx.basin.width = tcx.basin.right_node->point->x - tcx.basin.left_node->point->x; - tcx.basin.left_highest = tcx.basin.left_node->point->y > tcx.basin.right_node->point->y; - - FillBasinReq(tcx, tcx.basin.bottom_node); -} - -void Sweep::FillBasinReq(SweepContext& tcx, Node* node) -{ - // if shallow stop filling - if (IsShallow(tcx, *node)) { - return; - } - - Fill(tcx, *node); - - if (node->prev == tcx.basin.left_node && node->next == tcx.basin.right_node) { - return; - } else if (node->prev == tcx.basin.left_node) { - Orientation o = Orient2d(*node->point, *node->next->point, *node->next->next->point); - if (o == CW) { - return; - } - node = node->next; - } else if (node->next == tcx.basin.right_node) { - Orientation o = Orient2d(*node->point, *node->prev->point, *node->prev->prev->point); - if (o == CCW) { - return; - } - node = node->prev; - } else { - // Continue with the neighbor node with lowest Y value - if (node->prev->point->y < node->next->point->y) { - node = node->prev; - } else { - node = node->next; - } - } - - FillBasinReq(tcx, node); -} - -bool Sweep::IsShallow(SweepContext& tcx, Node& node) -{ - double height; - - if (tcx.basin.left_highest) { - height = tcx.basin.left_node->point->y - node.point->y; - } else { - height = tcx.basin.right_node->point->y - node.point->y; - } - - // if shallow stop filling - if (tcx.basin.width > height) { - return true; - } - return false; -} - -void Sweep::FillEdgeEvent(SweepContext& tcx, Edge* edge, Node* node) -{ - if (tcx.edge_event.right) { - FillRightAboveEdgeEvent(tcx, edge, node); - } else { - FillLeftAboveEdgeEvent(tcx, edge, node); - } -} - -void Sweep::FillRightAboveEdgeEvent(SweepContext& tcx, Edge* edge, Node* node) -{ - while (node->next->point->x < edge->p->x) { - // Check if next node is below the edge - if (Orient2d(*edge->q, *node->next->point, *edge->p) == CCW) { - FillRightBelowEdgeEvent(tcx, edge, *node); - } else { - node = node->next; - } - } -} - -void Sweep::FillRightBelowEdgeEvent(SweepContext& tcx, Edge* edge, Node& node) -{ - if (node.point->x < edge->p->x) { - if (Orient2d(*node.point, *node.next->point, *node.next->next->point) == CCW) { - // Concave - FillRightConcaveEdgeEvent(tcx, edge, node); - } else{ - // Convex - FillRightConvexEdgeEvent(tcx, edge, node); - // Retry this one - FillRightBelowEdgeEvent(tcx, edge, node); - } - } -} - -void Sweep::FillRightConcaveEdgeEvent(SweepContext& tcx, Edge* edge, Node& node) -{ - Fill(tcx, *node.next); - if (node.next->point != edge->p) { - // Next above or below edge? - if (Orient2d(*edge->q, *node.next->point, *edge->p) == CCW) { - // Below - if (Orient2d(*node.point, *node.next->point, *node.next->next->point) == CCW) { - // Next is concave - FillRightConcaveEdgeEvent(tcx, edge, node); - } else { - // Next is convex - } - } + tcx.InitTriangulation(); + tcx.CreateAdvancingFront(nodes_); + // Sweep points; build mesh + SweepPoints(tcx); + // Clean up + FinalizationPolygon(tcx); +} + +void Sweep::SweepPoints(SweepContext& tcx) +{ + for (size_t i = 1; i < tcx.point_count(); i++) { + Point& point = *tcx.GetPoint(i); + Node* node = &PointEvent(tcx, point); + for (unsigned int i = 0; i < point.edge_list.size(); i++) { + EdgeEvent(tcx, point.edge_list[i], node); + } + } +} + +void Sweep::FinalizationPolygon(SweepContext& tcx) +{ + // Get an Internal triangle to start with + Triangle* t = tcx.front()->head()->next->triangle; + Point* p = tcx.front()->head()->next->point; + while (!t->GetConstrainedEdgeCW(*p)) { + t = t->NeighborCCW(*p); + } + + // Collect interior triangles constrained by edges + tcx.MeshClean(*t); +} + +Node& Sweep::PointEvent(SweepContext& tcx, Point& point) +{ + Node& node = tcx.LocateNode(point); + Node& new_node = NewFrontTriangle(tcx, point, node); + + // Only need to check +epsilon since point never have smaller + // x value than node due to how we fetch nodes from the front + if (point.x <= node.point->x + EPSILON) { + Fill(tcx, node); + } + + //tcx.AddNode(new_node); + + FillAdvancingFront(tcx, new_node); + return new_node; +} + +void Sweep::EdgeEvent(SweepContext& tcx, Edge* edge, Node* node) +{ + tcx.edge_event.constrained_edge = edge; + tcx.edge_event.right = (edge->p->x > edge->q->x); + + if (IsEdgeSideOfTriangle(*node->triangle, *edge->p, *edge->q)) { + return; + } + + // For now we will do all needed filling + // TODO: integrate with flip process might give some better performance + // but for now this avoid the issue with cases that needs both flips and fills + FillEdgeEvent(tcx, edge, node); + EdgeEvent(tcx, *edge->p, *edge->q, node->triangle, *edge->q); +} + +void Sweep::EdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* triangle, Point& point) +{ + if (IsEdgeSideOfTriangle(*triangle, ep, eq)) { + return; + } + + Point* p1 = triangle->PointCCW(point); + Orientation o1 = Orient2d(eq, *p1, ep); + if (o1 == COLLINEAR) { + if( triangle->Contains(&eq, p1)) { + triangle->MarkConstrainedEdge(&eq, p1 ); + // We are modifying the constraint maybe it would be better to + // not change the given constraint and just keep a variable for the new constraint + tcx.edge_event.constrained_edge->q = p1; + triangle = &triangle->NeighborAcross(point); + EdgeEvent( tcx, ep, *p1, triangle, *p1 ); + } else { + std::runtime_error("EdgeEvent - collinear points not supported"); + assert(0); + } + return; + } + + Point* p2 = triangle->PointCW(point); + Orientation o2 = Orient2d(eq, *p2, ep); + if (o2 == COLLINEAR) { + if( triangle->Contains(&eq, p2)) { + triangle->MarkConstrainedEdge(&eq, p2 ); + // We are modifying the constraint maybe it would be better to + // not change the given constraint and just keep a variable for the new constraint + tcx.edge_event.constrained_edge->q = p2; + triangle = &triangle->NeighborAcross(point); + EdgeEvent( tcx, ep, *p2, triangle, *p2 ); + } else { + std::runtime_error("EdgeEvent - collinear points not supported"); + assert(0); + } + return; + } + + if (o1 == o2) { + // Need to decide if we are rotating CW or CCW to get to a triangle + // that will cross edge + if (o1 == CW) { + triangle = triangle->NeighborCCW(point); + } else{ + triangle = triangle->NeighborCW(point); + } + EdgeEvent(tcx, ep, eq, triangle, point); + } else { + // This triangle crosses constraint so lets flippin start! + FlipEdgeEvent(tcx, ep, eq, triangle, point); + } +} + +bool Sweep::IsEdgeSideOfTriangle(Triangle& triangle, Point& ep, Point& eq) +{ + int index = triangle.EdgeIndex(&ep, &eq); + + if (index != -1) { + triangle.MarkConstrainedEdge(index); + Triangle* t = triangle.GetNeighbor(index); + if (t) { + t->MarkConstrainedEdge(&ep, &eq); + } + return true; + } + return false; +} + +Node& Sweep::NewFrontTriangle(SweepContext& tcx, Point& point, Node& node) +{ + Triangle* triangle = new Triangle(point, *node.point, *node.next->point); + + triangle->MarkNeighbor(*node.triangle); + tcx.AddToMap(triangle); + + Node* new_node = new Node(point); + nodes_.push_back(new_node); + + new_node->next = node.next; + new_node->prev = &node; + node.next->prev = new_node; + node.next = new_node; + + if (!Legalize(tcx, *triangle)) { + tcx.MapTriangleToNodes(*triangle); + } + + return *new_node; +} + +void Sweep::Fill(SweepContext& tcx, Node& node) +{ + Triangle* triangle = new Triangle(*node.prev->point, *node.point, *node.next->point); + + // TODO: should copy the constrained_edge value from neighbor triangles + // for now constrained_edge values are copied during the legalize + triangle->MarkNeighbor(*node.prev->triangle); + triangle->MarkNeighbor(*node.triangle); + + tcx.AddToMap(triangle); + + // Update the advancing front + node.prev->next = node.next; + node.next->prev = node.prev; + + // If it was legalized the triangle has already been mapped + if (!Legalize(tcx, *triangle)) { + tcx.MapTriangleToNodes(*triangle); + } + +} + +void Sweep::FillAdvancingFront(SweepContext& tcx, Node& n) +{ + + // Fill right holes + Node* node = n.next; + + while (node->next) { + // if HoleAngle exceeds 90 degrees then break. + if (LargeHole_DontFill(node)) break; + Fill(tcx, *node); + node = node->next; + } + + // Fill left holes + node = n.prev; + + while (node->prev) { + // if HoleAngle exceeds 90 degrees then break. + if (LargeHole_DontFill(node)) break; + Fill(tcx, *node); + node = node->prev; + } + + // Fill right basins + if (n.next && n.next->next) { + double angle = BasinAngle(n); + if (angle < PI_3div4) { + FillBasin(tcx, n); + } + } +} + +// True if HoleAngle exceeds 90 degrees. +bool Sweep::LargeHole_DontFill(Node* node) { + + Node* nextNode = node->next; + Node* prevNode = node->prev; + if (!AngleExceeds90Degrees(node->point, nextNode->point, prevNode->point)) + return false; + + // Check additional points on front. + Node* next2Node = nextNode->next; + // "..Plus.." because only want angles on same side as point being added. + if ((next2Node != NULL) && !AngleExceedsPlus90DegreesOrIsNegative(node->point, next2Node->point, prevNode->point)) + return false; + + Node* prev2Node = prevNode->prev; + // "..Plus.." because only want angles on same side as point being added. + if ((prev2Node != NULL) && !AngleExceedsPlus90DegreesOrIsNegative(node->point, nextNode->point, prev2Node->point)) + return false; + + return true; +} + +bool Sweep::AngleExceeds90Degrees(Point* origin, Point* pa, Point* pb) { + double angle = Angle(*origin, *pa, *pb); + bool exceeds90Degrees = ((angle > PI_div2) || (angle < -PI_div2)); + return exceeds90Degrees; +} + +bool Sweep::AngleExceedsPlus90DegreesOrIsNegative(Point* origin, Point* pa, Point* pb) { + double angle = Angle(*origin, *pa, *pb); + bool exceedsPlus90DegreesOrIsNegative = (angle > PI_div2) || (angle < 0); + return exceedsPlus90DegreesOrIsNegative; +} + +double Sweep::Angle(Point& origin, Point& pa, Point& pb) { + /* Complex plane + * ab = cosA +i*sinA + * ab = (ax + ay*i)(bx + by*i) = (ax*bx + ay*by) + i(ax*by-ay*bx) + * atan2(y,x) computes the principal value of the argument function + * applied to the complex number x+iy + * Where x = ax*bx + ay*by + * y = ax*by - ay*bx + */ + double px = origin.x; + double py = origin.y; + double ax = pa.x- px; + double ay = pa.y - py; + double bx = pb.x - px; + double by = pb.y - py; + double x = ax * by - ay * bx; + double y = ax * bx + ay * by; + double angle = atan2(x, y); + return angle; +} + +double Sweep::BasinAngle(Node& node) +{ + double ax = node.point->x - node.next->next->point->x; + double ay = node.point->y - node.next->next->point->y; + return atan2(ay, ax); +} + +double Sweep::HoleAngle(Node& node) +{ + /* Complex plane + * ab = cosA +i*sinA + * ab = (ax + ay*i)(bx + by*i) = (ax*bx + ay*by) + i(ax*by-ay*bx) + * atan2(y,x) computes the principal value of the argument function + * applied to the complex number x+iy + * Where x = ax*bx + ay*by + * y = ax*by - ay*bx + */ + double ax = node.next->point->x - node.point->x; + double ay = node.next->point->y - node.point->y; + double bx = node.prev->point->x - node.point->x; + double by = node.prev->point->y - node.point->y; + return atan2(ax * by - ay * bx, ax * bx + ay * by); +} + +bool Sweep::Legalize(SweepContext& tcx, Triangle& t) +{ + // To legalize a triangle we start by finding if any of the three edges + // violate the Delaunay condition + for (int i = 0; i < 3; i++) { + if (t.delaunay_edge[i]) + continue; + + Triangle* ot = t.GetNeighbor(i); + + if (ot) { + Point* p = t.GetPoint(i); + Point* op = ot->OppositePoint(t, *p); + int oi = ot->Index(op); + + // If this is a Constrained Edge or a Delaunay Edge(only during recursive legalization) + // then we should not try to legalize + if (ot->constrained_edge[oi] || ot->delaunay_edge[oi]) { + t.constrained_edge[i] = ot->constrained_edge[oi]; + continue; + } + + bool inside = Incircle(*p, *t.PointCCW(*p), *t.PointCW(*p), *op); + + if (inside) { + // Lets mark this shared edge as Delaunay + t.delaunay_edge[i] = true; + ot->delaunay_edge[oi] = true; + + // Lets rotate shared edge one vertex CW to legalize it + RotateTrianglePair(t, *p, *ot, *op); + + // We now got one valid Delaunay Edge shared by two triangles + // This gives us 4 new edges to check for Delaunay + + // Make sure that triangle to node mapping is done only one time for a specific triangle + bool not_legalized = !Legalize(tcx, t); + if (not_legalized) { + tcx.MapTriangleToNodes(t); + } + + not_legalized = !Legalize(tcx, *ot); + if (not_legalized) + tcx.MapTriangleToNodes(*ot); + + // Reset the Delaunay edges, since they only are valid Delaunay edges + // until we add a new triangle or point. + // XXX: need to think about this. Can these edges be tried after we + // return to previous recursive level? + t.delaunay_edge[i] = false; + ot->delaunay_edge[oi] = false; + + // If triangle have been legalized no need to check the other edges since + // the recursive legalization will handles those so we can end here. + return true; + } + } + } + return false; +} + +bool Sweep::Incircle(Point& pa, Point& pb, Point& pc, Point& pd) +{ + double adx = pa.x - pd.x; + double ady = pa.y - pd.y; + double bdx = pb.x - pd.x; + double bdy = pb.y - pd.y; + + double adxbdy = adx * bdy; + double bdxady = bdx * ady; + double oabd = adxbdy - bdxady; + + if (oabd <= 0) + return false; + + double cdx = pc.x - pd.x; + double cdy = pc.y - pd.y; + + double cdxady = cdx * ady; + double adxcdy = adx * cdy; + double ocad = cdxady - adxcdy; + + if (ocad <= 0) + return false; + + double bdxcdy = bdx * cdy; + double cdxbdy = cdx * bdy; + + double alift = adx * adx + ady * ady; + double blift = bdx * bdx + bdy * bdy; + double clift = cdx * cdx + cdy * cdy; + + double det = alift * (bdxcdy - cdxbdy) + blift * ocad + clift * oabd; + + return det > 0; +} + +void Sweep::RotateTrianglePair(Triangle& t, Point& p, Triangle& ot, Point& op) +{ + Triangle* n1, *n2, *n3, *n4; + n1 = t.NeighborCCW(p); + n2 = t.NeighborCW(p); + n3 = ot.NeighborCCW(op); + n4 = ot.NeighborCW(op); + + bool ce1, ce2, ce3, ce4; + ce1 = t.GetConstrainedEdgeCCW(p); + ce2 = t.GetConstrainedEdgeCW(p); + ce3 = ot.GetConstrainedEdgeCCW(op); + ce4 = ot.GetConstrainedEdgeCW(op); + + bool de1, de2, de3, de4; + de1 = t.GetDelunayEdgeCCW(p); + de2 = t.GetDelunayEdgeCW(p); + de3 = ot.GetDelunayEdgeCCW(op); + de4 = ot.GetDelunayEdgeCW(op); + + t.Legalize(p, op); + ot.Legalize(op, p); + + // Remap delaunay_edge + ot.SetDelunayEdgeCCW(p, de1); + t.SetDelunayEdgeCW(p, de2); + t.SetDelunayEdgeCCW(op, de3); + ot.SetDelunayEdgeCW(op, de4); + + // Remap constrained_edge + ot.SetConstrainedEdgeCCW(p, ce1); + t.SetConstrainedEdgeCW(p, ce2); + t.SetConstrainedEdgeCCW(op, ce3); + ot.SetConstrainedEdgeCW(op, ce4); + + // Remap neighbors + // XXX: might optimize the markNeighbor by keeping track of + // what side should be assigned to what neighbor after the + // rotation. Now mark neighbor does lots of testing to find + // the right side. + t.ClearNeighbors(); + ot.ClearNeighbors(); + if (n1) ot.MarkNeighbor(*n1); + if (n2) t.MarkNeighbor(*n2); + if (n3) t.MarkNeighbor(*n3); + if (n4) ot.MarkNeighbor(*n4); + t.MarkNeighbor(ot); +} + +void Sweep::FillBasin(SweepContext& tcx, Node& node) +{ + if (Orient2d(*node.point, *node.next->point, *node.next->next->point) == CCW) { + tcx.basin.left_node = node.next->next; + } else { + tcx.basin.left_node = node.next; + } + + // Find the bottom and right node + tcx.basin.bottom_node = tcx.basin.left_node; + while (tcx.basin.bottom_node->next + && tcx.basin.bottom_node->point->y >= tcx.basin.bottom_node->next->point->y) { + tcx.basin.bottom_node = tcx.basin.bottom_node->next; + } + if (tcx.basin.bottom_node == tcx.basin.left_node) { + // No valid basin + return; + } + + tcx.basin.right_node = tcx.basin.bottom_node; + while (tcx.basin.right_node->next + && tcx.basin.right_node->point->y < tcx.basin.right_node->next->point->y) { + tcx.basin.right_node = tcx.basin.right_node->next; + } + if (tcx.basin.right_node == tcx.basin.bottom_node) { + // No valid basins + return; + } + + tcx.basin.width = tcx.basin.right_node->point->x - tcx.basin.left_node->point->x; + tcx.basin.left_highest = tcx.basin.left_node->point->y > tcx.basin.right_node->point->y; + + FillBasinReq(tcx, tcx.basin.bottom_node); +} + +void Sweep::FillBasinReq(SweepContext& tcx, Node* node) +{ + // if shallow stop filling + if (IsShallow(tcx, *node)) { + return; + } + + Fill(tcx, *node); + + if (node->prev == tcx.basin.left_node && node->next == tcx.basin.right_node) { + return; + } else if (node->prev == tcx.basin.left_node) { + Orientation o = Orient2d(*node->point, *node->next->point, *node->next->next->point); + if (o == CW) { + return; + } + node = node->next; + } else if (node->next == tcx.basin.right_node) { + Orientation o = Orient2d(*node->point, *node->prev->point, *node->prev->prev->point); + if (o == CCW) { + return; + } + node = node->prev; + } else { + // Continue with the neighbor node with lowest Y value + if (node->prev->point->y < node->next->point->y) { + node = node->prev; + } else { + node = node->next; + } + } + + FillBasinReq(tcx, node); +} + +bool Sweep::IsShallow(SweepContext& tcx, Node& node) +{ + double height; + + if (tcx.basin.left_highest) { + height = tcx.basin.left_node->point->y - node.point->y; + } else { + height = tcx.basin.right_node->point->y - node.point->y; + } + + // if shallow stop filling + if (tcx.basin.width > height) { + return true; + } + return false; +} + +void Sweep::FillEdgeEvent(SweepContext& tcx, Edge* edge, Node* node) +{ + if (tcx.edge_event.right) { + FillRightAboveEdgeEvent(tcx, edge, node); + } else { + FillLeftAboveEdgeEvent(tcx, edge, node); + } +} + +void Sweep::FillRightAboveEdgeEvent(SweepContext& tcx, Edge* edge, Node* node) +{ + while (node->next->point->x < edge->p->x) { + // Check if next node is below the edge + if (Orient2d(*edge->q, *node->next->point, *edge->p) == CCW) { + FillRightBelowEdgeEvent(tcx, edge, *node); + } else { + node = node->next; + } + } +} + +void Sweep::FillRightBelowEdgeEvent(SweepContext& tcx, Edge* edge, Node& node) +{ + if (node.point->x < edge->p->x) { + if (Orient2d(*node.point, *node.next->point, *node.next->next->point) == CCW) { + // Concave + FillRightConcaveEdgeEvent(tcx, edge, node); + } else{ + // Convex + FillRightConvexEdgeEvent(tcx, edge, node); + // Retry this one + FillRightBelowEdgeEvent(tcx, edge, node); + } + } +} + +void Sweep::FillRightConcaveEdgeEvent(SweepContext& tcx, Edge* edge, Node& node) +{ + Fill(tcx, *node.next); + if (node.next->point != edge->p) { + // Next above or below edge? + if (Orient2d(*edge->q, *node.next->point, *edge->p) == CCW) { + // Below + if (Orient2d(*node.point, *node.next->point, *node.next->next->point) == CCW) { + // Next is concave + FillRightConcaveEdgeEvent(tcx, edge, node); + } else { + // Next is convex + } + } + } + +} + +void Sweep::FillRightConvexEdgeEvent(SweepContext& tcx, Edge* edge, Node& node) +{ + // Next concave or convex? + if (Orient2d(*node.next->point, *node.next->next->point, *node.next->next->next->point) == CCW) { + // Concave + FillRightConcaveEdgeEvent(tcx, edge, *node.next); + } else{ + // Convex + // Next above or below edge? + if (Orient2d(*edge->q, *node.next->next->point, *edge->p) == CCW) { + // Below + FillRightConvexEdgeEvent(tcx, edge, *node.next); + } else{ + // Above + } + } +} + +void Sweep::FillLeftAboveEdgeEvent(SweepContext& tcx, Edge* edge, Node* node) +{ + while (node->prev->point->x > edge->p->x) { + // Check if next node is below the edge + if (Orient2d(*edge->q, *node->prev->point, *edge->p) == CW) { + FillLeftBelowEdgeEvent(tcx, edge, *node); + } else { + node = node->prev; + } + } +} + +void Sweep::FillLeftBelowEdgeEvent(SweepContext& tcx, Edge* edge, Node& node) +{ + if (node.point->x > edge->p->x) { + if (Orient2d(*node.point, *node.prev->point, *node.prev->prev->point) == CW) { + // Concave + FillLeftConcaveEdgeEvent(tcx, edge, node); + } else { + // Convex + FillLeftConvexEdgeEvent(tcx, edge, node); + // Retry this one + FillLeftBelowEdgeEvent(tcx, edge, node); + } + } +} + +void Sweep::FillLeftConvexEdgeEvent(SweepContext& tcx, Edge* edge, Node& node) +{ + // Next concave or convex? + if (Orient2d(*node.prev->point, *node.prev->prev->point, *node.prev->prev->prev->point) == CW) { + // Concave + FillLeftConcaveEdgeEvent(tcx, edge, *node.prev); + } else{ + // Convex + // Next above or below edge? + if (Orient2d(*edge->q, *node.prev->prev->point, *edge->p) == CW) { + // Below + FillLeftConvexEdgeEvent(tcx, edge, *node.prev); + } else{ + // Above + } + } +} + +void Sweep::FillLeftConcaveEdgeEvent(SweepContext& tcx, Edge* edge, Node& node) +{ + Fill(tcx, *node.prev); + if (node.prev->point != edge->p) { + // Next above or below edge? + if (Orient2d(*edge->q, *node.prev->point, *edge->p) == CW) { + // Below + if (Orient2d(*node.point, *node.prev->point, *node.prev->prev->point) == CW) { + // Next is concave + FillLeftConcaveEdgeEvent(tcx, edge, node); + } else{ + // Next is convex + } + } + } + +} + +void Sweep::FlipEdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* t, Point& p) +{ + Triangle& ot = t->NeighborAcross(p); + Point& op = *ot.OppositePoint(*t, p); + + if (&ot == NULL) { + // If we want to integrate the fillEdgeEvent do it here + // With current implementation we should never get here + //throw new RuntimeException( "[BUG:FIXME] FLIP failed due to missing triangle"); + assert(0); + } + + if (InScanArea(p, *t->PointCCW(p), *t->PointCW(p), op)) { + // Lets rotate shared edge one vertex CW + RotateTrianglePair(*t, p, ot, op); + tcx.MapTriangleToNodes(*t); + tcx.MapTriangleToNodes(ot); + + if (p == eq && op == ep) { + if (eq == *tcx.edge_event.constrained_edge->q && ep == *tcx.edge_event.constrained_edge->p) { + t->MarkConstrainedEdge(&ep, &eq); + ot.MarkConstrainedEdge(&ep, &eq); + Legalize(tcx, *t); + Legalize(tcx, ot); + } else { + // XXX: I think one of the triangles should be legalized here? + } + } else { + Orientation o = Orient2d(eq, op, ep); + t = &NextFlipTriangle(tcx, (int)o, *t, ot, p, op); + FlipEdgeEvent(tcx, ep, eq, t, p); + } + } else { + Point& newP = NextFlipPoint(ep, eq, ot, op); + FlipScanEdgeEvent(tcx, ep, eq, *t, ot, newP); + EdgeEvent(tcx, ep, eq, t, p); + } +} + +Triangle& Sweep::NextFlipTriangle(SweepContext& tcx, int o, Triangle& t, Triangle& ot, Point& p, Point& op) +{ + if (o == CCW) { + // ot is not crossing edge after flip + int edge_index = ot.EdgeIndex(&p, &op); + ot.delaunay_edge[edge_index] = true; + Legalize(tcx, ot); + ot.ClearDelunayEdges(); + return t; + } + + // t is not crossing edge after flip + int edge_index = t.EdgeIndex(&p, &op); + + t.delaunay_edge[edge_index] = true; + Legalize(tcx, t); + t.ClearDelunayEdges(); + return ot; +} + +Point& Sweep::NextFlipPoint(Point& ep, Point& eq, Triangle& ot, Point& op) +{ + Orientation o2d = Orient2d(eq, op, ep); + if (o2d == CW) { + // Right + return *ot.PointCCW(op); + } else if (o2d == CCW) { + // Left + return *ot.PointCW(op); + } + throw std::runtime_error("[Unsupported] Opposing point on constrained edge"); +} + +void Sweep::FlipScanEdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle& flip_triangle, + Triangle& t, Point& p) +{ + Triangle& ot = t.NeighborAcross(p); + Point& op = *ot.OppositePoint(t, p); + + if (&t.NeighborAcross(p) == NULL) { + // If we want to integrate the fillEdgeEvent do it here + // With current implementation we should never get here + //throw new RuntimeException( "[BUG:FIXME] FLIP failed due to missing triangle"); + assert(0); + } + + if (InScanArea(eq, *flip_triangle.PointCCW(eq), *flip_triangle.PointCW(eq), op)) { + // flip with new edge op->eq + FlipEdgeEvent(tcx, eq, op, &ot, op); + // TODO: Actually I just figured out that it should be possible to + // improve this by getting the next ot and op before the the above + // flip and continue the flipScanEdgeEvent here + // set new ot and op here and loop back to inScanArea test + // also need to set a new flip_triangle first + // Turns out at first glance that this is somewhat complicated + // so it will have to wait. + } else{ + Point& newP = NextFlipPoint(ep, eq, ot, op); + FlipScanEdgeEvent(tcx, ep, eq, flip_triangle, ot, newP); } - -} - -void Sweep::FillRightConvexEdgeEvent(SweepContext& tcx, Edge* edge, Node& node) -{ - // Next concave or convex? - if (Orient2d(*node.next->point, *node.next->next->point, *node.next->next->next->point) == CCW) { - // Concave - FillRightConcaveEdgeEvent(tcx, edge, *node.next); - } else{ - // Convex - // Next above or below edge? - if (Orient2d(*edge->q, *node.next->next->point, *edge->p) == CCW) { - // Below - FillRightConvexEdgeEvent(tcx, edge, *node.next); - } else{ - // Above - } - } -} - -void Sweep::FillLeftAboveEdgeEvent(SweepContext& tcx, Edge* edge, Node* node) -{ - while (node->prev->point->x > edge->p->x) { - // Check if next node is below the edge - if (Orient2d(*edge->q, *node->prev->point, *edge->p) == CW) { - FillLeftBelowEdgeEvent(tcx, edge, *node); - } else { - node = node->prev; - } - } -} - -void Sweep::FillLeftBelowEdgeEvent(SweepContext& tcx, Edge* edge, Node& node) -{ - if (node.point->x > edge->p->x) { - if (Orient2d(*node.point, *node.prev->point, *node.prev->prev->point) == CW) { - // Concave - FillLeftConcaveEdgeEvent(tcx, edge, node); - } else { - // Convex - FillLeftConvexEdgeEvent(tcx, edge, node); - // Retry this one - FillLeftBelowEdgeEvent(tcx, edge, node); - } - } -} - -void Sweep::FillLeftConvexEdgeEvent(SweepContext& tcx, Edge* edge, Node& node) -{ - // Next concave or convex? - if (Orient2d(*node.prev->point, *node.prev->prev->point, *node.prev->prev->prev->point) == CW) { - // Concave - FillLeftConcaveEdgeEvent(tcx, edge, *node.prev); - } else{ - // Convex - // Next above or below edge? - if (Orient2d(*edge->q, *node.prev->prev->point, *edge->p) == CW) { - // Below - FillLeftConvexEdgeEvent(tcx, edge, *node.prev); - } else{ - // Above - } - } -} - -void Sweep::FillLeftConcaveEdgeEvent(SweepContext& tcx, Edge* edge, Node& node) -{ - Fill(tcx, *node.prev); - if (node.prev->point != edge->p) { - // Next above or below edge? - if (Orient2d(*edge->q, *node.prev->point, *edge->p) == CW) { - // Below - if (Orient2d(*node.point, *node.prev->point, *node.prev->prev->point) == CW) { - // Next is concave - FillLeftConcaveEdgeEvent(tcx, edge, node); - } else{ - // Next is convex - } - } - } - -} - -void Sweep::FlipEdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* t, Point& p) -{ - Triangle& ot = t->NeighborAcross(p); - Point& op = *ot.OppositePoint(*t, p); - - if (&ot == NULL) { - // If we want to integrate the fillEdgeEvent do it here - // With current implementation we should never get here - //throw new RuntimeException( "[BUG:FIXME] FLIP failed due to missing triangle"); - assert(0); - } - - if (InScanArea(p, *t->PointCCW(p), *t->PointCW(p), op)) { - // Lets rotate shared edge one vertex CW - RotateTrianglePair(*t, p, ot, op); - tcx.MapTriangleToNodes(*t); - tcx.MapTriangleToNodes(ot); - - if (p == eq && op == ep) { - if (eq == *tcx.edge_event.constrained_edge->q && ep == *tcx.edge_event.constrained_edge->p) { - t->MarkConstrainedEdge(&ep, &eq); - ot.MarkConstrainedEdge(&ep, &eq); - Legalize(tcx, *t); - Legalize(tcx, ot); - } else { - // XXX: I think one of the triangles should be legalized here? - } - } else { - Orientation o = Orient2d(eq, op, ep); - t = &NextFlipTriangle(tcx, (int)o, *t, ot, p, op); - FlipEdgeEvent(tcx, ep, eq, t, p); - } - } else { - Point& newP = NextFlipPoint(ep, eq, ot, op); - FlipScanEdgeEvent(tcx, ep, eq, *t, ot, newP); - EdgeEvent(tcx, ep, eq, t, p); - } -} - -Triangle& Sweep::NextFlipTriangle(SweepContext& tcx, int o, Triangle& t, Triangle& ot, Point& p, Point& op) -{ - if (o == CCW) { - // ot is not crossing edge after flip - int edge_index = ot.EdgeIndex(&p, &op); - ot.delaunay_edge[edge_index] = true; - Legalize(tcx, ot); - ot.ClearDelunayEdges(); - return t; - } - - // t is not crossing edge after flip - int edge_index = t.EdgeIndex(&p, &op); - - t.delaunay_edge[edge_index] = true; - Legalize(tcx, t); - t.ClearDelunayEdges(); - return ot; -} - -Point& Sweep::NextFlipPoint(Point& ep, Point& eq, Triangle& ot, Point& op) -{ - Orientation o2d = Orient2d(eq, op, ep); - if (o2d == CW) { - // Right - return *ot.PointCCW(op); - } else if (o2d == CCW) { - // Left - return *ot.PointCW(op); - } - throw std::runtime_error("[Unsupported] Opposing point on constrained edge"); -} - -void Sweep::FlipScanEdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle& flip_triangle, - Triangle& t, Point& p) -{ - Triangle& ot = t.NeighborAcross(p); - Point& op = *ot.OppositePoint(t, p); - - if (&t.NeighborAcross(p) == NULL) { - // If we want to integrate the fillEdgeEvent do it here - // With current implementation we should never get here - //throw new RuntimeException( "[BUG:FIXME] FLIP failed due to missing triangle"); - assert(0); - } - - if (InScanArea(eq, *flip_triangle.PointCCW(eq), *flip_triangle.PointCW(eq), op)) { - // flip with new edge op->eq - FlipEdgeEvent(tcx, eq, op, &ot, op); - // TODO: Actually I just figured out that it should be possible to - // improve this by getting the next ot and op before the the above - // flip and continue the flipScanEdgeEvent here - // set new ot and op here and loop back to inScanArea test - // also need to set a new flip_triangle first - // Turns out at first glance that this is somewhat complicated - // so it will have to wait. - } else{ - Point& newP = NextFlipPoint(ep, eq, ot, op); - FlipScanEdgeEvent(tcx, ep, eq, flip_triangle, ot, newP); - } } Sweep::~Sweep() { @@ -805,7 +805,7 @@ Sweep::~Sweep() { delete nodes_[i]; } -} - -} - +} + +} + diff --git a/poly2tri/sweep/sweep.h b/poly2tri/sweep/sweep.h index f62c4cc..addd3d4 100644 --- a/poly2tri/sweep/sweep.h +++ b/poly2tri/sweep/sweep.h @@ -33,7 +33,7 @@ * Zalik, B.(2008)'Sweep-line algorithm for constrained Delaunay triangulation', * International Journal of Geographical Information Science * - * "FlipScan" Constrained Edge Algorithm invented by Thomas Åhlén, thahlen@gmail.com + * "FlipScan" Constrained Edge Algorithm invented by Thomas ?hl?n, thahlen@gmail.com */ #ifndef SWEEP_H @@ -49,17 +49,17 @@ struct Point; struct Edge; class Triangle; -class Sweep +class Sweep { public: /** * Triangulate - * + * * @param tcx */ void Triangulate(SweepContext& tcx); - + /** * Destructor - clean up memory */ @@ -69,7 +69,7 @@ private: /** * Start sweeping the Y-sorted point set from bottom to top - * + * * @param tcx */ void SweepPoints(SweepContext& tcx); @@ -86,8 +86,8 @@ private: Node& PointEvent(SweepContext& tcx, Point& point); /** - * - * + * + * * @param tcx * @param edge * @param node @@ -98,7 +98,7 @@ private: /** * Creates a new front triangle and legalize it - * + * * @param tcx * @param point * @param node @@ -168,8 +168,8 @@ private: * @param n */ void FillAdvancingFront(SweepContext& tcx, Node& n); - - // Decision-making about when to Fill hole. + + // Decision-making about when to Fill hole. // Contributed by ToolmakerSteve2 bool LargeHole_DontFill(Node* node); bool AngleExceeds90Degrees(Point* origin, Point* pa, Point* pb); @@ -235,22 +235,22 @@ private: /** * 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 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); /** - * When we need to traverse from one triangle to the next we need + * 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. - * + * triangle. + * * @param ep * @param eq * @param ot @@ -261,10 +261,10 @@ private: /** * 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 + * 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 @@ -282,4 +282,4 @@ private: } -#endif +#endif \ No newline at end of file diff --git a/poly2tri/sweep/sweep_context.cc b/poly2tri/sweep/sweep_context.cc index 9410f6a..e9176c5 100644 --- a/poly2tri/sweep/sweep_context.cc +++ b/poly2tri/sweep/sweep_context.cc @@ -194,4 +194,4 @@ SweepContext::~SweepContext() } -} +} \ No newline at end of file diff --git a/poly2tri/sweep/sweep_context.h b/poly2tri/sweep/sweep_context.h index dffe254..2853a1e 100644 --- a/poly2tri/sweep/sweep_context.h +++ b/poly2tri/sweep/sweep_context.h @@ -1,186 +1,186 @@ -/* - * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors - * http://code.google.com/p/poly2tri/ - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * * Neither the name of Poly2Tri nor the names of its contributors may be - * used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef SWEEP_CONTEXT_H -#define SWEEP_CONTEXT_H - -#include -#include -#include - -namespace p2t { - -// Inital triangle factor, seed triangle will extend 30% of -// PointSet width to both left and right. -const double kAlpha = 0.3; - -struct Point; -class Triangle; -struct Node; -struct Edge; -class AdvancingFront; - -class SweepContext { -public: - -/// Constructor -SweepContext(std::vector polyline); -/// Destructor -~SweepContext(); - -void set_head(Point* p1); - -Point* head(); - -void set_tail(Point* p1); - -Point* tail(); - -size_t point_count(); - -Node& LocateNode(Point& point); - -void RemoveNode(Node* node); - -void CreateAdvancingFront(std::vector nodes); - -/// Try to map a node to all sides of this triangle that don't have a neighbor -void MapTriangleToNodes(Triangle& t); - -void AddToMap(Triangle* triangle); - -Point* GetPoint(size_t index); - -Point* GetPoints(); - -void RemoveFromMap(Triangle* triangle); - -void AddHole(std::vector polyline); - -void AddPoint(Point* point); - -AdvancingFront* front(); - -void MeshClean(Triangle& triangle); - -std::vector GetTriangles(); -std::list GetMap(); - -std::vector edge_list; - -struct Basin { - Node* left_node; - Node* bottom_node; - Node* right_node; - double width; - bool left_highest; - - Basin() : left_node(NULL), bottom_node(NULL), right_node(NULL), width(0.0), left_highest(false) - { - } - - void Clear() - { - left_node = NULL; - bottom_node = NULL; - right_node = NULL; - width = 0.0; - left_highest = false; - } -}; - -struct EdgeEvent { - Edge* constrained_edge; - bool right; - - EdgeEvent() : constrained_edge(NULL), right(false) - { - } -}; - -Basin basin; -EdgeEvent edge_event; - -private: - -friend class Sweep; - -std::vector triangles_; -std::list map_; -std::vector points_; - -// Advancing front -AdvancingFront* front_; -// head point used with advancing front -Point* head_; -// tail point used with advancing front -Point* tail_; - -Node *af_head_, *af_middle_, *af_tail_; - -void InitTriangulation(); -void InitEdges(std::vector polyline); - -}; - -inline AdvancingFront* SweepContext::front() -{ - return front_; -} - -inline size_t SweepContext::point_count() -{ - return points_.size(); -} - -inline void SweepContext::set_head(Point* p1) -{ - head_ = p1; -} - -inline Point* SweepContext::head() -{ - return head_; -} - -inline void SweepContext::set_tail(Point* p1) -{ - tail_ = p1; -} - -inline Point* SweepContext::tail() -{ - return tail_; -} - -} - -#endif +/* + * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors + * http://code.google.com/p/poly2tri/ + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Poly2Tri nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SWEEP_CONTEXT_H +#define SWEEP_CONTEXT_H + +#include +#include +#include + +namespace p2t { + +// Inital triangle factor, seed triangle will extend 30% of +// PointSet width to both left and right. +const double kAlpha = 0.3; + +struct Point; +class Triangle; +struct Node; +struct Edge; +class AdvancingFront; + +class SweepContext { +public: + +/// Constructor +SweepContext(std::vector polyline); +/// Destructor +~SweepContext(); + +void set_head(Point* p1); + +Point* head(); + +void set_tail(Point* p1); + +Point* tail(); + +size_t point_count(); + +Node& LocateNode(Point& point); + +void RemoveNode(Node* node); + +void CreateAdvancingFront(std::vector nodes); + +/// Try to map a node to all sides of this triangle that don't have a neighbor +void MapTriangleToNodes(Triangle& t); + +void AddToMap(Triangle* triangle); + +Point* GetPoint(size_t index); + +Point* GetPoints(); + +void RemoveFromMap(Triangle* triangle); + +void AddHole(std::vector polyline); + +void AddPoint(Point* point); + +AdvancingFront* front(); + +void MeshClean(Triangle& triangle); + +std::vector GetTriangles(); +std::list GetMap(); + +std::vector edge_list; + +struct Basin { + Node* left_node; + Node* bottom_node; + Node* right_node; + double width; + bool left_highest; + + Basin() : left_node(NULL), bottom_node(NULL), right_node(NULL), width(0.0), left_highest(false) + { + } + + void Clear() + { + left_node = NULL; + bottom_node = NULL; + right_node = NULL; + width = 0.0; + left_highest = false; + } +}; + +struct EdgeEvent { + Edge* constrained_edge; + bool right; + + EdgeEvent() : constrained_edge(NULL), right(false) + { + } +}; + +Basin basin; +EdgeEvent edge_event; + +private: + +friend class Sweep; + +std::vector triangles_; +std::list map_; +std::vector points_; + +// Advancing front +AdvancingFront* front_; +// head point used with advancing front +Point* head_; +// tail point used with advancing front +Point* tail_; + +Node *af_head_, *af_middle_, *af_tail_; + +void InitTriangulation(); +void InitEdges(std::vector polyline); + +}; + +inline AdvancingFront* SweepContext::front() +{ + return front_; +} + +inline size_t SweepContext::point_count() +{ + return points_.size(); +} + +inline void SweepContext::set_head(Point* p1) +{ + head_ = p1; +} + +inline Point* SweepContext::head() +{ + return head_; +} + +inline void SweepContext::set_tail(Point* p1) +{ + tail_ = p1; +} + +inline Point* SweepContext::tail() +{ + return tail_; +} + +} + +#endif diff --git a/testbed/main.cc b/testbed/main.cc index 95a7120..c0bb828 100644 --- a/testbed/main.cc +++ b/testbed/main.cc @@ -1,463 +1,463 @@ -/* - * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors - * http://code.google.com/p/poly2tri/ - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * * Neither the name of Poly2Tri nor the names of its contributors may be - * used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -using namespace std; - -#include "../poly2tri/poly2tri.h" -using namespace p2t; - -void Init(); -void ShutDown(int return_code); -void MainLoop(const double zoom); -void Draw(const double zoom); -void DrawMap(const double zoom); -void ConstrainedColor(bool constrain); -double StringToDouble(const std::string& s); -double Random(double (*fun)(double), double xmin, double xmax); -double Fun(double x); - -/// Dude hole examples -vector CreateHeadHole(); -vector CreateChestHole(); - -float rotate_y = 0, - rotate_z = 0; -const float rotations_per_tick = .2; - -/// Screen center x -double cx = 0.0; -/// Screen center y -double cy = 0.0; - -/// Constrained triangles -vector triangles; -/// Triangle map -list map; -/// Polylines -vector< vector > polylines; - -/// Draw the entire triangle map? -bool draw_map = false; -/// Create a random distribution of points? -bool random_distribution = false; - -template void FreeClear( C & cntr ) { - for ( typename C::iterator it = cntr.begin(); - it != cntr.end(); ++it ) { - delete * it; - } - cntr.clear(); -} - -int main(int argc, char* argv[]) -{ - - int num_points = 0; - double max, min; - double zoom; - - if (argc != 5) { - cout << "-== USAGE ==-" << endl; - cout << "Load Data File: p2t filename center_x center_y zoom" << endl; - cout << "Example: ./build/p2t testbed/data/dude.dat 500 500 1" << endl; - return 1; - } - - if(string(argv[1]) == "random") { - num_points = atoi(argv[2]); - random_distribution = true; - char* pEnd; - max = strtod(argv[3], &pEnd); - min = -max; - cx = cy = 0; - zoom = atof(argv[4]); - } else { - zoom = atof(argv[4]); - cx = atof(argv[2]); - cy = atof(argv[3]); - } - - vector polyline; - - if(random_distribution) { - // Create a simple bounding box - polyline.push_back(new Point(min,min)); - polyline.push_back(new Point(min,max)); - polyline.push_back(new Point(max,max)); - polyline.push_back(new Point(max,min)); - } else { - // Load pointset from file - - // Parse and tokenize data file - string line; - ifstream myfile(argv[1]); - if (myfile.is_open()) { - while (!myfile.eof()) { - getline(myfile, line); - if (line.size() == 0) { - break; - } - istringstream iss(line); - vector tokens; - copy(istream_iterator(iss), istream_iterator(), - back_inserter >(tokens)); - double x = StringToDouble(tokens[0]); - double y = StringToDouble(tokens[1]); - polyline.push_back(new Point(x, y)); - num_points++; - } - myfile.close(); - } else { - cout << "File not opened" << endl; - } - } - - cout << "Number of constrained edges = " << polyline.size() << endl; - polylines.push_back(polyline); - - Init(); - - /* - * Perform triangulation! - */ - - double init_time = glfwGetTime(); - - /* - * STEP 1: Create CDT and add primary polyline - * NOTE: polyline must be a simple polygon. The polyline's points - * constitute constrained edges. No repeat points!!! - */ - CDT* cdt = new CDT(polyline); - - /* - * STEP 2: Add holes or Steiner points if necessary - */ - - string s(argv[1]); - if(s.find("dude.dat", 0) != string::npos) { - // Add head hole - vector head_hole = CreateHeadHole(); - num_points += head_hole.size(); - cdt->AddHole(head_hole); - // Add chest hole - vector chest_hole = CreateChestHole(); - num_points += chest_hole.size(); - cdt->AddHole(chest_hole); - polylines.push_back(head_hole); - polylines.push_back(chest_hole); - } else if (random_distribution) { - max-=(1e-4); - min+=(1e-4); - for(int i = 0; i < num_points; i++) { - double x = Random(Fun, min, max); - double y = Random(Fun, min, max); - cdt->AddPoint(new Point(x, y)); - } - } - - /* - * STEP 3: Triangulate! - */ - cdt->Triangulate(); - - double dt = glfwGetTime() - init_time; - - triangles = cdt->GetTriangles(); - map = cdt->GetMap(); - - cout << "Number of points = " << num_points << endl; - cout << "Number of triangles = " << triangles.size() << endl; - cout << "Elapsed time (ms) = " << dt*1000.0 << endl; - - MainLoop(zoom); - - // Cleanup - - delete cdt; - - // Free points - for(int i = 0; i < polylines.size(); i++) { - vector poly = polylines[i]; - FreeClear(poly); - } - - ShutDown(0); - return 0; -} - -void Init() -{ - const int window_width = 800, - window_height = 600; - - if (glfwInit() != GL_TRUE) - ShutDown(1); - // 800 x 600, 16 bit color, no depth, alpha or stencil buffers, windowed - if (glfwOpenWindow(window_width, window_height, 5, 6, 5, 0, 0, 0, GLFW_WINDOW) != GL_TRUE) - ShutDown(1); - - glfwSetWindowTitle("Poly2Tri - C++"); - glfwSwapInterval(1); - - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glClearColor(0.0, 0.0, 0.0, 0.0); - glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); -} - -void ShutDown(int return_code) -{ - glfwTerminate(); - exit(return_code); -} - -void MainLoop(const double zoom) -{ - // the time of the previous frame - double old_time = glfwGetTime(); - // this just loops as long as the program runs - bool running = true; - - while (running) { - // calculate time elapsed, and the amount by which stuff rotates - double current_time = glfwGetTime(), - delta_rotate = (current_time - old_time) * rotations_per_tick * 360; - old_time = current_time; - - // escape to quit, arrow keys to rotate view - // Check if ESC key was pressed or window was closed - running = !glfwGetKey(GLFW_KEY_ESC) && glfwGetWindowParam(GLFW_OPENED); - - if (glfwGetKey(GLFW_KEY_LEFT) == GLFW_PRESS) - rotate_y += delta_rotate; - if (glfwGetKey(GLFW_KEY_RIGHT) == GLFW_PRESS) - rotate_y -= delta_rotate; - // z axis always rotates - rotate_z += delta_rotate; - - // Draw the scene - if (draw_map) { - DrawMap(zoom); - } else { - Draw(zoom); - } - - // swap back and front buffers - glfwSwapBuffers(); - } -} - -void ResetZoom(double zoom, double cx, double cy, double width, double height) -{ - double left = -width / zoom; - double right = width / zoom; - double bottom = -height / zoom; - double top = height / zoom; - - // Reset viewport - glLoadIdentity(); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - - // Reset ortho view - glOrtho(left, right, bottom, top, 1, -1); - glTranslatef(-cx, -cy, 0); - glMatrixMode(GL_MODELVIEW); - glDisable(GL_DEPTH_TEST); - glLoadIdentity(); - - // Clear the screen - glClear(GL_COLOR_BUFFER_BIT); -} - -void Draw(const double zoom) -{ - // reset zoom - Point center = Point(cx, cy); - - ResetZoom(zoom, center.x, center.y, 800, 600); - - for (int i = 0; i < triangles.size(); i++) { - Triangle& t = *triangles[i]; - Point& a = *t.GetPoint(0); - Point& b = *t.GetPoint(1); - Point& c = *t.GetPoint(2); - - // Red - glColor3f(1, 0, 0); - - glBegin(GL_LINE_LOOP); - glVertex2f(a.x, a.y); - glVertex2f(b.x, b.y); - glVertex2f(c.x, c.y); - glEnd(); - } - - // green - glColor3f(0, 1, 0); - - for(int i = 0; i < polylines.size(); i++) { - vector poly = polylines[i]; - glBegin(GL_LINE_LOOP); - for(int j = 0; j < poly.size(); j++) { - glVertex2f(poly[j]->x, poly[j]->y); - } - glEnd(); - } -} - -void DrawMap(const double zoom) -{ - // reset zoom - Point center = Point(cx, cy); - - ResetZoom(zoom, center.x, center.y, 800, 600); - - list::iterator it; - for (it = map.begin(); it != map.end(); it++) { - Triangle& t = **it; - Point& a = *t.GetPoint(0); - Point& b = *t.GetPoint(1); - Point& c = *t.GetPoint(2); - - ConstrainedColor(t.constrained_edge[2]); - glBegin(GL_LINES); - glVertex2f(a.x, a.y); - glVertex2f(b.x, b.y); - glEnd( ); - - ConstrainedColor(t.constrained_edge[0]); - glBegin(GL_LINES); - glVertex2f(b.x, b.y); - glVertex2f(c.x, c.y); - glEnd( ); - - ConstrainedColor(t.constrained_edge[1]); - glBegin(GL_LINES); - glVertex2f(c.x, c.y); - glVertex2f(a.x, a.y); - glEnd( ); - } -} - -void ConstrainedColor(bool constrain) -{ - if (constrain) { - // Green - glColor3f(0, 1, 0); - } else { - // Red - glColor3f(1, 0, 0); - } -} - -vector CreateHeadHole() { - - vector head_hole; - head_hole.push_back(new Point(325, 437)); - head_hole.push_back(new Point(320, 423)); - head_hole.push_back(new Point(329, 413)); - head_hole.push_back(new Point(332, 423)); - - return head_hole; -} - -vector CreateChestHole() { - - vector chest_hole; - chest_hole.push_back(new Point(320.72342,480)); - chest_hole.push_back(new Point(338.90617,465.96863)); - chest_hole.push_back(new Point(347.99754,480.61584)); - chest_hole.push_back(new Point(329.8148,510.41534)); - chest_hole.push_back(new Point(339.91632,480.11077)); - chest_hole.push_back(new Point(334.86556,478.09046)); - - return chest_hole; - -} - - - -double StringToDouble(const std::string& s) -{ - std::istringstream i(s); - double x; - if (!(i >> x)) - return 0; - return x; -} - -double Fun(double x) -{ - return 2.5 + sin(10 * x) / x; -} - -double Random(double (*fun)(double), double xmin = 0, double xmax = 1) -{ - static double (*Fun)(double) = NULL, YMin, YMax; - static bool First = true; - - // Initialises random generator for first call - if (First) - { - First = false; - srand((unsigned) time(NULL)); - } - - // Evaluates maximum of function - if (fun != Fun) - { - Fun = fun; - YMin = 0, YMax = Fun(xmin); - for (int iX = 1; iX < RAND_MAX; iX++) - { - double X = xmin + (xmax - xmin) * iX / RAND_MAX; - double Y = Fun(X); - YMax = Y > YMax ? Y : YMax; - } - } - - // Gets random values for X & Y - double X = xmin + (xmax - xmin) * rand() / RAND_MAX; - double Y = YMin + (YMax - YMin) * rand() / RAND_MAX; - - // Returns if valid and try again if not valid - return Y < fun(X) ? X : Random(Fun, xmin, xmax); -} +/* + * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors + * http://code.google.com/p/poly2tri/ + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Poly2Tri nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +using namespace std; + +#include "../poly2tri/poly2tri.h" +using namespace p2t; + +void Init(); +void ShutDown(int return_code); +void MainLoop(const double zoom); +void Draw(const double zoom); +void DrawMap(const double zoom); +void ConstrainedColor(bool constrain); +double StringToDouble(const std::string& s); +double Random(double (*fun)(double), double xmin, double xmax); +double Fun(double x); + +/// Dude hole examples +vector CreateHeadHole(); +vector CreateChestHole(); + +float rotate_y = 0, + rotate_z = 0; +const float rotations_per_tick = .2; + +/// Screen center x +double cx = 0.0; +/// Screen center y +double cy = 0.0; + +/// Constrained triangles +vector triangles; +/// Triangle map +list map; +/// Polylines +vector< vector > polylines; + +/// Draw the entire triangle map? +bool draw_map = false; +/// Create a random distribution of points? +bool random_distribution = false; + +template void FreeClear( C & cntr ) { + for ( typename C::iterator it = cntr.begin(); + it != cntr.end(); ++it ) { + delete * it; + } + cntr.clear(); +} + +int main(int argc, char* argv[]) +{ + + int num_points = 0; + double max, min; + double zoom; + + if (argc != 5) { + cout << "-== USAGE ==-" << endl; + cout << "Load Data File: p2t filename center_x center_y zoom" << endl; + cout << "Example: ./build/p2t testbed/data/dude.dat 500 500 1" << endl; + return 1; + } + + if(string(argv[1]) == "random") { + num_points = atoi(argv[2]); + random_distribution = true; + char* pEnd; + max = strtod(argv[3], &pEnd); + min = -max; + cx = cy = 0; + zoom = atof(argv[4]); + } else { + zoom = atof(argv[4]); + cx = atof(argv[2]); + cy = atof(argv[3]); + } + + vector polyline; + + if(random_distribution) { + // Create a simple bounding box + polyline.push_back(new Point(min,min)); + polyline.push_back(new Point(min,max)); + polyline.push_back(new Point(max,max)); + polyline.push_back(new Point(max,min)); + } else { + // Load pointset from file + + // Parse and tokenize data file + string line; + ifstream myfile(argv[1]); + if (myfile.is_open()) { + while (!myfile.eof()) { + getline(myfile, line); + if (line.size() == 0) { + break; + } + istringstream iss(line); + vector tokens; + copy(istream_iterator(iss), istream_iterator(), + back_inserter >(tokens)); + double x = StringToDouble(tokens[0]); + double y = StringToDouble(tokens[1]); + polyline.push_back(new Point(x, y)); + num_points++; + } + myfile.close(); + } else { + cout << "File not opened" << endl; + } + } + + cout << "Number of constrained edges = " << polyline.size() << endl; + polylines.push_back(polyline); + + Init(); + + /* + * Perform triangulation! + */ + + double init_time = glfwGetTime(); + + /* + * STEP 1: Create CDT and add primary polyline + * NOTE: polyline must be a simple polygon. The polyline's points + * constitute constrained edges. No repeat points!!! + */ + CDT* cdt = new CDT(polyline); + + /* + * STEP 2: Add holes or Steiner points if necessary + */ + + string s(argv[1]); + if(s.find("dude.dat", 0) != string::npos) { + // Add head hole + vector head_hole = CreateHeadHole(); + num_points += head_hole.size(); + cdt->AddHole(head_hole); + // Add chest hole + vector chest_hole = CreateChestHole(); + num_points += chest_hole.size(); + cdt->AddHole(chest_hole); + polylines.push_back(head_hole); + polylines.push_back(chest_hole); + } else if (random_distribution) { + max-=(1e-4); + min+=(1e-4); + for(int i = 0; i < num_points; i++) { + double x = Random(Fun, min, max); + double y = Random(Fun, min, max); + cdt->AddPoint(new Point(x, y)); + } + } + + /* + * STEP 3: Triangulate! + */ + cdt->Triangulate(); + + double dt = glfwGetTime() - init_time; + + triangles = cdt->GetTriangles(); + map = cdt->GetMap(); + + cout << "Number of points = " << num_points << endl; + cout << "Number of triangles = " << triangles.size() << endl; + cout << "Elapsed time (ms) = " << dt*1000.0 << endl; + + MainLoop(zoom); + + // Cleanup + + delete cdt; + + // Free points + for(int i = 0; i < polylines.size(); i++) { + vector poly = polylines[i]; + FreeClear(poly); + } + + ShutDown(0); + return 0; +} + +void Init() +{ + const int window_width = 800, + window_height = 600; + + if (glfwInit() != GL_TRUE) + ShutDown(1); + // 800 x 600, 16 bit color, no depth, alpha or stencil buffers, windowed + if (glfwOpenWindow(window_width, window_height, 5, 6, 5, 0, 0, 0, GLFW_WINDOW) != GL_TRUE) + ShutDown(1); + + glfwSetWindowTitle("Poly2Tri - C++"); + glfwSwapInterval(1); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glClearColor(0.0, 0.0, 0.0, 0.0); + glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); +} + +void ShutDown(int return_code) +{ + glfwTerminate(); + exit(return_code); +} + +void MainLoop(const double zoom) +{ + // the time of the previous frame + double old_time = glfwGetTime(); + // this just loops as long as the program runs + bool running = true; + + while (running) { + // calculate time elapsed, and the amount by which stuff rotates + double current_time = glfwGetTime(), + delta_rotate = (current_time - old_time) * rotations_per_tick * 360; + old_time = current_time; + + // escape to quit, arrow keys to rotate view + // Check if ESC key was pressed or window was closed + running = !glfwGetKey(GLFW_KEY_ESC) && glfwGetWindowParam(GLFW_OPENED); + + if (glfwGetKey(GLFW_KEY_LEFT) == GLFW_PRESS) + rotate_y += delta_rotate; + if (glfwGetKey(GLFW_KEY_RIGHT) == GLFW_PRESS) + rotate_y -= delta_rotate; + // z axis always rotates + rotate_z += delta_rotate; + + // Draw the scene + if (draw_map) { + DrawMap(zoom); + } else { + Draw(zoom); + } + + // swap back and front buffers + glfwSwapBuffers(); + } +} + +void ResetZoom(double zoom, double cx, double cy, double width, double height) +{ + double left = -width / zoom; + double right = width / zoom; + double bottom = -height / zoom; + double top = height / zoom; + + // Reset viewport + glLoadIdentity(); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + + // Reset ortho view + glOrtho(left, right, bottom, top, 1, -1); + glTranslatef(-cx, -cy, 0); + glMatrixMode(GL_MODELVIEW); + glDisable(GL_DEPTH_TEST); + glLoadIdentity(); + + // Clear the screen + glClear(GL_COLOR_BUFFER_BIT); +} + +void Draw(const double zoom) +{ + // reset zoom + Point center = Point(cx, cy); + + ResetZoom(zoom, center.x, center.y, 800, 600); + + for (int i = 0; i < triangles.size(); i++) { + Triangle& t = *triangles[i]; + Point& a = *t.GetPoint(0); + Point& b = *t.GetPoint(1); + Point& c = *t.GetPoint(2); + + // Red + glColor3f(1, 0, 0); + + glBegin(GL_LINE_LOOP); + glVertex2f(a.x, a.y); + glVertex2f(b.x, b.y); + glVertex2f(c.x, c.y); + glEnd(); + } + + // green + glColor3f(0, 1, 0); + + for(int i = 0; i < polylines.size(); i++) { + vector poly = polylines[i]; + glBegin(GL_LINE_LOOP); + for(int j = 0; j < poly.size(); j++) { + glVertex2f(poly[j]->x, poly[j]->y); + } + glEnd(); + } +} + +void DrawMap(const double zoom) +{ + // reset zoom + Point center = Point(cx, cy); + + ResetZoom(zoom, center.x, center.y, 800, 600); + + list::iterator it; + for (it = map.begin(); it != map.end(); it++) { + Triangle& t = **it; + Point& a = *t.GetPoint(0); + Point& b = *t.GetPoint(1); + Point& c = *t.GetPoint(2); + + ConstrainedColor(t.constrained_edge[2]); + glBegin(GL_LINES); + glVertex2f(a.x, a.y); + glVertex2f(b.x, b.y); + glEnd( ); + + ConstrainedColor(t.constrained_edge[0]); + glBegin(GL_LINES); + glVertex2f(b.x, b.y); + glVertex2f(c.x, c.y); + glEnd( ); + + ConstrainedColor(t.constrained_edge[1]); + glBegin(GL_LINES); + glVertex2f(c.x, c.y); + glVertex2f(a.x, a.y); + glEnd( ); + } +} + +void ConstrainedColor(bool constrain) +{ + if (constrain) { + // Green + glColor3f(0, 1, 0); + } else { + // Red + glColor3f(1, 0, 0); + } +} + +vector CreateHeadHole() { + + vector head_hole; + head_hole.push_back(new Point(325, 437)); + head_hole.push_back(new Point(320, 423)); + head_hole.push_back(new Point(329, 413)); + head_hole.push_back(new Point(332, 423)); + + return head_hole; +} + +vector CreateChestHole() { + + vector chest_hole; + chest_hole.push_back(new Point(320.72342,480)); + chest_hole.push_back(new Point(338.90617,465.96863)); + chest_hole.push_back(new Point(347.99754,480.61584)); + chest_hole.push_back(new Point(329.8148,510.41534)); + chest_hole.push_back(new Point(339.91632,480.11077)); + chest_hole.push_back(new Point(334.86556,478.09046)); + + return chest_hole; + +} + + + +double StringToDouble(const std::string& s) +{ + std::istringstream i(s); + double x; + if (!(i >> x)) + return 0; + return x; +} + +double Fun(double x) +{ + return 2.5 + sin(10 * x) / x; +} + +double Random(double (*fun)(double), double xmin = 0, double xmax = 1) +{ + static double (*Fun)(double) = NULL, YMin, YMax; + static bool First = true; + + // Initialises random generator for first call + if (First) + { + First = false; + srand((unsigned) time(NULL)); + } + + // Evaluates maximum of function + if (fun != Fun) + { + Fun = fun; + YMin = 0, YMax = Fun(xmin); + for (int iX = 1; iX < RAND_MAX; iX++) + { + double X = xmin + (xmax - xmin) * iX / RAND_MAX; + double Y = Fun(X); + YMax = Y > YMax ? Y : YMax; + } + } + + // Gets random values for X & Y + double X = xmin + (xmax - xmin) * rand() / RAND_MAX; + double Y = YMin + (YMax - YMin) * rand() / RAND_MAX; + + // Returns if valid and try again if not valid + return Y < fun(X) ? X : Random(Fun, xmin, xmax); +} \ No newline at end of file diff --git a/wscript b/wscript index 7500451..a3bb55c 100644 --- a/wscript +++ b/wscript @@ -1,47 +1,47 @@ -#!/usr/bin/env python -# encoding: utf-8 -# waf 1.6.10 - -VERSION='0.3.3' -import sys -APPNAME='p2t' -top = '.' -out = 'build' - -CPP_SOURCES = ['poly2tri/common/shapes.cc', - 'poly2tri/sweep/cdt.cc', - 'poly2tri/sweep/advancing_front.cc', - 'poly2tri/sweep/sweep_context.cc', - 'poly2tri/sweep/sweep.cc', - 'testbed/main.cc'] - -from waflib.Tools.compiler_cxx import cxx_compiler -cxx_compiler['win32'] = ['g++'] - -#Platform specific libs -if sys.platform == 'win32': - # MS Windows - sys_libs = ['glfw', 'opengl32'] -elif sys.platform == 'darwin': - # Apple OSX - sys_libs = ['glfw'] -else: - # GNU/Linux, BSD, etc - sys_libs = ['glfw', 'GL'] - -def options(opt): - print(' set_options') - opt.load('compiler_cxx') - -def configure(conf): - print(' calling the configuration') - conf.load('compiler_cxx') - conf.env.CXXFLAGS = ['-O3', '-ffast-math'] - conf.env.DEFINES_P2T = ['P2T'] - conf.env.LIB_P2T = sys_libs - if sys.platform == 'darwin': - conf.env.FRAMEWORK = ['OpenGL', 'Cocoa'] - -def build(bld): - print(' building') - bld.program(features = 'cxx cxxprogram', source=CPP_SOURCES, target = 'p2t', uselib = 'P2T') +#!/usr/bin/env python +# encoding: utf-8 +# waf 1.6.10 + +VERSION='0.3.3' +import sys +APPNAME='p2t' +top = '.' +out = 'build' + +CPP_SOURCES = ['poly2tri/common/shapes.cc', + 'poly2tri/sweep/cdt.cc', + 'poly2tri/sweep/advancing_front.cc', + 'poly2tri/sweep/sweep_context.cc', + 'poly2tri/sweep/sweep.cc', + 'testbed/main.cc'] + +from waflib.Tools.compiler_cxx import cxx_compiler +cxx_compiler['win32'] = ['g++'] + +#Platform specific libs +if sys.platform == 'win32': + # MS Windows + sys_libs = ['glfw', 'opengl32'] +elif sys.platform == 'darwin': + # Apple OSX + sys_libs = ['glfw'] +else: + # GNU/Linux, BSD, etc + sys_libs = ['glfw', 'GL'] + +def options(opt): + print(' set_options') + opt.load('compiler_cxx') + +def configure(conf): + print(' calling the configuration') + conf.load('compiler_cxx') + conf.env.CXXFLAGS = ['-O3', '-ffast-math'] + conf.env.DEFINES_P2T = ['P2T'] + conf.env.LIB_P2T = sys_libs + if sys.platform == 'darwin': + conf.env.FRAMEWORK = ['OpenGL', 'Cocoa'] + +def build(bld): + print(' building') + bld.program(features = 'cxx cxxprogram', source=CPP_SOURCES, target = 'p2t', uselib = 'P2T') \ No newline at end of file