1""" 2Cython wrapper for the C++ translation of the Angus Johnson's Clipper 3library (ver. 6.2.1) (http://www.angusj.com/delphi/clipper.php) 4 5This wrapper was written by Maxime Chalton, Lukas Treyer and Gregor Ratajc. 6 7""" 8 9SILENT = True 10 11""" 12SCALING_FACTOR has been deprecated. See https://github.com/greginvm/pyclipper/wiki/Deprecating-SCALING_FACTOR 13for an explanation. 14""" 15SCALING_FACTOR = 1 16 17 18def log_action(description): 19 if not SILENT: 20 print description 21 22log_action("Python binding clipper library") 23 24import sys as _sys 25import struct 26import copy as _copy 27import unicodedata as _unicodedata 28import time as _time 29import warnings as _warnings 30import numbers as _numbers 31 32from cython.operator cimport dereference as deref 33 34cdef extern from "Python.h": 35 Py_INCREF(object o) 36 object Py_BuildValue(char *format, ...) 37 object PyBuffer_FromMemory(void *ptr, int size) 38 #int PyArg_ParseTuple(object struct,void* ptr) 39 char*PyString_AsString(object string) 40 int PyArg_VaParse(object args, char *format, ...) 41 int PyArg_Parse(object args, char *format, ...) 42 int PyObject_AsReadBuffer(object obj, void*buffer, int*buffer_len) 43 object PyBuffer_FromObject(object base, int offset, int size) 44 object PyBuffer_FromReadWriteObject(object base, int offset, int size) 45 PyBuffer_New(object o) 46 47 48cdef extern from "stdio.h": 49 cdef void printf(char*, ...) 50 51cdef extern from "stdlib.h": 52 cdef void*malloc(unsigned int size) 53 cdef void*free(void*p) 54 char *strdup(char *str) 55 int strcpy(void*str, void*src) 56 int memcpy(void*str, void*src, int size) 57 58from libcpp.vector cimport vector 59 60cdef extern from "extra_defines.hpp": 61 cdef int _USE_XYZ 62 63cdef extern from "clipper.hpp" namespace "ClipperLib": 64 65 # enum ClipType { ctIntersection, ctUnion, ctDifference, ctXor }; 66 cdef enum ClipType: 67 ctIntersection = 1, 68 ctUnion = 2, 69 ctDifference = 3, 70 ctXor = 4 71 72 # enum PolyType { ptSubject, ptClip }; 73 cdef enum PolyType: 74 ptSubject = 1, 75 ptClip = 2 76 77 # By far the most widely used winding rules for polygon filling are 78 # EvenOdd & NonZero (GDI, GDI+, XLib, OpenGL, Cairo, AGG, Quartz, SVG, Gr32) 79 # Others rules include Positive, Negative and ABS_GTR_EQ_TWO (only in OpenGL) 80 # see http://glprogramming.com/red/chapter11.html 81 # enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative }; 82 cdef enum PolyFillType: 83 pftEvenOdd = 1, 84 pftNonZero = 2, 85 pftPositive = 3, 86 pftNegative = 4 87 88 # The correct type definition is taken from cpp source, so 89 # the use_int32 is handled correctly. 90 # If you need 32 bit ints, just uncomment //#define use_int32 in clipper.hpp 91 # and recompile 92 ctypedef signed long long cInt 93 ctypedef signed long long long64 94 ctypedef unsigned long long ulong64 95 96 ctypedef char bool 97 98 # TODO: handle "use_xyz" that adds Z coordinate 99 cdef struct IntPoint: 100 cInt X 101 cInt Y 102 103 #typedef std::vector< IntPoint > Path; 104 cdef cppclass Path: 105 Path() 106 void push_back(IntPoint &) 107 IntPoint& operator[](int) 108 IntPoint& at(int) 109 int size() 110 111 #typedef std::vector< Path > Paths; 112 cdef cppclass Paths: 113 Paths() 114 void push_back(Path &) 115 Path& operator[](int) 116 Path& at(int) 117 int size() 118 119 cdef cppclass PolyNode: 120 PolyNode() 121 Path Contour 122 PolyNodes Childs 123 PolyNode*Parent 124 PolyNode*GetNext() 125 bool IsHole() 126 bool IsOpen() 127 int ChildCount() 128 129 cdef cppclass PolyNodes: 130 PolyNodes() 131 void push_back(PolyNode &) 132 PolyNode*operator[](int) 133 PolyNode*at(int) 134 int size() 135 136 cdef cppclass PolyTree(PolyNode): 137 PolyTree() 138 PolyNode& GetFirst() 139 void Clear() 140 int Total() 141 142 #enum InitOptions {ioReverseSolution = 1, ioStrictlySimple = 2, ioPreserveCollinear = 4}; 143 cdef enum InitOptions: 144 ioReverseSolution = 1, 145 ioStrictlySimple = 2, 146 ioPreserveCollinear = 4 147 148 #enum JoinType { jtSquare, jtRound, jtMiter }; 149 cdef enum JoinType: 150 jtSquare = 1, 151 jtRound = 2, 152 jtMiter = 3 153 154 #enum EndType {etClosedPolygon, etClosedLine, etOpenButt, etOpenSquare, etOpenRound}; 155 cdef enum EndType: 156 etClosedPolygon = 1, 157 etClosedLine = 2, 158 etOpenButt = 3, 159 etOpenSquare = 4, 160 etOpenRound = 5 161 162 cdef struct IntRect: 163 cInt left 164 cInt top 165 cInt right 166 cInt bottom 167 168 cdef cppclass Clipper: 169 Clipper(int initOptions) 170 Clipper() 171 #~Clipper() 172 void Clear() 173 bool Execute(ClipType clipType, Paths & solution, PolyFillType subjFillType, PolyFillType clipFillType) nogil 174 bool Execute(ClipType clipType, PolyTree & solution, PolyFillType subjFillType, PolyFillType clipFillType) nogil 175 bool ReverseSolution() 176 void ReverseSolution(bool value) 177 bool StrictlySimple() 178 void StrictlySimple(bool value) 179 bool PreserveCollinear() 180 void PreserveCollinear(bool value) 181 bool AddPath(Path & path, PolyType polyType, bool closed) 182 bool AddPaths(Paths & paths, PolyType polyType, bool closed) 183 IntRect GetBounds() nogil 184 185 cdef cppclass ClipperOffset: 186 ClipperOffset(double miterLimit, double roundPrecision) 187 ClipperOffset(double miterLimit) 188 ClipperOffset() 189 #~ClipperOffset() 190 void AddPath(Path & path, JoinType joinType, EndType endType) 191 void AddPaths(Paths & paths, JoinType joinType, EndType endType) 192 void Execute(Paths & solution, double delta) nogil 193 void Execute(PolyTree & solution, double delta) nogil 194 void Clear() 195 double MiterLimit 196 double ArcTolerance 197 198 # prefixes are added to original functions to prevent naming collisions 199 bool c_Orientation "Orientation"(const Path & poly) nogil 200 double c_Area "Area"(const Path & poly) nogil 201 int c_PointInPolygon "PointInPolygon"(const IntPoint & pt, const Path & path) nogil 202 203 # In the following 4 functions default values for fillType and distance were removed 204 # because it caused a bug in C++ code generated by Cython. 205 # See Cython bug report: http://trac.cython.org/ticket/816 206 void c_SimplifyPolygon "SimplifyPolygon"(const Path & in_poly, Paths & out_polys, PolyFillType fillType) nogil 207 void c_SimplifyPolygons "SimplifyPolygons"(const Paths & in_polys, Paths & out_polys, PolyFillType fillType) nogil 208 void c_CleanPolygon "CleanPolygon"(const Path& in_poly, Path& out_poly, double distance) nogil 209 void c_CleanPolygons "CleanPolygons"(Paths& polys, double distance) nogil 210 211 void c_MinkowskiSum "MinkowskiSum"(const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed) nogil 212 void c_MinkowskiSum "MinkowskiSum"(const Path& pattern, const Paths& paths, Paths& solution, bool pathIsClosed) nogil 213 void c_MinkowskiDiff "MinkowskiDiff"(const Path& poly1, const Path& poly2, Paths& solution) nogil 214 215 void c_PolyTreeToPaths "PolyTreeToPaths"(const PolyTree& polytree, Paths& paths) 216 void c_ClosedPathsFromPolyTree "ClosedPathsFromPolyTree"(const PolyTree& polytree, Paths& paths) 217 void c_OpenPathsFromPolyTree "OpenPathsFromPolyTree"(PolyTree& polytree, Paths& paths) 218 219 void c_ReversePath "ReversePath"(Path& p) nogil 220 void c_ReversePaths "ReversePaths"(Paths& p) nogil 221 222#============================= Enum mapping ================ 223 224JT_SQUARE = jtSquare 225JT_ROUND = jtRound 226JT_MITER = jtMiter 227 228ET_CLOSEDPOLYGON = etClosedPolygon 229ET_CLOSEDLINE = etClosedLine 230ET_OPENBUTT = etOpenButt 231ET_OPENSQUARE = etOpenSquare 232ET_OPENROUND = etOpenRound 233 234CT_INTERSECTION = ctIntersection 235CT_UNION = ctUnion 236CT_DIFFERENCE = ctDifference 237CT_XOR = ctXor 238 239PT_SUBJECT = ptSubject 240PT_CLIP = ptClip 241 242PFT_EVENODD = pftEvenOdd 243PFT_NONZERO = pftNonZero 244PFT_POSITIVE = pftPositive 245PFT_NEGATIVE = pftNegative 246 247#============================= PyPolyNode ================= 248class PyPolyNode: 249 """ 250 Represents ClipperLibs' PolyTree and PolyNode data structures. 251 """ 252 def __init__(self): 253 self.Contour = [] 254 self.Childs = [] 255 self.Parent = None 256 self.IsHole = False 257 self.IsOpen = False 258 self.depth = 0 259 260#============================= Other objects ============== 261from collections import namedtuple 262PyIntRect = namedtuple('PyIntRect', ['left', 'top', 'right', 'bottom']) 263 264class ClipperException(Exception): 265 pass 266 267#============================= Namespace functions ========= 268def Orientation(poly): 269 """ Get orientation of the supplied polygon. 270 More info: http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Functions/Orientation.htm 271 272 Keyword arguments: 273 poly -- closed polygon 274 275 Returns: 276 True -- counter-clockwise orientation 277 False -- clockwise orientation 278 """ 279 cdef Path c_path = _to_clipper_path(poly) 280 cdef bint result 281 with nogil: 282 result = <bint>c_Orientation(c_path) 283 return result 284 285 286def Area(poly): 287 """ Get area of the supplied polygon. 288 More info: http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Functions/Area.htm 289 290 Keyword arguments: 291 poly -- closed polygon 292 293 Returns: 294 Positive number if orientation is True 295 Negative number if orientation is False 296 """ 297 298 cdef Path c_path = _to_clipper_path(poly) 299 cdef double result 300 with nogil: 301 result = <double>c_Area(c_path) 302 return result 303 304 305def PointInPolygon(point, poly): 306 """ Determine where does the point lie regarding the provided polygon. 307 More info: http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Functions/PointInPolygon.htm 308 309 Keyword arguments: 310 point -- point in question 311 poly -- closed polygon 312 313 Returns: 314 0 -- point is not in polygon 315 -1 -- point is on polygon 316 1 -- point is in polygon 317 """ 318 319 cdef IntPoint c_point = _to_clipper_point(point) 320 cdef Path c_path = _to_clipper_path(poly) 321 cdef int result 322 with nogil: 323 result = <int>c_PointInPolygon(c_point, c_path) 324 return result 325 326 327def SimplifyPolygon(poly, PolyFillType fill_type=pftEvenOdd): 328 """ Removes self-intersections from the supplied polygon. 329 More info: http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Functions/SimplifyPolygon.htm 330 331 Keyword arguments: 332 poly -- polygon to be simplified 333 fill_type -- PolyFillType used with the boolean union operation 334 335 Returns: 336 list of simplified polygons (containing one or more polygons) 337 """ 338 cdef Paths out_polys 339 cdef Path c_path = _to_clipper_path(poly) 340 with nogil: 341 c_SimplifyPolygon(c_path, out_polys, fill_type) 342 return _from_clipper_paths(out_polys) 343 344 345def SimplifyPolygons(polys, PolyFillType fill_type=pftEvenOdd): 346 """ Removes self-intersections from the supplied polygons. 347 More info: http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Functions/SimplifyPolygons.htm 348 349 Keyword arguments: 350 polys -- polygons to be simplified 351 fill_type -- PolyFillType used with the boolean union operation 352 353 Returns: 354 list of simplified polygons 355 """ 356 cdef Paths out_polys 357 cdef Paths c_polys = _to_clipper_paths(polys) 358 with nogil: 359 c_SimplifyPolygons(c_polys, out_polys, fill_type) 360 return _from_clipper_paths(out_polys) 361 362 363def CleanPolygon(poly, double distance=1.415): 364 """ Removes unnecessary vertices from the provided polygon. 365 More info: http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Functions/CleanPolygon.htm 366 367 Keyword arguments: 368 poly -- polygon to be cleaned 369 distance -- distance on which vertices are removed, see 'More info' (default: approx. sqrt of 2) 370 371 Returns: 372 cleaned polygon 373 """ 374 cdef Path out_poly 375 cdef Path c_path = _to_clipper_path(poly) 376 with nogil: 377 c_CleanPolygon(c_path, out_poly, distance) 378 return _from_clipper_path(out_poly) 379 380 381def CleanPolygons(polys, double distance=1.415): 382 """ Removes unnecessary vertices from the provided polygons. 383 More info: http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Functions/CleanPolygons.htm 384 385 Keyword arguments: 386 polys -- polygons to be cleaned 387 distance -- distance on which vertices are removed, see 'More info' (default: approx. sqrt of 2) 388 389 Returns: 390 list of cleaned polygons 391 """ 392 cdef Paths out_polys = _to_clipper_paths(polys) 393 with nogil: 394 c_CleanPolygons(out_polys, distance) 395 return _from_clipper_paths(out_polys) 396 397 398def MinkowskiSum(pattern, path, bint path_is_closed): 399 """ Performs Minkowski Addition of the pattern and path. 400 More info: http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Functions/MinkowskiSum.htm 401 402 Keyword arguments: 403 pattern -- polygon whose points are added to the path 404 path -- open or closed path 405 path_is_closed -- set to True if passed path is closed, False if open 406 407 Returns: 408 list of polygons (containing one or more polygons) 409 """ 410 cdef Paths solution 411 cdef Path c_pat = _to_clipper_path(pattern) 412 cdef Path c_path = _to_clipper_path(path) 413 with nogil: 414 c_MinkowskiSum(c_pat, c_path, solution, path_is_closed) 415 return _from_clipper_paths(solution) 416 417 418def MinkowskiSum2(pattern, paths, bint path_is_closed): 419 """ Performs Minkowski Addition of the pattern and paths. 420 More info: http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Functions/MinkowskiSum.htm 421 422 Keyword arguments: 423 pattern -- polygon whose points are added to the paths 424 paths -- open or closed paths 425 path_is_closed -- set to True if passed paths are closed, False if open 426 427 Returns: 428 list of polygons 429 """ 430 cdef Paths solution 431 cdef Path c_pat = _to_clipper_path(pattern) 432 cdef Paths c_paths = _to_clipper_paths(paths) 433 with nogil: 434 c_MinkowskiSum(c_pat, c_paths, solution, path_is_closed) 435 return _from_clipper_paths(solution) 436 437 438def MinkowskiDiff(poly1, poly2): 439 """ Performs Minkowski Difference. 440 More info: http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Functions/MinkowskiDiff.htm 441 442 Keyword arguments: 443 poly1 -- polygon 444 poly2 -- polygon 445 446 Returns: 447 list of polygons 448 """ 449 cdef Paths solution 450 cdef Path c_path1 = _to_clipper_path(poly1) 451 cdef Path c_path2 = _to_clipper_path(poly2) 452 with nogil: 453 c_MinkowskiDiff(c_path1, c_path2, solution) 454 return _from_clipper_paths(solution) 455 456 457def PolyTreeToPaths(poly_node): 458 """ Converts a PyPolyNode to a list of paths. 459 More info: http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Functions/PolyTreeToPaths.htm 460 461 Keyword arguments: 462 py_poly_node -- PyPolyNode to be filtered 463 464 Returns: 465 list of paths 466 """ 467 paths = [] 468 _filter_polynode(poly_node, paths, filter_func=None) 469 return paths 470 471 472def ClosedPathsFromPolyTree(poly_node): 473 """ Filters out open paths from the PyPolyNode and returns only closed paths. 474 More info: http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Functions/ClosedPathsFromPolyTree.htm 475 476 Keyword arguments: 477 py_poly_node -- PyPolyNode to be filtered 478 479 Returns: 480 list of closed paths 481 """ 482 483 paths = [] 484 _filter_polynode(poly_node, paths, filter_func=lambda pn: not pn.IsOpen) 485 return paths 486 487 488def OpenPathsFromPolyTree(poly_node): 489 """ Filters out closed paths from the PyPolyNode and returns only open paths. 490 More info: http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Functions/OpenPathsFromPolyTree.htm 491 492 Keyword arguments: 493 py_poly_node -- PyPolyNode to be filtered 494 495 Returns: 496 list of open paths 497 """ 498 paths = [] 499 _filter_polynode(poly_node, paths, filter_func=lambda pn: pn.IsOpen) 500 return paths 501 502 503def ReversePath(path): 504 """ Reverses the vertex order (and hence orientation) in the specified path. 505 More info: http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Functions/ReversePath.htm 506 507 Note: Might be more effective to reverse the path outside of this package (eg. via [::-1] on a list) 508 so there is no unneeded conversions to internal structures of this package. 509 510 Keyword arguments: 511 path -- path to be reversed 512 513 Returns: 514 reversed path 515 """ 516 cdef Path c_path = _to_clipper_path(path) 517 with nogil: 518 c_ReversePath(c_path) 519 return _from_clipper_path(c_path) 520 521 522def ReversePaths(paths): 523 """ Reverses the vertex order (and hence orientation) in each path. 524 More info: http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Functions/ReversePaths.htm 525 526 Note: Might be more effective to reverse each path outside of this package (eg. via [::-1] on a list) 527 so there is no unneeded conversions to internal structures of this package. 528 529 Keyword arguments: 530 paths -- paths to be reversed 531 532 Returns: 533 list if reversed paths 534 """ 535 cdef Paths c_paths = _to_clipper_paths(paths) 536 with nogil: 537 c_ReversePaths(c_paths) 538 return _from_clipper_paths(c_paths) 539 540 541def scale_to_clipper(path_or_paths, scale = 2 ** 31): 542 """ 543 Take a path or list of paths with coordinates represented by floats and scale them using the specified factor. 544 This function can be user to convert paths to a representation which is more appropriate for Clipper. 545 546 Clipper, and thus Pyclipper, uses 64-bit integers to represent coordinates internally. The actual supported 547 range (+/- 2 ** 62) is a bit smaller than the maximal values for this type. To operate on paths which use 548 fractional coordinates, it is necessary to translate them from and to a representation which does not depend 549 on floats. This can be done using this function and it's reverse, `scale_from_clipper()`. 550 551 For details, see http://www.angusj.com/delphi/clipper/documentation/Docs/Overview/Rounding.htm. 552 553 For example, to perform a clip operation on two polygons, the arguments to `Pyclipper.AddPath()` need to be wrapped 554 in `scale_to_clipper()` while the return value needs to be converted back with `scale_from_clipper()`: 555 556 >>> pc = Pyclipper() 557 >>> path = [[0, 0], [1, 0], [1 / 2, (3 / 4) ** (1 / 2)]] # A triangle. 558 >>> clip = [[0, 1 / 3], [1, 1 / 3], [1, 2 / 3], [0, 1 / 3]] # A rectangle. 559 >>> pc.AddPath(scale_to_clipper(path), PT_SUBJECT) 560 >>> pc.AddPath(scale_to_clipper(clip), PT_CLIP) 561 >>> scale_from_clipper(pc.Execute(CT_INTERSECTION)) 562 [[[0.6772190444171429, 0.5590730146504939], [0.2383135547861457, 0.41277118446305394], 563 [0.19245008938014507, 0.3333333330228925], [0.8075499106198549, 0.3333333330228925]]] 564 565 :param path_or_paths: Either a list of paths or a path. A path is a list of tuples of numbers. 566 :param scale: The factor with which to multiply coordinates before converting rounding them to ints. The default 567 will give you a range of +/- 2 ** 31 with a precision of 2 ** -31. 568 """ 569 570 def scale_value(x): 571 if hasattr(x, "__len__"): 572 return [scale_value(i) for i in x] 573 else: 574 return <cInt>(<double>x * scale) 575 576 return scale_value(path_or_paths) 577 578 579def scale_from_clipper(path_or_paths, scale = 2 ** 31): 580 """ 581 Take a path or list of paths with coordinates represented by ints and scale them back to a fractional 582 representation. This function does the inverse of `scale_to_clipper()`. 583 584 :param path_or_paths: Either a list of paths or a path. A path is a list of tuples of numbers. 585 :param scale: The factor by which to divide coordinates when converting them to floats. 586 """ 587 588 def scale_value(x): 589 if hasattr(x, "__len__"): 590 return [scale_value(i) for i in x] 591 else: 592 return <double>x / scale 593 594 return scale_value(path_or_paths) 595 596 597cdef class Pyclipper: 598 599 """Wraps the Clipper class. 600 601 More info: http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Classes/Clipper/_Body.htm 602 """ 603 cdef Clipper *thisptr # hold a C++ instance which we're wrapping 604 def __cinit__(self): 605 """ Creates an instance of the Clipper class. InitOptions from the Clipper class 606 are substituted with separate properties. 607 608 More info: http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Classes/Clipper/Methods/Constructor.htm 609 """ 610 611 log_action("Creating a Clipper instance") 612 self.thisptr = new Clipper() 613 614 def __dealloc__(self): 615 log_action("Deleting the Clipper instance") 616 del self.thisptr 617 618 def AddPath(self, path, PolyType poly_type, closed=True): 619 """ Add individual path. 620 More info: http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Classes/ClipperBase/Methods/AddPath.htm 621 622 Keyword arguments: 623 path -- path to be added 624 poly_type -- type of the added path - subject or clip 625 closed -- True if the added path is closed, False if open 626 627 Returns: 628 True -- path is valid for clipping and was added 629 630 Raises: 631 ClipperException -- if path is invalid for clipping 632 """ 633 cdef Path c_path = _to_clipper_path(path) 634 cdef bint result = <bint> self.thisptr.AddPath(c_path, poly_type, <bint> closed) 635 if not result: 636 raise ClipperException('The path is invalid for clipping') 637 return result 638 639 def AddPaths(self, paths, PolyType poly_type, closed=True): 640 """ Add a list of paths. 641 More info: http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Classes/ClipperBase/Methods/AddPaths.htm 642 643 Keyword arguments: 644 paths -- paths to be added 645 poly_type -- type of added paths - subject or clip 646 closed -- True if added paths are closed, False if open 647 648 Returns: 649 True -- all or some paths are valid for clipping and were added 650 651 Raises: 652 ClipperException -- all paths are invalid for clipping 653 """ 654 cdef Paths c_paths = _to_clipper_paths(paths) 655 cdef bint result = <bint> self.thisptr.AddPaths(c_paths, poly_type, <bint> closed) 656 if not result: 657 raise ClipperException('All paths are invalid for clipping') 658 return result 659 660 def Clear(self): 661 """ Removes all subject and clip polygons. 662 More info: http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Classes/ClipperBase/Methods/Clear.htm 663 """ 664 self.thisptr.Clear() 665 666 def GetBounds(self): 667 """ Returns an axis-aligned bounding rectangle that bounds all added polygons. 668 More info: http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Classes/ClipperBase/Methods/GetBounds.htm 669 670 Returns: 671 PyIntRect with left, right, bottom, top vertices that define the axis-aligned bounding rectangle. 672 """ 673 _check_scaling_factor() 674 675 cdef IntRect rr 676 with nogil: 677 rr = <IntRect> self.thisptr.GetBounds() 678 return PyIntRect(left=rr.left, top=rr.top, 679 right=rr.right, bottom=rr.bottom) 680 681 def Execute(self, ClipType clip_type, 682 PolyFillType subj_fill_type=pftEvenOdd, PolyFillType clip_fill_type=pftEvenOdd): 683 """ Performs the clipping operation and returns a list of paths. 684 More info: http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Classes/Clipper/Methods/Execute.htm 685 686 Keyword arguments: 687 clip_type -- type of the clipping operation 688 subj_fill_type -- fill rule of subject paths 689 clip_fill_type -- fill rule of clip paths 690 691 Returns: 692 list of resulting paths 693 694 Raises: 695 ClipperException -- operation did not succeed 696 """ 697 698 cdef Paths solution 699 cdef bint success 700 with nogil: 701 success = <bint> self.thisptr.Execute(clip_type, solution, subj_fill_type, clip_fill_type) 702 if not success: 703 raise ClipperException('Execution of clipper did not succeed!') 704 return _from_clipper_paths(solution) 705 706 def Execute2(self, ClipType clip_type, 707 PolyFillType subj_fill_type=pftEvenOdd, PolyFillType clip_fill_type=pftEvenOdd): 708 """ Performs the clipping operation and returns a PyPolyNode. 709 More info: http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Classes/Clipper/Methods/Execute.htm 710 711 Keyword arguments: 712 clip_type -- type of the clipping operation 713 subj_fill_type -- fill rule of subject paths 714 clip_fill_type -- fill rule of clip paths 715 716 Returns: 717 PyPolyNode 718 719 Raises: 720 ClipperException -- operation did not succeed 721 """ 722 cdef PolyTree solution 723 cdef bint success 724 with nogil: 725 success = <bint> self.thisptr.Execute(clip_type, solution, subj_fill_type, clip_fill_type) 726 if not success: 727 raise ClipperException('Execution of clipper did not succeed!') 728 return _from_poly_tree(solution) 729 730 property ReverseSolution: 731 """ Should polygons returned from Execute/Execute2 have their orientations 732 opposite to their normal orientations. 733 More info: http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Classes/Clipper/Properties/ReverseSolution.htm 734 """ 735 def __get__(self): 736 return <bint> self.thisptr.ReverseSolution() 737 738 def __set__(self, value): 739 self.thisptr.ReverseSolution(<bint> value) 740 741 property PreserveCollinear: 742 """ Should clipper preserve collinear vertices. 743 More info: http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Classes/Clipper/Properties/PreserveCollinear.htm 744 """ 745 def __get__(self): 746 return <bint> self.thisptr.PreserveCollinear() 747 748 def __set__(self, value): 749 self.thisptr.PreserveCollinear(<bint> value) 750 751 property StrictlySimple: 752 """ Should polygons returned from Execute/Execute2 be strictly simple (True) or may be weakly simple (False). 753 More info: http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Classes/Clipper/Properties/StrictlySimple.htm 754 """ 755 def __get__(self): 756 return <bint> self.thisptr.StrictlySimple() 757 758 def __set__(self, value): 759 self.thisptr.StrictlySimple(<bint> value) 760 761 762cdef class PyclipperOffset: 763 """ Wraps the ClipperOffset class. 764 765 More info: http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Classes/ClipperOffset/_Body.htm 766 """ 767 cdef ClipperOffset *thisptr 768 769 def __cinit__(self, double miter_limit=2.0, double arc_tolerance=0.25): 770 """ Creates an instance of the ClipperOffset class. 771 772 More info: http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Classes/ClipperOffset/Methods/Constructor.htm 773 """ 774 log_action("Creating an ClipperOffset instance") 775 self.thisptr = new ClipperOffset(miter_limit, arc_tolerance) 776 777 def __dealloc__(self): 778 log_action("Deleting the ClipperOffset instance") 779 del self.thisptr 780 781 def AddPath(self, path, JoinType join_type, EndType end_type): 782 """ Add individual path. 783 More info: http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Classes/ClipperOffset/Methods/AddPath.htm 784 785 Keyword arguments: 786 path -- path to be added 787 join_type -- join type of added path 788 end_type -- end type of added path 789 """ 790 cdef Path c_path = _to_clipper_path(path) 791 self.thisptr.AddPath(c_path, join_type, end_type) 792 793 def AddPaths(self, paths, JoinType join_type, EndType end_type): 794 """ Add a list of paths. 795 More info: http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Classes/ClipperOffset/Methods/AddPaths.htm 796 797 Keyword arguments: 798 path -- paths to be added 799 join_type -- join type of added paths 800 end_type -- end type of added paths 801 """ 802 cdef Paths c_paths = _to_clipper_paths(paths) 803 self.thisptr.AddPaths(c_paths, join_type, end_type) 804 805 def Execute(self, double delta): 806 """ Performs the offset operation and returns a list of offset paths. 807 More info: http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Classes/ClipperOffset/Methods/Execute.htm 808 809 Keyword arguments: 810 delta -- amount to which the supplied paths will be offset - negative delta shrinks polygons, 811 positive delta expands them. 812 813 Returns: 814 list of offset paths 815 """ 816 cdef Paths c_solution 817 with nogil: 818 self.thisptr.Execute(c_solution, delta) 819 return _from_clipper_paths(c_solution) 820 821 def Execute2(self, double delta): 822 """ Performs the offset operation and returns a PyPolyNode with offset paths. 823 More info: http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Classes/ClipperOffset/Methods/Execute.htm 824 825 Keyword arguments: 826 delta -- amount to which the supplied paths will be offset - negative delta shrinks polygons, 827 positive delta expands them. 828 829 Returns: 830 PyPolyNode 831 """ 832 cdef PolyTree solution 833 with nogil: 834 self.thisptr.Execute(solution, delta) 835 return _from_poly_tree(solution) 836 837 def Clear(self): 838 """ Clears all paths. 839 840 More info: http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Classes/ClipperOffset/Methods/Clear.htm 841 """ 842 self.thisptr.Clear() 843 844 property MiterLimit: 845 """ Maximum distance in multiples of delta that vertices can be offset from their 846 original positions. 847 848 More info: http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Classes/ClipperOffset/Properties/MiterLimit.htm 849 """ 850 def __get__(self): 851 return <double> self.thisptr.MiterLimit 852 853 def __set__(self, value): 854 self.thisptr.MiterLimit = <double> value 855 856 property ArcTolerance: 857 """ Maximum acceptable imprecision when arcs are approximated in 858 an offsetting operation. 859 860 More info: http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Classes/ClipperOffset/Properties/ArcTolerance.htm 861 """ 862 def __get__(self): 863 _check_scaling_factor() 864 865 return self.thisptr.ArcTolerance 866 867 def __set__(self, value): 868 _check_scaling_factor() 869 870 self.thisptr.ArcTolerance = value 871 872 873cdef _filter_polynode(pypolynode, result, filter_func=None): 874 if (filter_func is None or filter_func(pypolynode)) and len(pypolynode.Contour) > 0: 875 result.append(pypolynode.Contour) 876 877 for child in pypolynode.Childs: 878 _filter_polynode(child, result, filter_func) 879 880 881cdef _from_poly_tree(PolyTree &c_polytree): 882 poly_tree = PyPolyNode() 883 depths = [0] 884 for i in xrange(c_polytree.ChildCount()): 885 c_child = c_polytree.Childs[i] 886 py_child = _node_walk(c_child, poly_tree) 887 poly_tree.Childs.append(py_child) 888 depths.append(py_child.depth + 1) 889 poly_tree.depth = max(depths) 890 return poly_tree 891 892 893cdef _node_walk(PolyNode *c_polynode, object parent): 894 895 py_node = PyPolyNode() 896 py_node.Parent = parent 897 898 cdef object ishole = <bint>c_polynode.IsHole() 899 py_node.IsHole = ishole 900 901 cdef object isopen = <bint>c_polynode.IsOpen() 902 py_node.IsOpen = isopen 903 904 py_node.Contour = _from_clipper_path(c_polynode.Contour) 905 906 # kids 907 cdef PolyNode *cNode 908 depths = [0] 909 for i in range(c_polynode.ChildCount()): 910 c_node = c_polynode.Childs[i] 911 py_child = _node_walk(c_node, py_node) 912 913 depths.append(py_child.depth + 1) 914 py_node.Childs.append(py_child) 915 916 py_node.depth = max(depths) 917 918 return py_node 919 920 921cdef Paths _to_clipper_paths(object polygons): 922 cdef Paths paths = Paths() 923 for poly in polygons: 924 paths.push_back(_to_clipper_path(poly)) 925 return paths 926 927 928cdef Path _to_clipper_path(object polygon): 929 _check_scaling_factor() 930 931 cdef Path path = Path() 932 cdef IntPoint p 933 for v in polygon: 934 path.push_back(_to_clipper_point(v)) 935 return path 936 937 938cdef IntPoint _to_clipper_point(object py_point): 939 return IntPoint(py_point[0], py_point[1]) 940 941 942cdef object _from_clipper_paths(Paths paths): 943 944 polys = [] 945 946 cdef Path path 947 for i in xrange(paths.size()): 948 path = paths[i] 949 polys.append(_from_clipper_path(path)) 950 951 return polys 952 953 954cdef object _from_clipper_path(Path path): 955 _check_scaling_factor() 956 957 poly = [] 958 cdef IntPoint point 959 for i in xrange(path.size()): 960 point = path[i] 961 poly.append([point.X, point.Y]) 962 return poly 963 964 965def _check_scaling_factor(): 966 """ 967 Check whether SCALING_FACTOR has been set by the code using this library and warn the user that it has been 968 deprecated and it's value is ignored. 969 """ 970 971 if SCALING_FACTOR != 1: 972 _warnings.warn('SCALING_FACTOR is deprecated and it\'s value is ignored. See https://github.com/greginvm/pyclipper/wiki/Deprecating-SCALING_FACTOR for more information.', DeprecationWarning) 973