summaryrefslogtreecommitdiff
path: root/src/3rdparty/clip2tri
diff options
context:
space:
mode:
authorPaolo Angelelli <paolo.angelelli@theqtcompany.com>2016-04-07 15:50:46 +0200
committerPaolo Angelelli <paolo.angelelli@theqtcompany.com>2016-06-15 09:41:05 +0000
commit0da6abc2cb62ec9cf51c594493142c1626c83ffe (patch)
treebdd0db1afb49a0a54007e7ee305417ef935d38a0 /src/3rdparty/clip2tri
parentd90b2bffd76bf74ab4b6e6c0c68a26f6830c8d6f (diff)
downloadqtlocation-0da6abc2cb62ec9cf51c594493142c1626c83ffe.tar.gz
Fix for crash on tessellation of self-intersectiong GeoPolygons
QtLocation makes use of a 3rd party library for polygon tessellation, poly2tri. This library is known to be weak in handling complex input, like, for example, polygons with self intersecting edges. Sanitizing the input data through clipper solves the problem, and it has already been implemented in the clip2tri library, which this patch includes and uses. Task-number: QTBUG-52076 Change-Id: I071a58e202345bc71da583995f7245361f00e8c4 Reviewed-by: Lars Knoll <lars.knoll@qt.io> Reviewed-by: Alex Blasche <alexander.blasche@theqtcompany.com>
Diffstat (limited to 'src/3rdparty/clip2tri')
-rw-r--r--src/3rdparty/clip2tri/LICENSE21
-rw-r--r--src/3rdparty/clip2tri/clip2tri.cpp312
-rw-r--r--src/3rdparty/clip2tri/clip2tri.h86
-rw-r--r--src/3rdparty/clip2tri/clip2tri.pro18
4 files changed, 437 insertions, 0 deletions
diff --git a/src/3rdparty/clip2tri/LICENSE b/src/3rdparty/clip2tri/LICENSE
new file mode 100644
index 00000000..9d99b888
--- /dev/null
+++ b/src/3rdparty/clip2tri/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Bitfighter developers
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/src/3rdparty/clip2tri/clip2tri.cpp b/src/3rdparty/clip2tri/clip2tri.cpp
new file mode 100644
index 00000000..86870fc1
--- /dev/null
+++ b/src/3rdparty/clip2tri/clip2tri.cpp
@@ -0,0 +1,312 @@
+/*
+ * Authors: kaen, raptor, sam686, watusimoto
+ *
+ * Originally from the bitfighter source code
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2014 Bitfighter developers
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "clip2tri.h"
+#include "../poly2tri/poly2tri.h"
+
+#include <cstdio>
+
+
+using namespace p2t;
+
+namespace c2t
+{
+
+
+static const F32 CLIPPER_SCALE_FACT = 1000.0f;
+static const F32 CLIPPER_SCALE_FACT_INVERSE = 0.001f;
+
+
+/////////////////////////////////
+
+Point::Point()
+{
+ x = 0;
+ y = 0;
+}
+
+Point::Point(const Point& pt)
+{
+ x = pt.x;
+ y = pt.y;
+}
+
+
+/////////////////////////////////
+
+clip2tri::clip2tri()
+{
+ // Do nothing!
+}
+
+clip2tri::~clip2tri()
+{
+ // Do nothing!
+}
+
+
+void clip2tri::triangulate(const vector<vector<Point> > &inputPolygons, vector<Point> &outputTriangles,
+ const vector<Point> &boundingPolygon)
+{
+ // Use clipper to clean. This upscales the floating point input
+ PolyTree solution;
+ mergePolysToPolyTree(inputPolygons, solution);
+
+ Path bounds = upscaleClipperPoints(boundingPolygon);
+
+ // This will downscale the Clipper output and use poly2tri to triangulate
+ triangulateComplex(outputTriangles, bounds, solution);
+}
+
+
+Path clip2tri::upscaleClipperPoints(const vector<Point> &inputPolygon)
+{
+ Path outputPolygon;
+ outputPolygon.resize(inputPolygon.size());
+
+ for(S32 i = 0; i < inputPolygon.size(); i++)
+ outputPolygon[i] = IntPoint(S64(inputPolygon[i].x * CLIPPER_SCALE_FACT), S64(inputPolygon[i].y * CLIPPER_SCALE_FACT));
+
+ return outputPolygon;
+}
+
+
+Paths clip2tri::upscaleClipperPoints(const vector<vector<Point> > &inputPolygons)
+{
+ Paths outputPolygons;
+
+ outputPolygons.resize(inputPolygons.size());
+
+ for(S32 i = 0; i < inputPolygons.size(); i++)
+ {
+ outputPolygons[i].resize(inputPolygons[i].size());
+
+ for(S32 j = 0; j < inputPolygons[i].size(); j++)
+ outputPolygons[i][j] = IntPoint(S64(inputPolygons[i][j].x * CLIPPER_SCALE_FACT), S64(inputPolygons[i][j].y * CLIPPER_SCALE_FACT));
+ }
+
+ return outputPolygons;
+}
+
+
+vector<vector<Point> > clip2tri::downscaleClipperPoints(const Paths &inputPolygons)
+{
+ vector<vector<Point> > outputPolygons;
+
+ outputPolygons.resize(inputPolygons.size());
+
+ for(U32 i = 0; i < inputPolygons.size(); i++)
+ {
+ outputPolygons[i].resize(inputPolygons[i].size());
+
+ for(U32 j = 0; j < inputPolygons[i].size(); j++)
+ outputPolygons[i][j] = Point(F32(inputPolygons[i][j].X) * CLIPPER_SCALE_FACT_INVERSE, F32(inputPolygons[i][j].Y) * CLIPPER_SCALE_FACT_INVERSE);
+ }
+
+ return outputPolygons;
+}
+
+
+// Use Clipper to merge inputPolygons, placing the result in a Polytree
+// NOTE: this does NOT downscale the Clipper points. You must do this afterwards
+//
+// Here you add all your non-navigatable objects (e.g. walls, barriers, etc.)
+bool clip2tri::mergePolysToPolyTree(const vector<vector<Point> > &inputPolygons, PolyTree &solution)
+{
+ Paths input = upscaleClipperPoints(inputPolygons);
+
+ // Fire up clipper and union!
+ Clipper clipper;
+ clipper.StrictlySimple(true);
+
+ try // there is a "throw" in AddPolygon
+ {
+ clipper.AddPaths(input, ptSubject, true);
+ }
+ catch(...)
+ {
+ printf("clipper.AddPaths, something went wrong\n");
+ }
+
+ return clipper.Execute(ctUnion, solution, pftNonZero, pftNonZero);
+}
+
+
+// Delete all poly2tri points from a vector and clear the vector
+static void deleteAndClear(vector<p2t::Point*> &vec)
+{
+ for(U32 i = 0; i < vec.size(); i++)
+ delete vec[i];
+
+ vec.clear();
+}
+
+
+// Shrink large polygons by reducing each coordinate by 1 in the
+// general direction of the last point as we wind around
+//
+// This normally wouldn't work in every case, but our upscaled-by-1000 polygons
+// have little chance to create new duplicate points with this method.
+//
+// For information on why this was needed, see:
+//
+// https://code.google.com/p/poly2tri/issues/detail?id=90
+//
+static void edgeShrink(Path &path)
+{
+ U32 prev = path.size() - 1;
+ for(U32 i = 0; i < path.size(); i++)
+ {
+ // Adjust coordinate by 1 depending on the direction
+ path[i].X - path[prev].X > 0 ? path[i].X-- : path[i].X++;
+ path[i].Y - path[prev].Y > 0 ? path[i].Y-- : path[i].Y++;
+
+ prev = i;
+ }
+}
+
+
+// This uses poly2tri to triangulate. poly2tri isn't very robust so clipper needs to do
+// the cleaning of points before getting here.
+//
+// A tree structure of polygons is required for doing complex polygons-within-polygons.
+// For reference discussion on how this started to be developed, see here:
+//
+// https://code.google.com/p/poly2tri/issues/detail?id=74
+//
+// For assistance with a special case crash, see this utility:
+// http://javascript.poly2tri.googlecode.com/hg/index.html
+//
+// FIXME: what is ignoreFills and ignoreHoles for? kaen?
+bool clip2tri::triangulateComplex(vector<Point> &outputTriangles, const Path &outline,
+ const PolyTree &polyTree, bool ignoreFills, bool ignoreHoles)
+{
+ // Keep track of memory for all the poly2tri objects we create
+ vector<p2t::CDT*> cdtRegistry;
+ vector<vector<p2t::Point*> > holesRegistry;
+ vector<vector<p2t::Point*> > polylinesRegistry;
+
+
+ // Let's be tricky and add our outline to the root node (it should have none), it'll be
+ // our first Clipper hole
+ PolyNode *rootNode = NULL;
+
+ PolyNode tempNode;
+ if(polyTree.Total() == 0) // Polytree is empty with no root node, e.g. on an empty level
+ rootNode = &tempNode;
+ else
+ rootNode = polyTree.GetFirst()->Parent;
+
+ rootNode->Contour = outline;
+
+ // Now traverse our polyline nodes and triangulate them with only their children holes
+ PolyNode *currentNode = rootNode;
+ while(currentNode != NULL)
+ {
+ // A Clipper hole is actually what we want to build zones for; they become our bounding
+ // polylines. poly2tri holes are therefore the inverse
+ if((!ignoreHoles && currentNode->IsHole()) ||
+ (!ignoreFills && !currentNode->IsHole()))
+ {
+ // Build up this polyline in poly2tri's format (downscale Clipper points)
+ vector<p2t::Point*> polyline;
+ for(U32 j = 0; j < currentNode->Contour.size(); j++)
+ polyline.push_back(new p2t::Point(F64(currentNode->Contour[j].X), F64(currentNode->Contour[j].Y)));
+
+ polylinesRegistry.push_back(polyline); // Memory
+
+ // Set our polyline in poly2tri
+ p2t::CDT* cdt = new p2t::CDT(polyline);
+ cdtRegistry.push_back(cdt);
+
+ for(U32 j = 0; j < currentNode->Childs.size(); j++)
+ {
+ PolyNode *childNode = currentNode->Childs[j];
+
+ // Slightly modify the polygon to guarantee no duplicate points
+ edgeShrink(childNode->Contour);
+
+ vector<p2t::Point*> hole;
+ for(U32 k = 0; k < childNode->Contour.size(); k++)
+ hole.push_back(new p2t::Point(F64(childNode->Contour[k].X), F64(childNode->Contour[k].Y)));
+
+ holesRegistry.push_back(hole); // Memory
+
+ // Add the holes for this polyline
+ cdt->AddHole(hole);
+ }
+
+ cdt->Triangulate();
+
+ // Add current output triangles to our total
+ vector<p2t::Triangle*> currentOutput = cdt->GetTriangles();
+
+ // Copy our data to TNL::Point and to our output Vector
+ p2t::Triangle *currentTriangle;
+ for(U32 j = 0; j < currentOutput.size(); j++)
+ {
+ currentTriangle = currentOutput[j];
+ outputTriangles.push_back(Point(currentTriangle->GetPoint(0)->x * CLIPPER_SCALE_FACT_INVERSE, currentTriangle->GetPoint(0)->y * CLIPPER_SCALE_FACT_INVERSE));
+ outputTriangles.push_back(Point(currentTriangle->GetPoint(1)->x * CLIPPER_SCALE_FACT_INVERSE, currentTriangle->GetPoint(1)->y * CLIPPER_SCALE_FACT_INVERSE));
+ outputTriangles.push_back(Point(currentTriangle->GetPoint(2)->x * CLIPPER_SCALE_FACT_INVERSE, currentTriangle->GetPoint(2)->y * CLIPPER_SCALE_FACT_INVERSE));
+ }
+ }
+
+ currentNode = currentNode->GetNext();
+ }
+
+
+ // Clean up memory used with poly2tri
+ //
+ // Clean-up workers
+ for(S32 i = 0; i < cdtRegistry.size(); i++)
+ delete cdtRegistry[i];
+
+ // Free the polylines
+ for(S32 i = 0; i < polylinesRegistry.size(); i++)
+ {
+ vector<p2t::Point*> polyline = polylinesRegistry[i];
+ deleteAndClear(polyline);
+ }
+
+ // Free the holes
+ for(S32 i = 0; i < holesRegistry.size(); i++)
+ {
+ vector<p2t::Point*> hole = holesRegistry[i];
+ deleteAndClear(hole);
+ }
+
+ // Make sure we have output data
+ if(outputTriangles.size() == 0)
+ return false;
+
+ return true;
+}
+
+
+} /* namespace c2t */
diff --git a/src/3rdparty/clip2tri/clip2tri.h b/src/3rdparty/clip2tri/clip2tri.h
new file mode 100644
index 00000000..982c8049
--- /dev/null
+++ b/src/3rdparty/clip2tri/clip2tri.h
@@ -0,0 +1,86 @@
+/*
+ * Authors: kaen, raptor, sam686, watusimoto
+ *
+ * Originally from the bitfighter source code
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2014 Bitfighter developers
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef CLIP2TRI_H_
+#define CLIP2TRI_H_
+
+#include <vector>
+#include "../clipper/clipper.h"
+
+using namespace std;
+using namespace ClipperLib;
+
+namespace c2t
+{
+
+typedef signed int S32;
+typedef signed long long S64;
+typedef unsigned int U32;
+typedef float F32;
+typedef double F64;
+
+
+struct Point
+{
+ F32 x;
+ F32 y;
+
+ Point();
+ Point(const Point &pt);
+
+ template<class T, class U>
+ Point(T in_x, U in_y) { x = static_cast<F32>(in_x); y = static_cast<F32>(in_y); }
+};
+
+
+class clip2tri
+{
+private:
+ //
+ Path upscaleClipperPoints(const vector<Point> &inputPolygon);
+
+ // These operate on a vector of polygons
+ Paths upscaleClipperPoints(const vector<vector<Point> > &inputPolygons);
+ vector<vector<Point> > downscaleClipperPoints(const Paths &inputPolygons);
+
+ bool mergePolysToPolyTree(const vector<vector<Point> > &inputPolygons, PolyTree &solution);
+
+ bool triangulateComplex(vector<Point> &outputTriangles, const Path &outline,
+ const PolyTree &polyTree, bool ignoreFills = true, bool ignoreHoles = false);
+
+public:
+ clip2tri();
+ virtual ~clip2tri();
+
+ void triangulate(const vector<vector<Point> > &inputPolygons, vector<Point> &outputTriangles,
+ const vector<Point> &boundingPolygon);
+};
+
+} /* namespace c2t */
+
+#endif /* CLIP2TRI_H_ */
diff --git a/src/3rdparty/clip2tri/clip2tri.pro b/src/3rdparty/clip2tri/clip2tri.pro
new file mode 100644
index 00000000..50901c06
--- /dev/null
+++ b/src/3rdparty/clip2tri/clip2tri.pro
@@ -0,0 +1,18 @@
+TARGET = clip2tri
+
+CONFIG += staticlib exceptions
+
+load(qt_helper_lib)
+
+# workaround for QTBUG-31586
+contains(QT_CONFIG, c++11): CONFIG += c++11
+
+*-g++* {
+ QMAKE_CXXFLAGS += -O3 -ftree-vectorize -ffast-math -funsafe-math-optimizations -Wno-error=return-type
+}
+
+HEADERS += clip2tri.h
+SOURCES += clip2tri.cpp
+
+LIBS_PRIVATE += -L$$MODULE_BASE_OUTDIR/lib -lpoly2tri$$qtPlatformTargetSuffix() -lclipper$$qtPlatformTargetSuffix()
+