| 1 | /* | 
| 2 |  * Authors: kaen, raptor, sam686, watusimoto | 
| 3 |  * | 
| 4 |  * Originally from the bitfighter source code | 
| 5 |  * | 
| 6 |  * The MIT License (MIT) | 
| 7 |  * | 
| 8 |  * Copyright (c) 2014 Bitfighter developers | 
| 9 |  * | 
| 10 |  * Permission is hereby granted, free of charge, to any person obtaining a copy | 
| 11 |  * of this software and associated documentation files (the "Software"), to deal | 
| 12 |  * in the Software without restriction, including without limitation the rights | 
| 13 |  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | 
| 14 |  * copies of the Software, and to permit persons to whom the Software is | 
| 15 |  * furnished to do so, subject to the following conditions: | 
| 16 |  * | 
| 17 |  * The above copyright notice and this permission notice shall be included in all | 
| 18 |  * copies or substantial portions of the Software. | 
| 19 |  * | 
| 20 |  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 
| 21 |  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
| 22 |  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | 
| 23 |  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | 
| 24 |  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | 
| 25 |  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | 
| 26 |  * SOFTWARE. | 
| 27 |  */ | 
| 28 |  | 
| 29 | #include "clip2tri.h" | 
| 30 | #include <poly2tri.h> | 
| 31 |  | 
| 32 | #include <cstdio> | 
| 33 |  | 
| 34 | static const double clipperScaleFactor = 1073741822.0; | 
| 35 | static const double clipperScaleFactorInv = 1.0 / 1073741822.0; | 
| 36 |  | 
| 37 | using namespace p2t; | 
| 38 |  | 
| 39 | namespace c2t | 
| 40 | { | 
| 41 |  | 
| 42 |  | 
| 43 | static const F32 CLIPPER_SCALE_FACT = 1000.0f; | 
| 44 | static const F32 CLIPPER_SCALE_FACT_INVERSE = 0.001f; | 
| 45 |  | 
| 46 | ///////////////////////////////// | 
| 47 |  | 
| 48 | Point::Point() | 
| 49 | { | 
| 50 |    x = 0; | 
| 51 |    y = 0; | 
| 52 | } | 
| 53 |  | 
| 54 | Point::Point(const Point& pt) | 
| 55 | { | 
| 56 |    x = pt.x; | 
| 57 |    y = pt.y; | 
| 58 | } | 
| 59 |  | 
| 60 |  | 
| 61 | ///////////////////////////////// | 
| 62 |  | 
| 63 | clip2tri::clip2tri() : openSubject(false) | 
| 64 | { | 
| 65 |    // Do nothing! | 
| 66 | } | 
| 67 |  | 
| 68 | clip2tri::~clip2tri() | 
| 69 | { | 
| 70 |    // Do nothing! | 
| 71 | } | 
| 72 |  | 
| 73 |  | 
| 74 | void clip2tri::triangulate(const vector<vector<Point> > &inputPolygons, vector<Point> &outputTriangles, | 
| 75 |       const vector<Point> &boundingPolygon) | 
| 76 | { | 
| 77 |    // Use clipper to clean.  This upscales the floating point input | 
| 78 |    PolyTree solution; | 
| 79 |    mergePolysToPolyTree(inputPolygons, solution); | 
| 80 |  | 
| 81 |    Path bounds = upscaleClipperPoints(inputPolygon: boundingPolygon); | 
| 82 |  | 
| 83 |    // This will downscale the Clipper output and use poly2tri to triangulate | 
| 84 |    triangulateComplex(outputTriangles, outline: bounds, polyTree: solution); | 
| 85 | } | 
| 86 |  | 
| 87 | void clip2tri::addClipPolygon(const Path &path) | 
| 88 | { | 
| 89 |     try  // prevent any exception to spill into Qt | 
| 90 |     { | 
| 91 |         clipper.AddPath(pg: path, PolyTyp: ptClip, Closed: true); | 
| 92 |     } | 
| 93 |     catch(QtClipperLib::clipperException &e) | 
| 94 |     { | 
| 95 |         printf(format: "addClipPolygon: %s\n" , e.what()); | 
| 96 |     } | 
| 97 | } | 
| 98 |  | 
| 99 | void clip2tri::addSubjectPath(const Path &path, bool closed) | 
| 100 | { | 
| 101 |     try  // prevent any exception to spill into Qt | 
| 102 |     { | 
| 103 |         clipper.AddPath(pg: path, PolyTyp: ptSubject, Closed: closed); | 
| 104 |     } | 
| 105 |     catch(QtClipperLib::clipperException &e) | 
| 106 |     { | 
| 107 |         printf(format: "addSubjectPath: %s\n" , e.what()); | 
| 108 |         return; | 
| 109 |     } | 
| 110 |     if (!closed) | 
| 111 |         openSubject = true; | 
| 112 | } | 
| 113 |  | 
| 114 | void clip2tri::clearClipper() | 
| 115 | { | 
| 116 |     // clear doesn't throw | 
| 117 |     clipper.Clear(); | 
| 118 |     openSubject = false; | 
| 119 | } | 
| 120 |  | 
| 121 | static QtClipperLib::ClipType operation(const clip2tri::Operation &op) | 
| 122 | { | 
| 123 |     switch (op) { | 
| 124 |     case clip2tri::Intersection: | 
| 125 |         return QtClipperLib::ctIntersection; | 
| 126 |     case clip2tri::Union: | 
| 127 |         return QtClipperLib::ctUnion; | 
| 128 |     case clip2tri::Difference: | 
| 129 |         return QtClipperLib::ctDifference; | 
| 130 |     case clip2tri::Xor: | 
| 131 |         return QtClipperLib::ctXor; | 
| 132 |     } | 
| 133 |     return ctIntersection; | 
| 134 | } | 
| 135 |  | 
| 136 | static std::string operationName(const clip2tri::Operation &op) | 
| 137 | { | 
| 138 |     switch (op) { | 
| 139 |     case clip2tri::Intersection: | 
| 140 |         return std::string("Intersection" ); | 
| 141 |     case clip2tri::Union: | 
| 142 |         return std::string("Union" ); | 
| 143 |     case clip2tri::Difference: | 
| 144 |         return std::string("Difference" ); | 
| 145 |     case clip2tri::Xor: | 
| 146 |         return std::string("Xor" ); | 
| 147 |     } | 
| 148 |     return std::string("Intersection" ); | 
| 149 | } | 
| 150 |  | 
| 151 | Paths clip2tri::execute(const clip2tri::Operation op, const PolyFillType subjFillType, const PolyFillType clipFillType) | 
| 152 | { | 
| 153 |     Paths solution; | 
| 154 |     try  // prevent any exception from spilling into Qt | 
| 155 |     { | 
| 156 |         if (!openSubject) { | 
| 157 |             clipper.Execute(clipType: operation(op), solution, subjFillType, clipFillType); | 
| 158 |         } else { | 
| 159 |             PolyTree res; | 
| 160 |             clipper.Execute(clipType: operation(op), polytree&: res, subjFillType, clipFillType); | 
| 161 |             PolyNode *n = res.GetFirst(); | 
| 162 |             if (n) { | 
| 163 |                 solution.push_back(x: n->Contour); | 
| 164 |                 while ((n = n->GetNext())) | 
| 165 |                     solution.push_back(x: n->Contour); | 
| 166 |             } | 
| 167 |         } | 
| 168 |     } | 
| 169 |     catch(QtClipperLib::clipperException &e) | 
| 170 |     { | 
| 171 |         printf(format: "executing %s: %s\n" , operationName(op).c_str(), e.what()); | 
| 172 |     } | 
| 173 |     return solution; | 
| 174 | } | 
| 175 |  | 
| 176 | int clip2tri::pointInPolygon(const IntPoint &pt, const Path &path) | 
| 177 | { | 
| 178 |     return PointInPolygon(pt, path); | 
| 179 | } | 
| 180 |  | 
| 181 | Path clip2tri::upscaleClipperPoints(const vector<Point> &inputPolygon) | 
| 182 | { | 
| 183 |    Path outputPolygon; | 
| 184 |    outputPolygon.resize(new_size: inputPolygon.size()); | 
| 185 |  | 
| 186 |    for(S32 i = 0; i < inputPolygon.size(); i++) | 
| 187 |       outputPolygon[i] = IntPoint(S64(inputPolygon[i].x * CLIPPER_SCALE_FACT), S64(inputPolygon[i].y * CLIPPER_SCALE_FACT)); | 
| 188 |  | 
| 189 |    return outputPolygon; | 
| 190 | } | 
| 191 |  | 
| 192 |  | 
| 193 | Paths clip2tri::upscaleClipperPoints(const vector<vector<Point> > &inputPolygons) | 
| 194 | { | 
| 195 |    Paths outputPolygons; | 
| 196 |  | 
| 197 |    outputPolygons.resize(new_size: inputPolygons.size()); | 
| 198 |  | 
| 199 |    for(S32 i = 0; i < inputPolygons.size(); i++) | 
| 200 |    { | 
| 201 |       outputPolygons[i].resize(new_size: inputPolygons[i].size()); | 
| 202 |  | 
| 203 |       for(S32 j = 0; j < inputPolygons[i].size(); j++) | 
| 204 |          outputPolygons[i][j] = IntPoint(S64(inputPolygons[i][j].x * CLIPPER_SCALE_FACT), S64(inputPolygons[i][j].y * CLIPPER_SCALE_FACT)); | 
| 205 |    } | 
| 206 |  | 
| 207 |    return outputPolygons; | 
| 208 | } | 
| 209 |  | 
| 210 |  | 
| 211 | vector<vector<Point> > clip2tri::downscaleClipperPoints(const Paths &inputPolygons) | 
| 212 | { | 
| 213 |    vector<vector<Point> > outputPolygons; | 
| 214 |  | 
| 215 |    outputPolygons.resize(new_size: inputPolygons.size()); | 
| 216 |  | 
| 217 |    for(U32 i = 0; i < inputPolygons.size(); i++) | 
| 218 |    { | 
| 219 |       outputPolygons[i].resize(new_size: inputPolygons[i].size()); | 
| 220 |  | 
| 221 |       for(U32 j = 0; j < inputPolygons[i].size(); j++) | 
| 222 |          outputPolygons[i][j] = Point(F32(inputPolygons[i][j].X) * CLIPPER_SCALE_FACT_INVERSE, F32(inputPolygons[i][j].Y) * CLIPPER_SCALE_FACT_INVERSE); | 
| 223 |    } | 
| 224 |  | 
| 225 |    return outputPolygons; | 
| 226 | } | 
| 227 |  | 
| 228 |  | 
| 229 | // Use Clipper to merge inputPolygons, placing the result in a Polytree | 
| 230 | // NOTE: this does NOT downscale the Clipper points.  You must do this afterwards | 
| 231 | // | 
| 232 | // Here you add all your non-navigatable objects (e.g. walls, barriers, etc.) | 
| 233 | bool clip2tri::mergePolysToPolyTree(const vector<vector<Point> > &inputPolygons, PolyTree &solution) | 
| 234 | { | 
| 235 |    Paths input = upscaleClipperPoints(inputPolygons); | 
| 236 |  | 
| 237 |    // Fire up clipper and union! | 
| 238 |    Clipper clipper; | 
| 239 |    clipper.StrictlySimple(value: true); | 
| 240 |  | 
| 241 |    try  // there is a "throw" in AddPolygon | 
| 242 |    { | 
| 243 |       clipper.AddPaths(ppg: input, PolyTyp: ptSubject, Closed: true); | 
| 244 |    } | 
| 245 |    catch(QtClipperLib::clipperException &e) | 
| 246 |    { | 
| 247 |        printf(format: "mergePolysToPolyTree: %s\n" , e.what()); | 
| 248 |    } | 
| 249 |  | 
| 250 |    return clipper.Execute(clipType: ctUnion, polytree&: solution, subjFillType: pftNonZero, clipFillType: pftNonZero); | 
| 251 | } | 
| 252 |  | 
| 253 |  | 
| 254 | // Delete all poly2tri points from a vector and clear the vector | 
| 255 | static void deleteAndClear(vector<p2t::Point*> &vec) | 
| 256 | { | 
| 257 |    for(U32 i = 0; i < vec.size(); i++) | 
| 258 |       delete vec[i]; | 
| 259 |  | 
| 260 |    vec.clear(); | 
| 261 | } | 
| 262 |  | 
| 263 |  | 
| 264 | // Shrink large polygons by reducing each coordinate by 1 in the | 
| 265 | // general direction of the last point as we wind around | 
| 266 | // | 
| 267 | // This normally wouldn't work in every case, but our upscaled-by-1000 polygons | 
| 268 | // have little chance to create new duplicate points with this method. | 
| 269 | // | 
| 270 | // For information on why this was needed, see: | 
| 271 | // | 
| 272 | //    https://code.google.com/p/poly2tri/issues/detail?id=90 | 
| 273 | // | 
| 274 | static void edgeShrink(Path &path) | 
| 275 | { | 
| 276 |    U32 prev = path.size() - 1; | 
| 277 |    for(U32 i = 0; i < path.size(); i++) | 
| 278 |    { | 
| 279 |       // Adjust coordinate by 1 depending on the direction | 
| 280 |       path[i].X - path[prev].X > 0 ? path[i].X-- : path[i].X++; | 
| 281 |       path[i].Y - path[prev].Y > 0 ? path[i].Y-- : path[i].Y++; | 
| 282 |  | 
| 283 |       prev = i; | 
| 284 |    } | 
| 285 | } | 
| 286 |  | 
| 287 |  | 
| 288 | // This uses poly2tri to triangulate.  poly2tri isn't very robust so clipper needs to do | 
| 289 | // the cleaning of points before getting here. | 
| 290 | // | 
| 291 | // A tree structure of polygons is required for doing complex polygons-within-polygons. | 
| 292 | // For reference discussion on how this started to be developed, see here: | 
| 293 | // | 
| 294 | //    https://code.google.com/p/poly2tri/issues/detail?id=74 | 
| 295 | // | 
| 296 | // For assistance with a special case crash, see this utility: | 
| 297 | //    http://javascript.poly2tri.googlecode.com/hg/index.html | 
| 298 | // | 
| 299 | // FIXME: what is ignoreFills and ignoreHoles for?  kaen? | 
| 300 | bool clip2tri::triangulateComplex(vector<Point> &outputTriangles, const Path &outline, | 
| 301 |       const PolyTree &polyTree, bool ignoreFills, bool ignoreHoles) | 
| 302 | { | 
| 303 |    // Keep track of memory for all the poly2tri objects we create | 
| 304 |    vector<p2t::CDT*> cdtRegistry; | 
| 305 |    vector<vector<p2t::Point*> > holesRegistry; | 
| 306 |    vector<vector<p2t::Point*> > polylinesRegistry; | 
| 307 |  | 
| 308 |  | 
| 309 |    // Let's be tricky and add our outline to the root node (it should have none), it'll be | 
| 310 |    // our first Clipper hole | 
| 311 |    PolyNode *rootNode = NULL; | 
| 312 |  | 
| 313 |    PolyNode tempNode; | 
| 314 |    if(polyTree.Total() == 0)  // Polytree is empty with no root node, e.g. on an empty level | 
| 315 |       rootNode = &tempNode; | 
| 316 |    else | 
| 317 |       rootNode = polyTree.GetFirst()->Parent; | 
| 318 |  | 
| 319 |    rootNode->Contour = outline; | 
| 320 |  | 
| 321 |    // Now traverse our polyline nodes and triangulate them with only their children holes | 
| 322 |    PolyNode *currentNode = rootNode; | 
| 323 |    while(currentNode != NULL) | 
| 324 |    { | 
| 325 |       // A Clipper hole is actually what we want to build zones for; they become our bounding | 
| 326 |       // polylines.  poly2tri holes are therefore the inverse | 
| 327 |       if((!ignoreHoles && currentNode->IsHole()) || | 
| 328 |          (!ignoreFills && !currentNode->IsHole())) | 
| 329 |       { | 
| 330 |          // Build up this polyline in poly2tri's format (downscale Clipper points) | 
| 331 |          vector<p2t::Point*> polyline; | 
| 332 |          for(U32 j = 0; j < currentNode->Contour.size(); j++) | 
| 333 |             polyline.push_back(x: new p2t::Point(F64(currentNode->Contour[j].X), F64(currentNode->Contour[j].Y))); | 
| 334 |  | 
| 335 |          polylinesRegistry.push_back(x: polyline);  // Memory | 
| 336 |  | 
| 337 |          // Set our polyline in poly2tri | 
| 338 |          p2t::CDT* cdt = new p2t::CDT(polyline); | 
| 339 |          cdtRegistry.push_back(x: cdt); | 
| 340 |  | 
| 341 |          for(U32 j = 0; j < currentNode->Childs.size(); j++) | 
| 342 |          { | 
| 343 |             PolyNode *childNode = currentNode->Childs[j]; | 
| 344 |  | 
| 345 |             // Slightly modify the polygon to guarantee no duplicate points | 
| 346 |             edgeShrink(path&: childNode->Contour); | 
| 347 |  | 
| 348 |             vector<p2t::Point*> hole; | 
| 349 |             for(U32 k = 0; k < childNode->Contour.size(); k++) | 
| 350 |                hole.push_back(x: new p2t::Point(F64(childNode->Contour[k].X), F64(childNode->Contour[k].Y))); | 
| 351 |  | 
| 352 |             holesRegistry.push_back(x: hole);  // Memory | 
| 353 |  | 
| 354 |             // Add the holes for this polyline | 
| 355 |             cdt->AddHole(polyline: hole); | 
| 356 |          } | 
| 357 |  | 
| 358 |          cdt->Triangulate(); | 
| 359 |  | 
| 360 |          // Add current output triangles to our total | 
| 361 |          vector<p2t::Triangle*> currentOutput = cdt->GetTriangles(); | 
| 362 |  | 
| 363 |          // Copy our data to TNL::Point and to our output Vector | 
| 364 |          p2t::Triangle *currentTriangle; | 
| 365 |          for(U32 j = 0; j < currentOutput.size(); j++) | 
| 366 |          { | 
| 367 |             currentTriangle = currentOutput[j]; | 
| 368 |             outputTriangles.push_back(x: Point(currentTriangle->GetPoint(index: 0)->x * CLIPPER_SCALE_FACT_INVERSE, currentTriangle->GetPoint(index: 0)->y * CLIPPER_SCALE_FACT_INVERSE)); | 
| 369 |             outputTriangles.push_back(x: Point(currentTriangle->GetPoint(index: 1)->x * CLIPPER_SCALE_FACT_INVERSE, currentTriangle->GetPoint(index: 1)->y * CLIPPER_SCALE_FACT_INVERSE)); | 
| 370 |             outputTriangles.push_back(x: Point(currentTriangle->GetPoint(index: 2)->x * CLIPPER_SCALE_FACT_INVERSE, currentTriangle->GetPoint(index: 2)->y * CLIPPER_SCALE_FACT_INVERSE)); | 
| 371 |          } | 
| 372 |       } | 
| 373 |  | 
| 374 |       currentNode = currentNode->GetNext(); | 
| 375 |    } | 
| 376 |  | 
| 377 |  | 
| 378 |    // Clean up memory used with poly2tri | 
| 379 |    // | 
| 380 |    // Clean-up workers | 
| 381 |    for(S32 i = 0; i < cdtRegistry.size(); i++) | 
| 382 |       delete cdtRegistry[i]; | 
| 383 |  | 
| 384 |    // Free the polylines | 
| 385 |    for(S32 i = 0; i < polylinesRegistry.size(); i++) | 
| 386 |    { | 
| 387 |       vector<p2t::Point*> polyline = polylinesRegistry[i]; | 
| 388 |       deleteAndClear(vec&: polyline); | 
| 389 |    } | 
| 390 |  | 
| 391 |    // Free the holes | 
| 392 |    for(S32 i = 0; i < holesRegistry.size(); i++) | 
| 393 |    { | 
| 394 |       vector<p2t::Point*> hole = holesRegistry[i]; | 
| 395 |       deleteAndClear(vec&: hole); | 
| 396 |    } | 
| 397 |  | 
| 398 |    // Make sure we have output data | 
| 399 |    if(outputTriangles.size() == 0) | 
| 400 |       return false; | 
| 401 |  | 
| 402 |    return true; | 
| 403 | } | 
| 404 |  | 
| 405 |  | 
| 406 | } /* namespace c2t */ | 
| 407 |  |