diff --git a/README.md b/README.md index 7f7a7d1..eaa0222 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,76 @@ Online demo : http://delapouite.github.io/JsClipper/demo.html Original : http://sourceforge.net/projects/jsclipper +v6.1.3.2 (1 February 2014) +* Update: Unnecessary closures removed around ref/out calls and major + speedup and code cleanliness achieved. Intersection operation speedup + in newest Chrome is 2.3x (from 2530 ms to 1107 ms) and in newest + Firefox 1.7x (from 7186 ms to 4164 ms). + +v6.1.3.1 (21 January 2014) +* Fixed potential endless loop condition when adding open + paths to Clipper. +* Added PointInPolygon function. +* Overloaded MinkowskiSum function to accommodate multi-contour + paths. + +v6.1.2.1 (15 December 2013) +* Minor improvement to joining polygons. + +v6.1.1.1 (13 December 2013) +* Fixed a couple of bugs affecting open paths that could + raise unhandled exceptions. +* Fixed Uncaught ReferenceError: DistanceFromLineSqrd is not defined when + using CleanPolygon or CleanPolygons +* Fixed SimplifyPolygon calls in Main Demo + +v6.1.0.1 (12 December 2013) +* Added: Info and Examples page: http://jsclipper.sourceforge.net/6.1.0.1/ +* Added: Clipper 6 documentation in + https://sourceforge.net/p/jsclipper/wiki/documentation/ +* Migration guide for Clipper 5 users in + https://sourceforge.net/p/jsclipper/wiki/migration5to6/ +* Modified: To accommodate open paths, several functions have been renamed: + Polygon -> Path + Polygons -> Paths + AddPolygon -> AddPath + AddPolygons -> AddPaths + PolyTreeToPolygons -> PolyTreeToPaths + ReversePolygons -> ReversePaths +* Modified: OffsetPolygons function is replaced by ClipperOffset + class, which is much more flexible. There is also now deprecated + OffsetPaths function, which may be removed in future update. +* Update: ExPolygons has been replaced with the PolyTree & + PolyNode classes to more fully represent the parent-child + relationships of the polygons returned by Clipper. There is + for backward compatibility ClipperLib.JS.PolyTreeToExPolygons. +* Added: Open path (polyline) clipping. +* Update: Major improvement in the merging of + shared/collinear edges in clip solutions (see Execute). +* Added: The IntPoint structure now has an optional 'Z' member. + (See the precompiler directive use_xyz.) +* Added: New CleanPolygon and CleanPolygons functions. +* Added: MinkowskiSum and MinkowskiDiff functions added. +* Added: Several other new functions have been added including + PolyTreeToPaths, OpenPathsFromPolyTree and ClosedPathsFromPolyTree. +* Added: ReverseSolution, PreserveCollinear and StrictlySimple properties to Clipper class +* Added: The Clipper constructor now accepts an optional InitOptions + parameter to simplify setting properties. +* Modified: The Clipper class has a new ZFillFunction property. +* Deprecated: Version 6 is a major upgrade from previous versions + and quite a number of changes have been made to exposed structures + and functions. To minimize inconvenience to existing library users, + some code has been retained and some added to maintain backward + compatibility. However, because this code will be removed in a + future update, it has been marked as deprecated and a precompiler + directive use_deprecated has been defined. +* Changed: The behaviour of the 'miter' JoinType has been + changed so that when squaring occurs, it's no longer + extended up to the miter limit but is squared off at + exactly 'delta' units. (This improves the look of mitering + with larger limits at acute angles.) +* Bugfixes: Several minor bugs have been fixed including + occasionally an incorrect nesting within the PolyTree structure. 5.0.2.1 - 12 January 2013 * Update: Clipper library updated to version 5.0.2. The Area algorithm has been updated and is faster. diff --git a/clipper.js b/clipper.js old mode 100755 new mode 100644 index 1a426a2..3919129 --- a/clipper.js +++ b/clipper.js @@ -1,209 +1,244 @@ +// rev 452 +/******************************************************************************** + * * + * Author : Angus Johnson * + * Version : 6.1.3a * + * Date : 22 January 2014 * + * Website : http://www.angusj.com * + * Copyright : Angus Johnson 2010-2014 * + * * + * License: * + * Use, modification & distribution is subject to Boost Software License Ver 1. * + * http://www.boost.org/LICENSE_1_0.txt * + * * + * Attributions: * + * The code in this library is an extension of Bala Vatti's clipping algorithm: * + * "A generic solution to polygon clipping" * + * Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. * + * http://portal.acm.org/citation.cfm?id=129906 * + * * + * Computer graphics and geometric modeling: implementation and algorithms * + * By Max K. Agoston * + * Springer; 1 edition (January 4, 2005) * + * http://books.google.com/books?q=vatti+clipping+agoston * + * * + * See also: * + * "Polygon Offsetting by Computing Winding Numbers" * + * Paper no. DETC2005-85513 pp. 565-575 * + * ASME 2005 International Design Engineering Technical Conferences * + * and Computers and Information in Engineering Conference (IDETC/CIE2005) * + * September 24-28, 2005 , Long Beach, California, USA * + * http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf * + * * + *******************************************************************************/ /******************************************************************************* -* * -* Author : Angus Johnson * -* Version : 5.0.2 * -* Date : 30 December 2012 * -* Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2012 * -* * -* License: * -* Use, modification & distribution is subject to Boost Software License Ver 1. * -* http://www.boost.org/LICENSE_1_0.txt * -* * -* Attributions: * -* The code in this library is an extension of Bala Vatti's clipping algorithm: * -* "A generic solution to polygon clipping" * -* Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. * -* http://portal.acm.org/citation.cfm?id=129906 * -* * -* Computer graphics and geometric modeling: implementation and algorithms * -* By Max K. Agoston * -* Springer; 1 edition (January 4, 2005) * -* http://books.google.com/books?q=vatti+clipping+agoston * -* * -* See also: * -* "Polygon Offsetting by Computing Winding Numbers" * -* Paper no. DETC2005-85513 pp. 565-575 * -* ASME 2005 International Design Engineering Technical Conferences * -* and Computers and Information in Engineering Conference (IDETC/CIE2005) * -* September 24–28, 2005 , Long Beach, California, USA * -* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf * -* * -*******************************************************************************/ - + * * + * Author : Timo * + * Version : 6.1.3.2 * + * Date : 1 February 2014 * + * * + * This is a translation of the C# Clipper library to Javascript. * + * Int128 struct of C# is implemented using JSBN of Tom Wu. * + * Because Javascript lacks support for 64-bit integers, the space * + * is a little more restricted than in C# version. * + * * + * C# version has support for coordinate space: * + * +-4611686018427387903 ( sqrt(2^127 -1)/2 ) * + * while Javascript version has support for space: * + * +-4503599627370495 ( sqrt(2^106 -1)/2 ) * + * * + * Tom Wu's JSBN proved to be the fastest big integer library: * + * http://jsperf.com/big-integer-library-test * + * * + * This class can be made simpler when (if ever) 64-bit integer support comes. * + * * + *******************************************************************************/ /******************************************************************************* -* * -* Author : Timo * -* Version : 5.0.2.1 * -* Date : 12 January 2013 * -* * -* This is a translation of the C# Clipper library to Javascript. * -* Int128 struct of C# is implemented using JSBN of Tom Wu. * -* Because Javascript lacks support for 64-bit integers, the space * -* is a little more restricted than in C# version. * -* * -* C# version has support for coordinate space: * -* +-4611686018427387903 ( sqrt(2^127 -1)/2 ) * -* while Javascript version has support for space: * -* +-4503599627370495 ( sqrt(2^106 -1)/2 ) * -* * -* Tom Wu's JSBN proved to be the fastest big integer library: * -* http://jsperf.com/big-integer-library-test * -* * -* This class can be made simpler when (if ever) 64-bit integer support comes. * -* * -*******************************************************************************/ - -/******************************************************************************* -* * -* Basic JavaScript BN library - subset useful for RSA encryption. * -* http://www-cs-students.stanford.edu/~tjw/jsbn/ * -* Copyright (c) 2005 Tom Wu * -* All Rights Reserved. * -* See "LICENSE" for details: * -* http://www-cs-students.stanford.edu/~tjw/jsbn/LICENSE * -* * -*******************************************************************************/ - -(function(Q){function j(a,b){d.biginteger_used=1;null!=a&&("number"==typeof a?this.fromString(Math.floor(a).toString(),10):null==b&&"string"!=typeof a?this.fromString(a,256):(-1!=a.indexOf(".")&&(a=a.substring(0,a.indexOf("."))),this.fromString(a,b)))}function n(){return new j(null)}function R(a,b,c,e,d,f){for(;0<=--f;){var h=b*this[a++]+c[e]+d;d=Math.floor(h/67108864);c[e++]=h&67108863}return d}function S(a,b,c,e,d,f){var h=b&32767;for(b>>=15;0<=--f;){var k=this[a]&32767,j=this[a++]>>15,l=b*k+j* -h,k=h*k+((l&32767)<<15)+c[e]+(d&1073741823);d=(k>>>30)+(l>>>15)+b*j+(d>>>30);c[e++]=k&1073741823}return d}function T(a,b,c,e,d,f){var h=b&16383;for(b>>=14;0<=--f;){var k=this[a]&16383,j=this[a++]>>14,l=b*k+j*h,k=h*k+((l&16383)<<14)+c[e]+d;d=(k>>28)+(l>>14)+b*j;c[e++]=k&268435455}return d}function L(a,b){var c=F[a.charCodeAt(b)];return null==c?-1:c}function A(a){var b=n();b.fromInt(a);return b}function G(a){var b=1,c;if(0!=(c=a>>>16))a=c,b+=16;if(0!=(c=a>>8))a=c,b+=8;if(0!=(c=a>>4))a=c,b+=4;if(0!= -(c=a>>2))a=c,b+=2;0!=a>>1&&(b+=1);return b}function B(a){this.m=a}function C(a){this.m=a;this.mp=a.invDigit();this.mpl=this.mp&32767;this.mph=this.mp>>15;this.um=(1<=v;++v)F[t++]=v;t=97;for(v=10;36>v;++v)F[t++]=v;t=65;for(v=10;36>v;++v)F[t++]=v;B.prototype.convert=function(a){return 0>a.s||0<=a.compareTo(this.m)?a.mod(this.m):a};B.prototype.revert=function(a){return a};B.prototype.reduce=function(a){a.divRemTo(this.m, -null,a)};B.prototype.mulTo=function(a,b,c){a.multiplyTo(b,c);this.reduce(c)};B.prototype.sqrTo=function(a,b){a.squareTo(b);this.reduce(b)};C.prototype.convert=function(a){var b=n();a.abs().dlShiftTo(this.m.t,b);b.divRemTo(this.m,null,b);0>a.s&&0>15)*this.mpl&this.um)<<15)&a.DM,c=b+this.m.t;for(a[c]+=this.m.am(0,e,a,b,0,this.m.t);a[c]>=a.DV;)a[c]-=a.DV,a[++c]++}a.clamp();a.drShiftTo(this.m.t,a);0<=a.compareTo(this.m)&&a.subTo(this.m,a)};C.prototype.mulTo=function(a,b,c){a.multiplyTo(b,c);this.reduce(c)};C.prototype.sqrTo=function(a,b){a.squareTo(b);this.reduce(b)};j.prototype.copyTo=function(a){for(var b=this.t-1;0<=b;--b)a[b]=this[b];a.t=this.t;a.s=this.s};j.prototype.fromInt=function(a){this.t=1;this.s=0>a?-1:0;0a? -this[0]=a+DV:this.t=0};j.prototype.fromString=function(a,b){var c;if(16==b)c=4;else if(8==b)c=3;else if(256==b)c=8;else if(2==b)c=1;else if(32==b)c=5;else if(4==b)c=2;else{this.fromRadix(a,b);return}this.s=this.t=0;for(var e=a.length,d=!1,f=0;0<=--e;){var h=8==c?a[e]&255:L(a,e);0>h?"-"==a.charAt(e)&&(d=!0):(d=!1,0==f?this[this.t++]=h:f+c>this.DB?(this[this.t-1]|=(h&(1<>this.DB-f):this[this.t-1]|=h<=this.DB&&(f-=this.DB))}8==c&&0!=(a[0]&128)&&(this.s=-1, -0>e|h,h=(this[k]&d)<=this.t)b.t=0;else{var e=a%this.DB,d=this.DB-e,f=(1<>e;for(var h=c+1;h>e;0>=this.DB;if(a.t>=this.DB;e+=this.s}else{for(e+=this.s;c>=this.DB;e-=a.s}b.s=0>e?-1:0;-1>e?b[c++]=this.DV+e:0=b.DV)a[c+b.t]-=b.DV,a[c+b.t+1]=1}0=e.t)){var d=this.abs();if(d.t>this.F2:0),l=this.FV/m,m=(1<h&&j.ZERO.subTo(c,c)}}}};j.prototype.invDigit=function(){if(1>this.t)return 0;var a=this[0];if(0==(a&1))return 0;var b=a&3,b=b*(2-(a&15)*b)&15,b=b*(2-(a&255)*b)&255,b=b*(2-((a&65535)*b&65535))&65535,b=b*(2-a*b%this.DV)%this.DV;return 0a)return j.ONE;var c=n(),e=n(),d=b.convert(this),f=G(a)-1;for(d.copyTo(c);0<=--f;)if(b.sqrTo(c,e),0<(a&1<this.s)return"-"+this.negate().toString(a);if(16==a)a=4;else if(8==a)a=3;else if(2==a)a=1;else if(32==a)a=5;else if(4==a)a=2;else return this.toRadix(a);var b=(1<>h))e=!0,d="0123456789abcdefghijklmnopqrstuvwxyz".charAt(c);for(;0<=f;)h>(h+=this.DB-a)):(c=this[f]>>(h-=a)&b,0>=h&&(h+=this.DB,--f)),0this.s?this.negate():this};j.prototype.compareTo=function(a){var b=this.s-a.s;if(0!=b)return b;var c=this.t,b=c-a.t;if(0!=b)return 0>this.s?-b:b;for(;0<=--c;)if(0!=(b=this[c]-a[c]))return b;return 0};j.prototype.bitLength=function(){return 0>=this.t?0:this.DB*(this.t-1)+G(this[this.t-1]^this.s&this.DM)};j.prototype.mod= -function(a){var b=n();this.abs().divRemTo(a,null,b);0>this.s&&0a||b.isEven()?new B(b):new C(b);return this.exp(a,c)};j.ZERO=A(0);j.ONE=A(1);E.prototype.convert=O;E.prototype.revert=O;E.prototype.mulTo=function(a,b,c){a.multiplyTo(b,c)};E.prototype.sqrTo=function(a,b){a.squareTo(b)};D.prototype.convert=function(a){if(0>a.s||a.t>2*this.m.t)return a.mod(this.m);if(0>a.compareTo(this.m))return a;var b=n();a.copyTo(b); -this.reduce(b);return b};D.prototype.revert=function(a){return a};D.prototype.reduce=function(a){a.drShiftTo(this.m.t-1,this.r2);a.t>this.m.t+1&&(a.t=this.m.t+1,a.clamp());this.mu.multiplyUpperTo(this.r2,this.m.t+1,this.q3);for(this.m.multiplyLowerTo(this.q3,this.m.t+1,this.r2);0>a.compareTo(this.r2);)a.dAddOffset(1,this.m.t+1);for(a.subTo(this.r2,a);0<=a.compareTo(this.m);)a.subTo(this.m,a)};D.prototype.mulTo=function(a,b,c){a.multiplyTo(b,c);this.reduce(c)};D.prototype.sqrTo=function(a,b){a.squareTo(b); -this.reduce(b)};var r=[2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,541,547,557,563,569,571,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691,701,709, -719,727,733,739,743,751,757,761,769,773,787,797,809,811,821,823,827,829,839,853,857,859,863,877,881,883,887,907,911,919,929,937,941,947,953,967,971,977,983,991,997],V=67108864/r[r.length-1];j.prototype.chunkSize=function(a){return Math.floor(Math.LN2*this.DB/Math.log(a))};j.prototype.toRadix=function(a){null==a&&(a=10);if(0==this.signum()||2>a||36m?"-"==a.charAt(k)&&0==this.signum()&&(d=!0):(h=b*h+m,++f>=c&&(this.dMultiply(e),this.dAddOffset(h,0),h=f=0))}0a)this.fromInt(1);else{this.fromNumber(a, -c);this.testBit(a-1)||this.bitwiseTo(j.ONE.shiftLeft(a-1),K,this);for(this.isEven()&&this.dAddOffset(1,0);!this.isProbablePrime(b);)this.dAddOffset(2,0),this.bitLength()>a&&this.subTo(j.ONE.shiftLeft(a-1),this)}else{c=[];var e=a&7;c.length=(a>>3)+1;b.nextBytes(c);c[0]=0>=this.DB;if(a.t>=this.DB;e+=this.s}else{for(e+=this.s;c>=this.DB;e+=a.s}b.s=0>e?-1:0;0e&&(b[c++]=this.DV+ -e);b.t=c;b.clamp()};j.prototype.dMultiply=function(a){this[this.t]=this.am(0,a-1,this,0,0,this.t);++this.t;this.clamp()};j.prototype.dAddOffset=function(a,b){if(0!=a){for(;this.t<=b;)this[this.t++]=0;for(this[b]+=a;this[b]>=this.DV;)this[b]-=this.DV,++b>=this.t&&(this[this.t++]=0),++this[b]}};j.prototype.multiplyLowerTo=function(a,b,c){var e=Math.min(this.t+a.t,b);c.s=0;for(c.t=e;0=a)return 0;var b=this.DV%a,c=0>this.s?a-1:0;if(0= -c)return!1;var e=b.shiftRight(c);a=a+1>>1;a>r.length&&(a=r.length);for(var d=n(),f=0;fthis.s){if(1==this.t)return this[0]-this.DV;if(0==this.t)return-1}else{if(1== -this.t)return this[0];if(0==this.t)return 0}return(this[1]&(1<<32-this.DB)-1)<>24};j.prototype.shortValue=function(){return 0==this.t?this.s:this[0]<<16>>16};j.prototype.signum=function(){return 0>this.s?-1:0>=this.t||1==this.t&&0>=this[0]?0:1};j.prototype.toByteArray=function(){var a=this.t,b=[];b[0]=this.s;var c=this.DB-a*this.DB%8,e,d=0;if(0>c)!=(this.s&this.DM)>>c)b[d++]=e|this.s<< -this.DB-c;for(;0<=a;)if(8>c?(e=(this[a]&(1<>(c+=this.DB-8)):(e=this[a]>>(c-=8)&255,0>=c&&(c+=this.DB,--a)),0!=(e&128)&&(e|=-256),0==d&&(this.s&128)!=(e&128)&&++d,0this.compareTo(a)?this:a};j.prototype.max=function(a){return 0a?this.rShiftTo(-a,b):this.lShiftTo(a,b);return b};j.prototype.shiftRight=function(a){var b=n();0>a?this.lShiftTo(-a,b):this.rShiftTo(a,b);return b};j.prototype.getLowestSetBit= -function(){for(var a=0;a>=16,c+=16);0==(a&255)&&(a>>=8,c+=8);0==(a&15)&&(a>>=4,c+=4);0==(a&3)&&(a>>=2,c+=2);0==(a&1)&&++c;a=c}return b+a}return 0>this.s?this.t*this.DB:-1};j.prototype.bitCount=function(){for(var a=0,b=this.s&this.DM,c=0;c=this.t?0!=this.s:0!=(this[b]& -1<=c)return g;d=18>c?1:48>c?3:144>c?4:768>c?5:6;f=8>c?new B(b):b.isEven()?new D(b):new C(b);var h=[],k=3,j=d-1,l=(1<=j?q=a[w]>>c-j&l:(q=(a[w]&(1<>this.DB+c-j));for(k=d;0==(q&1);)q>>=1,--k;if(0>(c-=k))c+=this.DB,--w;if(p)h[q].copyTo(g),p=!1;else{for(;1--c&&(c=this.DB-1,--w)}return f.revert(g)};j.prototype.modInverse=function(a){var b=a.isEven();if(this.isEven()&&b||0==a.signum())return j.ZERO;for(var c=a.clone(),d=this.clone(),g=A(1),f=A(0),h=A(0),k=A(1);0!=c.signum();){for(;c.isEven();){c.rShiftTo(1, -c);if(b){if(!g.isEven()||!f.isEven())g.addTo(this,g),f.subTo(a,f);g.rShiftTo(1,g)}else f.isEven()||f.subTo(a,f);f.rShiftTo(1,f)}for(;d.isEven();){d.rShiftTo(1,d);if(b){if(!h.isEven()||!k.isEven())h.addTo(this,h),k.subTo(a,k);h.rShiftTo(1,h)}else k.isEven()||k.subTo(a,k);k.rShiftTo(1,k)}0<=c.compareTo(d)?(c.subTo(d,c),b&&g.subTo(h,g),f.subTo(k,f)):(d.subTo(c,d),b&&h.subTo(g,h),k.subTo(f,k))}if(0!=d.compareTo(j.ONE))return j.ZERO;if(0<=k.compareTo(a))return k.subtract(a);if(0>k.signum())k.addTo(a,k); -else return k;return 0>k.signum()?k.add(a):k};j.prototype.pow=function(a){return this.exp(a,new E)};j.prototype.gcd=function(a){var b=0>this.s?this.negate():this.clone();a=0>a.s?a.negate():a.clone();if(0>b.compareTo(a)){var c=b,b=a;a=c}var c=b.getLowestSetBit(),d=a.getLowestSetBit();if(0>d)return b;ca||2147483647a?Math.ceil(a):Math.floor(a):~~a}:H&&"function"==typeof Number.toInteger?function(a){return Number.toInteger(a)}:P||J?function(a){return parseInt(a,10)}:p?function(a){return-2147483648>a||2147483647a?Math.ceil(a):Math.floor(a):a|0}:function(a){return 0>a?Math.ceil(a):Math.floor(a)};d.Clear=function(a){a.length= -0};d.MaxSteps=64;d.PI=3.141592653589793;d.PI2=6.283185307179586;d.IntPoint=function(){var a=arguments;1==a.length&&(this.X=a[0].X,this.Y=a[0].Y);2==a.length&&(this.X=a[0],this.Y=a[1])};d.IntRect=function(){var a=arguments;if(4==a.length){var b=a[1],c=a[2],d=a[3];this.left=a[0];this.top=b;this.right=c;this.bottom=d}else this.bottom=this.right=this.top=this.left=0};d.Polygon=function(){return[]};d.Polygons=function(){return[[]]};d.ExPolygon=function(){this.holes=this.outer=null};d.ClipType={ctIntersection:0, -ctUnion:1,ctDifference:2,ctXor:3};d.PolyType={ptSubject:0,ptClip:1};d.PolyFillType={pftEvenOdd:0,pftNonZero:1,pftPositive:2,pftNegative:3};d.JoinType={jtSquare:0,jtRound:1,jtMiter:2};d.EdgeSide={esLeft:1,esRight:2};d.Protects={ipNone:0,ipLeft:1,ipRight:2,ipBoth:3};d.Direction={dRightToLeft:0,dLeftToRight:1};d.TEdge=function(){this.tmpX=this.deltaY=this.deltaX=this.dx=this.ytop=this.xtop=this.ycurr=this.xcurr=this.ybot=this.xbot=0;this.polyType=d.PolyType.ptSubject;this.side=null;this.outIdx=this.windCnt2= -this.windCnt=this.windDelta=0;this.prevInSEL=this.nextInSEL=this.prevInAEL=this.nextInAEL=this.nextInLML=this.prev=this.next=null};d.IntersectNode=function(){this.next=this.pt=this.edge2=this.edge1=null};d.LocalMinima=function(){this.Y=0;this.next=this.rightBound=this.leftBound=null};d.Scanbeam=function(){this.Y=0;this.next=null};d.OutRec=function(){this.idx=0;this.isHole=!1;this.bottomPt=this.pts=this.AppendLink=this.FirstLeft=null};d.OutPt=function(){this.idx=0;this.prev=this.next=this.pt=null}; -d.JoinRec=function(){this.pt1b=this.pt1a=null;this.poly1Idx=0;this.pt2b=this.pt2a=null;this.poly2Idx=0};d.HorzJoinRec=function(){this.edge=null;this.savedIdx=0};d.ClipperBase=function(){this.m_CurrentLM=this.m_MinimaList=null;this.m_edges=[[]];this.m_UseFullRange=!1};d.ClipperBase.horizontal=-9007199254740992;d.ClipperBase.loRange=47453132;d.ClipperBase.hiRange=0xfffffffffffff;d.ClipperBase.PointsEqual=function(a,b){return a.X==b.X&&a.Y==b.Y};d.ClipperBase.prototype.PointIsVertex=function(a,b){var c= -b;do{if(d.ClipperBase.PointsEqual(c.pt,a))return!0;c=c.next}while(c!=b);return!1};d.ClipperBase.prototype.PointInPolygon=function(a,b,c){var d=b,g=!1;if(c){do{if((d.pt.Y<=a.Y&&a.Y(new j(a.X-d.pt.X)).compareTo((new j(d.prev.pt.X-d.pt.X)).multiply(new j(a.Y-d.pt.Y)).divide(new j(d.prev.pt.Y-d.pt.Y))))g=!g;d=d.next}while(d!=b)}else{do{if((d.pt.Y<=a.Y&&a.Ye)return!1;var g=new d.Polygon;g.push(new d.IntPoint(a[0].X,a[0].Y));var f=0,h,k=!1;for(h=1;hj||d.Math_Abs_Int64(a[h].Y)>j){if(d.Math_Abs_Int64(a[h].X)>d.ClipperBase.hiRange||d.Math_Abs_Int64(a[h].Y)>d.ClipperBase.hiRange){if("undefined"!=typeof c)return"exceed";k=!0;break}this.m_UseFullRange=!0}d.ClipperBase.PointsEqual(g[f],a[h])||(0f)return!1;for(e=f+1;2e)return!1;a=[];for(h=0;h=a.next.ycurr?(a.xbot=a.xcurr,a.ybot=a.ycurr,a.xtop=a.next.xcurr,a.ytop=a.next.ycurr,a.windDelta=1):(a.xtop=a.xcurr,a.ytop=a.ycurr,a.xbot=a.next.xcurr,a.ybot=a.next.ycurr,a.windDelta=-1);this.SetDx(a);a.polyType=g;a.outIdx=-1};d.ClipperBase.prototype.SetDx=function(a){a.deltaX=a.xtop-a.xbot;a.deltaY=a.ytop-a.ybot;a.dx=0==a.deltaY?d.ClipperBase.horizontal:a.deltaX/a.deltaY}; -d.ClipperBase.prototype.AddBoundsToLML=function(a){a.nextInLML=null;for(a=a.next;;){if(a.dx==d.ClipperBase.horizontal){if(a.next.ytopa.prev.xbot)break;a.xtop!=a.prev.xbot&&this.SwapX(a);a.nextInLML=a.prev}else if(a.ycurr==a.prev.ycurr)break;else a.nextInLML=a.prev;a=a.next}var b=new d.LocalMinima;b.next=null;b.Y=a.prev.ybot;a.dx==d.ClipperBase.horizontal?(a.xbot!=a.prev.xbot&&this.SwapX(a),b.leftBound=a.prev,b.rightBound=a):a.dx=this.m_MinimaList.Y)a.next=this.m_MinimaList,this.m_MinimaList=a;else{for(var b=this.m_MinimaList;null!= -b.next&&a.Ya.bottom&&(a.bottom=b.leftBound.ybot);for(var c=b.leftBound;;){for(var e=c;null!=c.nextInLML;)c.xbota.right&&(a.right=c.xbot),c=c.nextInLML;c.xbota.right&&(a.right=c.xbot);c.xtopa.right&&(a.right=c.xtop);c.ytopthis.m_Scanbeam.Y)b=new d.Scanbeam,b.Y=a,b.next=this.m_Scanbeam,this.m_Scanbeam=b;else{for(var c=this.m_Scanbeam;null!=c.next&&a<=c.next.Y;)c=c.next;a!=c.Y&&(b=new d.Scanbeam,b.Y=a,b.next=c.next,c.next=b)}};d.Clipper.prototype.Execute=function(a,b,c,e){var g;2==arguments.length&&(e=c=d.PolyFillType.pftEvenOdd);if(b.hasOwnProperty("outer")){if(this.m_ExecuteLocked)return!1;this.m_ExecuteLocked= -!0;d.Clear(b);this.m_SubjFillType=c;this.m_ClipFillType=e;this.m_ClipType=a;this.m_UsingExPolygons=!0;(g=this.ExecuteInternal())&&this.BuildResultEx(b)}else{if(this.m_ExecuteLocked)return!1;this.m_ExecuteLocked=!0;d.Clear(b);this.m_SubjFillType=c;this.m_ClipFillType=e;this.m_ClipType=a;this.m_UsingExPolygons=!1;(g=this.ExecuteInternal())&&this.BuildResult(b)}this.m_ExecuteLocked=!1;return g};d.Clipper.prototype.PolySort=function(a,b){if(a==b)return 0;if(null==a.pts||null==b.pts)return null==a.pts!= -(null==b.pts)?null==a.pts?1:-1:0;var c=(a.isHole?a.FirstLeft.idx:a.idx)-(b.isHole?b.FirstLeft.idx:b.idx);return 0==c&&a.isHole!=b.isHole?a.isHole?1:-1:c};d.Clipper.prototype.FindAppendLinkEnd=function(a){for(;null!=a.AppendLink;)a=a.AppendLink;return a};d.Clipper.prototype.FixHoleLinkage=function(a){var b;b=null!=a.bottomPt?this.m_PolyOuts[a.bottomPt.idx].FirstLeft:a.FirstLeft;a==b&&d.Error("HoleLinkage error");null!=b&&(null!=b.AppendLink&&(b=this.FindAppendLinkEnd(b)),b==a?b=null:b.isHole&&(this.FixHoleLinkage(b), -b=b.FirstLeft));a.FirstLeft=b;null==b&&(a.isHole=!1);a.AppendLink=null};d.Clipper.prototype.ExecuteInternal=function(){var a;try{this.Reset();if(null==this.m_CurrentLM)return!0;var b=this.PopScanbeam();do{this.InsertLocalMinimaIntoAEL(b);d.Clear(this.m_HorizJoins);this.ProcessHorizontals();var c=this.PopScanbeam();a=this.ProcessIntersections(b,c);if(!a)break;this.ProcessEdgesAtTopOfScanbeam(c);b=c}while(null!=this.m_Scanbeam)}catch(e){a=!1}if(a){for(c=0;ca.dx:b.xcurra.windCnt2}case d.ClipType.ctUnion:switch(c){case d.PolyFillType.pftEvenOdd:case d.PolyFillType.pftNonZero:return 0==a.windCnt2;case d.PolyFillType.pftPositive:return 0>=a.windCnt2;default:return 0<=a.windCnt2}case d.ClipType.ctDifference:if(a.polyType==d.PolyType.ptSubject)switch(c){case d.PolyFillType.pftEvenOdd:case d.PolyFillType.pftNonZero:return 0==a.windCnt2;case d.PolyFillType.pftPositive:return 0>=a.windCnt2; -default:return 0<=a.windCnt2}else switch(c){case d.PolyFillType.pftEvenOdd:case d.PolyFillType.pftNonZero:return 0!=a.windCnt2;case d.PolyFillType.pftPositive:return 0a.windCnt2}}return!0};d.Clipper.prototype.SetWindingCount=function(a){for(var b=a.prevInAEL;null!=b&&b.polyType!=a.polyType;)b=b.prevInAEL;null==b?(a.windCnt=a.windDelta,a.windCnt2=0,b=this.m_ActiveEdges):(a.windCnt=this.IsEvenOddFillType(a)?1:0>b.windCnt*b.windDelta?1b.windDelta* -a.windDelta?b.windCnt:b.windCnt+a.windDelta:b.windCnt+b.windDelta+a.windDelta:1b.windDelta*a.windDelta?b.windCnt:0==b.windCnt+a.windDelta?b.windCnt:b.windCnt+a.windDelta,a.windCnt2=b.windCnt2,b=b.nextInAEL);if(this.IsEvenOddAltFillType(a))for(;b!=a;)a.windCnt2=0==a.windCnt2?1:0,b=b.nextInAEL;else for(;b!=a;)a.windCnt2+=b.windDelta,b=b.nextInAEL};d.Clipper.prototype.AddEdgeToSEL=function(a){null==this.m_SortedEdges?(this.m_SortedEdges=a,a.prevInSEL=null,a.nextInSEL= -null):(a.nextInSEL=this.m_SortedEdges,a.prevInSEL=null,this.m_SortedEdges=this.m_SortedEdges.prevInSEL=a)};d.Clipper.prototype.CopyAELToSEL=function(){var a=this.m_ActiveEdges;this.m_SortedEdges=a;if(null!=this.m_ActiveEdges){this.m_SortedEdges.prevInSEL=null;for(a=a.nextInAEL;null!=a;)a.prevInSEL=a.prevInAEL,a.prevInSEL.nextInSEL=a,a.nextInSEL=null,a=a.nextInAEL}};d.Clipper.prototype.SwapPositionsInAEL=function(a,b){var c,d;!(null==a.nextInAEL&&null==a.prevInAEL)&&!(null==b.nextInAEL&&null==b.prevInAEL)&& -(a.nextInAEL==b?(c=b.nextInAEL,null!=c&&(c.prevInAEL=a),d=a.prevInAEL,null!=d&&(d.nextInAEL=b),b.prevInAEL=d,b.nextInAEL=a,a.prevInAEL=b,a.nextInAEL=c):b.nextInAEL==a?(c=a.nextInAEL,null!=c&&(c.prevInAEL=b),d=b.prevInAEL,null!=d&&(d.nextInAEL=a),a.prevInAEL=d,a.nextInAEL=b,b.prevInAEL=a,b.nextInAEL=c):(c=a.nextInAEL,d=a.prevInAEL,a.nextInAEL=b.nextInAEL,null!=a.nextInAEL&&(a.nextInAEL.prevInAEL=a),a.prevInAEL=b.prevInAEL,null!=a.prevInAEL&&(a.prevInAEL.nextInAEL=a),b.nextInAEL=c,null!=b.nextInAEL&& -(b.nextInAEL.prevInAEL=b),b.prevInAEL=d,null!=b.prevInAEL&&(b.prevInAEL.nextInAEL=b)),null==a.prevInAEL?this.m_ActiveEdges=a:null==b.prevInAEL&&(this.m_ActiveEdges=b))};d.Clipper.prototype.SwapPositionsInSEL=function(a,b){var c,d;!(null==a.nextInSEL&&null==a.prevInSEL)&&!(null==b.nextInSEL&&null==b.prevInSEL)&&(a.nextInSEL==b?(c=b.nextInSEL,null!=c&&(c.prevInSEL=a),d=a.prevInSEL,null!=d&&(d.nextInSEL=b),b.prevInSEL=d,b.nextInSEL=a,a.prevInSEL=b,a.nextInSEL=c):b.nextInSEL==a?(c=a.nextInSEL,null!=c&& -(c.prevInSEL=b),d=b.prevInSEL,null!=d&&(d.nextInSEL=a),a.prevInSEL=d,a.nextInSEL=b,b.prevInSEL=a,b.nextInSEL=c):(c=a.nextInSEL,d=a.prevInSEL,a.nextInSEL=b.nextInSEL,null!=a.nextInSEL&&(a.nextInSEL.prevInSEL=a),a.prevInSEL=b.prevInSEL,null!=a.prevInSEL&&(a.prevInSEL.nextInSEL=a),b.nextInSEL=c,null!=b.nextInSEL&&(b.nextInSEL.prevInSEL=b),b.prevInSEL=d,null!=b.prevInSEL&&(b.prevInSEL.nextInSEL=b)),null==a.prevInSEL?this.m_SortedEdges=a:null==b.prevInSEL&&(this.m_SortedEdges=b))};d.Clipper.prototype.AddLocalMaxPoly= -function(a,b,c){this.AddOutPt(a,c);a.outIdx==b.outIdx?(a.outIdx=-1,b.outIdx=-1):a.outIdxb.dx?(this.AddOutPt(a,c),b.outIdx=a.outIdx,a.side=d.EdgeSide.esLeft,b.side=d.EdgeSide.esRight,e=a,a=e.prevInAEL==b?b.prevInAEL:e.prevInAEL):(this.AddOutPt(b,c),a.outIdx=b.outIdx,a.side=d.EdgeSide.esRight,b.side=d.EdgeSide.esLeft,e=b,a=e.prevInAEL==a?a.prevInAEL: -e.prevInAEL);null!=a&&(0<=a.outIdx&&d.Clipper.TopX(a,c.Y)==d.Clipper.TopX(e,c.Y)&&this.SlopesEqual(e,a,this.m_UseFullRange))&&this.AddJoin(e,a,-1,-1)};d.Clipper.prototype.CreateOutRec=function(){var a=new d.OutRec;a.idx=-1;a.isHole=!1;a.FirstLeft=null;a.AppendLink=null;a.pts=null;a.bottomPt=null;return a};d.Clipper.prototype.AddOutPt=function(a,b){var c,e,g=a.side==d.EdgeSide.esLeft;if(0>a.outIdx)c=this.CreateOutRec(),this.m_PolyOuts.push(c),c.idx=this.m_PolyOuts.length-1,a.outIdx=c.idx,e=new d.OutPt, -c.pts=e,c.bottomPt=e,e.pt=b,e.idx=c.idx,e.next=e,e.prev=e,this.SetHoleState(a,c);else{c=this.m_PolyOuts[a.outIdx];e=c.pts;var f;g&&d.ClipperBase.PointsEqual(b,e.pt)||!g&&d.ClipperBase.PointsEqual(b,e.prev.pt)||(f=new d.OutPt,f.pt=b,f.idx=c.idx,f.pt.Y==c.bottomPt.pt.Y&&f.pt.Xd.Math_Abs_Int64(a.Y-b.Y))return a.X>b.X&&(a={Value:a},b={Value:b},this.SwapPoints(a,b),a=a.Value,b=b.Value),c.X>e.X&&(c={Value:c},e={Value:e},this.SwapPoints(c,e),c=c.Value,e=e.Value),g.Value=a.X>c.X?a:c,f.Value=b.Xe.Y?b:e;return g.Value.Y>f.Value.Y}; -d.Clipper.prototype.FindSegment=function(a,b,c){if(null==a.Value)return!1;var e=a.Value,g=new d.IntPoint(b.Value),f=new d.IntPoint(c.Value);do{if(this.SlopesEqual(g,f,a.Value.pt,a.Value.prev.pt,!0)&&this.SlopesEqual(g,f,a.Value.pt,!0)&&this.GetOverlapSegment(g,f,a.Value.pt,a.Value.prev.pt,b,c))return!0;a.Value=a.Value.next}while(a.Value!=e);return!1};d.Clipper.prototype.Pt3IsBetweenPt1AndPt2=function(a,b,c){return d.ClipperBase.PointsEqual(a,c)||d.ClipperBase.PointsEqual(b,c)?!0:a.X!=b.X?a.X=f&&e>=c||g>=f&&g>=c};d.Clipper.prototype.GetBottomPt= -function(a){for(var b=null,c=a.next;c!=a;)c.pt.Y>a.pt.Y?(a=c,b=null):c.pt.Y==a.pt.Y&&c.pt.X<=a.pt.X&&(c.pt.Xd.pt.Y?a:c.pt.Yd.pt.X?b:c.next==c?b:d.next==d?a:this.FirstIsBottomPt(c,d)?a:b};d.Clipper.prototype.Param1RightOfParam2= -function(a,b){do if(a=a.FirstLeft,a==b)return!0;while(null!=a);return!1};d.Clipper.prototype.AppendPolygon=function(a,b){var c=this.m_PolyOuts[a.outIdx],e=this.m_PolyOuts[b.outIdx],g;g=this.Param1RightOfParam2(c,e)?e:this.Param1RightOfParam2(e,c)?c:this.GetLowermostRec(c,e);var f=c.pts,h=f.prev,k=e.pts,j=k.prev;a.side==d.EdgeSide.esLeft?(b.side==d.EdgeSide.esLeft?(this.ReversePolyPtLinks(k),k.next=f,f.prev=k,h.next=j,j.prev=h,c.pts=j):(j.next=f,f.prev=j,k.prev=h,h.next=k,c.pts=k),f=d.EdgeSide.esLeft): -(b.side==d.EdgeSide.esRight?(this.ReversePolyPtLinks(k),h.next=j,j.prev=h,k.next=f,f.prev=k):(h.next=k,k.prev=h,f.prev=j,j.next=f),f=d.EdgeSide.esRight);g==e&&(c.bottomPt=e.bottomPt,c.bottomPt.idx=c.idx,e.FirstLeft!=c&&(c.FirstLeft=e.FirstLeft),c.isHole=e.isHole);e.pts=null;e.bottomPt=null;e.AppendLink=c;c=a.outIdx;e=b.outIdx;a.outIdx=-1;b.outIdx=-1;for(g=this.m_ActiveEdges;null!=g;){if(g.outIdx==e){g.outIdx=c;g.side=f;break}g=g.nextInAEL}for(g=0;g=f&&0>=h&&this.AddLocalMinPoly(a,b,c);break; -case d.ClipType.ctDifference:(a.polyType==d.PolyType.ptClip&&0=f&&0>=h)&&this.AddLocalMinPoly(a,b,c);break;case d.ClipType.ctXor:this.AddLocalMinPoly(a,b,c)}else d.Clipper.SwapSides(a,b)}if(g!=e&&(g&&0<=a.outIdx||e&&0<=b.outIdx))d.Clipper.SwapSides(a,b),d.Clipper.SwapPolyIndexes(a,b);g&&this.DeleteFromAEL(a);e&&this.DeleteFromAEL(b)};d.Clipper.prototype.DeleteFromAEL=function(a){var b=a.prevInAEL,c=a.nextInAEL;null==b&&null==c&&a!=this.m_ActiveEdges||(null!= -b?b.nextInAEL=c:this.m_ActiveEdges=c,null!=c&&(c.prevInAEL=b),a.nextInAEL=null,a.prevInAEL=null)};d.Clipper.prototype.DeleteFromSEL=function(a){var b=a.prevInSEL,c=a.nextInSEL;null==b&&null==c&&a!=this.m_SortedEdges||(null!=b?b.nextInSEL=c:this.m_SortedEdges=c,null!=c&&(c.prevInSEL=b),a.nextInSEL=null,a.prevInSEL=null)};d.Clipper.prototype.UpdateEdgeIntoAEL=function(a){null==a.Value.nextInLML&&d.Error("UpdateEdgeIntoAEL: invalid call");var b=a.Value.prevInAEL,c=a.Value.nextInAEL;a.Value.nextInLML.outIdx= -a.Value.outIdx;null!=b?b.nextInAEL=a.Value.nextInLML:this.m_ActiveEdges=a.Value.nextInLML;null!=c&&(c.prevInAEL=a.Value.nextInLML);a.Value.nextInLML.side=a.Value.side;a.Value.nextInLML.windDelta=a.Value.windDelta;a.Value.nextInLML.windCnt=a.Value.windCnt;a.Value.nextInLML.windCnt2=a.Value.windCnt2;a.Value=a.Value.nextInLML;a.Value.prevInAEL=b;a.Value.nextInAEL=c;a.Value.dx!=d.ClipperBase.horizontal&&this.InsertScanbeam(a.Value.ytop)};d.Clipper.prototype.ProcessHorizontals=function(){for(var a=this.m_SortedEdges;null!= -a;)this.DeleteFromSEL(a),this.ProcessHorizontal(a),a=this.m_SortedEdges};d.Clipper.prototype.ProcessHorizontal=function(a){var b,c,e;a.xcurr=c){if(f.xcurr==a.xtop&&null==g)if(this.SlopesEqual(f, -a.nextInLML,this.m_UseFullRange)){0<=a.outIdx&&0<=f.outIdx&&this.AddJoin(a.nextInLML,f,a.outIdx,-1);break}else if(f.dxe&&null==a.nextInSEL||b==d.Direction.dRightToLeft&&f.xcurr=Math.min(c.xcurr,c.xtop)&&b<=Math.max(c.xcurr,c.xtop))return!1;c=c.nextInSEL}return!0};d.Clipper.prototype.GetNextInAEL=function(a,b){return b==d.Direction.dLeftToRight?a.nextInAEL:a.prevInAEL};d.Clipper.prototype.IsMinima=function(a){return null!=a&&a.prev.nextInLML!=a&&a.next.nextInLML!=a};d.Clipper.prototype.IsMaxima=function(a,b){return null!= -a&&a.ytop==b&&null==a.nextInLML};d.Clipper.prototype.IsIntermediate=function(a,b){return a.ytop==b&&null!=a.nextInLML};d.Clipper.prototype.GetMaximaPair=function(a){return!this.IsMaxima(a.next,a.ytop)||a.next.xtop!=a.xtop?a.prev:a.next};d.Clipper.prototype.ProcessIntersections=function(a,b){if(null==this.m_ActiveEdges)return!0;try{this.BuildIntersectList(a,b);if(null==this.m_IntersectNodes)return!0;if(this.FixupIntersections())this.ProcessIntersectList();else return!1}catch(c){this.m_SortedEdges= -null,this.DisposeIntersectNodes(),d.Error("ProcessIntersections error")}return!0};d.Clipper.prototype.BuildIntersectList=function(a,b){if(null!=this.m_ActiveEdges){var c=this.m_ActiveEdges;c.tmpX=d.Clipper.TopX(c,b);this.m_SortedEdges=c;this.m_SortedEdges.prevInSEL=null;for(c=c.nextInAEL;null!=c;)c.prevInSEL=c.prevInAEL,c.prevInSEL.nextInSEL=c,c.nextInSEL=null,c.tmpX=d.Clipper.TopX(c,b),c=c.nextInAEL;for(var e=!0;e&&null!=this.m_SortedEdges;){e=!1;for(c=this.m_SortedEdges;null!=c.nextInSEL;){var g= -c.nextInSEL,f=new d.IntPoint,h;if(h=c.tmpX>g.tmpX)f={Value:f},h=this.IntersectPoint(c,g,f),f=f.Value;h?(f.Y>a&&(f.Y=a,f.X=d.Clipper.TopX(c,f.Y)),this.AddIntersectNode(c,g,f),this.SwapPositionsInSEL(c,g),e=!0):c=g}if(null!=c.prevInSEL)c.prevInSEL.nextInSEL=null;else break}this.m_SortedEdges=null}};d.Clipper.prototype.FixupIntersections=function(){if(null==this.m_IntersectNodes.next)return!0;this.CopyAELToSEL();for(var a=this.m_IntersectNodes,b=this.m_IntersectNodes.next;null!=b;){var c=a.edge1;if(c.prevInSEL== -a.edge2)b=c.prevInSEL;else if(c.nextInSEL==a.edge2)b=c.nextInSEL;else{for(;null!=b&&!(b.edge1.nextInSEL==b.edge2||b.edge1.prevInSEL==b.edge2);)b=b.next;if(null==b)return!1;this.SwapIntersectNodes(a,b);c=a.edge1;b=a.edge2}this.SwapPositionsInSEL(c,b);a=a.next;b=a.next}this.m_SortedEdges=null;return a.edge1.prevInSEL==a.edge2||a.edge1.nextInSEL==a.edge2};d.Clipper.prototype.ProcessIntersectList=function(){for(;null!=this.m_IntersectNodes;){var a=this.m_IntersectNodes.next;this.IntersectEdges(this.m_IntersectNodes.edge1, -this.m_IntersectNodes.edge2,this.m_IntersectNodes.pt,d.Protects.ipBoth);this.SwapPositionsInAEL(this.m_IntersectNodes.edge1,this.m_IntersectNodes.edge2);this.m_IntersectNodes=null;this.m_IntersectNodes=a}};y=function(a){return 0>a?Math.ceil(a-0.5):Math.round(a)};H=function(a){return 0>a?Math.ceil(a-0.5):Math.floor(a+0.5)};I=function(a){return 0>a?-Math.round(Math.abs(a)):Math.round(a)};J=function(a){if(0>a)return a-=0.5,-2147483648>a?Math.ceil(a):a|0;a+=0.5;return 2147483647a.pt.X,0a.pt.X,0a.pt.X:a.pt.Y>b.pt.Y};d.Clipper.prototype.SwapIntersectNodes=function(a,b){var c=a.edge1,d=a.edge2,g=a.pt;a.edge1=b.edge1;a.edge2=b.edge2;a.pt=b.pt;b.edge1=c;b.edge2=d;b.pt=g};d.Clipper.prototype.IntersectPoint=function(a,b,c){var e, -g;if(this.SlopesEqual(a,b,this.m_UseFullRange))return!1;if(0==a.dx)c.Value.X=a.xbot,b.dx==d.ClipperBase.horizontal?c.Value.Y=b.ybot:(g=b.ybot-b.xbot/b.dx,c.Value.Y=d.Clipper.Round(c.Value.X/b.dx+g));else if(0==b.dx)c.Value.X=b.xbot,a.dx==d.ClipperBase.horizontal?c.Value.Y=a.ybot:(e=a.ybot-a.xbot/a.dx,c.Value.Y=d.Clipper.Round(c.Value.X/a.dx+e));else{e=a.xbot-a.ybot*a.dx;g=b.xbot-b.ybot*b.dx;var f=(g-e)/(a.dx-b.dx);c.Value.Y=d.Clipper.Round(f);c.Value.X=d.Math_Abs_Double(a.dx)b.ytop)return c.Value.X=a.xtop,c.Value.Y=a.ytop,d.Clipper.TopX(b,a.ytop)b.xtop}return!0};d.Clipper.prototype.DisposeIntersectNodes=function(){for(;null!=this.m_IntersectNodes;){var a=this.m_IntersectNodes.next;this.m_IntersectNodes=null;this.m_IntersectNodes=a}};d.Clipper.prototype.ProcessEdgesAtTopOfScanbeam=function(a){for(var b= -this.m_ActiveEdges,c;null!=b;)if(this.IsMaxima(b,a)&&this.GetMaximaPair(b).dx!=d.ClipperBase.horizontal)c=b.prevInAEL,this.DoMaxima(b,a),b=null==c?this.m_ActiveEdges:c.nextInAEL;else{if(this.IsIntermediate(b,a)&&b.nextInLML.dx==d.ClipperBase.horizontal){if(0<=b.outIdx){this.AddOutPt(b,new d.IntPoint(b.xtop,b.ytop));for(c=0;cc.ytop&&this.SlopesEqual(b,c,this.m_UseFullRange)?(this.AddOutPt(c,new d.IntPoint(b.xbot,b.ybot)),this.AddJoin(b,c,-1,-1)):null!=e&&(e.xcurr==b.xbot&&e.ycurr==b.ybot&&0<=b.outIdx&&0<=e.outIdx&&e.ycurr>e.ytop&&this.SlopesEqual(b,e,this.m_UseFullRange))&&(this.AddOutPt(e,new d.IntPoint(b.xbot,b.ybot)),this.AddJoin(b,e,-1,-1))),b=b.nextInAEL}; -d.Clipper.prototype.DoMaxima=function(a,b){for(var c=this.GetMaximaPair(a),e=a.xtop,g=a.nextInAEL;g!=c;)null==g&&d.Error("DoMaxima error"),this.IntersectEdges(a,g,new d.IntPoint(e,b),d.Protects.ipBoth),g=g.nextInAEL;0>a.outIdx&&0>c.outIdx?(this.DeleteFromAEL(a),this.DeleteFromAEL(c)):0<=a.outIdx&&0<=c.outIdx?this.IntersectEdges(a,c,new d.IntPoint(e,b),d.Protects.ipNone):d.Error("DoMaxima error")};d.Clipper.ReversePolygons=function(a){for(var b=a.length,c=0;cg)){for(var f=new d.Polygon(g),h=0;hc)){var g=new d.ExPolygon;g.outer=new d.Polygon;g.holes=new d.Polygons;for(var f=0;fthis.Area(f,this.m_UseFullRange),f.isHole&& -null==f.FirstLeft&&(f.FirstLeft=h.FirstLeft));b=f.idx;var j=h.idx;h.pts=null;h.bottomPt=null;h.AppendLink=f;for(a=c+1;ad.ClipperBase.hiRange||d.Math_Abs_Int64(a[c].Y)>d.ClipperBase.hiRange)d.Error("Coordinate exceeds range bounds in FullRangeNeeded().");else if(d.Math_Abs_Int64(a[c].X)>d.ClipperBase.loRange|| -d.Math_Abs_Int64(a[c].Y)>d.ClipperBase.loRange)b=!0;return b};d.Clipper.prototype.Area=d.Clipper.Area=function(){var a=arguments,b;if(1==a.length){var c=a[0],e=c.length-1;if(2>e)return 0;if(d.Clipper.FullRangeNeeded(c)){a=(new j(c[e].X+c[0].X)).multiply(new j(c[0].Y-c[e].Y));for(b=1;b<=e;++b)a=a.add((new j(c[b-1].X+c[b].X)).multiply(new j(c[b].Y-c[b-1].Y)));return parseFloat(a.toString())/2}a=(c[e].X+c[0].X)*(c[0].Y-c[e].Y);for(b=1;b<=e;++b)a+=(c[b-1].X+c[b].X)*(c[b].Y-c[b-1].Y);return a/2}if(2== -a.length){b=a[0];c=b.pts;if(null==c)return 0;if(a[1]){a=new j(j.ZERO);do a=a.add((new j(c.pt.X+c.prev.pt.X)).multiply(new j(c.prev.pt.Y-c.pt.Y))),c=c.next;while(c!=b.pts);return parseFloat(a.toString())/2}a=0;do a+=(c.pt.X+c.prev.pt.X)*(c.prev.pt.Y-c.pt.Y),c=c.next;while(c!=b.pts);return a/2}};d.Clipper.BuildArc=function(a,b,c,e){var g=Math.sqrt(d.Math_Abs_Double(e))*d.Math_Abs_Double(c-b),g=g/4;6>g&&(g=6);64=g&&(g=1);f=2/(g*g);this.normals=[];b.Value=new d.Polygons;d.Clear(b.Value);for(this.m_i=0;this.m_ih&&0>=c))if(1==h)a=d.Clipper.BuildArc(this.pts[this.m_i][h-1],0,d.PI2,c),b.Value.push(a);else{d.Clear(this.normals);for(a=0;a=f?this.DoMiter():this.DoSquare(g);break;case d.JoinType.jtRound:this.DoRound();break;case d.JoinType.jtSquare:this.DoSquare(1)}this.m_k=this.m_j}b.Value.push(this.currentPoly)}e= -new d.Clipper;e.AddPolygons(b.Value,d.PolyType.ptSubject);if(0this.botPt.Y||a.Y==this.botPt.Y&&a.Xd.PI&&(e=d.PI2-e);a=Math.tan((d.PI-e)/4)*Math.abs(this.delta*a);b=new d.IntPoint(d.Cast_Int64(b.X-this.normals[this.m_k].Y*a),d.Cast_Int64(b.Y+this.normals[this.m_k].X*a));this.AddPoint(b);c=new d.IntPoint(d.Cast_Int64(c.X+this.normals[this.m_j].Y*a),d.Cast_Int64(c.Y-this.normals[this.m_j].X*a))}else this.AddPoint(b),this.AddPoint(this.pts[this.m_i][this.m_j]);this.AddPoint(c)};d.Clipper.PolyOffsetBuilder.prototype.DoMiter=function(){if(0<=(this.normals[this.m_k].X*this.normals[this.m_j].Y- -this.normals[this.m_j].X*this.normals[this.m_k].Y)*this.delta){var a=this.delta/this.m_R;this.AddPoint(new d.IntPoint(d.Cast_Int64(d.Clipper.Round(this.pts[this.m_i][this.m_j].X+(this.normals[this.m_k].X+this.normals[this.m_j].X)*a)),d.Cast_Int64(d.Clipper.Round(this.pts[this.m_i][this.m_j].Y+(this.normals[this.m_k].Y+this.normals[this.m_j].Y)*a))))}else{var a=new d.IntPoint(d.Cast_Int64(d.Clipper.Round(this.pts[this.m_i][this.m_j].X+this.normals[this.m_k].X*this.delta)),d.Cast_Int64(d.Clipper.Round(this.pts[this.m_i][this.m_j].Y+ -this.normals[this.m_k].Y*this.delta))),b=new d.IntPoint(d.Cast_Int64(d.Clipper.Round(this.pts[this.m_i][this.m_j].X+this.normals[this.m_j].X*this.delta)),d.Cast_Int64(d.Clipper.Round(this.pts[this.m_i][this.m_j].Y+this.normals[this.m_j].Y*this.delta)));this.AddPoint(a);this.AddPoint(this.pts[this.m_i][this.m_j]);this.AddPoint(b)}};d.Clipper.PolyOffsetBuilder.prototype.DoRound=function(){var a=new d.IntPoint(d.Clipper.Round(this.pts[this.m_i][this.m_j].X+this.normals[this.m_k].X*this.delta),d.Clipper.Round(this.pts[this.m_i][this.m_j].Y+ -this.normals[this.m_k].Y*this.delta)),b=new d.IntPoint(d.Clipper.Round(this.pts[this.m_i][this.m_j].X+this.normals[this.m_j].X*this.delta),d.Clipper.Round(this.pts[this.m_i][this.m_j].Y+this.normals[this.m_j].Y*this.delta));this.AddPoint(a);if(0<=(this.normals[this.m_k].X*this.normals[this.m_j].Y-this.normals[this.m_j].X*this.normals[this.m_k].Y)*this.delta){if(0.985>this.normals[this.m_j].X*this.normals[this.m_k].X+this.normals[this.m_j].Y*this.normals[this.m_k].Y){var a=Math.atan2(this.normals[this.m_k].Y, -this.normals[this.m_k].X),c=Math.atan2(this.normals[this.m_j].Y,this.normals[this.m_j].X);0this.delta&&c>a&&(c-=d.PI2);a=d.Clipper.BuildArc(this.pts[this.m_i][this.m_j],a,c,this.delta);for(c=0;cb)return a;c||(a=[a]);for(var e=a.length,g,f,h,j,m,l,n,q=[],p=0;pg)h=f,q.push(h);else{h=f;j=b*b;m=f[0];for(n=l=1;nb)return d.Clone(a);a[0]instanceof Array||(a=[a]);var c,e,g,f,h,j,m,l,n,p,r,s,t,v=a.length,u=[];for(c=0;cf;f++){h=[];j=g.length;g[j-1].X!=g[0].X||g[j-1].Y!=g[0].Y?(s=1,g.push({X:g[0].X,Y:g[0].Y}),j=g.length):s=0;r=[];for(e=0;e>=15;0<=--g;){var l=this[a]&32767,k=this[a++]>>15,n=b*l+k*h,l=h*l+((n&32767)<< +15)+c[e]+(d&1073741823);d=(l>>>30)+(n>>>15)+b*k+(d>>>30);c[e++]=l&1073741823}return d}function S(a,b,c,e,d,g){var h=b&16383;for(b>>=14;0<=--g;){var l=this[a]&16383,k=this[a++]>>14,n=b*l+k*h,l=h*l+((n&16383)<<14)+c[e]+d;d=(l>>28)+(n>>14)+b*k;c[e++]=l&268435455}return d}function L(a,b){var c=B[a.charCodeAt(b)];return null==c?-1:c}function v(a){var b=q();b.fromInt(a);return b}function C(a){var b=1,c;0!=(c=a>>>16)&&(a=c,b+=16);0!=(c=a>>8)&&(a=c,b+=8);0!=(c=a>>4)&&(a=c,b+=4);0!=(c=a>>2)&&(a=c,b+=2);0!= +a>>1&&(b+=1);return b}function x(a){this.m=a}function y(a){this.m=a;this.mp=a.invDigit();this.mpl=this.mp&32767;this.mph=this.mp>>15;this.um=(1<=u;++u)B[r++]=u;r=97;for(u=10;36>u;++u)B[r++]=u;r=65;for(u=10;36>u;++u)B[r++]=u;x.prototype.convert= +function(a){return 0>a.s||0<=a.compareTo(this.m)?a.mod(this.m):a};x.prototype.revert=function(a){return a};x.prototype.reduce=function(a){a.divRemTo(this.m,null,a)};x.prototype.mulTo=function(a,b,c){a.multiplyTo(b,c);this.reduce(c)};x.prototype.sqrTo=function(a,b){a.squareTo(b);this.reduce(b)};y.prototype.convert=function(a){var b=q();a.abs().dlShiftTo(this.m.t,b);b.divRemTo(this.m,null,b);0>a.s&&0>15)*this.mpl&this.um)<<15)&a.DM,c=b+this.m.t;for(a[c]+=this.m.am(0,e,a,b,0,this.m.t);a[c]>=a.DV;)a[c]-=a.DV,a[++c]++}a.clamp();a.drShiftTo(this.m.t,a);0<=a.compareTo(this.m)&&a.subTo(this.m,a)};y.prototype.mulTo=function(a,b,c){a.multiplyTo(b,c);this.reduce(c)};y.prototype.sqrTo=function(a,b){a.squareTo(b);this.reduce(b)};k.prototype.copyTo= +function(a){for(var b=this.t-1;0<=b;--b)a[b]=this[b];a.t=this.t;a.s=this.s};k.prototype.fromInt=function(a){this.t=1;this.s=0>a?-1:0;0a?this[0]=a+this.DV:this.t=0};k.prototype.fromString=function(a,b){var c;if(16==b)c=4;else if(8==b)c=3;else if(256==b)c=8;else if(2==b)c=1;else if(32==b)c=5;else if(4==b)c=2;else{this.fromRadix(a,b);return}this.s=this.t=0;for(var e=a.length,d=!1,g=0;0<=--e;){var h=8==c?a[e]&255:L(a,e);0>h?"-"==a.charAt(e)&&(d=!0):(d=!1,0==g?this[this.t++]=h:g+c>this.DB? +(this[this.t-1]|=(h&(1<>this.DB-g):this[this.t-1]|=h<=this.DB&&(g-=this.DB))}8==c&&0!=(a[0]&128)&&(this.s=-1,0>e|h,h=(this[l]&d)<=this.t)b.t=0;else{var e=a%this.DB,d=this.DB-e,g=(1<>e;for(var h=c+1;h>e;0>=this.DB;if(a.t>=this.DB;e+=this.s}else{for(e+=this.s;c>=this.DB;e-=a.s}b.s=0>e?-1:0;-1>e?b[c++]=this.DV+e:0=b.DV&&(a[c+b.t]-=b.DV,a[c+b.t+1]=1)}0=e.t)){var d=this.abs();if(d.t< +e.t)null!=b&&b.fromInt(0),null!=c&&this.copyTo(c);else{null==c&&(c=q());var g=q(),h=this.s;a=a.s;var l=this.DB-C(e[e.t-1]);0>this.F2:0),n=this.FV/z,z=(1<h&&k.ZERO.subTo(c,c)}}}};k.prototype.invDigit=function(){if(1>this.t)return 0;var a=this[0];if(0==(a&1))return 0;var b=a&3,b=b*(2-(a&15)*b)&15,b=b*(2-(a&255)*b)&255,b=b*(2-((a&65535)*b&65535))&65535,b=b*(2-a*b%this.DV)%this.DV;return 0a)return k.ONE;var c=q(),e=q(),d=b.convert(this),g=C(a)-1;for(d.copyTo(c);0<=--g;)if(b.sqrTo(c,e),0<(a&1<this.s)return"-"+this.negate().toString(a);if(16==a)a=4;else if(8==a)a=3;else if(2==a)a=1;else if(32==a)a=5;else if(4==a)a=2;else return this.toRadix(a);var b=(1<> +h)&&(e=!0,d="0123456789abcdefghijklmnopqrstuvwxyz".charAt(c));0<=g;)h>(h+=this.DB-a)):(c=this[g]>>(h-=a)&b,0>=h&&(h+=this.DB,--g)),0this.s?this.negate():this};k.prototype.compareTo=function(a){var b=this.s-a.s;if(0!=b)return b;var c=this.t,b=c-a.t;if(0!=b)return 0>this.s? +-b:b;for(;0<=--c;)if(0!=(b=this[c]-a[c]))return b;return 0};k.prototype.bitLength=function(){return 0>=this.t?0:this.DB*(this.t-1)+C(this[this.t-1]^this.s&this.DM)};k.prototype.mod=function(a){var b=q();this.abs().divRemTo(a,null,b);0>this.s&&0a||b.isEven()?new x(b):new y(b);return this.exp(a,c)};k.ZERO=v(0);k.ONE=v(1);A.prototype.convert=O;A.prototype.revert=O;A.prototype.mulTo=function(a,b,c){a.multiplyTo(b, +c)};A.prototype.sqrTo=function(a,b){a.squareTo(b)};w.prototype.convert=function(a){if(0>a.s||a.t>2*this.m.t)return a.mod(this.m);if(0>a.compareTo(this.m))return a;var b=q();a.copyTo(b);this.reduce(b);return b};w.prototype.revert=function(a){return a};w.prototype.reduce=function(a){a.drShiftTo(this.m.t-1,this.r2);a.t>this.m.t+1&&(a.t=this.m.t+1,a.clamp());this.mu.multiplyUpperTo(this.r2,this.m.t+1,this.q3);for(this.m.multiplyLowerTo(this.q3,this.m.t+1,this.r2);0>a.compareTo(this.r2);)a.dAddOffset(1, +this.m.t+1);for(a.subTo(this.r2,a);0<=a.compareTo(this.m);)a.subTo(this.m,a)};w.prototype.mulTo=function(a,b,c){a.multiplyTo(b,c);this.reduce(c)};w.prototype.sqrTo=function(a,b){a.squareTo(b);this.reduce(b)};var t=[2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401, +409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,541,547,557,563,569,571,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691,701,709,719,727,733,739,743,751,757,761,769,773,787,797,809,811,821,823,827,829,839,853,857,859,863,877,881,883,887,907,911,919,929,937,941,947,953,967,971,977,983,991,997],V=67108864/t[t.length-1];k.prototype.chunkSize=function(a){return Math.floor(Math.LN2*this.DB/Math.log(a))};k.prototype.toRadix=function(a){null== +a&&(a=10);if(0==this.signum()||2>a||36z?"-"==a.charAt(l)&&0==this.signum()&&(d=!0):(h=b*h+z,++g>=c&&(this.dMultiply(e),this.dAddOffset(h, +0),h=g=0))}0a)this.fromInt(1);else for(this.fromNumber(a,c),this.testBit(a-1)||this.bitwiseTo(k.ONE.shiftLeft(a-1),I,this),this.isEven()&&this.dAddOffset(1,0);!this.isProbablePrime(b);)this.dAddOffset(2,0),this.bitLength()>a&&this.subTo(k.ONE.shiftLeft(a-1),this);else{c=[];var e=a&7;c.length=(a>>3)+1;b.nextBytes(c);c[0]=0>=this.DB;if(a.t< +this.t){for(e+=a.s;c>=this.DB;e+=this.s}else{for(e+=this.s;c>=this.DB;e+=a.s}b.s=0>e?-1:0;0e&&(b[c++]=this.DV+e);b.t=c;b.clamp()};k.prototype.dMultiply=function(a){this[this.t]=this.am(0,a-1,this,0,0,this.t);++this.t;this.clamp()};k.prototype.dAddOffset=function(a,b){if(0!=a){for(;this.t<=b;)this[this.t++]=0;for(this[b]+=a;this[b]>=this.DV;)this[b]-=this.DV,++b>=this.t&&(this[this.t++]=0),++this[b]}};k.prototype.multiplyLowerTo= +function(a,b,c){var e=Math.min(this.t+a.t,b);c.s=0;for(c.t=e;0=a)return 0;var b=this.DV%a,c=0>this.s?a- +1:0;if(0=c)return!1;var e=b.shiftRight(c);a=a+1>>1;a>t.length&&(a=t.length);for(var d=q(),g=0;gthis.s){if(1==this.t)return this[0]-this.DV;if(0==this.t)return-1}else{if(1==this.t)return this[0];if(0==this.t)return 0}return(this[1]&(1<<32-this.DB)-1)<>24};k.prototype.shortValue=function(){return 0==this.t?this.s:this[0]<<16>>16};k.prototype.signum=function(){return 0>this.s?-1:0>=this.t||1==this.t&&0>=this[0]? +0:1};k.prototype.toByteArray=function(){var a=this.t,b=[];b[0]=this.s;var c=this.DB-a*this.DB%8,e,d=0;if(0>c)!=(this.s&this.DM)>>c&&(b[d++]=e|this.s<c?(e=(this[a]&(1<>(c+=this.DB-8)):(e=this[a]>>(c-=8)&255,0>=c&&(c+=this.DB,--a)),0!=(e&128)&&(e|=-256),0==d&&(this.s&128)!=(e&128)&&++d,0this.compareTo(a)? +this:a};k.prototype.max=function(a){return 0a?this.rShiftTo(-a,b):this.lShiftTo(a,b);return b};k.prototype.shiftRight=function(a){var b=q();0>a?this.lShiftTo(-a,b):this.rShiftTo(a,b);return b};k.prototype.getLowestSetBit=function(){for(var a=0;a>=16,c+=16);0==(a&255)&&(a>>=8,c+=8);0==(a&15)&&(a>>=4,c+=4);0==(a&3)&&(a>>=2,c+=2);0==(a&1)&&++c;a=c}return b+a}return 0>this.s?this.t*this.DB:-1};k.prototype.bitCount=function(){for(var a=0,b=this.s& +this.DM,c=0;c=this.t?0!=this.s:0!=(this[b]&1<=c)return d;e=18>c?1:48>c?3:144>c?4:768>c?5:6;g=8>c?new x(b):b.isEven()?new w(b):new y(b);var h=[],l=3,k=e-1,n=(1<< +e)-1;h[1]=g.convert(this);if(1=k?p=a[m]>>c-k&n:(p=(a[m]&(1<>this.DB+c-k));for(l=e;0==(p&1);)p>>=1,--l;0>(c-=l)&&(c+=this.DB,--m);if(r)h[p].copyTo(d),r=!1;else{for(;1--c&&(c=this.DB-1,--m)}return g.revert(d)};k.prototype.modInverse= +function(a){var b=a.isEven();if(this.isEven()&&b||0==a.signum())return k.ZERO;for(var c=a.clone(),e=this.clone(),d=v(1),g=v(0),h=v(0),l=v(1);0!=c.signum();){for(;c.isEven();)c.rShiftTo(1,c),b?(d.isEven()&&g.isEven()||(d.addTo(this,d),g.subTo(a,g)),d.rShiftTo(1,d)):g.isEven()||g.subTo(a,g),g.rShiftTo(1,g);for(;e.isEven();)e.rShiftTo(1,e),b?(h.isEven()&&l.isEven()||(h.addTo(this,h),l.subTo(a,l)),h.rShiftTo(1,h)):l.isEven()||l.subTo(a,l),l.rShiftTo(1,l);0<=c.compareTo(e)?(c.subTo(e,c),b&&d.subTo(h,d), +g.subTo(l,g)):(e.subTo(c,e),b&&h.subTo(d,h),l.subTo(g,l))}if(0!=e.compareTo(k.ONE))return k.ZERO;if(0<=l.compareTo(a))return l.subtract(a);if(0>l.signum())l.addTo(a,l);else return l;return 0>l.signum()?l.add(a):l};k.prototype.pow=function(a){return this.exp(a,new A)};k.prototype.gcd=function(a){var b=0>this.s?this.negate():this.clone();a=0>a.s?a.negate():a.clone();if(0>b.compareTo(a)){var c=b,b=a;a=c}var c=b.getLowestSetBit(),e=a.getLowestSetBit();if(0>e)return b;ca.compareTo(b)?!0:!1};m.op_Addition=function(a,b){return(new m(a)).add(new m(b))};m.op_Subtraction= +function(a,b){return(new m(a)).subtract(new m(b))};m.Int128Mul=function(a,b){return(new m(a)).multiply(new m(b))};m.op_Division=function(a,b){return a.divide(b)};m.prototype.ToDouble=function(){return parseFloat(this.toString())};if("undefined"==typeof K)var K=function(a,b){var c;if("undefined"==typeof Object.getOwnPropertyNames)for(c in b.prototype){if("undefined"==typeof a.prototype[c]||a.prototype[c]==Object.prototype[c])a.prototype[c]=b.prototype[c]}else for(var e=Object.getOwnPropertyNames(b.prototype), +d=0;da||2147483647a?Math.ceil(a):Math.floor(a):~~a}:F&&"function"==typeof Number.toInteger?function(a){return Number.toInteger(a)}:P||H?function(a){return parseInt(a,10)}:p?function(a){return-2147483648>a||2147483647a?Math.ceil(a):Math.floor(a):a|0}:function(a){return 0>a?Math.ceil(a): +Math.floor(a)};d.Clear=function(a){a.length=0};d.PI=3.141592653589793;d.PI2=6.283185307179586;d.IntPoint=function(){var a;a=arguments;var b=a.length;this.Y=this.X=0;2==b?(this.X=a[0],this.Y=a[1]):1==b?a[0]instanceof d.DoublePoint?(a=a[0],this.X=d.Clipper.Round(a.X),this.Y=d.Clipper.Round(a.Y)):(a=a[0],this.X=a.X,this.Y=a.Y):this.Y=this.X=0};d.IntPoint.op_Equality=function(a,b){return a.X==b.X&&a.Y==b.Y};d.IntPoint.op_Inequality=function(a,b){return a.X!=b.X||a.Y!=b.Y};d.IntPoint0=function(){this.Y= +this.X=0};d.IntPoint1=function(a){this.X=a.X;this.Y=a.Y};d.IntPoint1dp=function(a){this.X=d.Clipper.Round(a.X);this.Y=d.Clipper.Round(a.Y)};d.IntPoint2=function(a,b){this.X=a;this.Y=b};d.IntRect=function(){var a=arguments,b=a.length;4==b?(this.left=a[0],this.top=a[1],this.right=a[2],this.bottom=a[3]):1==b?(this.left=ir.left,this.top=ir.top,this.right=ir.right,this.bottom=ir.bottom):this.bottom=this.right=this.top=this.left=0};d.IntRect0=function(){this.bottom=this.right=this.top=this.left=0};d.IntRect1= +function(a){this.left=a.left;this.top=a.top;this.right=a.right;this.bottom=a.bottom};d.IntRect4=function(a,b,c,e){this.left=a;this.top=b;this.right=c;this.bottom=e};d.ClipType={ctIntersection:0,ctUnion:1,ctDifference:2,ctXor:3};d.PolyType={ptSubject:0,ptClip:1};d.PolyFillType={pftEvenOdd:0,pftNonZero:1,pftPositive:2,pftNegative:3};d.JoinType={jtSquare:0,jtRound:1,jtMiter:2};d.EndType={etOpenSquare:0,etOpenRound:1,etOpenButt:2,etClosedLine:3,etClosedPolygon:4};d.EdgeSide={esLeft:0,esRight:1};d.Direction= +{dRightToLeft:0,dLeftToRight:1};d.TEdge=function(){this.Bot=new d.IntPoint;this.Curr=new d.IntPoint;this.Top=new d.IntPoint;this.Delta=new d.IntPoint;this.Dx=0;this.PolyTyp=d.PolyType.ptSubject;this.Side=d.EdgeSide.esLeft;this.OutIdx=this.WindCnt2=this.WindCnt=this.WindDelta=0;this.PrevInSEL=this.NextInSEL=this.PrevInAEL=this.NextInAEL=this.NextInLML=this.Prev=this.Next=null};d.IntersectNode=function(){this.Edge2=this.Edge1=null;this.Pt=new d.IntPoint};d.MyIntersectNodeSort=function(){};d.MyIntersectNodeSort.Compare= +function(a,b){return b.Pt.Y-a.Pt.Y};d.LocalMinima=function(){this.Y=0;this.Next=this.RightBound=this.LeftBound=null};d.Scanbeam=function(){this.Y=0;this.Next=null};d.OutRec=function(){this.Idx=0;this.IsOpen=this.IsHole=!1;this.PolyNode=this.BottomPt=this.Pts=this.FirstLeft=null};d.OutPt=function(){this.Idx=0;this.Pt=new d.IntPoint;this.Prev=this.Next=null};d.Join=function(){this.OutPt2=this.OutPt1=null;this.OffPt=new d.IntPoint};d.ClipperBase=function(){this.m_CurrentLM=this.m_MinimaList=null;this.m_edges= +[];this.PreserveCollinear=this.m_HasOpenPaths=this.m_UseFullRange=!1;this.m_CurrentLM=this.m_MinimaList=null;this.m_HasOpenPaths=this.m_UseFullRange=!1};d.ClipperBase.horizontal=-9007199254740992;d.ClipperBase.Skip=-2;d.ClipperBase.Unassigned=-1;d.ClipperBase.tolerance=1E-20;d.ClipperBase.loRange=47453132;d.ClipperBase.hiRange=0xfffffffffffff;d.ClipperBase.near_zero=function(a){return a>-d.ClipperBase.tolerance&&ab.X==a.Xb.Y==a.Yb.X==a.Xb.Y==a.Yd.ClipperBase.hiRange||a.Y>d.ClipperBase.hiRange||-a.X>d.ClipperBase.hiRange||-a.Y>d.ClipperBase.hiRange)&& +d.Error("Coordinate outside allowed range in RangeTest().");else if(a.X>d.ClipperBase.loRange||a.Y>d.ClipperBase.loRange||-a.X>d.ClipperBase.loRange||-a.Y>d.ClipperBase.loRange)b.Value=!0,this.RangeTest(a,b)};d.ClipperBase.prototype.InitEdge=function(a,b,c,e){a.Next=b;a.Prev=c;a.Curr.X=e.X;a.Curr.Y=e.Y;a.OutIdx=-1};d.ClipperBase.prototype.InitEdge2=function(a,b){a.Curr.Y>=a.Next.Curr.Y?(a.Bot.X=a.Curr.X,a.Bot.Y=a.Curr.Y,a.Top.X=a.Next.Curr.X,a.Top.Y=a.Next.Curr.Y):(a.Top.X=a.Curr.X,a.Top.Y=a.Curr.Y, +a.Bot.X=a.Next.Curr.X,a.Bot.Y=a.Next.Curr.Y);this.SetDx(a);a.PolyTyp=b};d.ClipperBase.prototype.FindNextLocMin=function(a){for(var b;;){for(;d.IntPoint.op_Inequality(a.Bot,a.Prev.Bot)||d.IntPoint.op_Equality(a.Curr,a.Top);)a=a.Next;if(a.Dx!=d.ClipperBase.horizontal&&a.Prev.Dx!=d.ClipperBase.horizontal)break;for(;a.Prev.Dx==d.ClipperBase.horizontal;)a=a.Prev;for(b=a;a.Dx==d.ClipperBase.horizontal;)a=a.Next;if(a.Top.Y!=a.Prev.Bot.Y){b.Prev.Bot.Xe.Next.Top.X&&(e=f.Prev)}for(;a!=e;)a.NextInLML=a.Next,a.Dx==d.ClipperBase.horizontal&& +a!=c&&a.Bot.X!=a.Prev.Top.X&&this.ReverseHorizontal(a),a=a.Next;a.Dx==d.ClipperBase.horizontal&&a!=c&&a.Bot.X!=a.Prev.Top.X&&this.ReverseHorizontal(a);e=e.Next}else{for(;e.Top.Y==e.Prev.Bot.Y&&e.Prev.OutIdx!=d.ClipperBase.Skip;)e=e.Prev;if(e.Dx==d.ClipperBase.horizontal&&e.Prev.OutIdx!=d.ClipperBase.Skip){for(f=e;f.Next.Dx==d.ClipperBase.horizontal;)f=f.Next;f.Next.Top.X==e.Prev.Top.X?b||(e=f.Next):f.Next.Top.X>e.Prev.Top.X&&(e=f.Next)}for(;a!=e;)a.NextInLML=a.Prev,a.Dx==d.ClipperBase.horizontal&& +a!=c&&a.Bot.X!=a.Next.Top.X&&this.ReverseHorizontal(a),a=a.Prev;a.Dx==d.ClipperBase.horizontal&&a!=c&&a.Bot.X!=a.Next.Top.X&&this.ReverseHorizontal(a);e=e.Prev}if(e.OutIdx==d.ClipperBase.Skip){a=e;if(b){for(;a.Top.Y==a.Next.Bot.Y;)a=a.Next;for(;a!=e&&a.Dx==d.ClipperBase.horizontal;)a=a.Prev}else{for(;a.Top.Y==a.Prev.Bot.Y;)a=a.Prev;for(;a!=e&&a.Dx==d.ClipperBase.horizontal;)a=a.Next}a==e?e=b?a.Next:a.Prev:(a=b?e.Next:e.Prev,c=new d.LocalMinima,c.Next=null,c.Y=a.Bot.Y,c.LeftBound=null,c.RightBound= +a,c.RightBound.WindDelta=0,e=this.ProcessBound(c.RightBound,b),this.InsertLocalMinima(c))}return e};d.ClipperBase.prototype.AddPath=function(a,b,c){c||b!=d.PolyType.ptClip||d.Error("AddPath: Open paths must be subject.");var e=a.length-1;if(c)for(;0e||!c&&1>e)return!1;for(var f=[],g=0;g<=e;g++)f.push(new d.TEdge);var h=!0;f[1].Curr.X=a[1].X;f[1].Curr.Y=a[1].Y;var l={Value:this.m_UseFullRange};this.RangeTest(a[0], +l);this.m_UseFullRange=l.Value;l.Value=this.m_UseFullRange;this.RangeTest(a[e],l);this.m_UseFullRange=l.Value;this.InitEdge(f[0],f[1],f[e],a[0]);this.InitEdge(f[e],f[0],f[e-1],a[e]);for(g=e-1;1<=g;--g)l.Value=this.m_UseFullRange,this.RangeTest(a[g],l),this.m_UseFullRange=l.Value,this.InitEdge(f[g],f[g+1],f[g-1],a[g]);for(g=a=e=f[0];;)if(d.IntPoint.op_Equality(a.Curr,a.Next.Curr)){if(a==a.Next)break;a==e&&(e=a.Next);g=a=this.RemoveEdge(a)}else{if(a.Prev==a.Next)break;else if(c&&d.ClipperBase.SlopesEqual(a.Prev.Curr, +a.Curr,a.Next.Curr,this.m_UseFullRange)&&(!this.PreserveCollinear||!this.Pt2IsBetweenPt1AndPt3(a.Prev.Curr,a.Curr,a.Next.Curr))){a==e&&(e=a.Next);a=this.RemoveEdge(a);g=a=a.Prev;continue}a=a.Next;if(a==g)break}if(!c&&a==a.Next||c&&a.Prev==a.Next)return!1;c||(this.m_HasOpenPaths=!0,e.Prev.OutIdx=d.ClipperBase.Skip);a=e;do this.InitEdge2(a,b),a=a.Next,h&&a.Curr.Y!=e.Curr.Y&&(h=!1);while(a!=e);if(h){if(c)return!1;a.Prev.OutIdx=d.ClipperBase.Skip;a.Prev.Bot.Xa.X==b.Xa.Y==b.Y=this.m_MinimaList.Y)a.Next=this.m_MinimaList,this.m_MinimaList=a;else{for(var b=this.m_MinimaList;null!==b.Next&&a.Ythis.m_Scanbeam.Y){var b=new d.Scanbeam;b.Y=a;b.Next=this.m_Scanbeam;this.m_Scanbeam=b}else{for(var c= +this.m_Scanbeam;null!==c.Next&&a<=c.Next.Y;)c=c.Next;a!=c.Y&&(b=new d.Scanbeam,b.Y=a,b.Next=c.Next,c.Next=b)}};d.Clipper.prototype.Execute=function(){var a=arguments,b=a.length,c=a[1]instanceof d.PolyTree;if(4!=b||c){if(4==b&&c){var b=a[0],e=a[1],c=a[2],a=a[3];if(this.m_ExecuteLocked)return!1;this.m_ExecuteLocked=!0;this.m_SubjFillType=c;this.m_ClipFillType=a;this.m_ClipType=b;this.m_UsingPolyTree=!0;try{(f=this.ExecuteInternal())&&this.BuildResult2(e)}finally{this.DisposeAllPolyPts(),this.m_ExecuteLocked= +!1}return f}if(2==b&&!c||2==b&&c)return b=a[0],e=a[1],this.Execute(b,e,d.PolyFillType.pftEvenOdd,d.PolyFillType.pftEvenOdd)}else{b=a[0];e=a[1];c=a[2];a=a[3];if(this.m_ExecuteLocked)return!1;this.m_HasOpenPaths&&d.Error("Error: PolyTree struct is need for open path clipping.");this.m_ExecuteLocked=!0;d.Clear(e);this.m_SubjFillType=c;this.m_ClipFillType=a;this.m_ClipType=b;this.m_UsingPolyTree=!1;try{var f=this.ExecuteInternal();f&&this.BuildResult(e)}finally{this.DisposeAllPolyPts(),this.m_ExecuteLocked= +!1}return f}};d.Clipper.prototype.FixHoleLinkage=function(a){if(null!==a.FirstLeft&&(a.IsHole==a.FirstLeft.IsHole||null===a.FirstLeft.Pts)){for(var b=a.FirstLeft;null!==b&&(b.IsHole==a.IsHole||null===b.Pts);)b=b.FirstLeft;a.FirstLeft=b}};d.Clipper.prototype.ExecuteInternal=function(){try{this.Reset();if(null===this.m_CurrentLM)return!1;var a=this.PopScanbeam();do{this.InsertLocalMinimaIntoAEL(a);d.Clear(this.m_GhostJoins);this.ProcessHorizontals(!1);if(null===this.m_Scanbeam)break;var b=this.PopScanbeam(); +if(!this.ProcessIntersections(a,b))return!1;this.ProcessEdgesAtTopOfScanbeam(b);a=b}while(null!==this.m_Scanbeam||null!==this.m_CurrentLM);for(var a=0,c=this.m_PolyOuts.length;aa.Top.Y?b.Top.Xd.Clipper.TopX(b,a.Top.Y):b.Curr.Xa.WindCnt2}case d.ClipType.ctUnion:switch(c){case d.PolyFillType.pftEvenOdd:case d.PolyFillType.pftNonZero:return 0=== +a.WindCnt2;case d.PolyFillType.pftPositive:return 0>=a.WindCnt2;default:return 0<=a.WindCnt2}case d.ClipType.ctDifference:if(a.PolyTyp==d.PolyType.ptSubject)switch(c){case d.PolyFillType.pftEvenOdd:case d.PolyFillType.pftNonZero:return 0===a.WindCnt2;case d.PolyFillType.pftPositive:return 0>=a.WindCnt2;default:return 0<=a.WindCnt2}else switch(c){case d.PolyFillType.pftEvenOdd:case d.PolyFillType.pftNonZero:return 0!==a.WindCnt2;case d.PolyFillType.pftPositive:return 0 +a.WindCnt2}case d.ClipType.ctXor:if(0===a.WindDelta)switch(c){case d.PolyFillType.pftEvenOdd:case d.PolyFillType.pftNonZero:return 0===a.WindCnt2;case d.PolyFillType.pftPositive:return 0>=a.WindCnt2;default:return 0<=a.WindCnt2}}return!0};d.Clipper.prototype.SetWindingCount=function(a){for(var b=a.PrevInAEL;null!==b&&(b.PolyTyp!=a.PolyTyp||0===b.WindDelta);)b=b.PrevInAEL;if(null===b)a.WindCnt=0===a.WindDelta?1:a.WindDelta,a.WindCnt2=0,b=this.m_ActiveEdges;else{if(0===a.WindDelta&&this.m_ClipType!= +d.ClipType.ctUnion)a.WindCnt=1;else if(this.IsEvenOddFillType(a))if(0===a.WindDelta){for(var c=!0,e=b.PrevInAEL;null!==e;)e.PolyTyp==b.PolyTyp&&0!==e.WindDelta&&(c=!c),e=e.PrevInAEL;a.WindCnt=c?0:1}else a.WindCnt=a.WindDelta;else 0>b.WindCnt*b.WindDelta?1b.WindDelta*a.WindDelta?b.WindCnt:b.WindCnt+a.WindDelta:a.WindCnt=0===a.WindDelta?1:a.WindDelta:a.WindCnt=0===a.WindDelta?0>b.WindCnt?b.WindCnt-1:b.WindCnt+1:0>b.WindDelta*a.WindDelta?b.WindCnt:b.WindCnt+a.WindDelta; +a.WindCnt2=b.WindCnt2;b=b.NextInAEL}if(this.IsEvenOddAltFillType(a))for(;b!=a;)0!==b.WindDelta&&(a.WindCnt2=0===a.WindCnt2?1:0),b=b.NextInAEL;else for(;b!=a;)a.WindCnt2+=b.WindDelta,b=b.NextInAEL};d.Clipper.prototype.AddEdgeToSEL=function(a){null===this.m_SortedEdges?(this.m_SortedEdges=a,a.PrevInSEL=null,a.NextInSEL=null):(a.NextInSEL=this.m_SortedEdges,a.PrevInSEL=null,this.m_SortedEdges=this.m_SortedEdges.PrevInSEL=a)};d.Clipper.prototype.CopyAELToSEL=function(){var a=this.m_ActiveEdges;for(this.m_SortedEdges= +a;null!==a;)a.PrevInSEL=a.PrevInAEL,a=a.NextInSEL=a.NextInAEL};d.Clipper.prototype.SwapPositionsInAEL=function(a,b){if(a.NextInAEL!=a.PrevInAEL&&b.NextInAEL!=b.PrevInAEL){if(a.NextInAEL==b){var c=b.NextInAEL;null!==c&&(c.PrevInAEL=a);var e=a.PrevInAEL;null!==e&&(e.NextInAEL=b);b.PrevInAEL=e;b.NextInAEL=a;a.PrevInAEL=b;a.NextInAEL=c}else b.NextInAEL==a?(c=a.NextInAEL,null!==c&&(c.PrevInAEL=b),e=b.PrevInAEL,null!==e&&(e.NextInAEL=a),a.PrevInAEL=e,a.NextInAEL=b,b.PrevInAEL=a,b.NextInAEL=c):(c=a.NextInAEL, +e=a.PrevInAEL,a.NextInAEL=b.NextInAEL,null!==a.NextInAEL&&(a.NextInAEL.PrevInAEL=a),a.PrevInAEL=b.PrevInAEL,null!==a.PrevInAEL&&(a.PrevInAEL.NextInAEL=a),b.NextInAEL=c,null!==b.NextInAEL&&(b.NextInAEL.PrevInAEL=b),b.PrevInAEL=e,null!==b.PrevInAEL&&(b.PrevInAEL.NextInAEL=b));null===a.PrevInAEL?this.m_ActiveEdges=a:null===b.PrevInAEL&&(this.m_ActiveEdges=b)}};d.Clipper.prototype.SwapPositionsInSEL=function(a,b){if(null!==a.NextInSEL||null!==a.PrevInSEL)if(null!==b.NextInSEL||null!==b.PrevInSEL){if(a.NextInSEL== +b){var c=b.NextInSEL;null!==c&&(c.PrevInSEL=a);var e=a.PrevInSEL;null!==e&&(e.NextInSEL=b);b.PrevInSEL=e;b.NextInSEL=a;a.PrevInSEL=b;a.NextInSEL=c}else b.NextInSEL==a?(c=a.NextInSEL,null!==c&&(c.PrevInSEL=b),e=b.PrevInSEL,null!==e&&(e.NextInSEL=a),a.PrevInSEL=e,a.NextInSEL=b,b.PrevInSEL=a,b.NextInSEL=c):(c=a.NextInSEL,e=a.PrevInSEL,a.NextInSEL=b.NextInSEL,null!==a.NextInSEL&&(a.NextInSEL.PrevInSEL=a),a.PrevInSEL=b.PrevInSEL,null!==a.PrevInSEL&&(a.PrevInSEL.NextInSEL=a),b.NextInSEL=c,null!==b.NextInSEL&& +(b.NextInSEL.PrevInSEL=b),b.PrevInSEL=e,null!==b.PrevInSEL&&(b.PrevInSEL.NextInSEL=b));null===a.PrevInSEL?this.m_SortedEdges=a:null===b.PrevInSEL&&(this.m_SortedEdges=b)}};d.Clipper.prototype.AddLocalMaxPoly=function(a,b,c){this.AddOutPt(a,c);0==b.WindDelta&&this.AddOutPt(b,c);a.OutIdx==b.OutIdx?(a.OutIdx=-1,b.OutIdx=-1):a.OutIdxb.Dx?(e=this.AddOutPt(a, +c),b.OutIdx=a.OutIdx,a.Side=d.EdgeSide.esLeft,b.Side=d.EdgeSide.esRight,f=a,a=f.PrevInAEL==b?b.PrevInAEL:f.PrevInAEL):(e=this.AddOutPt(b,c),a.OutIdx=b.OutIdx,a.Side=d.EdgeSide.esRight,b.Side=d.EdgeSide.esLeft,f=b,a=f.PrevInAEL==a?a.PrevInAEL:f.PrevInAEL);null!==a&&0<=a.OutIdx&&d.Clipper.TopX(a,c.Y)==d.Clipper.TopX(f,c.Y)&&d.ClipperBase.SlopesEqual(f,a,this.m_UseFullRange)&&0!==f.WindDelta&&0!==a.WindDelta&&(c=this.AddOutPt(a,c),this.AddJoin(e,c,f.Top));return e};d.Clipper.prototype.CreateOutRec=function(){var a= +new d.OutRec;a.Idx=-1;a.IsHole=!1;a.IsOpen=!1;a.FirstLeft=null;a.Pts=null;a.BottomPt=null;a.PolyNode=null;this.m_PolyOuts.push(a);a.Idx=this.m_PolyOuts.length-1;return a};d.Clipper.prototype.AddOutPt=function(a,b){var c=a.Side==d.EdgeSide.esLeft;if(0>a.OutIdx){var e=this.CreateOutRec();e.IsOpen=0===a.WindDelta;var f=new d.OutPt;e.Pts=f;f.Idx=e.Idx;f.Pt.X=b.X;f.Pt.Y=b.Y;f.Next=f;f.Prev=f;e.IsOpen||this.SetHoleState(a,e);a.OutIdx=e.Idx}else{var e=this.m_PolyOuts[a.OutIdx],g=e.Pts;if(c&&d.IntPoint.op_Equality(b, +g.Pt))return g;if(!c&&d.IntPoint.op_Equality(b,g.Prev.Pt))return g.Prev;f=new d.OutPt;f.Idx=e.Idx;f.Pt.X=b.X;f.Pt.Y=b.Y;f.Next=g;f.Prev=g.Prev;f.Prev.Next=f;g.Prev=f;c&&(e.Pts=f)}return f};d.Clipper.prototype.SwapPoints=function(a,b){var c=new d.IntPoint(a.Value);a.Value.X=b.Value.X;a.Value.Y=b.Value.Y;b.Value.X=c.X;b.Value.Y=c.Y};d.Clipper.prototype.HorzSegmentsOverlap=function(a,b,c,e){return a.X>c.X==a.Xc.X==b.Xa.X==c.Xa.X==e.X=g&&e>=c||f>=g&&f>= +c};d.Clipper.prototype.GetBottomPt=function(a){for(var b=null,c=a.Next;c!=a;)c.Pt.Y>a.Pt.Y?(a=c,b=null):c.Pt.Y==a.Pt.Y&&c.Pt.X<=a.Pt.X&&(c.Pt.Xe.Pt.Y?a:c.Pt.Ye.Pt.X?b:c.Next==c?b:e.Next==e?a:this.FirstIsBottomPt(c,e)?a:b};d.Clipper.prototype.Param1RightOfParam2=function(a,b){do if(a=a.FirstLeft,a==b)return!0;while(null!==a);return!1};d.Clipper.prototype.GetOutRec=function(a){for(a=this.m_PolyOuts[a];a!=this.m_PolyOuts[a.Idx];)a=this.m_PolyOuts[a.Idx];return a};d.Clipper.prototype.AppendPolygon=function(a,b){var c=this.m_PolyOuts[a.OutIdx],e=this.m_PolyOuts[b.OutIdx],f;f=this.Param1RightOfParam2(c, +e)?e:this.Param1RightOfParam2(e,c)?c:this.GetLowermostRec(c,e);var g=c.Pts,h=g.Prev,l=e.Pts,k=l.Prev;a.Side==d.EdgeSide.esLeft?(b.Side==d.EdgeSide.esLeft?(this.ReversePolyPtLinks(l),l.Next=g,g.Prev=l,h.Next=k,k.Prev=h,c.Pts=k):(k.Next=g,g.Prev=k,l.Prev=h,h.Next=l,c.Pts=l),g=d.EdgeSide.esLeft):(b.Side==d.EdgeSide.esRight?(this.ReversePolyPtLinks(l),h.Next=k,k.Prev=h,l.Next=g,g.Prev=l):(h.Next=l,l.Prev=h,g.Prev=k,k.Next=g),g=d.EdgeSide.esRight);c.BottomPt=null;f==e&&(e.FirstLeft!=c&&(c.FirstLeft=e.FirstLeft), +c.IsHole=e.IsHole);e.Pts=null;e.BottomPt=null;e.FirstLeft=c;f=a.OutIdx;h=b.OutIdx;a.OutIdx=-1;b.OutIdx=-1;for(l=this.m_ActiveEdges;null!==l;){if(l.OutIdx==h){l.OutIdx=f;l.Side=g;break}l=l.NextInAEL}e.Idx=c.Idx};d.Clipper.prototype.ReversePolyPtLinks=function(a){if(null!==a){var b,c;b=a;do c=b.Next,b.Next=b.Prev,b=b.Prev=c;while(b!=a)}};d.Clipper.SwapSides=function(a,b){var c=a.Side;a.Side=b.Side;b.Side=c};d.Clipper.SwapPolyIndexes=function(a,b){var c=a.OutIdx;a.OutIdx=b.OutIdx;b.OutIdx=c};d.Clipper.prototype.IntersectEdges= +function(a,b,c,e){var f=!e&&null===a.NextInLML&&a.Top.X==c.X&&a.Top.Y==c.Y;e=!e&&null===b.NextInLML&&b.Top.X==c.X&&b.Top.Y==c.Y;var g=0<=a.OutIdx,h=0<=b.OutIdx;if(0===a.WindDelta||0===b.WindDelta)0===a.WindDelta&&0===b.WindDelta?(f||e)&&g&&h&&this.AddLocalMaxPoly(a,b,c):a.PolyTyp==b.PolyTyp&&a.WindDelta!=b.WindDelta&&this.m_ClipType==d.ClipType.ctUnion?0===a.WindDelta?h&&(this.AddOutPt(a,c),g&&(a.OutIdx=-1)):g&&(this.AddOutPt(b,c),h&&(b.OutIdx=-1)):a.PolyTyp!=b.PolyTyp&&(0!==a.WindDelta||1!=Math.abs(b.WindCnt)|| +this.m_ClipType==d.ClipType.ctUnion&&0!==b.WindCnt2?0!==b.WindDelta||1!=Math.abs(a.WindCnt)||this.m_ClipType==d.ClipType.ctUnion&&0!==a.WindCnt2||(this.AddOutPt(b,c),h&&(b.OutIdx=-1)):(this.AddOutPt(a,c),g&&(a.OutIdx=-1))),f&&(0>a.OutIdx?this.DeleteFromAEL(a):d.Error("Error intersecting polylines")),e&&(0>b.OutIdx?this.DeleteFromAEL(b):d.Error("Error intersecting polylines"));else{if(a.PolyTyp==b.PolyTyp)if(this.IsEvenOddFillType(a)){var l=a.WindCnt;a.WindCnt=b.WindCnt;b.WindCnt=l}else a.WindCnt= +0===a.WindCnt+b.WindDelta?-a.WindCnt:a.WindCnt+b.WindDelta,b.WindCnt=0===b.WindCnt-a.WindDelta?-b.WindCnt:b.WindCnt-a.WindDelta;else this.IsEvenOddFillType(b)?a.WindCnt2=0===a.WindCnt2?1:0:a.WindCnt2+=b.WindDelta,this.IsEvenOddFillType(a)?b.WindCnt2=0===b.WindCnt2?1:0:b.WindCnt2-=a.WindDelta;var k,n,m;a.PolyTyp==d.PolyType.ptSubject?(k=this.m_SubjFillType,m=this.m_ClipFillType):(k=this.m_ClipFillType,m=this.m_SubjFillType);b.PolyTyp==d.PolyType.ptSubject?(n=this.m_SubjFillType,l=this.m_ClipFillType): +(n=this.m_ClipFillType,l=this.m_SubjFillType);switch(k){case d.PolyFillType.pftPositive:k=a.WindCnt;break;case d.PolyFillType.pftNegative:k=-a.WindCnt;break;default:k=Math.abs(a.WindCnt)}switch(n){case d.PolyFillType.pftPositive:n=b.WindCnt;break;case d.PolyFillType.pftNegative:n=-b.WindCnt;break;default:n=Math.abs(b.WindCnt)}if(g&&h)f||e||0!==k&&1!=k||0!==n&&1!=n||a.PolyTyp!=b.PolyTyp&&this.m_ClipType!=d.ClipType.ctXor?this.AddLocalMaxPoly(a,b,c):(this.AddOutPt(a,c),this.AddOutPt(b,c),d.Clipper.SwapSides(a, +b),d.Clipper.SwapPolyIndexes(a,b));else if(g){if(0===n||1==n)this.AddOutPt(a,c),d.Clipper.SwapSides(a,b),d.Clipper.SwapPolyIndexes(a,b)}else if(h){if(0===k||1==k)this.AddOutPt(b,c),d.Clipper.SwapSides(a,b),d.Clipper.SwapPolyIndexes(a,b)}else if(!(0!==k&&1!=k||0!==n&&1!=n||f||e)){switch(m){case d.PolyFillType.pftPositive:g=a.WindCnt2;break;case d.PolyFillType.pftNegative:g=-a.WindCnt2;break;default:g=Math.abs(a.WindCnt2)}switch(l){case d.PolyFillType.pftPositive:h=b.WindCnt2;break;case d.PolyFillType.pftNegative:h= +-b.WindCnt2;break;default:h=Math.abs(b.WindCnt2)}if(a.PolyTyp!=b.PolyTyp)this.AddLocalMinPoly(a,b,c);else if(1==k&&1==n)switch(this.m_ClipType){case d.ClipType.ctIntersection:0=g&&0>=h&&this.AddLocalMinPoly(a,b,c);break;case d.ClipType.ctDifference:(a.PolyTyp==d.PolyType.ptClip&&0=g&&0>=h)&&this.AddLocalMinPoly(a,b,c);break;case d.ClipType.ctXor:this.AddLocalMinPoly(a,b,c)}else d.Clipper.SwapSides(a, +b)}f!=e&&(f&&0<=a.OutIdx||e&&0<=b.OutIdx)&&(d.Clipper.SwapSides(a,b),d.Clipper.SwapPolyIndexes(a,b));f&&this.DeleteFromAEL(a);e&&this.DeleteFromAEL(b)}};d.Clipper.prototype.DeleteFromAEL=function(a){var b=a.PrevInAEL,c=a.NextInAEL;if(null!==b||null!==c||a==this.m_ActiveEdges)null!==b?b.NextInAEL=c:this.m_ActiveEdges=c,null!==c&&(c.PrevInAEL=b),a.NextInAEL=null,a.PrevInAEL=null};d.Clipper.prototype.DeleteFromSEL=function(a){var b=a.PrevInSEL,c=a.NextInSEL;if(null!==b||null!==c||a==this.m_SortedEdges)null!== +b?b.NextInSEL=c:this.m_SortedEdges=c,null!==c&&(c.PrevInSEL=b),a.NextInSEL=null,a.PrevInSEL=null};d.Clipper.prototype.UpdateEdgeIntoAEL=function(a){null===a.NextInLML&&d.Error("UpdateEdgeIntoAEL: invalid call");var b=a.PrevInAEL,c=a.NextInAEL;a.NextInLML.OutIdx=a.OutIdx;null!==b?b.NextInAEL=a.NextInLML:this.m_ActiveEdges=a.NextInLML;null!==c&&(c.PrevInAEL=a.NextInLML);a.NextInLML.Side=a.Side;a.NextInLML.WindDelta=a.WindDelta;a.NextInLML.WindCnt=a.WindCnt;a.NextInLML.WindCnt2=a.WindCnt2;a=a.NextInLML; +a.Curr.X=a.Bot.X;a.Curr.Y=a.Bot.Y;a.PrevInAEL=b;a.NextInAEL=c;d.ClipperBase.IsHorizontal(a)||this.InsertScanbeam(a.Top.Y);return a};d.Clipper.prototype.ProcessHorizontals=function(a){for(var b=this.m_SortedEdges;null!==b;)this.DeleteFromSEL(b),this.ProcessHorizontal(b,a),b=this.m_SortedEdges};d.Clipper.prototype.GetHorzDirection=function(a,b){a.Bot.X=f){0<=a.OutIdx&&0!=a.WindDelta&&this.PrepareHorzJoins(a,b);if(n==l&&k){e==d.Direction.dLeftToRight?this.IntersectEdges(a,n,n.Top,!1):this.IntersectEdges(n,a,n.Top,!1);0<=l.OutIdx&&d.Error("ProcessHorizontal error");return}if(e==d.Direction.dLeftToRight){var m=new d.IntPoint(n.Curr.X,a.Curr.Y);this.IntersectEdges(a, +n,m,!0)}else m=new d.IntPoint(n.Curr.X,a.Curr.Y),this.IntersectEdges(n,a,m,!0);this.SwapPositionsInAEL(a,n)}else if(e==d.Direction.dLeftToRight&&n.Curr.X>=g||e==d.Direction.dRightToLeft&&n.Curr.X<=f)break;n=c}0<=a.OutIdx&&0!==a.WindDelta&&this.PrepareHorzJoins(a,b);if(null!==a.NextInLML&&d.ClipperBase.IsHorizontal(a.NextInLML))a=this.UpdateEdgeIntoAEL(a),0<=a.OutIdx&&this.AddOutPt(a,a.Bot),c={Dir:e,Left:f,Right:g},this.GetHorzDirection(a,c),e=c.Dir,f=c.Left,g=c.Right;else break}null!==a.NextInLML? +0<=a.OutIdx?(e=this.AddOutPt(a,a.Top),a=this.UpdateEdgeIntoAEL(a),0!==a.WindDelta&&(f=a.PrevInAEL,c=a.NextInAEL,null!==f&&f.Curr.X==a.Bot.X&&f.Curr.Y==a.Bot.Y&&0!==f.WindDelta&&0<=f.OutIdx&&f.Curr.Y>f.Top.Y&&d.ClipperBase.SlopesEqual(a,f,this.m_UseFullRange)?(c=this.AddOutPt(f,a.Bot),this.AddJoin(e,c,a.Top)):null!==c&&c.Curr.X==a.Bot.X&&c.Curr.Y==a.Bot.Y&&0!==c.WindDelta&&0<=c.OutIdx&&c.Curr.Y>c.Top.Y&&d.ClipperBase.SlopesEqual(a,c,this.m_UseFullRange)&&(c=this.AddOutPt(c,a.Bot),this.AddJoin(e,c, +a.Top)))):this.UpdateEdgeIntoAEL(a):null!==l?0<=l.OutIdx?(e==d.Direction.dLeftToRight?this.IntersectEdges(a,l,a.Top,!1):this.IntersectEdges(l,a,a.Top,!1),0<=l.OutIdx&&d.Error("ProcessHorizontal error")):(this.DeleteFromAEL(a),this.DeleteFromAEL(l)):(0<=a.OutIdx&&this.AddOutPt(a,a.Top),this.DeleteFromAEL(a))};d.Clipper.prototype.GetNextInAEL=function(a,b){return b==d.Direction.dLeftToRight?a.NextInAEL:a.PrevInAEL};d.Clipper.prototype.IsMinima=function(a){return null!==a&&a.Prev.NextInLML!=a&&a.Next.NextInLML!= +a};d.Clipper.prototype.IsMaxima=function(a,b){return null!==a&&a.Top.Y==b&&null===a.NextInLML};d.Clipper.prototype.IsIntermediate=function(a,b){return a.Top.Y==b&&null!==a.NextInLML};d.Clipper.prototype.GetMaximaPair=function(a){var b=null;d.IntPoint.op_Equality(a.Next.Top,a.Top)&&null===a.Next.NextInLML?b=a.Next:d.IntPoint.op_Equality(a.Prev.Top,a.Top)&&null===a.Prev.NextInLML&&(b=a.Prev);return null===b||-2!=b.OutIdx&&(b.NextInAEL!=b.PrevInAEL||d.ClipperBase.IsHorizontal(b))?b:null};d.Clipper.prototype.ProcessIntersections= +function(a,b){if(null==this.m_ActiveEdges)return!0;try{this.BuildIntersectList(a,b);if(0==this.m_IntersectList.length)return!0;if(1==this.m_IntersectList.length||this.FixupIntersectionOrder())this.ProcessIntersectList();else return!1}catch(c){this.m_SortedEdges=null,this.m_IntersectList.length=0,d.Error("ProcessIntersections error")}this.m_SortedEdges=null;return!0};d.Clipper.prototype.BuildIntersectList=function(a,b){if(null!==this.m_ActiveEdges){var c=this.m_ActiveEdges;for(this.m_SortedEdges=c;null!== +c;)c.PrevInSEL=c.PrevInAEL,c.NextInSEL=c.NextInAEL,c.Curr.X=d.Clipper.TopX(c,b),c=c.NextInAEL;for(var e=!0;e&&null!==this.m_SortedEdges;){e=!1;for(c=this.m_SortedEdges;null!==c.NextInSEL;){var f=c.NextInSEL,g=new d.IntPoint;c.Curr.X>f.Curr.X?(!this.IntersectPoint(c,f,g)&&c.Curr.X>f.Curr.X+1&&d.Error("Intersection error"),g.Y>a&&(g.Y=a,Math.abs(c.Dx)>Math.abs(f.Dx)?g.X=d.Clipper.TopX(f,a):g.X=d.Clipper.TopX(c,a)),e=new d.IntersectNode,e.Edge1=c,e.Edge2=f,e.Pt.X=g.X,e.Pt.Y=g.Y,this.m_IntersectList.push(e), +this.SwapPositionsInSEL(c,f),e=!0):c=f}if(null!==c.PrevInSEL)c.PrevInSEL.NextInSEL=null;else break}this.m_SortedEdges=null}};d.Clipper.prototype.EdgesAdjacent=function(a){return a.Edge1.NextInSEL==a.Edge2||a.Edge1.PrevInSEL==a.Edge2};d.Clipper.IntersectNodeSort=function(a,b){return b.Pt.Y-a.Pt.Y};d.Clipper.prototype.FixupIntersectionOrder=function(){this.m_IntersectList.sort(this.m_IntersectNodeComparer);this.CopyAELToSEL();for(var a=this.m_IntersectList.length,b=0;ba?Math.ceil(a-0.5):Math.round(a)};F=function(a){return 0>a?Math.ceil(a-0.5):Math.floor(a+0.5)};G=function(a){return 0>a?-Math.round(Math.abs(a)):Math.round(a)};H=function(a){if(0>a)return a-=0.5,-2147483648>a?Math.ceil(a):a|0;a+=0.5;return 2147483647a.Bot.Y?(c.X=b.Bot.X,c.Y=b.Bot.Y):(c.X=a.Bot.X,c.Y=a.Bot.Y),!1;if(0===a.Delta.X)c.X=a.Bot.X,d.ClipperBase.IsHorizontal(b)?c.Y=b.Bot.Y:(f=b.Bot.Y-b.Bot.X/b.Dx,c.Y=d.Clipper.Round(c.X/b.Dx+f));else if(0===b.Delta.X)c.X=b.Bot.X,d.ClipperBase.IsHorizontal(a)?c.Y=a.Bot.Y:(e=a.Bot.Y-a.Bot.X/a.Dx,c.Y=d.Clipper.Round(c.X/a.Dx+e));else{e=a.Bot.X-a.Bot.Y*a.Dx;f=b.Bot.X-b.Bot.Y*b.Dx;var g=(f-e)/(a.Dx-b.Dx);c.Y=d.Clipper.Round(g);Math.abs(a.Dx)b.Top.Y)return c.Y=a.Top.Y,c.X=d.Clipper.TopX(b,a.Top.Y),c.Xe.Top.Y&&d.ClipperBase.SlopesEqual(b,e,this.m_UseFullRange)&&0!==b.WindDelta&&0!==e.WindDelta?(e=this.AddOutPt(e,b.Bot),this.AddJoin(c,e,b.Top)):null!==f&&f.Curr.X==b.Bot.X&&f.Curr.Y==b.Bot.Y&&null!==c&&0<=f.OutIdx&&f.Curr.Y>f.Top.Y&&d.ClipperBase.SlopesEqual(b,f,this.m_UseFullRange)&& +0!==b.WindDelta&&0!==f.WindDelta&&(e=this.AddOutPt(f,b.Bot),this.AddJoin(c,e,b.Top))}b=b.NextInAEL}};d.Clipper.prototype.DoMaxima=function(a){var b=this.GetMaximaPair(a);if(null===b)0<=a.OutIdx&&this.AddOutPt(a,a.Top),this.DeleteFromAEL(a);else{for(var c=a.NextInAEL;null!==c&&c!=b;)this.IntersectEdges(a,c,a.Top,!0),this.SwapPositionsInAEL(a,c),c=a.NextInAEL;-1==a.OutIdx&&-1==b.OutIdx?(this.DeleteFromAEL(a),this.DeleteFromAEL(b)):0<=a.OutIdx&&0<=b.OutIdx?this.IntersectEdges(a,b,a.Top,!1):0===a.WindDelta? +(0<=a.OutIdx&&(this.AddOutPt(a,a.Top),a.OutIdx=-1),this.DeleteFromAEL(a),0<=b.OutIdx&&(this.AddOutPt(b,a.Top),b.OutIdx=-1),this.DeleteFromAEL(b)):d.Error("DoMaxima error")}};d.Clipper.ReversePaths=function(a){for(var b=0,c=a.length;bf)){for(var g=Array(f),h=0;hf||!e.IsOpen&&3>f)){this.FixHoleLinkage(e);var g=new d.PolyNode;a.m_AllPolys.push(g);e.PolyNode=g;g.m_polygon.length=f;for(var e=e.Pts.Prev,h=0;h< +f;h++)g.m_polygon[h]=e.Pt,e=e.Prev}}b=0;for(c=this.m_PolyOuts.length;bb.Pt.X?d.Direction.dRightToLeft:d.Direction.dLeftToRight;e=c.Pt.X>e.Pt.X?d.Direction.dRightToLeft:d.Direction.dLeftToRight;if(h==e)return!1;if(h==d.Direction.dLeftToRight){for(;a.Next.Pt.X<=f.X&&a.Next.Pt.X>= +a.Pt.X&&a.Next.Pt.Y==f.Y;)a=a.Next;g&&a.Pt.X!=f.X&&(a=a.Next);b=this.DupOutPt(a,!g);d.IntPoint.op_Inequality(b.Pt,f)&&(a=b,a.Pt.X=f.X,a.Pt.Y=f.Y,b=this.DupOutPt(a,!g))}else{for(;a.Next.Pt.X>=f.X&&a.Next.Pt.X<=a.Pt.X&&a.Next.Pt.Y==f.Y;)a=a.Next;g||a.Pt.X==f.X||(a=a.Next);b=this.DupOutPt(a,g);d.IntPoint.op_Inequality(b.Pt,f)&&(a=b,a.Pt.X=f.X,a.Pt.Y=f.Y,b=this.DupOutPt(a,g))}if(e==d.Direction.dLeftToRight){for(;c.Next.Pt.X<=f.X&&c.Next.Pt.X>=c.Pt.X&&c.Next.Pt.Y==f.Y;)c=c.Next;g&&c.Pt.X!=f.X&&(c=c.Next); +e=this.DupOutPt(c,!g);d.IntPoint.op_Inequality(e.Pt,f)&&(c=e,c.Pt.X=f.X,c.Pt.Y=f.Y,e=this.DupOutPt(c,!g))}else{for(;c.Next.Pt.X>=f.X&&c.Next.Pt.X<=c.Pt.X&&c.Next.Pt.Y==f.Y;)c=c.Next;g||c.Pt.X==f.X||(c=c.Next);e=this.DupOutPt(c,g);d.IntPoint.op_Inequality(e.Pt,f)&&(c=e,c.Pt.X=f.X,c.Pt.Y=f.Y,e=this.DupOutPt(c,g))}h==d.Direction.dLeftToRight==g?(a.Prev=c,c.Next=a,b.Next=e,e.Prev=b):(a.Next=c,c.Prev=a,b.Prev=e,e.Next=b);return!0};d.Clipper.prototype.JoinPoints=function(a,b,c){var e=a.OutPt1,f=new d.OutPt, +g=a.OutPt2,h=new d.OutPt;if((h=a.OutPt1.Pt.Y==a.OffPt.Y)&&d.IntPoint.op_Equality(a.OffPt,a.OutPt1.Pt)&&d.IntPoint.op_Equality(a.OffPt,a.OutPt2.Pt)){for(f=a.OutPt1.Next;f!=e&&d.IntPoint.op_Equality(f.Pt,a.OffPt);)f=f.Next;f=f.Pt.Y>a.OffPt.Y;for(h=a.OutPt2.Next;h!=g&&d.IntPoint.op_Equality(h.Pt,a.OffPt);)h=h.Next;if(f==h.Pt.Y>a.OffPt.Y)return!1;f?(f=this.DupOutPt(e,!1),h=this.DupOutPt(g,!0),e.Prev=g,g.Next=e,f.Next=h,h.Prev=f):(f=this.DupOutPt(e,!0),h=this.DupOutPt(g,!1),e.Next=g,g.Prev=e,f.Prev=h, +h.Next=f);a.OutPt1=e;a.OutPt2=f;return!0}if(h){for(f=e;e.Prev.Pt.Y==e.Pt.Y&&e.Prev!=f&&e.Prev!=g;)e=e.Prev;for(;f.Next.Pt.Y==f.Pt.Y&&f.Next!=e&&f.Next!=g;)f=f.Next;if(f.Next==e||f.Next==g)return!1;for(h=g;g.Prev.Pt.Y==g.Pt.Y&&g.Prev!=h&&g.Prev!=f;)g=g.Prev;for(;h.Next.Pt.Y==h.Pt.Y&&h.Next!=g&&h.Next!=e;)h=h.Next;if(h.Next==g||h.Next==e)return!1;c={Left:null,Right:null};if(!this.GetOverlap(e.Pt.X,f.Pt.X,g.Pt.X,h.Pt.X,c))return!1;b=c.Left;var l=c.Right;c=new d.IntPoint;e.Pt.X>=b&&e.Pt.X<=l?(c.X=e.Pt.X, +c.Y=e.Pt.Y,b=e.Pt.X>f.Pt.X):g.Pt.X>=b&&g.Pt.X<=l?(c.X=g.Pt.X,c.Y=g.Pt.Y,b=g.Pt.X>h.Pt.X):f.Pt.X>=b&&f.Pt.X<=l?(c.X=f.Pt.X,c.Y=f.Pt.Y,b=f.Pt.X>e.Pt.X):(c.X=h.Pt.X,c.Y=h.Pt.Y,b=h.Pt.X>g.Pt.X);a.OutPt1=e;a.OutPt2=g;return this.JoinHorz(e,f,g,h,c,b)}for(f=e.Next;d.IntPoint.op_Equality(f.Pt,e.Pt)&&f!=e;)f=f.Next;if(l=f.Pt.Y>e.Pt.Y||!d.ClipperBase.SlopesEqual(e.Pt,f.Pt,a.OffPt,this.m_UseFullRange)){for(f=e.Prev;d.IntPoint.op_Equality(f.Pt,e.Pt)&&f!=e;)f=f.Prev;if(f.Pt.Y>e.Pt.Y||!d.ClipperBase.SlopesEqual(e.Pt, +f.Pt,a.OffPt,this.m_UseFullRange))return!1}for(h=g.Next;d.IntPoint.op_Equality(h.Pt,g.Pt)&&h!=g;)h=h.Next;var k=h.Pt.Y>g.Pt.Y||!d.ClipperBase.SlopesEqual(g.Pt,h.Pt,a.OffPt,this.m_UseFullRange);if(k){for(h=g.Prev;d.IntPoint.op_Equality(h.Pt,g.Pt)&&h!=g;)h=h.Prev;if(h.Pt.Y>g.Pt.Y||!d.ClipperBase.SlopesEqual(g.Pt,h.Pt,a.OffPt,this.m_UseFullRange))return!1}if(f==e||h==g||f==h||b==c&&l==k)return!1;l?(f=this.DupOutPt(e,!1),h=this.DupOutPt(g,!0),e.Prev=g,g.Next=e,f.Next=h,h.Prev=f):(f=this.DupOutPt(e,!0), +h=this.DupOutPt(g,!1),e.Next=g,g.Prev=e,f.Prev=h,h.Next=f);a.OutPt1=e;a.OutPt2=f;return!0};d.Clipper.GetBounds=function(a){for(var b=0,c=a.length;be.right&&(e.right=a[b][f].X),a[b][f].Ye.bottom&&(e.bottom=a[b][f].Y);return e};d.Clipper.prototype.GetBounds2= +function(a){var b=a,c=new d.IntRect;c.left=a.Pt.X;c.right=a.Pt.X;c.top=a.Pt.Y;c.bottom=a.Pt.Y;for(a=a.Next;a!=b;)a.Pt.Xc.right&&(c.right=a.Pt.X),a.Pt.Yc.bottom&&(c.bottom=a.Pt.Y),a=a.Next;return c};d.Clipper.PointInPolygon=function(a,b){var c=0,e=b.length;if(3>e)return 0;for(var d=b[0],g=1;g<=e;++g){var h=g==e?b[0]:b[g];if(h.Y==a.Y&&(h.X==a.X||d.Y==a.Y&&h.X>a.X==d.X=a.X)if(h.X>a.X)c=1-c;else{var l= +(d.X-a.X)*(h.Y-a.Y)-(h.X-a.X)*(d.Y-a.Y);if(0==l)return-1;0d.Y&&(c=1-c)}else if(h.X>a.X){l=(d.X-a.X)*(h.Y-a.Y)-(h.X-a.X)*(d.Y-a.Y);if(0==l)return-1;0d.Y&&(c=1-c)}d=h}return c};d.Clipper.prototype.PointInPolygon=function(a,b){for(var c=0,e=b;;){var d=b.Pt.X,g=b.Pt.Y,h=b.Next.Pt.X,l=b.Next.Pt.Y;if(l==a.Y&&(h==a.X||g==a.Y&&h>a.X==d=a.X)if(h>a.X)c=1-c;else{d=(d-a.X)*(l-a.Y)-(h-a.X)*(g-a.Y);if(0==d)return-1;0g&&(c=1-c)}else if(h>a.X){d=(d-a.X)*(l- +a.Y)-(h-a.X)*(g-a.Y);if(0==d)return-1;0g&&(c=1-c)}b=b.Next;if(e==b)break}return c};d.Clipper.prototype.Poly2ContainsPoly1=function(a,b){var c=a;do{var e=this.PointInPolygon(c.Pt,b);if(0<=e)return 0!=e;c=c.Next}while(c!=a);return!0};d.Clipper.prototype.FixupFirstLefts1=function(a,b){for(var c=0,e=this.m_PolyOuts.length;cb)return 0;for(var c=0,e=0,d=b-1;ec&&(c=0);for(var g=Array(c),f=0;fe)){var f=new d.PolyNode;f.m_jointype=b;f.m_endtype=c;if(c==d.EndType.etClosedLine||c==d.EndType.etClosedPolygon)for(;0f.m_polygon[b].Y||a[h].Y==f.m_polygon[b].Y&&a[h].Xg||c!=d.EndType.etClosedPolygon&&0>g)&&(this.m_polyNodes.AddChild(f),c==d.EndType.etClosedPolygon))if(0>this.m_lowest.X)this.m_lowest=new d.IntPoint(0,b);else if(a=this.m_polyNodes.Childs()[this.m_lowest.X].m_polygon[this.m_lowest.Y],f.m_polygon[b].Y>a.Y||f.m_polygon[b].Y==a.Y&&f.m_polygon[b].X=this.ArcTolerance?d.ClipperOffset.def_arc_tolerance:this.ArcTolerance>Math.abs(a)*d.ClipperOffset.def_arc_tolerance?Math.abs(a)*d.ClipperOffset.def_arc_tolerance:this.ArcTolerance,e=3.14159265358979/Math.acos(1-b/Math.abs(a));this.m_sin=Math.sin(d.ClipperOffset.two_pi/e);this.m_cos=Math.cos(d.ClipperOffset.two_pi/e);this.m_StepsPerRad=e/d.ClipperOffset.two_pi;0>a&&(this.m_sin= +-this.m_sin);for(b=0;b=a&&(3>f||c.m_endtype!=d.EndType.etClosedPolygon))){this.m_destPoly=[];if(1==f)if(c.m_jointype==d.JoinType.jtRound)for(var c=1,f=0,g=1;g<=e;g++){this.m_destPoly.push(new d.IntPoint(d.ClipperOffset.Round(this.m_srcPoly[0].X+c*a),d.ClipperOffset.Round(this.m_srcPoly[0].Y+f*a)));var h=c,c=c*this.m_cos-this.m_sin*f,f=h*this.m_sin+f*this.m_cos}else for(f= +c=-1,g=0;4>g;++g)this.m_destPoly.push(new d.IntPoint(d.ClipperOffset.Round(this.m_srcPoly[0].X+c*a),d.ClipperOffset.Round(this.m_srcPoly[0].Y+f*a))),0>c?c=1:0>f?f=1:c=-1;else{for(g=this.m_normals.length=0;gthis.m_sinA&&-5E-5this.m_sinA&&(this.m_sinA=-1);if(0>this.m_sinA*this.m_delta)this.m_destPoly.push(new d.IntPoint(d.ClipperOffset.Round(this.m_srcPoly[a].X+this.m_normals[b].X*this.m_delta),d.ClipperOffset.Round(this.m_srcPoly[a].Y+this.m_normals[b].Y*this.m_delta))),this.m_destPoly.push(new d.IntPoint(this.m_srcPoly[a])),this.m_destPoly.push(new d.IntPoint(d.ClipperOffset.Round(this.m_srcPoly[a].X+this.m_normals[a].X*this.m_delta),d.ClipperOffset.Round(this.m_srcPoly[a].Y+this.m_normals[a].Y* +this.m_delta)));else switch(c){case d.JoinType.jtMiter:c=1+(this.m_normals[a].X*this.m_normals[b].X+this.m_normals[a].Y*this.m_normals[b].Y);c>=this.m_miterLim?this.DoMiter(a,b,c):this.DoSquare(a,b);break;case d.JoinType.jtSquare:this.DoSquare(a,b);break;case d.JoinType.jtRound:this.DoRound(a,b)}return a};d.ClipperOffset.prototype.DoSquare=function(a,b){var c=Math.tan(Math.atan2(this.m_sinA,this.m_normals[b].X*this.m_normals[a].X+this.m_normals[b].Y*this.m_normals[a].Y)/4);this.m_destPoly.push(new d.IntPoint(d.ClipperOffset.Round(this.m_srcPoly[a].X+ +this.m_delta*(this.m_normals[b].X-this.m_normals[b].Y*c)),d.ClipperOffset.Round(this.m_srcPoly[a].Y+this.m_delta*(this.m_normals[b].Y+this.m_normals[b].X*c))));this.m_destPoly.push(new d.IntPoint(d.ClipperOffset.Round(this.m_srcPoly[a].X+this.m_delta*(this.m_normals[a].X+this.m_normals[a].Y*c)),d.ClipperOffset.Round(this.m_srcPoly[a].Y+this.m_delta*(this.m_normals[a].Y-this.m_normals[a].X*c))))};d.ClipperOffset.prototype.DoMiter=function(a,b,c){c=this.m_delta/c;this.m_destPoly.push(new d.IntPoint(d.ClipperOffset.Round(this.m_srcPoly[a].X+ +(this.m_normals[b].X+this.m_normals[a].X)*c),d.ClipperOffset.Round(this.m_srcPoly[a].Y+(this.m_normals[b].Y+this.m_normals[a].Y)*c)))};d.ClipperOffset.prototype.DoRound=function(a,b){for(var c=Math.atan2(this.m_sinA,this.m_normals[b].X*this.m_normals[a].X+this.m_normals[b].Y*this.m_normals[a].Y),c=d.Cast_Int32(d.ClipperOffset.Round(this.m_StepsPerRad*Math.abs(c))),e=this.m_normals[b].X,f=this.m_normals[b].Y,g,h=0;hb)return a;c||(a=[a]);for(var e=a.length,f,g,h,l,k,n,m,p=[],q=0;qf)h=g,p.push(h);else{h=g;l=b*b;k=g[0];for(m=n=1;mb)return d.JS.Clone(a);a[0]instanceof Array||(a=[a]);var c,e, +f,g,h,l,k,m,p,q,r,s,t,u,v,x=a.length,y=b*b,w=[];for(c=0;cg;g++){h=[];l=f.length;f[l-1].X!=f[0].X||f[l-1].Y!=f[0].Y?(r=1,f.push({X:f[0].X,Y:f[0].Y}),l=f.length):r=0;q=[];for(e=0;en)return 0;b&&(a[n]=a[0],n++);for(;--n;)g=a[n],k=g.X,g=g.Y,h=a[n-1],m=h.X,h=h.Y,d+=e((k-m)*(k-m)+(g-h)*(g-h));b&&a.pop();return d/c};d.JS.PerimeterOfPaths= +function(a,b,c){c||(c=1);for(var e=0,f=0;f 0) this[0] = x; - else if (x < -1) this[0] = x + DV; + else if (x < -1) this[0] = x + this.DV; else this.t = 0; } // return bigint initialized to value @@ -263,13 +303,15 @@ continue; } mi = false; - if (sh == 0) this[this.t++] = x; + if (sh == 0) + this[this.t++] = x; else if (sh + k > this.DB) { this[this.t - 1] |= (x & ((1 << (this.DB - sh)) - 1)) << sh; this[this.t++] = (x >> (this.DB - sh)); } - else this[this.t - 1] |= x << sh; + else + this[this.t - 1] |= x << sh; sh += k; if (sh >= this.DB) sh -= this.DB; } @@ -279,7 +321,7 @@ if (sh > 0) this[this.t - 1] |= ((1 << (this.DB - sh)) - 1) << sh; } this.clamp(); - if (mi) Int128.ZERO.subTo(this, this); + if (mi) BigInteger.ZERO.subTo(this, this); } // (protected) clamp off excess high words function bnpClamp() @@ -290,8 +332,7 @@ // (public) return string representation in given radix function bnToString(b) { - if (this.s < 0) return "-" + this.negate() - .toString(b); + if (this.s < 0) return "-" + this.negate().toString(b); var k; if (b == 16) k = 4; else if (b == 8) k = 3; @@ -337,7 +378,7 @@ function bnNegate() { var r = nbi(); - Int128.ZERO.subTo(this, r); + BigInteger.ZERO.subTo(this, r); return r; } // (public) |this| @@ -353,7 +394,8 @@ var i = this.t; r = i - a.t; if (r != 0) return (this.s < 0) ? -r : r; - while (--i >= 0) if ((r = this[i] - a[i]) != 0) return r; + while (--i >= 0) + if ((r = this[i] - a[i]) != 0) return r; return 0; } // returns bit length of the integer x @@ -505,7 +547,7 @@ for (i = 0; i < y.t; ++i) r[i + x.t] = x.am(0, y[i], r, i, 0, x.t); r.s = 0; r.clamp(); - if (this.s != a.s) Int128.ZERO.subTo(r, r); + if (this.s != a.s) BigInteger.ZERO.subTo(r, r); } // (protected) r = this^2, r != this (HAC 14.16) function bnpSquareTo(r) @@ -570,7 +612,7 @@ r[r.t++] = 1; r.subTo(t, r); } - Int128.ONE.dlShiftTo(ys, t); + BigInteger.ONE.dlShiftTo(ys, t); t.subTo(y, y); // "negative" y so we can replace sub with am later while (y.t < ys) y[y.t++] = 0; while (--j >= 0) @@ -587,20 +629,19 @@ if (q != null) { r.drShiftTo(ys, q); - if (ts != ms) Int128.ZERO.subTo(q, q); + if (ts != ms) BigInteger.ZERO.subTo(q, q); } r.t = ys; r.clamp(); if (nsh > 0) r.rShiftTo(nsh, r); // Denormalize remainder - if (ts < 0) Int128.ZERO.subTo(r, r); + if (ts < 0) BigInteger.ZERO.subTo(r, r); } // (public) this mod a function bnMod(a) { var r = nbi(); - this.abs() - .divRemTo(a, null, r); - if (this.s < 0 && r.compareTo(Int128.ZERO) > 0) a.subTo(r, r); + this.abs().divRemTo(a, null, r); + if (this.s < 0 && r.compareTo(BigInteger.ZERO) > 0) a.subTo(r, r); return r; } // Modular reduction using "classic" algorithm @@ -680,10 +721,9 @@ function montConvert(x) { var r = nbi(); - x.abs() - .dlShiftTo(this.m.t, r); + x.abs().dlShiftTo(this.m.t, r); r.divRemTo(this.m, null, r); - if (x.s < 0 && r.compareTo(Int128.ZERO) > 0) this.m.subTo(r, r); + if (x.s < 0 && r.compareTo(BigInteger.ZERO) > 0) this.m.subTo(r, r); return r; } // x/R mod m @@ -698,7 +738,7 @@ function montReduce(x) { while (x.t <= this.mt2) // pad x so am has enough room later - x[x.t++] = 0; + x[x.t++] = 0; for (var i = 0; i < this.m.t; ++i) { // faster way of calculating u0 = x[i]*mp mod DV @@ -743,7 +783,7 @@ // (protected) this^e, e < 2^32, doing sqr and mul with "r" (HAC 14.79) function bnpExp(e, z) { - if (e > 0xffffffff || e < 1) return Int128.ONE; + if (e > 0xffffffff || e < 1) return BigInteger.ONE; var r = nbi(), r2 = nbi(), g = z.convert(this), @@ -771,37 +811,37 @@ return this.exp(e, z); } // protected - Int128.prototype.copyTo = bnpCopyTo; - Int128.prototype.fromInt = bnpFromInt; - Int128.prototype.fromString = bnpFromString; - Int128.prototype.clamp = bnpClamp; - Int128.prototype.dlShiftTo = bnpDLShiftTo; - Int128.prototype.drShiftTo = bnpDRShiftTo; - Int128.prototype.lShiftTo = bnpLShiftTo; - Int128.prototype.rShiftTo = bnpRShiftTo; - Int128.prototype.subTo = bnpSubTo; - Int128.prototype.multiplyTo = bnpMultiplyTo; - Int128.prototype.squareTo = bnpSquareTo; - Int128.prototype.divRemTo = bnpDivRemTo; - Int128.prototype.invDigit = bnpInvDigit; - Int128.prototype.isEven = bnpIsEven; - Int128.prototype.exp = bnpExp; + BigInteger.prototype.copyTo = bnpCopyTo; + BigInteger.prototype.fromInt = bnpFromInt; + BigInteger.prototype.fromString = bnpFromString; + BigInteger.prototype.clamp = bnpClamp; + BigInteger.prototype.dlShiftTo = bnpDLShiftTo; + BigInteger.prototype.drShiftTo = bnpDRShiftTo; + BigInteger.prototype.lShiftTo = bnpLShiftTo; + BigInteger.prototype.rShiftTo = bnpRShiftTo; + BigInteger.prototype.subTo = bnpSubTo; + BigInteger.prototype.multiplyTo = bnpMultiplyTo; + BigInteger.prototype.squareTo = bnpSquareTo; + BigInteger.prototype.divRemTo = bnpDivRemTo; + BigInteger.prototype.invDigit = bnpInvDigit; + BigInteger.prototype.isEven = bnpIsEven; + BigInteger.prototype.exp = bnpExp; // public - Int128.prototype.toString = bnToString; - Int128.prototype.negate = bnNegate; - Int128.prototype.abs = bnAbs; - Int128.prototype.compareTo = bnCompareTo; - Int128.prototype.bitLength = bnBitLength; - Int128.prototype.mod = bnMod; - Int128.prototype.modPowInt = bnModPowInt; + BigInteger.prototype.toString = bnToString; + BigInteger.prototype.negate = bnNegate; + BigInteger.prototype.abs = bnAbs; + BigInteger.prototype.compareTo = bnCompareTo; + BigInteger.prototype.bitLength = bnBitLength; + BigInteger.prototype.mod = bnMod; + BigInteger.prototype.modPowInt = bnModPowInt; // "constants" - Int128.ZERO = nbv(0); - Int128.ONE = nbv(1); + BigInteger.ZERO = nbv(0); + BigInteger.ONE = nbv(1); // Copyright (c) 2005-2009 Tom Wu // All Rights Reserved. // See "LICENSE" for details. // Extended JavaScript BN functions, required for RSA private ops. - // Version 1.1: new Int128("0", 10) returns "proper" zero + // Version 1.1: new BigInteger("0", 10) returns "proper" zero // Version 1.2: square() API, isProbablePrime fix // (public) function bnClone() @@ -859,13 +899,10 @@ this.divRemTo(d, y, z); while (y.signum() > 0) { - r = (a + z.intValue()) - .toString(b) - .substr(1) + r; + r = (a + z.intValue()).toString(b).substr(1) + r; y.divRemTo(d, y, z); } - return z.intValue() - .toString(b) + r; + return z.intValue().toString(b) + r; } // (protected) convert from radix string function bnpFromRadix(s, b) @@ -899,32 +936,32 @@ this.dMultiply(Math.pow(b, j)); this.dAddOffset(w, 0); } - if (mi) Int128.ZERO.subTo(this, this); + if (mi) BigInteger.ZERO.subTo(this, this); } // (protected) alternate constructor function bnpFromNumber(a, b, c) { if ("number" == typeof b) { - // new Int128(int,int,RNG) + // new BigInteger(int,int,RNG) if (a < 2) this.fromInt(1); else { this.fromNumber(a, c); if (!this.testBit(a - 1)) // force MSB set - this.bitwiseTo(Int128.ONE.shiftLeft(a - 1), op_or, this); + this.bitwiseTo(BigInteger.ONE.shiftLeft(a - 1), op_or, this); if (this.isEven()) this.dAddOffset(1, 0); // force odd while (!this.isProbablePrime(b)) { this.dAddOffset(2, 0); - if (this.bitLength() > a) this.subTo(Int128.ONE.shiftLeft(a - 1), this); + if (this.bitLength() > a) this.subTo(BigInteger.ONE.shiftLeft(a - 1), this); } } } else { - // new Int128(int,RNG) - var x = [], + // new BigInteger(int,RNG) + var x = new Array(), t = a & 7; x.length = (a >> 3) + 1; b.nextBytes(x); @@ -937,13 +974,14 @@ function bnToByteArray() { var i = this.t, - r = []; + r = new Array(); r[0] = this.s; var p = this.DB - (i * this.DB) % 8, d, k = 0; if (i-- > 0) { - if (p < this.DB && (d = this[i] >> p) != (this.s & this.DM) >> p) r[k++] = d | (this.s << (this.DB - p)); + if (p < this.DB && (d = this[i] >> p) != (this.s & this.DM) >> p) + r[k++] = d | (this.s << (this.DB - p)); while (i >= 0) { if (p < 8) @@ -1107,7 +1145,7 @@ function bnGetLowestSetBit() { for (var i = 0; i < this.t; ++i) - if (this[i] != 0) return i * this.DB + lbit(this[i]); + if (this[i] != 0) return i * this.DB + lbit(this[i]); if (this.s < 0) return this.t * this.DB; return -1; } @@ -1140,7 +1178,7 @@ // (protected) this op (1<= 0 while (--i >= 0) r[i] = 0; for (i = Math.max(n - this.t, 0); i < a.t; ++i) - r[this.t + i - n] = this.am(n - i, a[i], r, 0, 0, this.t + i - n); + r[this.t + i - n] = this.am(n - i, a[i], r, 0, 0, this.t + i - n); r.clamp(); r.drShiftTo(1, r); } @@ -1328,7 +1366,7 @@ // setup Barrett this.r2 = nbi(); this.q3 = nbi(); - Int128.ONE.dlShiftTo(2 * m.t, this.r2); + BigInteger.ONE.dlShiftTo(2 * m.t, this.r2); this.mu = this.r2.divide(m); this.m = m; } @@ -1394,11 +1432,14 @@ else if (i < 144) k = 4; else if (i < 768) k = 5; else k = 6; - if (i < 8) z = new Classic(m); - else if (m.isEven()) z = new Barrett(m); - else z = new Montgomery(m); + if (i < 8) + z = new Classic(m); + else if (m.isEven()) + z = new Barrett(m); + else + z = new Montgomery(m); // precomputation - var g = [], + var g = new Array(), n = 3, k1 = k - 1, km = (1 << k) - 1; @@ -1519,15 +1560,17 @@ if (n <= 0) return 0; var d = this.DV % n, r = (this.s < 0) ? n - 1 : 0; - if (this.t > 0) if (d == 0) r = this[0] % n; - else for (var i = this.t - 1; i >= 0; --i) r = (d * r + this[i]) % n; + if (this.t > 0) + if (d == 0) r = this[0] % n; + else + for (var i = this.t - 1; i >= 0; --i) r = (d * r + this[i]) % n; return r; } // (public) 1/this % m (HAC 14.61) function bnModInverse(m) { var ac = m.isEven(); - if ((this.isEven() && ac) || m.signum() == 0) return Int128.ZERO; + if ((this.isEven() && ac) || m.signum() == 0) return BigInteger.ZERO; var u = m.clone(), v = this.clone(); var a = nbv(1), @@ -1579,7 +1622,7 @@ d.subTo(b, d); } } - if (v.compareTo(Int128.ONE) != 0) return Int128.ZERO; + if (v.compareTo(BigInteger.ONE) != 0) return BigInteger.ZERO; if (d.compareTo(m) >= 0) return d.subtract(m); if (d.signum() < 0) d.addTo(m, d); else return d; @@ -1595,7 +1638,7 @@ if (x.t == 1 && x[0] <= lowprimes[lowprimes.length - 1]) { for (i = 0; i < lowprimes.length; ++i) - if (x[0] == lowprimes[i]) return true; + if (x[0] == lowprimes[i]) return true; return false; } if (x.isEven()) return false; @@ -1606,14 +1649,15 @@ j = i + 1; while (j < lowprimes.length && m < lplim) m *= lowprimes[j++]; m = x.modInt(m); - while (i < j) if (m % lowprimes[i++] == 0) return false; + while (i < j) + if (m % lowprimes[i++] == 0) return false; } return x.millerRabin(t); } // (protected) true if probably prime (HAC 4.24, Miller-Rabin) function bnpMillerRabin(t) { - var n1 = this.subtract(Int128.ONE); + var n1 = this.subtract(BigInteger.ONE); var k = n1.getLowestSetBit(); if (k <= 0) return false; var r = n1.shiftRight(k); @@ -1625,13 +1669,13 @@ //Pick bases at random, instead of starting at 2 a.fromInt(lowprimes[Math.floor(Math.random() * lowprimes.length)]); var y = a.modPow(r, this); - if (y.compareTo(Int128.ONE) != 0 && y.compareTo(n1) != 0) + if (y.compareTo(BigInteger.ONE) != 0 && y.compareTo(n1) != 0) { var j = 1; while (j++ < k && y.compareTo(n1) != 0) { y = y.modPowInt(2, this); - if (y.compareTo(Int128.ONE) == 0) return false; + if (y.compareTo(BigInteger.ONE) == 0) return false; } if (y.compareTo(n1) != 0) return false; } @@ -1639,61 +1683,281 @@ return true; } // protected - Int128.prototype.chunkSize = bnpChunkSize; - Int128.prototype.toRadix = bnpToRadix; - Int128.prototype.fromRadix = bnpFromRadix; - Int128.prototype.fromNumber = bnpFromNumber; - Int128.prototype.bitwiseTo = bnpBitwiseTo; - Int128.prototype.changeBit = bnpChangeBit; - Int128.prototype.addTo = bnpAddTo; - Int128.prototype.dMultiply = bnpDMultiply; - Int128.prototype.dAddOffset = bnpDAddOffset; - Int128.prototype.multiplyLowerTo = bnpMultiplyLowerTo; - Int128.prototype.multiplyUpperTo = bnpMultiplyUpperTo; - Int128.prototype.modInt = bnpModInt; - Int128.prototype.millerRabin = bnpMillerRabin; + BigInteger.prototype.chunkSize = bnpChunkSize; + BigInteger.prototype.toRadix = bnpToRadix; + BigInteger.prototype.fromRadix = bnpFromRadix; + BigInteger.prototype.fromNumber = bnpFromNumber; + BigInteger.prototype.bitwiseTo = bnpBitwiseTo; + BigInteger.prototype.changeBit = bnpChangeBit; + BigInteger.prototype.addTo = bnpAddTo; + BigInteger.prototype.dMultiply = bnpDMultiply; + BigInteger.prototype.dAddOffset = bnpDAddOffset; + BigInteger.prototype.multiplyLowerTo = bnpMultiplyLowerTo; + BigInteger.prototype.multiplyUpperTo = bnpMultiplyUpperTo; + BigInteger.prototype.modInt = bnpModInt; + BigInteger.prototype.millerRabin = bnpMillerRabin; // public - Int128.prototype.clone = bnClone; - Int128.prototype.intValue = bnIntValue; - Int128.prototype.byteValue = bnByteValue; - Int128.prototype.shortValue = bnShortValue; - Int128.prototype.signum = bnSigNum; - Int128.prototype.toByteArray = bnToByteArray; - Int128.prototype.equals = bnEquals; - Int128.prototype.min = bnMin; - Int128.prototype.max = bnMax; - Int128.prototype.and = bnAnd; - Int128.prototype.or = bnOr; - Int128.prototype.xor = bnXor; - Int128.prototype.andNot = bnAndNot; - Int128.prototype.not = bnNot; - Int128.prototype.shiftLeft = bnShiftLeft; - Int128.prototype.shiftRight = bnShiftRight; - Int128.prototype.getLowestSetBit = bnGetLowestSetBit; - Int128.prototype.bitCount = bnBitCount; - Int128.prototype.testBit = bnTestBit; - Int128.prototype.setBit = bnSetBit; - Int128.prototype.clearBit = bnClearBit; - Int128.prototype.flipBit = bnFlipBit; - Int128.prototype.add = bnAdd; - Int128.prototype.subtract = bnSubtract; - Int128.prototype.multiply = bnMultiply; - Int128.prototype.divide = bnDivide; - Int128.prototype.remainder = bnRemainder; - Int128.prototype.divideAndRemainder = bnDivideAndRemainder; - Int128.prototype.modPow = bnModPow; - Int128.prototype.modInverse = bnModInverse; - Int128.prototype.pow = bnPow; - Int128.prototype.gcd = bnGCD; - Int128.prototype.isProbablePrime = bnIsProbablePrime; + BigInteger.prototype.clone = bnClone; + BigInteger.prototype.intValue = bnIntValue; + BigInteger.prototype.byteValue = bnByteValue; + BigInteger.prototype.shortValue = bnShortValue; + BigInteger.prototype.signum = bnSigNum; + BigInteger.prototype.toByteArray = bnToByteArray; + BigInteger.prototype.equals = bnEquals; + BigInteger.prototype.min = bnMin; + BigInteger.prototype.max = bnMax; + BigInteger.prototype.and = bnAnd; + BigInteger.prototype.or = bnOr; + BigInteger.prototype.xor = bnXor; + BigInteger.prototype.andNot = bnAndNot; + BigInteger.prototype.not = bnNot; + BigInteger.prototype.shiftLeft = bnShiftLeft; + BigInteger.prototype.shiftRight = bnShiftRight; + BigInteger.prototype.getLowestSetBit = bnGetLowestSetBit; + BigInteger.prototype.bitCount = bnBitCount; + BigInteger.prototype.testBit = bnTestBit; + BigInteger.prototype.setBit = bnSetBit; + BigInteger.prototype.clearBit = bnClearBit; + BigInteger.prototype.flipBit = bnFlipBit; + BigInteger.prototype.add = bnAdd; + BigInteger.prototype.subtract = bnSubtract; + BigInteger.prototype.multiply = bnMultiply; + BigInteger.prototype.divide = bnDivide; + BigInteger.prototype.remainder = bnRemainder; + BigInteger.prototype.divideAndRemainder = bnDivideAndRemainder; + BigInteger.prototype.modPow = bnModPow; + BigInteger.prototype.modInverse = bnModInverse; + BigInteger.prototype.pow = bnPow; + BigInteger.prototype.gcd = bnGCD; + BigInteger.prototype.isProbablePrime = bnIsProbablePrime; // JSBN-specific extension - Int128.prototype.square = bnSquare; - + BigInteger.prototype.square = bnSquare; + var Int128 = BigInteger; + // BigInteger interfaces not implemented in jsbn: + // BigInteger(int signum, byte[] magnitude) + // double doubleValue() + // float floatValue() + // int hashCode() + // long longValue() + // static BigInteger valueOf(long val) + // Helper functions to make BigInteger functions callable with two parameters + // as in original C# Clipper + Int128.prototype.IsNegative = function () + { + if (this.compareTo(Int128.ZERO) == -1) return true; + else return false; + }; + Int128.op_Equality = function (val1, val2) + { + if (val1.compareTo(val2) == 0) return true; + else return false; + }; + Int128.op_Inequality = function (val1, val2) + { + if (val1.compareTo(val2) != 0) return true; + else return false; + }; + Int128.op_GreaterThan = function (val1, val2) + { + if (val1.compareTo(val2) > 0) return true; + else return false; + }; + Int128.op_LessThan = function (val1, val2) + { + if (val1.compareTo(val2) < 0) return true; + else return false; + }; + Int128.op_Addition = function (lhs, rhs) + { + return new Int128(lhs).add(new Int128(rhs)); + }; + Int128.op_Subtraction = function (lhs, rhs) + { + return new Int128(lhs).subtract(new Int128(rhs)); + }; + Int128.Int128Mul = function (lhs, rhs) + { + return new Int128(lhs).multiply(new Int128(rhs)); + }; + Int128.op_Division = function (lhs, rhs) + { + return lhs.divide(rhs); + }; + Int128.prototype.ToDouble = function () + { + return parseFloat(this.toString()); // This could be something faster + }; // end of Int128 section - - //window.Int128 = Int128; // Uncomment this if you want to use Int128 outside ClipperLib - + /* + // Uncomment the following two lines if you want to use Int128 outside ClipperLib + if (typeof(document) !== "undefined") window.Int128 = Int128; + else self.Int128 = Int128; + */ + // --------------------------------------------- // Here starts the actual Clipper library: + // Helper function to support Inheritance in Javascript + if (typeof (Inherit) == 'undefined') + { + var Inherit = function (ce, ce2) + { + var p; + if (typeof (Object.getOwnPropertyNames) == 'undefined') + { + for (p in ce2.prototype) + if (typeof (ce.prototype[p]) == 'undefined' || ce.prototype[p] == Object.prototype[p]) ce.prototype[p] = ce2.prototype[p]; + for (p in ce2) + if (typeof (ce[p]) == 'undefined') ce[p] = ce2[p]; + ce.$baseCtor = ce2; + } + else + { + var props = Object.getOwnPropertyNames(ce2.prototype); + for (var i = 0; i < props.length; i++) + if (typeof (Object.getOwnPropertyDescriptor(ce.prototype, props[i])) == 'undefined') Object.defineProperty(ce.prototype, props[i], Object.getOwnPropertyDescriptor(ce2.prototype, props[i])); + for (p in ce2) + if (typeof (ce[p]) == 'undefined') ce[p] = ce2[p]; + ce.$baseCtor = ce2; + } + }; + } + ClipperLib.Path = function () + { + return []; + }; + ClipperLib.Paths = function () + { + return []; // Was previously [[]], but caused problems when pushed + }; + // Preserves the calling way of original C# Clipper + // Is essential due to compatibility, because DoublePoint is public class in original C# version + ClipperLib.DoublePoint = function () + { + var a = arguments; + this.X = 0; + this.Y = 0; + // public DoublePoint(DoublePoint dp) + // public DoublePoint(IntPoint ip) + if (a.length == 1) + { + this.X = a[0].X; + this.Y = a[0].Y; + } + else if (a.length == 2) + { + this.X = a[0]; + this.Y = a[1]; + } + }; // This is internal faster function when called without arguments + ClipperLib.DoublePoint0 = function () + { + this.X = 0; + this.Y = 0; + }; + // This is internal faster function when called with 1 argument (dp or ip) + ClipperLib.DoublePoint1 = function (dp) + { + this.X = dp.X; + this.Y = dp.Y; + }; + // This is internal faster function when called with 2 arguments (x and y) + ClipperLib.DoublePoint2 = function (x, y) + { + this.X = x; + this.Y = y; + }; + // PolyTree & PolyNode start + // ------------------------------- + ClipperLib.PolyNode = function () + { + this.m_Parent = null; + this.m_polygon = new ClipperLib.Path(); + this.m_Index = 0; + this.m_jointype = 0; + this.m_endtype = 0; + this.m_Childs = []; + this.IsOpen = false; + }; + ClipperLib.PolyNode.prototype.IsHoleNode = function () + { + var result = true; + var node = this.m_Parent; + while (node !== null) + { + result = !result; + node = node.m_Parent; + } + return result; + }; + ClipperLib.PolyNode.prototype.ChildCount = function () + { + return this.m_Childs.length; + }; + ClipperLib.PolyNode.prototype.Contour = function () + { + return this.m_polygon; + }; + ClipperLib.PolyNode.prototype.AddChild = function (Child) + { + var cnt = this.m_Childs.length; + this.m_Childs.push(Child); + Child.m_Parent = this; + Child.m_Index = cnt; + }; + ClipperLib.PolyNode.prototype.GetNext = function () + { + if (this.m_Childs.length > 0) + return this.m_Childs[0]; + else + return this.GetNextSiblingUp(); + }; + ClipperLib.PolyNode.prototype.GetNextSiblingUp = function () + { + if (this.m_Parent === null) + return null; + else if (this.m_Index == this.m_Parent.m_Childs.length - 1) + return this.m_Parent.GetNextSiblingUp(); + else + return this.m_Parent.m_Childs[this.m_Index + 1]; + }; + ClipperLib.PolyNode.prototype.Childs = function () + { + return this.m_Childs; + }; + ClipperLib.PolyNode.prototype.Parent = function () + { + return this.m_Parent; + }; + ClipperLib.PolyNode.prototype.IsHole = function () + { + return this.IsHoleNode(); + }; + // PolyTree : PolyNode + ClipperLib.PolyTree = function () + { + this.m_AllPolys = []; + ClipperLib.PolyNode.call(this); + }; + ClipperLib.PolyTree.prototype.Clear = function () + { + for (var i = 0, ilen = this.m_AllPolys.length; i < ilen; i++) + this.m_AllPolys[i] = null; + this.m_AllPolys.length = 0; + this.m_Childs.length = 0; + }; + ClipperLib.PolyTree.prototype.GetFirst = function () + { + if (this.m_Childs.length > 0) + return this.m_Childs[0]; + else + return null; + }; + ClipperLib.PolyTree.prototype.Total = function () + { + return this.m_AllPolys.length; + }; + Inherit(ClipperLib.PolyTree, ClipperLib.PolyNode); + // ------------------------------- + // PolyTree & PolyNode end ClipperLib.Math_Abs_Int64 = ClipperLib.Math_Abs_Int32 = ClipperLib.Math_Abs_Double = function (a) { return Math.abs(a); @@ -1707,11 +1971,13 @@ cast_32 speedtest: http://jsperf.com/truncate-float-to-integer/2 ----------------------------------- */ - if (browser.msie || browser.opera || browser.safari) ClipperLib.Cast_Int32 = function (a) { + if (browser.msie || browser.opera || browser.safari) ClipperLib.Cast_Int32 = function (a) + { return a | 0; }; - else ClipperLib.Cast_Int32 = function (a) { // eg. browser.chrome || browser.chromium || browser.firefox - return ~~a; + else ClipperLib.Cast_Int32 = function (a) + { // eg. browser.chrome || browser.chromium || browser.firefox + return~~ a; }; /* -------------------------- @@ -1727,63 +1993,212 @@ Safari: to_floor_or_ceil -------------------------- */ - if (browser.chrome) ClipperLib.Cast_Int64 = function (a) { + if (browser.chrome) ClipperLib.Cast_Int64 = function (a) + { if (a < -2147483648 || a > 2147483647) - return a < 0 ? Math.ceil(a): Math.floor(a); - else return ~~a; + return a < 0 ? Math.ceil(a) : Math.floor(a); + else return~~ a; }; - else if (browser.firefox && typeof(Number.toInteger) == "function") ClipperLib.Cast_Int64 = function(a) { + else if (browser.firefox && typeof (Number.toInteger) == "function") ClipperLib.Cast_Int64 = function (a) + { return Number.toInteger(a); }; - else if (browser.msie7 || browser.msie8) ClipperLib.Cast_Int64 = function(a) { + else if (browser.msie7 || browser.msie8) ClipperLib.Cast_Int64 = function (a) + { return parseInt(a, 10); }; - else if (browser.msie) ClipperLib.Cast_Int64 = function (a) { + else if (browser.msie) ClipperLib.Cast_Int64 = function (a) + { if (a < -2147483648 || a > 2147483647) - return a < 0 ? Math.ceil(a): Math.floor(a); + return a < 0 ? Math.ceil(a) : Math.floor(a); return a | 0; }; // eg. browser.chromium || browser.firefox || browser.opera || browser.safari - else ClipperLib.Cast_Int64 = function(a) { - return a < 0 ? Math.ceil(a): Math.floor(a); + else ClipperLib.Cast_Int64 = function (a) + { + return a < 0 ? Math.ceil(a) : Math.floor(a); }; ClipperLib.Clear = function (a) { a.length = 0; }; - ClipperLib.MaxSteps = 64; // How many steps at maximum in arc in BuildArc() function + //ClipperLib.MaxSteps = 64; // How many steps at maximum in arc in BuildArc() function ClipperLib.PI = 3.141592653589793; ClipperLib.PI2 = 2 * 3.141592653589793; ClipperLib.IntPoint = function () { - var a = arguments; - if (a.length == 1) + var a = arguments, + alen = a.length; + this.X = 0; + this.Y = 0; + if (use_xyz) { - this.X = a[0].X; - this.Y = a[0].Y; - + this.Z = 0; + if (alen == 3) // public IntPoint(cInt x, cInt y, cInt z = 0) + { + this.X = a[0]; + this.Y = a[1]; + this.Z = a[2]; + } + else if (alen == 2) // public IntPoint(cInt x, cInt y) + { + this.X = a[0]; + this.Y = a[1]; + this.Z = 0; + } + else if (alen == 1) + { + if (a[0] instanceof ClipperLib.DoublePoint) // public IntPoint(DoublePoint dp) + { + var dp = a[0]; + this.X = ClipperLib.Clipper.Round(dp.X); + this.Y = ClipperLib.Clipper.Round(dp.Y); + this.Z = 0; + } + else // public IntPoint(IntPoint pt) + { + var pt = a[0]; + if (typeof (pt.Z) == "undefined") pt.Z = 0; + this.X = pt.X; + this.Y = pt.Y; + this.Z = pt.Z; + } + } + else // public IntPoint() + { + this.X = 0; + this.Y = 0; + this.Z = 0; + } } - if (a.length == 2) + else // if (!use_xyz) { - this.X = a[0]; - this.Y = a[1]; + if (alen == 2) // public IntPoint(cInt X, cInt Y) + { + this.X = a[0]; + this.Y = a[1]; + } + else if (alen == 1) + { + if (a[0] instanceof ClipperLib.DoublePoint) // public IntPoint(DoublePoint dp) + { + var dp = a[0]; + this.X = ClipperLib.Clipper.Round(dp.X); + this.Y = ClipperLib.Clipper.Round(dp.Y); + } + else // public IntPoint(IntPoint pt) + { + var pt = a[0]; + this.X = pt.X; + this.Y = pt.Y; + } + } + else // public IntPoint(IntPoint pt) + { + this.X = 0; + this.Y = 0; + } } }; - ClipperLib.IntRect = function () + ClipperLib.IntPoint.op_Equality = function (a, b) { - var a = arguments; - if (a.length == 4) // function (l, t, r, b) + //return a == b; + return a.X == b.X && a.Y == b.Y; + }; + ClipperLib.IntPoint.op_Inequality = function (a, b) + { + //return a != b; + return a.X != b.X || a.Y != b.Y; + }; + /* + ClipperLib.IntPoint.prototype.Equals = function (obj) + { + if (obj === null) + return false; + if (obj instanceof ClipperLib.IntPoint) { - var l = a[0], - t = a[1], - r = a[2], - b = a[3]; - this.left = l; - this.top = t; - this.right = r; - this.bottom = b; + var a = Cast(obj, ClipperLib.IntPoint); + return (this.X == a.X) && (this.Y == a.Y); } else + return false; + }; +*/ + if (use_xyz) + { + ClipperLib.IntPoint0 = function () + { + this.X = 0; + this.Y = 0; + this.Z = 0; + }; + ClipperLib.IntPoint1 = function (pt) + { + this.X = pt.X; + this.Y = pt.Y; + this.Z = pt.Z; + }; + ClipperLib.IntPoint1dp = function (dp) + { + this.X = ClipperLib.Clipper.Round(dp.X); + this.Y = ClipperLib.Clipper.Round(dp.Y); + this.Z = 0; + }; + ClipperLib.IntPoint2 = function (x, y) + { + this.X = x; + this.Y = y; + this.Z = 0; + }; + ClipperLib.IntPoint3 = function (x, y, z) + { + this.X = x; + this.Y = y; + this.Z = z; + }; + } + else // if (!use_xyz) + { + ClipperLib.IntPoint0 = function () + { + this.X = 0; + this.Y = 0; + }; + ClipperLib.IntPoint1 = function (pt) + { + this.X = pt.X; + this.Y = pt.Y; + }; + ClipperLib.IntPoint1dp = function (dp) + { + this.X = ClipperLib.Clipper.Round(dp.X); + this.Y = ClipperLib.Clipper.Round(dp.Y); + }; + ClipperLib.IntPoint2 = function (x, y) + { + this.X = x; + this.Y = y; + }; + } + ClipperLib.IntRect = function () + { + var a = arguments, + alen = a.length; + if (alen == 4) // function (l, t, r, b) + { + this.left = a[0]; + this.top = a[1]; + this.right = a[2]; + this.bottom = a[3]; + } + else if (alen == 1) // function (ir) + { + this.left = ir.left; + this.top = ir.top; + this.right = ir.right; + this.bottom = ir.bottom; + } + else // function () { this.left = 0; this.top = 0; @@ -1791,18 +2206,26 @@ this.bottom = 0; } }; - ClipperLib.Polygon = function () + ClipperLib.IntRect0 = function () { - return []; + this.left = 0; + this.top = 0; + this.right = 0; + this.bottom = 0; }; - ClipperLib.Polygons = function () + ClipperLib.IntRect1 = function (ir) { - return [[]]; + this.left = ir.left; + this.top = ir.top; + this.right = ir.right; + this.bottom = ir.bottom; }; - ClipperLib.ExPolygon = function () + ClipperLib.IntRect4 = function (l, t, r, b) { - this.outer = null; - this.holes = null; + this.left = l; + this.top = t; + this.right = r; + this.bottom = b; }; ClipperLib.ClipType = { ctIntersection: 0, @@ -1825,16 +2248,23 @@ jtRound: 1, jtMiter: 2 }; - + ClipperLib.EndType = { + etOpenSquare: 0, + etOpenRound: 1, + etOpenButt: 2, + etClosedLine: 3, + etClosedPolygon: 4 + }; + if (use_deprecated) + ClipperLib.EndType_ = { + etSquare: 0, + etRound: 1, + etButt: 2, + etClosed: 3 + }; ClipperLib.EdgeSide = { - esLeft: 1, - esRight: 2 - }; - ClipperLib.Protects = { - ipNone: 0, - ipLeft: 1, - ipRight: 2, - ipBoth: 3 + esLeft: 0, + esRight: 1 }; ClipperLib.Direction = { dRightToLeft: 0, @@ -1842,505 +2272,772 @@ }; ClipperLib.TEdge = function () { - this.xbot = 0; - this.ybot = 0; - this.xcurr = 0; - this.ycurr = 0; - this.xtop = 0; - this.ytop = 0; - this.dx = 0; - this.deltaX = 0; - this.deltaY = 0; - this.tmpX = 0; - this.polyType = ClipperLib.PolyType.ptSubject; - this.side = null; //= ClipperLib.EdgeSide.esNeither; - this.windDelta = 0; - this.windCnt = 0; - this.windCnt2 = 0; - this.outIdx = 0; - this.next = null; - this.prev = null; - this.nextInLML = null; - this.nextInAEL = null; - this.prevInAEL = null; - this.nextInSEL = null; - this.prevInSEL = null; + this.Bot = new ClipperLib.IntPoint(); + this.Curr = new ClipperLib.IntPoint(); + this.Top = new ClipperLib.IntPoint(); + this.Delta = new ClipperLib.IntPoint(); + this.Dx = 0; + this.PolyTyp = ClipperLib.PolyType.ptSubject; + this.Side = ClipperLib.EdgeSide.esLeft; + this.WindDelta = 0; + this.WindCnt = 0; + this.WindCnt2 = 0; + this.OutIdx = 0; + this.Next = null; + this.Prev = null; + this.NextInLML = null; + this.NextInAEL = null; + this.PrevInAEL = null; + this.NextInSEL = null; + this.PrevInSEL = null; }; ClipperLib.IntersectNode = function () { - this.edge1 = null; - this.edge2 = null; - this.pt = null; - this.next = null; + this.Edge1 = null; + this.Edge2 = null; + this.Pt = new ClipperLib.IntPoint(); + }; + ClipperLib.MyIntersectNodeSort = function () {}; + ClipperLib.MyIntersectNodeSort.Compare = function (node1, node2) + { + return (node2.Pt.Y - node1.Pt.Y); }; ClipperLib.LocalMinima = function () { this.Y = 0; - this.leftBound = null; - this.rightBound = null; - this.next = null; + this.LeftBound = null; + this.RightBound = null; + this.Next = null; }; ClipperLib.Scanbeam = function () { this.Y = 0; - this.next = null; + this.Next = null; }; ClipperLib.OutRec = function () { - this.idx = 0; - this.isHole = false; + this.Idx = 0; + this.IsHole = false; + this.IsOpen = false; this.FirstLeft = null; - this.AppendLink = null; - this.pts = null; - this.bottomPt = null; + this.Pts = null; + this.BottomPt = null; + this.PolyNode = null; }; ClipperLib.OutPt = function () { - this.idx = 0; - this.pt = null; - this.next = null; - this.prev = null; - }; - ClipperLib.JoinRec = function () - { - this.pt1a = null; - this.pt1b = null; - this.poly1Idx = 0; - this.pt2a = null; - this.pt2b = null; - this.poly2Idx = 0; + this.Idx = 0; + this.Pt = new ClipperLib.IntPoint(); + this.Next = null; + this.Prev = null; }; - ClipperLib.HorzJoinRec = function () + ClipperLib.Join = function () { - this.edge = null; - this.savedIdx = 0; + this.OutPt1 = null; + this.OutPt2 = null; + this.OffPt = new ClipperLib.IntPoint(); }; ClipperLib.ClipperBase = function () { this.m_MinimaList = null; this.m_CurrentLM = null; - this.m_edges = [ - [] - ]; // 2-dimensional array + this.m_edges = new Array(); + this.m_UseFullRange = false; + this.m_HasOpenPaths = false; + this.PreserveCollinear = false; + this.m_MinimaList = null; + this.m_CurrentLM = null; this.m_UseFullRange = false; + this.m_HasOpenPaths = false; }; - // Ranges are in original C# too high for Javascript (in current state 2012 December): + // Ranges are in original C# too high for Javascript (in current state 2013 september): // protected const double horizontal = -3.4E+38; - // internal const Int64 loRange = 0x3FFFFFFF; // = 1073741823 = sqrt(2^63 -1)/2 - // internal const Int64 hiRange = 0x3FFFFFFFFFFFFFFFL; // = 4611686018427387903 = sqrt(2^127 -1)/2 - // So had to adjust them to more suitable: - ClipperLib.ClipperBase.horizontal = -9007199254740992; //-2^53 - ClipperLib.ClipperBase.loRange = 47453132; // sqrt(2^53 -1)/2 - ClipperLib.ClipperBase.hiRange = 4503599627370495; // sqrt(2^106 -1)/2 + // internal const cInt loRange = 0x3FFFFFFF; // = 1073741823 = sqrt(2^63 -1)/2 + // internal const cInt hiRange = 0x3FFFFFFFFFFFFFFFL; // = 4611686018427387903 = sqrt(2^127 -1)/2 + // So had to adjust them to more suitable for Javascript. // If JS some day supports truly 64-bit integers, then these ranges can be as in C# // and biginteger library can be more simpler (as then 128bit can be represented as two 64bit numbers) - ClipperLib.ClipperBase.PointsEqual = function (pt1, pt2) + ClipperLib.ClipperBase.horizontal = -9007199254740992; //-2^53 + ClipperLib.ClipperBase.Skip = -2; + ClipperLib.ClipperBase.Unassigned = -1; + ClipperLib.ClipperBase.tolerance = 1E-20; + if (use_int32) + { + ClipperLib.ClipperBase.loRange = 46340; + ClipperLib.ClipperBase.hiRange = 46340; + } + else + { + ClipperLib.ClipperBase.loRange = 47453132; // sqrt(2^53 -1)/2 + ClipperLib.ClipperBase.hiRange = 4503599627370495; // sqrt(2^106 -1)/2 + } + ClipperLib.ClipperBase.near_zero = function (val) { - return (pt1.X == pt2.X && pt1.Y == pt2.Y); + return (val > -ClipperLib.ClipperBase.tolerance) && (val < ClipperLib.ClipperBase.tolerance); + }; + ClipperLib.ClipperBase.IsHorizontal = function (e) + { + return e.Delta.Y === 0; }; ClipperLib.ClipperBase.prototype.PointIsVertex = function (pt, pp) { var pp2 = pp; do { - if (ClipperLib.ClipperBase.PointsEqual(pp2.pt, pt)) return true; - pp2 = pp2.next; + if (ClipperLib.IntPoint.op_Equality(pp2.Pt, pt)) + return true; + pp2 = pp2.Next; } - while (pp2 != pp); + while (pp2 != pp) return false; }; - ClipperLib.ClipperBase.prototype.PointInPolygon = function (pt, pp, UseFulllongRange) + ClipperLib.ClipperBase.prototype.PointOnLineSegment = function (pt, linePt1, linePt2, UseFullRange) { - var pp2 = pp; - var result = false; - if (UseFulllongRange) - { - do { - if ((((pp2.pt.Y <= pt.Y) && (pt.Y < pp2.prev.pt.Y)) || ((pp2.prev.pt.Y <= pt.Y) && (pt.Y < pp2.pt.Y))) && new Int128(pt.X - pp2.pt.X) - .compareTo( - new Int128(pp2.prev.pt.X - pp2.pt.X) - .multiply(new Int128(pt.Y - pp2.pt.Y)) - .divide( - new Int128(pp2.prev.pt.Y - pp2.pt.Y))) < 0) result = !result; - pp2 = pp2.next; - } - while (pp2 != pp); - } + if (UseFullRange) + return ((pt.X == linePt1.X) && (pt.Y == linePt1.Y)) || + ((pt.X == linePt2.X) && (pt.Y == linePt2.Y)) || + (((pt.X > linePt1.X) == (pt.X < linePt2.X)) && + ((pt.Y > linePt1.Y) == (pt.Y < linePt2.Y)) && + (Int128.op_Equality(Int128.Int128Mul((pt.X - linePt1.X), (linePt2.Y - linePt1.Y)), + Int128.Int128Mul((linePt2.X - linePt1.X), (pt.Y - linePt1.Y))))); else + return ((pt.X == linePt1.X) && (pt.Y == linePt1.Y)) || ((pt.X == linePt2.X) && (pt.Y == linePt2.Y)) || (((pt.X > linePt1.X) == (pt.X < linePt2.X)) && ((pt.Y > linePt1.Y) == (pt.Y < linePt2.Y)) && ((pt.X - linePt1.X) * (linePt2.Y - linePt1.Y) == (linePt2.X - linePt1.X) * (pt.Y - linePt1.Y))); + }; + ClipperLib.ClipperBase.prototype.PointOnPolygon = function (pt, pp, UseFullRange) + { + var pp2 = pp; + while (true) { - do { - if ((((pp2.pt.Y <= pt.Y) && (pt.Y < pp2.prev.pt.Y)) || ((pp2.prev.pt.Y <= pt.Y) && (pt.Y < pp2.pt.Y))) && (pt.X - pp2.pt.X < (pp2.prev.pt.X - pp2.pt.X) * (pt.Y - pp2.pt.Y) / (pp2.prev.pt.Y - pp2.pt.Y))) result = !result; - pp2 = pp2.next; - } - while (pp2 != pp); + if (this.PointOnLineSegment(pt, pp2.Pt, pp2.Next.Pt, UseFullRange)) + return true; + pp2 = pp2.Next; + if (pp2 == pp) + break; } - return result; + return false; }; - ClipperLib.ClipperBase.prototype.SlopesEqual = function () + ClipperLib.ClipperBase.prototype.SlopesEqual = ClipperLib.ClipperBase.SlopesEqual = function () { - var a = arguments; + var a = arguments, + alen = a.length; var e1, e2, pt1, pt2, pt3, pt4, UseFullRange; - if (a.length == 3) // function (e1, e2, UseFullRange) + if (alen == 3) // function (e1, e2, UseFullRange) { - e1 = a[0], e2 = a[1], UseFullRange = a[2]; - if (UseFullRange) return new Int128(e1.deltaY) - .multiply(new Int128(e2.deltaX)) - .toString() == new Int128(e1.deltaX) - .multiply(new Int128(e2.deltaY)) - .toString(); - else return (e1.deltaY) * (e2.deltaX) == (e1.deltaX) * (e2.deltaY); + e1 = a[0]; + e2 = a[1]; + UseFullRange = a[2]; + if (UseFullRange) + return Int128.op_Equality(Int128.Int128Mul(e1.Delta.Y, e2.Delta.X), Int128.Int128Mul(e1.Delta.X, e2.Delta.Y)); + else + return ClipperLib.Cast_Int64((e1.Delta.Y) * (e2.Delta.X)) == ClipperLib.Cast_Int64((e1.Delta.X) * (e2.Delta.Y)); } - else if (a.length == 4) // function (pt1, pt2, pt3, UseFullRange) + else if (alen == 4) // function (pt1, pt2, pt3, UseFullRange) { - pt1 = a[0], pt2 = a[1], pt3 = a[2], UseFullRange = a[3]; - if (UseFullRange) return new Int128(pt1.Y - pt2.Y) - .multiply(new Int128(pt2.X - pt3.X)) - .toString() == new Int128(pt1.X - pt2.X) - .multiply(new Int128(pt2.Y - pt3.Y)) - .toString(); - else return (pt1.Y - pt2.Y) * (pt2.X - pt3.X) - (pt1.X - pt2.X) * (pt2.Y - pt3.Y) == 0; + pt1 = a[0]; + pt2 = a[1]; + pt3 = a[2]; + UseFullRange = a[3]; + if (UseFullRange) + return Int128.op_Equality(Int128.Int128Mul(pt1.Y - pt2.Y, pt2.X - pt3.X), Int128.Int128Mul(pt1.X - pt2.X, pt2.Y - pt3.Y)); + else + return ClipperLib.Cast_Int64((pt1.Y - pt2.Y) * (pt2.X - pt3.X)) - ClipperLib.Cast_Int64((pt1.X - pt2.X) * (pt2.Y - pt3.Y)) === 0; } - else if (a.length == 5) // function (pt1, pt2, pt3, pt4, UseFullRange) + else // function (pt1, pt2, pt3, pt4, UseFullRange) { - pt1 = a[0], pt2 = a[1], pt3 = a[2], pt4 = a[3], UseFullRange = a[4]; - if (UseFullRange) return new Int128(pt1.Y - pt2.Y) - .multiply(new Int128(pt3.X - pt4.X)) - .toString() == new Int128(pt1.X - pt2.X) - .multiply(new Int128(pt3.Y - pt4.Y)) - .toString(); - else return (pt1.Y - pt2.Y) * (pt3.X - pt4.X) - (pt1.X - pt2.X) * (pt3.Y - pt4.Y) == 0; + pt1 = a[0]; + pt2 = a[1]; + pt3 = a[2]; + pt4 = a[3]; + UseFullRange = a[4]; + if (UseFullRange) + return Int128.op_Equality(Int128.Int128Mul(pt1.Y - pt2.Y, pt3.X - pt4.X), Int128.Int128Mul(pt1.X - pt2.X, pt3.Y - pt4.Y)); + else + return ClipperLib.Cast_Int64((pt1.Y - pt2.Y) * (pt3.X - pt4.X)) - ClipperLib.Cast_Int64((pt1.X - pt2.X) * (pt3.Y - pt4.Y)) === 0; } }; + ClipperLib.ClipperBase.SlopesEqual3 = function (e1, e2, UseFullRange) + { + if (UseFullRange) + return Int128.op_Equality(Int128.Int128Mul(e1.Delta.Y, e2.Delta.X), Int128.Int128Mul(e1.Delta.X, e2.Delta.Y)); + else + return ClipperLib.Cast_Int64((e1.Delta.Y) * (e2.Delta.X)) == ClipperLib.Cast_Int64((e1.Delta.X) * (e2.Delta.Y)); + }; + ClipperLib.ClipperBase.SlopesEqual4 = function (pt1, pt2, pt3, UseFullRange) + { + if (UseFullRange) + return Int128.op_Equality(Int128.Int128Mul(pt1.Y - pt2.Y, pt2.X - pt3.X), Int128.Int128Mul(pt1.X - pt2.X, pt2.Y - pt3.Y)); + else + return ClipperLib.Cast_Int64((pt1.Y - pt2.Y) * (pt2.X - pt3.X)) - ClipperLib.Cast_Int64((pt1.X - pt2.X) * (pt2.Y - pt3.Y)) === 0; + }; + ClipperLib.ClipperBase.SlopesEqual5 = function (pt1, pt2, pt3, pt4, UseFullRange) + { + if (UseFullRange) + return Int128.op_Equality(Int128.Int128Mul(pt1.Y - pt2.Y, pt3.X - pt4.X), Int128.Int128Mul(pt1.X - pt2.X, pt3.Y - pt4.Y)); + else + return ClipperLib.Cast_Int64((pt1.Y - pt2.Y) * (pt3.X - pt4.X)) - ClipperLib.Cast_Int64((pt1.X - pt2.X) * (pt3.Y - pt4.Y)) === 0; + }; ClipperLib.ClipperBase.prototype.Clear = function () { this.DisposeLocalMinimaList(); - for (var i = 0; i < this.m_edges.length; ++i) + for (var i = 0, ilen = this.m_edges.length; i < ilen; ++i) { - for (var j = 0; j < this.m_edges[i].length; ++j) - this.m_edges[i][j] = null; + for (var j = 0, jlen = this.m_edges[i].length; j < jlen; ++j) + this.m_edges[i][j] = null; ClipperLib.Clear(this.m_edges[i]); } ClipperLib.Clear(this.m_edges); this.m_UseFullRange = false; + this.m_HasOpenPaths = false; }; ClipperLib.ClipperBase.prototype.DisposeLocalMinimaList = function () { - while (this.m_MinimaList != null) + while (this.m_MinimaList !== null) { - var tmpLm = this.m_MinimaList.next; + var tmpLm = this.m_MinimaList.Next; this.m_MinimaList = null; this.m_MinimaList = tmpLm; } this.m_CurrentLM = null; }; - ClipperLib.ClipperBase.prototype.AddPolygons = function (ppg, polyType) - { - var result = false; - var res = false; - if (!(ppg instanceof Array)) return result; - for (var i = 0; i < ppg.length; ++i) - { - res = this.AddPolygon(ppg[i], polyType, true); - if (res && res != "exceed") result = true; - else if (res == "exceed") break; - } - if (res == "exceed") ClipperLib.Error("Coordinate exceeds range bounds in AddPolygons()."); - return result; - }; - ClipperLib.ClipperBase.prototype.AddPolygon = function (pg, polyType, multiple) + ClipperLib.ClipperBase.prototype.RangeTest = function (Pt, useFullRange) { - if (!(pg instanceof Array)) return false; - var len = pg.length; - if (len < 3) return false; - var p = new ClipperLib.Polygon(); - p.push(new ClipperLib.IntPoint(pg[0].X, pg[0].Y)); - var j = 0; - var i; - var exceed = false; - for (i = 1; i < len; ++i) + if (useFullRange.Value) { - var maxVal; - if (this.m_UseFullRange) maxVal = ClipperLib.ClipperBase.hiRange; - else maxVal = ClipperLib.ClipperBase.loRange; - if (ClipperLib.Math_Abs_Int64(pg[i].X) > maxVal || ClipperLib.Math_Abs_Int64(pg[i].Y) > maxVal) - { - if (ClipperLib.Math_Abs_Int64(pg[i].X) > ClipperLib.ClipperBase.hiRange || ClipperLib.Math_Abs_Int64(pg[i].Y) > ClipperLib.ClipperBase.hiRange) - { - if (typeof(multiple) != "undefined") return "exceed"; - exceed = true; - break; - } - maxVal = ClipperLib.ClipperBase.hiRange; - this.m_UseFullRange = true; - } - if (ClipperLib.ClipperBase.PointsEqual(p[j], pg[i])) continue; - else if (j > 0 && this.SlopesEqual(p[j - 1], p[j], pg[i], this.m_UseFullRange)) - { - if (ClipperLib.ClipperBase.PointsEqual(p[j - 1], pg[i])) j--; - } - else j++; - if (j < p.length) p[j] = pg[i]; - else p.push(new ClipperLib.IntPoint(pg[i].X, pg[i].Y)); + if (Pt.X > ClipperLib.ClipperBase.hiRange || Pt.Y > ClipperLib.ClipperBase.hiRange || -Pt.X > ClipperLib.ClipperBase.hiRange || -Pt.Y > ClipperLib.ClipperBase.hiRange) + ClipperLib.Error("Coordinate outside allowed range in RangeTest()."); } - if (exceed && typeof(multiple) == "undefined") - ClipperLib.Error("Coordinate exceeds range bounds in AddPolygon()"); - - if (j < 2) return false; - len = j + 1; - while (len > 2) + else if (Pt.X > ClipperLib.ClipperBase.loRange || Pt.Y > ClipperLib.ClipperBase.loRange || -Pt.X > ClipperLib.ClipperBase.loRange || -Pt.Y > ClipperLib.ClipperBase.loRange) { - if (ClipperLib.ClipperBase.PointsEqual(p[j], p[0])) j--; - else if (ClipperLib.ClipperBase.PointsEqual(p[0], p[1]) || this.SlopesEqual(p[j], p[0], p[1], this.m_UseFullRange)) p[0] = p[j--]; - else if (this.SlopesEqual(p[j - 1], p[j], p[0], this.m_UseFullRange)) j--; - else if (this.SlopesEqual(p[0], p[1], p[2], this.m_UseFullRange)) - { - for (i = 2; i <= j; ++i) - p[i - 1] = p[i]; - j--; - } - else break; - len--; - } - - if (len < 3) return false; - var edges = []; - for (i = 0; i < len; i++) - edges.push(new ClipperLib.TEdge()); - this.m_edges.push(edges); - edges[0].xcurr = p[0].X; - edges[0].ycurr = p[0].Y; - this.InitEdge(edges[len - 1], edges[0], edges[len - 2], p[len - 1], polyType); - for (i = len - 2; i > 0; --i) - this.InitEdge(edges[i], edges[i + 1], edges[i - 1], p[i], polyType); - this.InitEdge(edges[0], edges[1], edges[len - 1], p[0], polyType); - var e = edges[0]; - var eHighest = e; - do { - e.xcurr = e.xbot; - e.ycurr = e.ybot; - if (e.ytop < eHighest.ytop) eHighest = e; - e = e.next; - } - while (e != edges[0]); - if (eHighest.windDelta > 0) eHighest = eHighest.next; - if (eHighest.dx == ClipperLib.ClipperBase.horizontal) eHighest = eHighest.next; - e = eHighest; - do { - e = this.AddBoundsToLML(e); + useFullRange.Value = true; + this.RangeTest(Pt, useFullRange); } - while (e != eHighest); - return true; }; - ClipperLib.ClipperBase.prototype.InitEdge = function (e, eNext, ePrev, pt, polyType) + ClipperLib.ClipperBase.prototype.InitEdge = function (e, eNext, ePrev, pt) + { + e.Next = eNext; + e.Prev = ePrev; + //e.Curr = pt; + e.Curr.X = pt.X; + e.Curr.Y = pt.Y; + e.OutIdx = -1; + }; + ClipperLib.ClipperBase.prototype.InitEdge2 = function (e, polyType) { - e.next = eNext; - e.prev = ePrev; - e.xcurr = pt.X; - e.ycurr = pt.Y; - if (e.ycurr >= e.next.ycurr) + if (e.Curr.Y >= e.Next.Curr.Y) { - e.xbot = e.xcurr; - e.ybot = e.ycurr; - e.xtop = e.next.xcurr; - e.ytop = e.next.ycurr; - e.windDelta = 1; + //e.Bot = e.Curr; + e.Bot.X = e.Curr.X; + e.Bot.Y = e.Curr.Y; + //e.Top = e.Next.Curr; + e.Top.X = e.Next.Curr.X; + e.Top.Y = e.Next.Curr.Y; } else { - e.xtop = e.xcurr; - e.ytop = e.ycurr; - e.xbot = e.next.xcurr; - e.ybot = e.next.ycurr; - e.windDelta = -1; + //e.Top = e.Curr; + e.Top.X = e.Curr.X; + e.Top.Y = e.Curr.Y; + //e.Bot = e.Next.Curr; + e.Bot.X = e.Next.Curr.X; + e.Bot.Y = e.Next.Curr.Y; } this.SetDx(e); - e.polyType = polyType; - e.outIdx = -1; + e.PolyTyp = polyType; }; - ClipperLib.ClipperBase.prototype.SetDx = function (e) + ClipperLib.ClipperBase.prototype.FindNextLocMin = function (E) { - e.deltaX = (e.xtop - e.xbot); - e.deltaY = (e.ytop - e.ybot); - if (e.deltaY == 0) e.dx = ClipperLib.ClipperBase.horizontal; - else e.dx = (e.deltaX) / (e.deltaY); + var E2; + for (;;) + { + while (ClipperLib.IntPoint.op_Inequality(E.Bot, E.Prev.Bot) || ClipperLib.IntPoint.op_Equality(E.Curr, E.Top)) + E = E.Next; + if (E.Dx != ClipperLib.ClipperBase.horizontal && E.Prev.Dx != ClipperLib.ClipperBase.horizontal) + break; + while (E.Prev.Dx == ClipperLib.ClipperBase.horizontal) + E = E.Prev; + E2 = E; + while (E.Dx == ClipperLib.ClipperBase.horizontal) + E = E.Next; + if (E.Top.Y == E.Prev.Bot.Y) + continue; + //ie just an intermediate horz. + if (E2.Prev.Bot.X < E.Bot.X) + E = E2; + break; + } + return E; }; - ClipperLib.ClipperBase.prototype.AddBoundsToLML = function (e) + ClipperLib.ClipperBase.prototype.ProcessBound = function (E, IsClockwise) { - e.nextInLML = null; - e = e.next; - for (;;) + var EStart = E, + Result = E; + var Horz; + var StartX; + if (E.Dx == ClipperLib.ClipperBase.horizontal) + { + //it's possible for adjacent overlapping horz edges to start heading left + //before finishing right, so ... + if (IsClockwise) + StartX = E.Prev.Bot.X; + else + StartX = E.Next.Bot.X; + if (E.Bot.X != StartX) + this.ReverseHorizontal(E); + } + if (Result.OutIdx != ClipperLib.ClipperBase.Skip) { - if (e.dx == ClipperLib.ClipperBase.horizontal) + if (IsClockwise) + { + while (Result.Top.Y == Result.Next.Bot.Y && Result.Next.OutIdx != ClipperLib.ClipperBase.Skip) + Result = Result.Next; + if (Result.Dx == ClipperLib.ClipperBase.horizontal && Result.Next.OutIdx != ClipperLib.ClipperBase.Skip) + { + //nb: at the top of a bound, horizontals are added to the bound + //only when the preceding edge attaches to the horizontal's left vertex + //unless a Skip edge is encountered when that becomes the top divide + Horz = Result; + while (Horz.Prev.Dx == ClipperLib.ClipperBase.horizontal) + Horz = Horz.Prev; + if (Horz.Prev.Top.X == Result.Next.Top.X) + { + if (!IsClockwise) + Result = Horz.Prev; + } + else if (Horz.Prev.Top.X > Result.Next.Top.X) + Result = Horz.Prev; + } + while (E != Result) + { + E.NextInLML = E.Next; + if (E.Dx == ClipperLib.ClipperBase.horizontal && E != EStart && E.Bot.X != E.Prev.Top.X) + this.ReverseHorizontal(E); + E = E.Next; + } + if (E.Dx == ClipperLib.ClipperBase.horizontal && E != EStart && E.Bot.X != E.Prev.Top.X) + this.ReverseHorizontal(E); + Result = Result.Next; + //move to the edge just beyond current bound + } + else { - if (e.next.ytop < e.ytop && e.next.xbot > e.prev.xbot) break; - if (e.xtop != e.prev.xbot) this.SwapX(e); - e.nextInLML = e.prev; + while (Result.Top.Y == Result.Prev.Bot.Y && Result.Prev.OutIdx != ClipperLib.ClipperBase.Skip) + Result = Result.Prev; + if (Result.Dx == ClipperLib.ClipperBase.horizontal && Result.Prev.OutIdx != ClipperLib.ClipperBase.Skip) + { + Horz = Result; + while (Horz.Next.Dx == ClipperLib.ClipperBase.horizontal) + Horz = Horz.Next; + if (Horz.Next.Top.X == Result.Prev.Top.X) + { + if (!IsClockwise) + Result = Horz.Next; + } + else if (Horz.Next.Top.X > Result.Prev.Top.X) + Result = Horz.Next; + } + while (E != Result) + { + E.NextInLML = E.Prev; + if (E.Dx == ClipperLib.ClipperBase.horizontal && E != EStart && E.Bot.X != E.Next.Top.X) + this.ReverseHorizontal(E); + E = E.Prev; + } + if (E.Dx == ClipperLib.ClipperBase.horizontal && E != EStart && E.Bot.X != E.Next.Top.X) + this.ReverseHorizontal(E); + Result = Result.Prev; + //move to the edge just beyond current bound } - else if (e.ycurr == e.prev.ycurr) break; - else e.nextInLML = e.prev; - e = e.next; } - var newLm = new ClipperLib.LocalMinima(); - newLm.next = null; - newLm.Y = e.prev.ybot; - if (e.dx == ClipperLib.ClipperBase.horizontal) + if (Result.OutIdx == ClipperLib.ClipperBase.Skip) { - if (e.xbot != e.prev.xbot) this.SwapX(e); - newLm.leftBound = e.prev; - newLm.rightBound = e; + //if edges still remain in the current bound beyond the skip edge then + //create another LocMin and call ProcessBound once more + E = Result; + if (IsClockwise) + { + while (E.Top.Y == E.Next.Bot.Y) + E = E.Next; + //don't include top horizontals when parsing a bound a second time, + //they will be contained in the opposite bound ... + while (E != Result && E.Dx == ClipperLib.ClipperBase.horizontal) + E = E.Prev; + } + else + { + while (E.Top.Y == E.Prev.Bot.Y) + E = E.Prev; + while (E != Result && E.Dx == ClipperLib.ClipperBase.horizontal) + E = E.Next; + } + if (E == Result) + { + if (IsClockwise) + Result = E.Next; + else + Result = E.Prev; + } + else + { + //there are more edges in the bound beyond result starting with E + if (IsClockwise) + E = Result.Next; + else + E = Result.Prev; + var locMin = new ClipperLib.LocalMinima(); + locMin.Next = null; + locMin.Y = E.Bot.Y; + locMin.LeftBound = null; + locMin.RightBound = E; + locMin.RightBound.WindDelta = 0; + Result = this.ProcessBound(locMin.RightBound, IsClockwise); + this.InsertLocalMinima(locMin); + } } - else if (e.dx < e.prev.dx) + return Result; + }; + ClipperLib.ClipperBase.prototype.AddPath = function (pg, polyType, Closed) + { + if (use_lines) { - newLm.leftBound = e.prev; - newLm.rightBound = e; + if (!Closed && polyType == ClipperLib.PolyType.ptClip) + ClipperLib.Error("AddPath: Open paths must be subject."); } else { - newLm.leftBound = e; - newLm.rightBound = e.prev; + if (!Closed) + ClipperLib.Error("AddPath: Open paths have been disabled."); + } + var highI = pg.length - 1; + if (Closed) + while (highI > 0 && (ClipperLib.IntPoint.op_Equality(pg[highI], pg[0]))) + --highI; + while (highI > 0 && (ClipperLib.IntPoint.op_Equality(pg[highI], pg[highI - 1]))) + --highI; + if ((Closed && highI < 2) || (!Closed && highI < 1)) + return false; + //create a new edge array ... + var edges = new Array(); + for (var i = 0; i <= highI; i++) + edges.push(new ClipperLib.TEdge()); + var IsFlat = true; + //1. Basic (first) edge initialization ... + + //edges[1].Curr = pg[1]; + edges[1].Curr.X = pg[1].X; + edges[1].Curr.Y = pg[1].Y; + + var $1 = {Value: this.m_UseFullRange}; + this.RangeTest(pg[0], $1); + this.m_UseFullRange = $1.Value; + + $1.Value = this.m_UseFullRange; + this.RangeTest(pg[highI], $1); + this.m_UseFullRange = $1.Value; + + this.InitEdge(edges[0], edges[1], edges[highI], pg[0]); + this.InitEdge(edges[highI], edges[0], edges[highI - 1], pg[highI]); + for (var i = highI - 1; i >= 1; --i) + { + $1.Value = this.m_UseFullRange; + this.RangeTest(pg[i], $1); + this.m_UseFullRange = $1.Value; + + this.InitEdge(edges[i], edges[i + 1], edges[i - 1], pg[i]); + } + + var eStart = edges[0]; + //2. Remove duplicate vertices, and (when closed) collinear edges ... + var E = eStart, + eLoopStop = eStart; + for (;;) + { + if (ClipperLib.IntPoint.op_Equality(E.Curr, E.Next.Curr)) + { + if (E == E.Next) + break; + if (E == eStart) + eStart = E.Next; + E = this.RemoveEdge(E); + eLoopStop = E; + continue; + } + if (E.Prev == E.Next) + break; + else if (Closed && ClipperLib.ClipperBase.SlopesEqual(E.Prev.Curr, E.Curr, E.Next.Curr, this.m_UseFullRange) && (!this.PreserveCollinear || !this.Pt2IsBetweenPt1AndPt3(E.Prev.Curr, E.Curr, E.Next.Curr))) + { + //Collinear edges are allowed for open paths but in closed paths + //the default is to merge adjacent collinear edges into a single edge. + //However, if the PreserveCollinear property is enabled, only overlapping + //collinear edges (ie spikes) will be removed from closed paths. + if (E == eStart) + eStart = E.Next; + E = this.RemoveEdge(E); + E = E.Prev; + eLoopStop = E; + continue; + } + E = E.Next; + if (E == eLoopStop) + break; + } + if ((!Closed && (E == E.Next)) || (Closed && (E.Prev == E.Next))) + return false; + if (!Closed) + { + this.m_HasOpenPaths = true; + eStart.Prev.OutIdx = ClipperLib.ClipperBase.Skip; + } + //3. Do second stage of edge initialization ... + var eHighest = eStart; + E = eStart; + do { + this.InitEdge2(E, polyType); + E = E.Next; + if (IsFlat && E.Curr.Y != eStart.Curr.Y) + IsFlat = false; + } + while (E != eStart) + //4. Finally, add edge bounds to LocalMinima list ... + //Totally flat paths must be handled differently when adding them + //to LocalMinima list to avoid endless loops etc ... + if (IsFlat) + { + if (Closed) + return false; + E.Prev.OutIdx = ClipperLib.ClipperBase.Skip; + if (E.Prev.Bot.X < E.Prev.Top.X) + this.ReverseHorizontal(E.Prev); + var locMin = new ClipperLib.LocalMinima(); + locMin.Next = null; + locMin.Y = E.Bot.Y; + locMin.LeftBound = null; + locMin.RightBound = E; + locMin.RightBound.Side = ClipperLib.EdgeSide.esRight; + locMin.RightBound.WindDelta = 0; + while (E.Next.OutIdx != ClipperLib.ClipperBase.Skip) + { + E.NextInLML = E.Next; + if (E.Bot.X != E.Prev.Top.X) + this.ReverseHorizontal(E); + E = E.Next; + } + this.InsertLocalMinima(locMin); + this.m_edges.push(edges); + return true; } - newLm.leftBound.side = ClipperLib.EdgeSide.esLeft; - newLm.rightBound.side = ClipperLib.EdgeSide.esRight; - this.InsertLocalMinima(newLm); + this.m_edges.push(edges); + var clockwise; + var EMin = null; for (;;) { - if (e.next.ytop == e.ytop && e.next.dx != ClipperLib.ClipperBase.horizontal) break; - e.nextInLML = e.next; - e = e.next; - if (e.dx == ClipperLib.ClipperBase.horizontal && e.xbot != e.prev.xtop) this.SwapX(e); + E = this.FindNextLocMin(E); + if (E == EMin) + break; + else if (EMin == null) + EMin = E; + //E and E.Prev now share a local minima (left aligned if horizontal). + //Compare their slopes to find which starts which bound ... + var locMin = new ClipperLib.LocalMinima(); + locMin.Next = null; + locMin.Y = E.Bot.Y; + if (E.Dx < E.Prev.Dx) + { + locMin.LeftBound = E.Prev; + locMin.RightBound = E; + clockwise = false; + //Q.nextInLML = Q.prev + } + else + { + locMin.LeftBound = E; + locMin.RightBound = E.Prev; + clockwise = true; + //Q.nextInLML = Q.next + } + locMin.LeftBound.Side = ClipperLib.EdgeSide.esLeft; + locMin.RightBound.Side = ClipperLib.EdgeSide.esRight; + if (!Closed) + locMin.LeftBound.WindDelta = 0; + else if (locMin.LeftBound.Next == locMin.RightBound) + locMin.LeftBound.WindDelta = -1; + else + locMin.LeftBound.WindDelta = 1; + locMin.RightBound.WindDelta = -locMin.LeftBound.WindDelta; + E = this.ProcessBound(locMin.LeftBound, clockwise); + var E2 = this.ProcessBound(locMin.RightBound, !clockwise); + if (locMin.LeftBound.OutIdx == ClipperLib.ClipperBase.Skip) + locMin.LeftBound = null; + else if (locMin.RightBound.OutIdx == ClipperLib.ClipperBase.Skip) + locMin.RightBound = null; + this.InsertLocalMinima(locMin); + if (!clockwise) + E = E2; } - return e.next; + return true; + }; + ClipperLib.ClipperBase.prototype.AddPaths = function (ppg, polyType, closed) + { + // console.log("-------------------------------------------"); + // console.log(JSON.stringify(ppg)); + var result = false; + for (var i = 0, ilen = ppg.length; i < ilen; ++i) + if (this.AddPath(ppg[i], polyType, closed)) + result = true; + return result; + }; + //------------------------------------------------------------------------------ + ClipperLib.ClipperBase.prototype.Pt2IsBetweenPt1AndPt3 = function (pt1, pt2, pt3) + { + if ((ClipperLib.IntPoint.op_Equality(pt1, pt3)) || (ClipperLib.IntPoint.op_Equality(pt1, pt2)) || + (ClipperLib.IntPoint.op_Equality(pt3, pt2))) + return false; + else if (pt1.X != pt3.X) + return (pt2.X > pt1.X) == (pt2.X < pt3.X); + else + return (pt2.Y > pt1.Y) == (pt2.Y < pt3.Y); + }; + ClipperLib.ClipperBase.prototype.RemoveEdge = function (e) + { + //removes e from double_linked_list (but without removing from memory) + e.Prev.Next = e.Next; + e.Next.Prev = e.Prev; + var result = e.Next; + e.Prev = null; //flag as removed (see ClipperBase.Clear) + return result; + }; + ClipperLib.ClipperBase.prototype.SetDx = function (e) + { + e.Delta.X = (e.Top.X - e.Bot.X); + e.Delta.Y = (e.Top.Y - e.Bot.Y); + if (e.Delta.Y === 0) e.Dx = ClipperLib.ClipperBase.horizontal; + else e.Dx = (e.Delta.X) / (e.Delta.Y); }; ClipperLib.ClipperBase.prototype.InsertLocalMinima = function (newLm) { - if (this.m_MinimaList == null) + if (this.m_MinimaList === null) { this.m_MinimaList = newLm; } else if (newLm.Y >= this.m_MinimaList.Y) { - newLm.next = this.m_MinimaList; + newLm.Next = this.m_MinimaList; this.m_MinimaList = newLm; } else { var tmpLm = this.m_MinimaList; - while (tmpLm.next != null && (newLm.Y < tmpLm.next.Y)) - tmpLm = tmpLm.next; - newLm.next = tmpLm.next; - tmpLm.next = newLm; + while (tmpLm.Next !== null && (newLm.Y < tmpLm.Next.Y)) + tmpLm = tmpLm.Next; + newLm.Next = tmpLm.Next; + tmpLm.Next = newLm; } }; ClipperLib.ClipperBase.prototype.PopLocalMinima = function () { - if (this.m_CurrentLM == null) return; - this.m_CurrentLM = this.m_CurrentLM.next; + if (this.m_CurrentLM === null) + return; + this.m_CurrentLM = this.m_CurrentLM.Next; }; - ClipperLib.ClipperBase.prototype.SwapX = function (e) + ClipperLib.ClipperBase.prototype.ReverseHorizontal = function (e) { - e.xcurr = e.xtop; - e.xtop = e.xbot; - e.xbot = e.xcurr; + //swap horizontal edges' top and bottom x's so they follow the natural + //progression of the bounds - ie so their xbots will align with the + //adjoining lower edge. [Helpful in the ProcessHorizontal() method.] + var tmp = e.Top.X; + e.Top.X = e.Bot.X; + e.Bot.X = tmp; + if (use_xyz) + { + tmp = e.Top.Z; + e.Top.Z = e.Bot.Z; + e.Bot.Z = tmp; + } }; ClipperLib.ClipperBase.prototype.Reset = function () { this.m_CurrentLM = this.m_MinimaList; + if (this.m_CurrentLM == null) + return; + //ie nothing to process + //reset all edges ... var lm = this.m_MinimaList; while (lm != null) { - var e = lm.leftBound; - while (e != null) - { - e.xcurr = e.xbot; - e.ycurr = e.ybot; - e.side = ClipperLib.EdgeSide.esLeft; - e.outIdx = -1; - e = e.nextInLML; - } - e = lm.rightBound; - while (e != null) + var e = lm.LeftBound; + if (e != null) { - e.xcurr = e.xbot; - e.ycurr = e.ybot; - e.side = ClipperLib.EdgeSide.esRight; - e.outIdx = -1; - e = e.nextInLML; + //e.Curr = e.Bot; + e.Curr.X = e.Bot.X; + e.Curr.Y = e.Bot.Y; + e.Side = ClipperLib.EdgeSide.esLeft; + e.OutIdx = ClipperLib.ClipperBase.Unassigned; } - lm = lm.next; - } - return; - }; - ClipperLib.ClipperBase.prototype.GetBounds = function () - { - var result = new ClipperLib.IntRect(); - var lm = this.m_MinimaList; - if (lm == null) return result; - result.left = lm.leftBound.xbot; - result.top = lm.leftBound.ybot; - result.right = lm.leftBound.xbot; - result.bottom = lm.leftBound.ybot; - while (lm != null) - { - if (lm.leftBound.ybot > result.bottom) result.bottom = lm.leftBound.ybot; - var e = lm.leftBound; - for (;;) + e = lm.RightBound; + if (e != null) { - var bottomE = e; - while (e.nextInLML != null) - { - if (e.xbot < result.left) result.left = e.xbot; - if (e.xbot > result.right) result.right = e.xbot; - e = e.nextInLML; - } - if (e.xbot < result.left) result.left = e.xbot; - if (e.xbot > result.right) result.right = e.xbot; - if (e.xtop < result.left) result.left = e.xtop; - if (e.xtop > result.right) result.right = e.xtop; - if (e.ytop < result.top) result.top = e.ytop; - if (bottomE == lm.leftBound) e = lm.rightBound; - else break; + //e.Curr = e.Bot; + e.Curr.X = e.Bot.X; + e.Curr.Y = e.Bot.Y; + e.Side = ClipperLib.EdgeSide.esRight; + e.OutIdx = ClipperLib.ClipperBase.Unassigned; } - lm = lm.next; + lm = lm.Next; } - return result; }; - ClipperLib.Clipper = function () + ClipperLib.Clipper = function (InitOptions) // public Clipper(int InitOptions = 0) { + if (typeof (InitOptions) == "undefined") InitOptions = 0; this.m_PolyOuts = null; this.m_ClipType = ClipperLib.ClipType.ctIntersection; this.m_Scanbeam = null; this.m_ActiveEdges = null; this.m_SortedEdges = null; - this.m_IntersectNodes = null; + this.m_IntersectList = null; + this.m_IntersectNodeComparer = null; this.m_ExecuteLocked = false; this.m_ClipFillType = ClipperLib.PolyFillType.pftEvenOdd; this.m_SubjFillType = ClipperLib.PolyFillType.pftEvenOdd; this.m_Joins = null; - this.m_HorizJoins = null; - this.m_ReverseOutput = false; - this.m_UsingExPolygons = false; + this.m_GhostJoins = null; + this.m_UsingPolyTree = false; + this.ReverseSolution = false; + this.StrictlySimple = false; ClipperLib.ClipperBase.call(this); this.m_Scanbeam = null; this.m_ActiveEdges = null; this.m_SortedEdges = null; - this.m_IntersectNodes = null; + this.m_IntersectList = new Array(); + this.m_IntersectNodeComparer = ClipperLib.MyIntersectNodeSort.Compare; this.m_ExecuteLocked = false; - this.m_PolyOuts = []; - this.m_Joins = []; - this.m_HorizJoins = []; - this.m_ReverseOutput = false; - this.m_UsingExPolygons = false; + this.m_UsingPolyTree = false; + this.m_PolyOuts = new Array(); + this.m_Joins = new Array(); + this.m_GhostJoins = new Array(); + this.ReverseSolution = (1 & InitOptions) !== 0; + this.StrictlySimple = (2 & InitOptions) !== 0; + this.PreserveCollinear = (4 & InitOptions) !== 0; + if (use_xyz) + { + this.ZFillFunction = null; // function (IntPoint vert1, IntPoint vert2, ref IntPoint intersectPt); + } }; + ClipperLib.Clipper.ioReverseSolution = 1; + ClipperLib.Clipper.ioStrictlySimple = 2; + ClipperLib.Clipper.ioPreserveCollinear = 4; + ClipperLib.Clipper.prototype.Clear = function () { - if (this.m_edges.length == 0) return; + if (this.m_edges.length === 0) + return; + //avoids problems with ClipperBase destructor this.DisposeAllPolyPts(); ClipperLib.ClipperBase.prototype.Clear.call(this); }; + ClipperLib.Clipper.prototype.DisposeScanbeamList = function () { - while (this.m_Scanbeam != null) + while (this.m_Scanbeam !== null) { - var sb2 = this.m_Scanbeam.next; + var sb2 = this.m_Scanbeam.Next; this.m_Scanbeam = null; this.m_Scanbeam = sb2; } @@ -2351,357 +3048,389 @@ this.m_Scanbeam = null; this.m_ActiveEdges = null; this.m_SortedEdges = null; - this.DisposeAllPolyPts(); + var lm = this.m_MinimaList; - while (lm != null) + while (lm !== null) { this.InsertScanbeam(lm.Y); - this.InsertScanbeam(lm.leftBound.ytop); - lm = lm.next; + lm = lm.Next; } }; - ClipperLib.Clipper.prototype.get_ReverseSolution = function () - { - return this.m_ReverseOutput; - }; - ClipperLib.Clipper.prototype.set_ReverseSolution = function (value) - { - this.m_ReverseOutput = value; - }; ClipperLib.Clipper.prototype.InsertScanbeam = function (Y) { - var newSb; - if (this.m_Scanbeam == null) + if (this.m_Scanbeam === null) { this.m_Scanbeam = new ClipperLib.Scanbeam(); - this.m_Scanbeam.next = null; + this.m_Scanbeam.Next = null; this.m_Scanbeam.Y = Y; } else if (Y > this.m_Scanbeam.Y) { - newSb = new ClipperLib.Scanbeam(); + var newSb = new ClipperLib.Scanbeam(); newSb.Y = Y; - newSb.next = this.m_Scanbeam; + newSb.Next = this.m_Scanbeam; this.m_Scanbeam = newSb; } else { var sb2 = this.m_Scanbeam; - while (sb2.next != null && (Y <= sb2.next.Y)) - sb2 = sb2.next; - if (Y == sb2.Y) return; - newSb = new ClipperLib.Scanbeam(); + while (sb2.Next !== null && (Y <= sb2.Next.Y)) + sb2 = sb2.Next; + if (Y == sb2.Y) + return; + //ie ignores duplicates + var newSb = new ClipperLib.Scanbeam(); newSb.Y = Y; - newSb.next = sb2.next; - sb2.next = newSb; - } - }; - ClipperLib.Clipper.prototype.Execute = function (clipType, solution, subjFillType, clipFillType) - { - var succeeded; - if (arguments.length == 2) - { - subjFillType = ClipperLib.PolyFillType.pftEvenOdd; - clipFillType = ClipperLib.PolyFillType.pftEvenOdd; - } - if (!solution.hasOwnProperty("outer")) // hacky way to test if solution is expolygon or not - { - if (this.m_ExecuteLocked) return false; + newSb.Next = sb2.Next; + sb2.Next = newSb; + } + }; + // ************************************ + ClipperLib.Clipper.prototype.Execute = function () + { + var a = arguments, + alen = a.length, + ispolytree = a[1] instanceof ClipperLib.PolyTree; + if (alen == 4 && !ispolytree) // function (clipType, solution, subjFillType, clipFillType) + { + var clipType = a[0], + solution = a[1], + subjFillType = a[2], + clipFillType = a[3]; + if (this.m_ExecuteLocked) + return false; + if (this.m_HasOpenPaths) + ClipperLib.Error("Error: PolyTree struct is need for open path clipping."); this.m_ExecuteLocked = true; ClipperLib.Clear(solution); this.m_SubjFillType = subjFillType; this.m_ClipFillType = clipFillType; this.m_ClipType = clipType; - this.m_UsingExPolygons = false; - succeeded = this.ExecuteInternal(); - if (succeeded) + this.m_UsingPolyTree = false; + try + { + var succeeded = this.ExecuteInternal(); + //build the return polygons ... + if (succeeded) this.BuildResult(solution); + } + finally { - this.BuildResult(solution); + this.DisposeAllPolyPts(); + this.m_ExecuteLocked = false; } - this.m_ExecuteLocked = false; return succeeded; } - else + else if (alen == 4 && ispolytree) // function (clipType, polytree, subjFillType, clipFillType) { - if (this.m_ExecuteLocked) return false; + var clipType = a[0], + polytree = a[1], + subjFillType = a[2], + clipFillType = a[3]; + if (this.m_ExecuteLocked) + return false; this.m_ExecuteLocked = true; - ClipperLib.Clear(solution); this.m_SubjFillType = subjFillType; this.m_ClipFillType = clipFillType; this.m_ClipType = clipType; - this.m_UsingExPolygons = true; - succeeded = this.ExecuteInternal(); - if (succeeded) + this.m_UsingPolyTree = true; + try + { + var succeeded = this.ExecuteInternal(); + //build the return polygons ... + if (succeeded) this.BuildResult2(polytree); + } + finally { - this.BuildResultEx(solution); + this.DisposeAllPolyPts(); + this.m_ExecuteLocked = false; } - this.m_ExecuteLocked = false; return succeeded; } - }; - ClipperLib.Clipper.prototype.PolySort = function (or1, or2) - { - if (or1 == or2) return 0; - else if (or1.pts == null || or2.pts == null) + else if (alen == 2 && !ispolytree) // function (clipType, solution) { - if ((or1.pts == null) != (or2.pts == null)) - { - return or1.pts == null ? 1 : -1; - } - else return 0; + var clipType = a[0], + solution = a[1]; + return this.Execute(clipType, solution, ClipperLib.PolyFillType.pftEvenOdd, ClipperLib.PolyFillType.pftEvenOdd); } - var i1, i2; - if (or1.isHole) i1 = or1.FirstLeft.idx; - else i1 = or1.idx; - if (or2.isHole) i2 = or2.FirstLeft.idx; - else i2 = or2.idx; - var result = i1 - i2; - if (result == 0 && (or1.isHole != or2.isHole)) + else if (alen == 2 && ispolytree) // function (clipType, polytree) { - return or1.isHole ? 1 : -1; + var clipType = a[0], + polytree = a[1]; + return this.Execute(clipType, polytree, ClipperLib.PolyFillType.pftEvenOdd, ClipperLib.PolyFillType.pftEvenOdd); } - return result; - }; - ClipperLib.Clipper.prototype.FindAppendLinkEnd = function (outRec) - { - while (outRec.AppendLink != null) - outRec = outRec.AppendLink; - return outRec; }; ClipperLib.Clipper.prototype.FixHoleLinkage = function (outRec) { - var tmp; - if (outRec.bottomPt != null) tmp = this.m_PolyOuts[outRec.bottomPt.idx].FirstLeft; - else tmp = outRec.FirstLeft; - if (outRec == tmp) ClipperLib.Error("HoleLinkage error"); - if (tmp != null) - { - if (tmp.AppendLink != null) tmp = this.FindAppendLinkEnd(tmp); - if (tmp == outRec) tmp = null; - else if (tmp.isHole) - { - this.FixHoleLinkage(tmp); - tmp = tmp.FirstLeft; - } - } - outRec.FirstLeft = tmp; - if (tmp == null) outRec.isHole = false; - outRec.AppendLink = null; + //skip if an outermost polygon or + //already already points to the correct FirstLeft ... + if (outRec.FirstLeft === null || (outRec.IsHole != outRec.FirstLeft.IsHole && outRec.FirstLeft.Pts !== null)) + return; + var orfl = outRec.FirstLeft; + while (orfl !== null && ((orfl.IsHole == outRec.IsHole) || orfl.Pts === null)) + orfl = orfl.FirstLeft; + outRec.FirstLeft = orfl; }; ClipperLib.Clipper.prototype.ExecuteInternal = function () { - var succeeded; try { this.Reset(); - if (this.m_CurrentLM == null) return true; + if (this.m_CurrentLM === null) + return false; var botY = this.PopScanbeam(); do { this.InsertLocalMinimaIntoAEL(botY); - ClipperLib.Clear(this.m_HorizJoins); - this.ProcessHorizontals(); + ClipperLib.Clear(this.m_GhostJoins); + this.ProcessHorizontals(false); + if (this.m_Scanbeam === null) + break; var topY = this.PopScanbeam(); - succeeded = this.ProcessIntersections(botY, topY); - if (!succeeded) break; + //console.log("botY:" + botY + ", topY:" + topY); + if (!this.ProcessIntersections(botY, topY)) + return false; this.ProcessEdgesAtTopOfScanbeam(topY); botY = topY; } - while (this.m_Scanbeam != null); - } - catch ($$e1) - { - succeeded = false; - } - if (succeeded) - { - var outRec; - for (var i = 0; i < this.m_PolyOuts.length; i++) + while (this.m_Scanbeam !== null || this.m_CurrentLM !== null) + //fix orientations ... + for (var i = 0, ilen = this.m_PolyOuts.length; i < ilen; i++) { - outRec = this.m_PolyOuts[i]; - if (outRec.pts == null) continue; - this.FixupOutPolygon(outRec); - if (outRec.pts == null) continue; - if (outRec.isHole && this.m_UsingExPolygons) this.FixHoleLinkage(outRec); - - if ((outRec.isHole ^ this.m_ReverseOutput) == (this.Area(outRec, this.m_UseFullRange) > 0)) - this.ReversePolyPtLinks(outRec.pts); + var outRec = this.m_PolyOuts[i]; + if (outRec.Pts === null || outRec.IsOpen) + continue; + if ((outRec.IsHole ^ this.ReverseSolution) == (this.Area(outRec) > 0)) + this.ReversePolyPtLinks(outRec.Pts); } this.JoinCommonEdges(); - if (this.m_UsingExPolygons) this.m_PolyOuts.sort(this.PolySort); + for (var i = 0, ilen = this.m_PolyOuts.length; i < ilen; i++) + { + var outRec = this.m_PolyOuts[i]; + if (outRec.Pts !== null && !outRec.IsOpen) + this.FixupOutPolygon(outRec); + } + if (this.StrictlySimple) + this.DoSimplePolygons(); + return true; + } + finally + { + ClipperLib.Clear(this.m_Joins); + ClipperLib.Clear(this.m_GhostJoins); } - ClipperLib.Clear(this.m_Joins); - ClipperLib.Clear(this.m_HorizJoins); - return succeeded; }; ClipperLib.Clipper.prototype.PopScanbeam = function () { var Y = this.m_Scanbeam.Y; var sb2 = this.m_Scanbeam; - this.m_Scanbeam = this.m_Scanbeam.next; + this.m_Scanbeam = this.m_Scanbeam.Next; sb2 = null; return Y; }; ClipperLib.Clipper.prototype.DisposeAllPolyPts = function () { - for (var i = 0; i < this.m_PolyOuts.length; ++i) - this.DisposeOutRec(i); + for (var i = 0, ilen = this.m_PolyOuts.length; i < ilen; ++i) + this.DisposeOutRec(i); ClipperLib.Clear(this.m_PolyOuts); }; ClipperLib.Clipper.prototype.DisposeOutRec = function (index) { var outRec = this.m_PolyOuts[index]; - if (outRec.pts != null) this.DisposeOutPts(outRec.pts); + if (outRec.Pts !== null) + this.DisposeOutPts(outRec.Pts); outRec = null; this.m_PolyOuts[index] = null; }; ClipperLib.Clipper.prototype.DisposeOutPts = function (pp) { - if (pp == null) return; + if (pp === null) + return; var tmpPp = null; - pp.prev.next = null; - while (pp != null) + pp.Prev.Next = null; + while (pp !== null) { tmpPp = pp; - pp = pp.next; + pp = pp.Next; tmpPp = null; } }; - ClipperLib.Clipper.prototype.AddJoin = function (e1, e2, e1OutIdx, e2OutIdx) + ClipperLib.Clipper.prototype.AddJoin = function (Op1, Op2, OffPt) { - var jr = new ClipperLib.JoinRec(); - if (e1OutIdx >= 0) jr.poly1Idx = e1OutIdx; - else jr.poly1Idx = e1.outIdx; - jr.pt1a = new ClipperLib.IntPoint(e1.xcurr, e1.ycurr); - jr.pt1b = new ClipperLib.IntPoint(e1.xtop, e1.ytop); - if (e2OutIdx >= 0) jr.poly2Idx = e2OutIdx; - else jr.poly2Idx = e2.outIdx; - jr.pt2a = new ClipperLib.IntPoint(e2.xcurr, e2.ycurr); - jr.pt2b = new ClipperLib.IntPoint(e2.xtop, e2.ytop); - this.m_Joins.push(jr); + var j = new ClipperLib.Join(); + j.OutPt1 = Op1; + j.OutPt2 = Op2; + //j.OffPt = OffPt; + j.OffPt.X = OffPt.X; + j.OffPt.Y = OffPt.Y; + this.m_Joins.push(j); }; - ClipperLib.Clipper.prototype.AddHorzJoin = function (e, idx) + ClipperLib.Clipper.prototype.AddGhostJoin = function (Op, OffPt) { - var hj = new ClipperLib.HorzJoinRec(); - hj.edge = e; - hj.savedIdx = idx; - this.m_HorizJoins.push(hj); + var j = new ClipperLib.Join(); + j.OutPt1 = Op; + //j.OffPt = OffPt; + j.OffPt.X = OffPt.X; + j.OffPt.Y = OffPt.Y; + this.m_GhostJoins.push(j); }; + if (use_xyz) + { + ClipperLib.Clipper.prototype.SetZ = function (pt, e) + { + pt.Z = 0; + if (this.ZFillFunction !== null) + { + //put the 'preferred' point as first parameter ... + if (e.OutIdx < 0) + this.ZFillFunction(e.Bot, e.Top, pt); //outside a path so presume entering + else + this.ZFillFunction(e.Top, e.Bot, pt); //inside a path so presume exiting + } + }; + //------------------------------------------------------------------------------ + } ClipperLib.Clipper.prototype.InsertLocalMinimaIntoAEL = function (botY) { - var pt, pt2; - while (this.m_CurrentLM != null && (this.m_CurrentLM.Y == botY)) + while (this.m_CurrentLM !== null && (this.m_CurrentLM.Y == botY)) { - var lb = this.m_CurrentLM.leftBound; - var rb = this.m_CurrentLM.rightBound; - this.InsertEdgeIntoAEL(lb); - this.InsertScanbeam(lb.ytop); - this.InsertEdgeIntoAEL(rb); - if (this.IsEvenOddFillType(lb)) + var lb = this.m_CurrentLM.LeftBound; + var rb = this.m_CurrentLM.RightBound; + this.PopLocalMinima(); + var Op1 = null; + if (lb === null) + { + this.InsertEdgeIntoAEL(rb, null); + this.SetWindingCount(rb); + if (this.IsContributing(rb)) + Op1 = this.AddOutPt(rb, rb.Bot); + } + else if (rb == null) { - lb.windDelta = 1; - rb.windDelta = 1; + this.InsertEdgeIntoAEL(lb, null); + this.SetWindingCount(lb); + if (this.IsContributing(lb)) + Op1 = this.AddOutPt(lb, lb.Bot); + this.InsertScanbeam(lb.Top.Y); } else { - rb.windDelta = -lb.windDelta; + this.InsertEdgeIntoAEL(lb, null); + this.InsertEdgeIntoAEL(rb, lb); + this.SetWindingCount(lb); + rb.WindCnt = lb.WindCnt; + rb.WindCnt2 = lb.WindCnt2; + if (this.IsContributing(lb)) + Op1 = this.AddLocalMinPoly(lb, rb, lb.Bot); + this.InsertScanbeam(lb.Top.Y); } - this.SetWindingCount(lb); - rb.windCnt = lb.windCnt; - rb.windCnt2 = lb.windCnt2; - if (rb.dx == ClipperLib.ClipperBase.horizontal) + if (rb != null) { - this.AddEdgeToSEL(rb); - this.InsertScanbeam(rb.nextInLML.ytop); + if (ClipperLib.ClipperBase.IsHorizontal(rb)) + this.AddEdgeToSEL(rb); + else + this.InsertScanbeam(rb.Top.Y); } - else this.InsertScanbeam(rb.ytop); - if (this.IsContributing(lb)) this.AddLocalMinPoly(lb, rb, new ClipperLib.IntPoint(lb.xcurr, this.m_CurrentLM.Y)); - if (rb.outIdx >= 0) + if (lb == null || rb == null) continue; + //if output polygons share an Edge with a horizontal rb, they'll need joining later ... + if (Op1 !== null && ClipperLib.ClipperBase.IsHorizontal(rb) && this.m_GhostJoins.length > 0 && rb.WindDelta !== 0) { - if (rb.dx == ClipperLib.ClipperBase.horizontal) + for (var i = 0, ilen = this.m_GhostJoins.length; i < ilen; i++) { - for (var i = 0; i < this.m_HorizJoins.length; i++) - { - pt = new ClipperLib.IntPoint(), pt2 = new ClipperLib.IntPoint(); - var hj = this.m_HorizJoins[i]; - if ((function () - { - pt = { - Value: pt - }; - pt2 = { - Value: pt2 - }; - var $res = this.GetOverlapSegment(new ClipperLib.IntPoint(hj.edge.xbot, hj.edge.ybot), - new ClipperLib.IntPoint(hj.edge.xtop, hj.edge.ytop), - new ClipperLib.IntPoint(rb.xbot, rb.ybot), - new ClipperLib.IntPoint(rb.xtop, rb.ytop), - pt, pt2); - pt = pt.Value; - pt2 = pt2.Value; - return $res; - }) - .call(this)) this.AddJoin(hj.edge, rb, hj.savedIdx, -1); - } + //if the horizontal Rb and a 'ghost' horizontal overlap, then convert + //the 'ghost' join to a real join ready for later ... + var j = this.m_GhostJoins[i]; + if (this.HorzSegmentsOverlap(j.OutPt1.Pt, j.OffPt, rb.Bot, rb.Top)) + this.AddJoin(j.OutPt1, Op1, j.OffPt); } } - if (lb.nextInAEL != rb) + if (lb.OutIdx >= 0 && lb.PrevInAEL !== null && + lb.PrevInAEL.Curr.X == lb.Bot.X && + lb.PrevInAEL.OutIdx >= 0 && + ClipperLib.ClipperBase.SlopesEqual(lb.PrevInAEL, lb, this.m_UseFullRange) && + lb.WindDelta !== 0 && lb.PrevInAEL.WindDelta !== 0) + { + var Op2 = this.AddOutPt(lb.PrevInAEL, lb.Bot); + this.AddJoin(Op1, Op2, lb.Top); + } + if (lb.NextInAEL != rb) { - if (rb.outIdx >= 0 && rb.prevInAEL.outIdx >= 0 && this.SlopesEqual(rb.prevInAEL, rb, this.m_UseFullRange)) this.AddJoin(rb, rb.prevInAEL, -1, -1); - var e = lb.nextInAEL; - pt = new ClipperLib.IntPoint(lb.xcurr, lb.ycurr); - while (e != rb) + if (rb.OutIdx >= 0 && rb.PrevInAEL.OutIdx >= 0 && + ClipperLib.ClipperBase.SlopesEqual(rb.PrevInAEL, rb, this.m_UseFullRange) && + rb.WindDelta !== 0 && rb.PrevInAEL.WindDelta !== 0) { - if (e == null) ClipperLib.Error("InsertLocalMinimaIntoAEL: missing rightbound!"); - this.IntersectEdges(rb, e, pt, ClipperLib.Protects.ipNone); - e = e.nextInAEL; + var Op2 = this.AddOutPt(rb.PrevInAEL, rb.Bot); + this.AddJoin(Op1, Op2, rb.Top); } + var e = lb.NextInAEL; + if (e !== null) + while (e != rb) + { + //nb: For calculating winding counts etc, IntersectEdges() assumes + //that param1 will be to the right of param2 ABOVE the intersection ... + this.IntersectEdges(rb, e, lb.Curr, false); + //order important here + e = e.NextInAEL; + } } - this.PopLocalMinima(); } }; - ClipperLib.Clipper.prototype.InsertEdgeIntoAEL = function (edge) + ClipperLib.Clipper.prototype.InsertEdgeIntoAEL = function (edge, startEdge) { - edge.prevInAEL = null; - edge.nextInAEL = null; - if (this.m_ActiveEdges == null) + if (this.m_ActiveEdges === null) { + edge.PrevInAEL = null; + edge.NextInAEL = null; this.m_ActiveEdges = edge; } - else if (this.E2InsertsBeforeE1(this.m_ActiveEdges, edge)) + else if (startEdge === null && this.E2InsertsBeforeE1(this.m_ActiveEdges, edge)) { - edge.nextInAEL = this.m_ActiveEdges; - this.m_ActiveEdges.prevInAEL = edge; + edge.PrevInAEL = null; + edge.NextInAEL = this.m_ActiveEdges; + this.m_ActiveEdges.PrevInAEL = edge; this.m_ActiveEdges = edge; } else { - var e = this.m_ActiveEdges; - while (e.nextInAEL != null && !this.E2InsertsBeforeE1(e.nextInAEL, edge)) - e = e.nextInAEL; - edge.nextInAEL = e.nextInAEL; - if (e.nextInAEL != null) e.nextInAEL.prevInAEL = edge; - edge.prevInAEL = e; - e.nextInAEL = edge; + if (startEdge === null) + startEdge = this.m_ActiveEdges; + while (startEdge.NextInAEL !== null && !this.E2InsertsBeforeE1(startEdge.NextInAEL, edge)) + startEdge = startEdge.NextInAEL; + edge.NextInAEL = startEdge.NextInAEL; + if (startEdge.NextInAEL !== null) + startEdge.NextInAEL.PrevInAEL = edge; + edge.PrevInAEL = startEdge; + startEdge.NextInAEL = edge; } }; ClipperLib.Clipper.prototype.E2InsertsBeforeE1 = function (e1, e2) { - return e2.xcurr == e1.xcurr ? e2.dx > e1.dx : e2.xcurr < e1.xcurr; + if (e2.Curr.X == e1.Curr.X) + { + if (e2.Top.Y > e1.Top.Y) + return e2.Top.X < ClipperLib.Clipper.TopX(e1, e2.Top.Y); + else + return e1.Top.X > ClipperLib.Clipper.TopX(e2, e1.Top.Y); + } + else + return e2.Curr.X < e1.Curr.X; }; ClipperLib.Clipper.prototype.IsEvenOddFillType = function (edge) { - if (edge.polyType == ClipperLib.PolyType.ptSubject) return this.m_SubjFillType == ClipperLib.PolyFillType.pftEvenOdd; - else return this.m_ClipFillType == ClipperLib.PolyFillType.pftEvenOdd; + if (edge.PolyTyp == ClipperLib.PolyType.ptSubject) + return this.m_SubjFillType == ClipperLib.PolyFillType.pftEvenOdd; + else + return this.m_ClipFillType == ClipperLib.PolyFillType.pftEvenOdd; }; ClipperLib.Clipper.prototype.IsEvenOddAltFillType = function (edge) { - if (edge.polyType == ClipperLib.PolyType.ptSubject) return this.m_ClipFillType == ClipperLib.PolyFillType.pftEvenOdd; - else return this.m_SubjFillType == ClipperLib.PolyFillType.pftEvenOdd; + if (edge.PolyTyp == ClipperLib.PolyType.ptSubject) + return this.m_ClipFillType == ClipperLib.PolyFillType.pftEvenOdd; + else + return this.m_SubjFillType == ClipperLib.PolyFillType.pftEvenOdd; }; ClipperLib.Clipper.prototype.IsContributing = function (edge) { var pft, pft2; - if (edge.polyType == ClipperLib.PolyType.ptSubject) + if (edge.PolyTyp == ClipperLib.PolyType.ptSubject) { pft = this.m_SubjFillType; pft2 = this.m_ClipFillType; @@ -2713,134 +3442,201 @@ } switch (pft) { + case ClipperLib.PolyFillType.pftEvenOdd: + if (edge.WindDelta === 0 && edge.WindCnt != 1) + return false; + break; + case ClipperLib.PolyFillType.pftNonZero: + if (Math.abs(edge.WindCnt) != 1) + return false; + break; + case ClipperLib.PolyFillType.pftPositive: + if (edge.WindCnt != 1) + return false; + break; + default: + if (edge.WindCnt != -1) + return false; + break; + } + switch (this.m_ClipType) + { + case ClipperLib.ClipType.ctIntersection: + switch (pft2) + { case ClipperLib.PolyFillType.pftEvenOdd: case ClipperLib.PolyFillType.pftNonZero: - if (ClipperLib.Math_Abs_Int32(edge.windCnt) != 1) return false; - break; + return (edge.WindCnt2 !== 0); case ClipperLib.PolyFillType.pftPositive: - if (edge.windCnt != 1) return false; - break; + return (edge.WindCnt2 > 0); default: - if (edge.windCnt != -1) return false; - break; - } - switch (this.m_ClipType) - { - case ClipperLib.ClipType.ctIntersection: + return (edge.WindCnt2 < 0); + } + case ClipperLib.ClipType.ctUnion: + switch (pft2) + { + case ClipperLib.PolyFillType.pftEvenOdd: + case ClipperLib.PolyFillType.pftNonZero: + return (edge.WindCnt2 === 0); + case ClipperLib.PolyFillType.pftPositive: + return (edge.WindCnt2 <= 0); + default: + return (edge.WindCnt2 >= 0); + } + case ClipperLib.ClipType.ctDifference: + if (edge.PolyTyp == ClipperLib.PolyType.ptSubject) switch (pft2) { - case ClipperLib.PolyFillType.pftEvenOdd: - case ClipperLib.PolyFillType.pftNonZero: - return (edge.windCnt2 != 0); - case ClipperLib.PolyFillType.pftPositive: - return (edge.windCnt2 > 0); - default: - return (edge.windCnt2 < 0); + case ClipperLib.PolyFillType.pftEvenOdd: + case ClipperLib.PolyFillType.pftNonZero: + return (edge.WindCnt2 === 0); + case ClipperLib.PolyFillType.pftPositive: + return (edge.WindCnt2 <= 0); + default: + return (edge.WindCnt2 >= 0); } - break; - case ClipperLib.ClipType.ctUnion: + else switch (pft2) { - case ClipperLib.PolyFillType.pftEvenOdd: - case ClipperLib.PolyFillType.pftNonZero: - return (edge.windCnt2 == 0); - case ClipperLib.PolyFillType.pftPositive: - return (edge.windCnt2 <= 0); - default: - return (edge.windCnt2 >= 0); - } - break; - case ClipperLib.ClipType.ctDifference: - if (edge.polyType == ClipperLib.PolyType.ptSubject) switch (pft2) - { - case ClipperLib.PolyFillType.pftEvenOdd: - case ClipperLib.PolyFillType.pftNonZero: - return (edge.windCnt2 == 0); - case ClipperLib.PolyFillType.pftPositive: - return (edge.windCnt2 <= 0); - default: - return (edge.windCnt2 >= 0); + case ClipperLib.PolyFillType.pftEvenOdd: + case ClipperLib.PolyFillType.pftNonZero: + return (edge.WindCnt2 !== 0); + case ClipperLib.PolyFillType.pftPositive: + return (edge.WindCnt2 > 0); + default: + return (edge.WindCnt2 < 0); } - else switch (pft2) + case ClipperLib.ClipType.ctXor: + if (edge.WindDelta === 0) + switch (pft2) { - case ClipperLib.PolyFillType.pftEvenOdd: - case ClipperLib.PolyFillType.pftNonZero: - return (edge.windCnt2 != 0); - case ClipperLib.PolyFillType.pftPositive: - return (edge.windCnt2 > 0); - default: - return (edge.windCnt2 < 0); + case ClipperLib.PolyFillType.pftEvenOdd: + case ClipperLib.PolyFillType.pftNonZero: + return (edge.WindCnt2 === 0); + case ClipperLib.PolyFillType.pftPositive: + return (edge.WindCnt2 <= 0); + default: + return (edge.WindCnt2 >= 0); } + else + return true; } return true; }; ClipperLib.Clipper.prototype.SetWindingCount = function (edge) { - var e = edge.prevInAEL; - while (e != null && e.polyType != edge.polyType) - e = e.prevInAEL; - if (e == null) + var e = edge.PrevInAEL; + //find the edge of the same polytype that immediately preceeds 'edge' in AEL + while (e !== null && ((e.PolyTyp != edge.PolyTyp) || (e.WindDelta === 0))) + e = e.PrevInAEL; + if (e === null) { - edge.windCnt = edge.windDelta; - edge.windCnt2 = 0; + edge.WindCnt = (edge.WindDelta === 0 ? 1 : edge.WindDelta); + edge.WindCnt2 = 0; e = this.m_ActiveEdges; + //ie get ready to calc WindCnt2 + } + else if (edge.WindDelta === 0 && this.m_ClipType != ClipperLib.ClipType.ctUnion) + { + edge.WindCnt = 1; + edge.WindCnt2 = e.WindCnt2; + e = e.NextInAEL; + //ie get ready to calc WindCnt2 } else if (this.IsEvenOddFillType(edge)) { - edge.windCnt = 1; - edge.windCnt2 = e.windCnt2; - e = e.nextInAEL; + //EvenOdd filling ... + if (edge.WindDelta === 0) + { + //are we inside a subj polygon ... + var Inside = true; + var e2 = e.PrevInAEL; + while (e2 !== null) + { + if (e2.PolyTyp == e.PolyTyp && e2.WindDelta !== 0) + Inside = !Inside; + e2 = e2.PrevInAEL; + } + edge.WindCnt = (Inside ? 0 : 1); + } + else + { + edge.WindCnt = edge.WindDelta; + } + edge.WindCnt2 = e.WindCnt2; + e = e.NextInAEL; + //ie get ready to calc WindCnt2 } else { - if (e.windCnt * e.windDelta < 0) + //nonZero, Positive or Negative filling ... + if (e.WindCnt * e.WindDelta < 0) { - if (ClipperLib.Math_Abs_Int32(e.windCnt) > 1) + //prev edge is 'decreasing' WindCount (WC) toward zero + //so we're outside the previous polygon ... + if (Math.abs(e.WindCnt) > 1) { - if (e.windDelta * edge.windDelta < 0) edge.windCnt = e.windCnt; - else edge.windCnt = e.windCnt + edge.windDelta; + //outside prev poly but still inside another. + //when reversing direction of prev poly use the same WC + if (e.WindDelta * edge.WindDelta < 0) + edge.WindCnt = e.WindCnt; + else + edge.WindCnt = e.WindCnt + edge.WindDelta; } - else edge.windCnt = e.windCnt + e.windDelta + edge.windDelta; + else + edge.WindCnt = (edge.WindDelta === 0 ? 1 : edge.WindDelta); } else { - if (ClipperLib.Math_Abs_Int32(e.windCnt) > 1 && e.windDelta * edge.windDelta < 0) edge.windCnt = e.windCnt; - else if (e.windCnt + edge.windDelta == 0) edge.windCnt = e.windCnt; - else edge.windCnt = e.windCnt + edge.windDelta; + //prev edge is 'increasing' WindCount (WC) away from zero + //so we're inside the previous polygon ... + if (edge.WindDelta === 0) + edge.WindCnt = (e.WindCnt < 0 ? e.WindCnt - 1 : e.WindCnt + 1); + else if (e.WindDelta * edge.WindDelta < 0) + edge.WindCnt = e.WindCnt; + else + edge.WindCnt = e.WindCnt + edge.WindDelta; } - edge.windCnt2 = e.windCnt2; - e = e.nextInAEL; + edge.WindCnt2 = e.WindCnt2; + e = e.NextInAEL; + //ie get ready to calc WindCnt2 } + //update WindCnt2 ... if (this.IsEvenOddAltFillType(edge)) { + //EvenOdd filling ... while (e != edge) { - edge.windCnt2 = (edge.windCnt2 == 0) ? 1 : 0; - e = e.nextInAEL; + if (e.WindDelta !== 0) + edge.WindCnt2 = (edge.WindCnt2 === 0 ? 1 : 0); + e = e.NextInAEL; } } else { + //nonZero, Positive or Negative filling ... while (e != edge) { - edge.windCnt2 += e.windDelta; - e = e.nextInAEL; + edge.WindCnt2 += e.WindDelta; + e = e.NextInAEL; } } }; ClipperLib.Clipper.prototype.AddEdgeToSEL = function (edge) { - if (this.m_SortedEdges == null) + //SEL pointers in PEdge are reused to build a list of horizontal edges. + //However, we don't need to worry about order with horizontal edge processing. + if (this.m_SortedEdges === null) { this.m_SortedEdges = edge; - edge.prevInSEL = null; - edge.nextInSEL = null; + edge.PrevInSEL = null; + edge.NextInSEL = null; } else { - edge.nextInSEL = this.m_SortedEdges; - edge.prevInSEL = null; - this.m_SortedEdges.prevInSEL = edge; + edge.NextInSEL = this.m_SortedEdges; + edge.PrevInSEL = null; + this.m_SortedEdges.PrevInSEL = edge; this.m_SortedEdges = edge; } }; @@ -2848,576 +3644,669 @@ { var e = this.m_ActiveEdges; this.m_SortedEdges = e; - if (this.m_ActiveEdges == null) return; - this.m_SortedEdges.prevInSEL = null; - e = e.nextInAEL; - while (e != null) + while (e !== null) { - e.prevInSEL = e.prevInAEL; - e.prevInSEL.nextInSEL = e; - e.nextInSEL = null; - e = e.nextInAEL; + e.PrevInSEL = e.PrevInAEL; + e.NextInSEL = e.NextInAEL; + e = e.NextInAEL; } }; ClipperLib.Clipper.prototype.SwapPositionsInAEL = function (edge1, edge2) { - var next, prev; - if (edge1.nextInAEL == null && edge1.prevInAEL == null) return; - if (edge2.nextInAEL == null && edge2.prevInAEL == null) return; - if (edge1.nextInAEL == edge2) - { - next = edge2.nextInAEL; - if (next != null) next.prevInAEL = edge1; - prev = edge1.prevInAEL; - if (prev != null) prev.nextInAEL = edge2; - edge2.prevInAEL = prev; - edge2.nextInAEL = edge1; - edge1.prevInAEL = edge2; - edge1.nextInAEL = next; - } - else if (edge2.nextInAEL == edge1) - { - next = edge1.nextInAEL; - if (next != null) next.prevInAEL = edge2; - prev = edge2.prevInAEL; - if (prev != null) prev.nextInAEL = edge1; - edge1.prevInAEL = prev; - edge1.nextInAEL = edge2; - edge2.prevInAEL = edge1; - edge2.nextInAEL = next; + //check that one or other edge hasn't already been removed from AEL ... + if (edge1.NextInAEL == edge1.PrevInAEL || edge2.NextInAEL == edge2.PrevInAEL) + return; + if (edge1.NextInAEL == edge2) + { + var next = edge2.NextInAEL; + if (next !== null) + next.PrevInAEL = edge1; + var prev = edge1.PrevInAEL; + if (prev !== null) + prev.NextInAEL = edge2; + edge2.PrevInAEL = prev; + edge2.NextInAEL = edge1; + edge1.PrevInAEL = edge2; + edge1.NextInAEL = next; + } + else if (edge2.NextInAEL == edge1) + { + var next = edge1.NextInAEL; + if (next !== null) + next.PrevInAEL = edge2; + var prev = edge2.PrevInAEL; + if (prev !== null) + prev.NextInAEL = edge1; + edge1.PrevInAEL = prev; + edge1.NextInAEL = edge2; + edge2.PrevInAEL = edge1; + edge2.NextInAEL = next; } else { - next = edge1.nextInAEL; - prev = edge1.prevInAEL; - edge1.nextInAEL = edge2.nextInAEL; - if (edge1.nextInAEL != null) edge1.nextInAEL.prevInAEL = edge1; - edge1.prevInAEL = edge2.prevInAEL; - if (edge1.prevInAEL != null) edge1.prevInAEL.nextInAEL = edge1; - edge2.nextInAEL = next; - if (edge2.nextInAEL != null) edge2.nextInAEL.prevInAEL = edge2; - edge2.prevInAEL = prev; - if (edge2.prevInAEL != null) edge2.prevInAEL.nextInAEL = edge2; - } - if (edge1.prevInAEL == null) this.m_ActiveEdges = edge1; - else if (edge2.prevInAEL == null) this.m_ActiveEdges = edge2; + var next = edge1.NextInAEL; + var prev = edge1.PrevInAEL; + edge1.NextInAEL = edge2.NextInAEL; + if (edge1.NextInAEL !== null) + edge1.NextInAEL.PrevInAEL = edge1; + edge1.PrevInAEL = edge2.PrevInAEL; + if (edge1.PrevInAEL !== null) + edge1.PrevInAEL.NextInAEL = edge1; + edge2.NextInAEL = next; + if (edge2.NextInAEL !== null) + edge2.NextInAEL.PrevInAEL = edge2; + edge2.PrevInAEL = prev; + if (edge2.PrevInAEL !== null) + edge2.PrevInAEL.NextInAEL = edge2; + } + if (edge1.PrevInAEL === null) + this.m_ActiveEdges = edge1; + else if (edge2.PrevInAEL === null) + this.m_ActiveEdges = edge2; }; ClipperLib.Clipper.prototype.SwapPositionsInSEL = function (edge1, edge2) { - var next, prev; - if (edge1.nextInSEL == null && edge1.prevInSEL == null) return; - if (edge2.nextInSEL == null && edge2.prevInSEL == null) return; - if (edge1.nextInSEL == edge2) - { - next = edge2.nextInSEL; - if (next != null) next.prevInSEL = edge1; - prev = edge1.prevInSEL; - if (prev != null) prev.nextInSEL = edge2; - edge2.prevInSEL = prev; - edge2.nextInSEL = edge1; - edge1.prevInSEL = edge2; - edge1.nextInSEL = next; - } - else if (edge2.nextInSEL == edge1) - { - next = edge1.nextInSEL; - if (next != null) next.prevInSEL = edge2; - prev = edge2.prevInSEL; - if (prev != null) prev.nextInSEL = edge1; - edge1.prevInSEL = prev; - edge1.nextInSEL = edge2; - edge2.prevInSEL = edge1; - edge2.nextInSEL = next; + if (edge1.NextInSEL === null && edge1.PrevInSEL === null) + return; + if (edge2.NextInSEL === null && edge2.PrevInSEL === null) + return; + if (edge1.NextInSEL == edge2) + { + var next = edge2.NextInSEL; + if (next !== null) + next.PrevInSEL = edge1; + var prev = edge1.PrevInSEL; + if (prev !== null) + prev.NextInSEL = edge2; + edge2.PrevInSEL = prev; + edge2.NextInSEL = edge1; + edge1.PrevInSEL = edge2; + edge1.NextInSEL = next; + } + else if (edge2.NextInSEL == edge1) + { + var next = edge1.NextInSEL; + if (next !== null) + next.PrevInSEL = edge2; + var prev = edge2.PrevInSEL; + if (prev !== null) + prev.NextInSEL = edge1; + edge1.PrevInSEL = prev; + edge1.NextInSEL = edge2; + edge2.PrevInSEL = edge1; + edge2.NextInSEL = next; } else { - next = edge1.nextInSEL; - prev = edge1.prevInSEL; - edge1.nextInSEL = edge2.nextInSEL; - if (edge1.nextInSEL != null) edge1.nextInSEL.prevInSEL = edge1; - edge1.prevInSEL = edge2.prevInSEL; - if (edge1.prevInSEL != null) edge1.prevInSEL.nextInSEL = edge1; - edge2.nextInSEL = next; - if (edge2.nextInSEL != null) edge2.nextInSEL.prevInSEL = edge2; - edge2.prevInSEL = prev; - if (edge2.prevInSEL != null) edge2.prevInSEL.nextInSEL = edge2; - } - if (edge1.prevInSEL == null) this.m_SortedEdges = edge1; - else if (edge2.prevInSEL == null) this.m_SortedEdges = edge2; + var next = edge1.NextInSEL; + var prev = edge1.PrevInSEL; + edge1.NextInSEL = edge2.NextInSEL; + if (edge1.NextInSEL !== null) + edge1.NextInSEL.PrevInSEL = edge1; + edge1.PrevInSEL = edge2.PrevInSEL; + if (edge1.PrevInSEL !== null) + edge1.PrevInSEL.NextInSEL = edge1; + edge2.NextInSEL = next; + if (edge2.NextInSEL !== null) + edge2.NextInSEL.PrevInSEL = edge2; + edge2.PrevInSEL = prev; + if (edge2.PrevInSEL !== null) + edge2.PrevInSEL.NextInSEL = edge2; + } + if (edge1.PrevInSEL === null) + this.m_SortedEdges = edge1; + else if (edge2.PrevInSEL === null) + this.m_SortedEdges = edge2; }; ClipperLib.Clipper.prototype.AddLocalMaxPoly = function (e1, e2, pt) { this.AddOutPt(e1, pt); - if (e1.outIdx == e2.outIdx) + if (e2.WindDelta == 0) this.AddOutPt(e2, pt); + if (e1.OutIdx == e2.OutIdx) { - e1.outIdx = -1; - e2.outIdx = -1; + e1.OutIdx = -1; + e2.OutIdx = -1; } - else if (e1.outIdx < e2.outIdx) this.AppendPolygon(e1, e2); - else this.AppendPolygon(e2, e1); + else if (e1.OutIdx < e2.OutIdx) + this.AppendPolygon(e1, e2); + else + this.AppendPolygon(e2, e1); }; ClipperLib.Clipper.prototype.AddLocalMinPoly = function (e1, e2, pt) { + var result; var e, prevE; - if (e2.dx == ClipperLib.ClipperBase.horizontal || (e1.dx > e2.dx)) + if (ClipperLib.ClipperBase.IsHorizontal(e2) || (e1.Dx > e2.Dx)) { - this.AddOutPt(e1, pt); - e2.outIdx = e1.outIdx; - e1.side = ClipperLib.EdgeSide.esLeft; - e2.side = ClipperLib.EdgeSide.esRight; + result = this.AddOutPt(e1, pt); + e2.OutIdx = e1.OutIdx; + e1.Side = ClipperLib.EdgeSide.esLeft; + e2.Side = ClipperLib.EdgeSide.esRight; e = e1; - if (e.prevInAEL == e2) prevE = e2.prevInAEL; - else prevE = e.prevInAEL; + if (e.PrevInAEL == e2) + prevE = e2.PrevInAEL; + else + prevE = e.PrevInAEL; } else { - this.AddOutPt(e2, pt); - e1.outIdx = e2.outIdx; - e1.side = ClipperLib.EdgeSide.esRight; - e2.side = ClipperLib.EdgeSide.esLeft; + result = this.AddOutPt(e2, pt); + e1.OutIdx = e2.OutIdx; + e1.Side = ClipperLib.EdgeSide.esRight; + e2.Side = ClipperLib.EdgeSide.esLeft; e = e2; - if (e.prevInAEL == e1) prevE = e1.prevInAEL; - else prevE = e.prevInAEL; + if (e.PrevInAEL == e1) + prevE = e1.PrevInAEL; + else + prevE = e.PrevInAEL; + } + if (prevE !== null && prevE.OutIdx >= 0 && (ClipperLib.Clipper.TopX(prevE, pt.Y) == ClipperLib.Clipper.TopX(e, pt.Y)) && ClipperLib.ClipperBase.SlopesEqual(e, prevE, this.m_UseFullRange) && (e.WindDelta !== 0) && (prevE.WindDelta !== 0)) + { + var outPt = this.AddOutPt(prevE, pt); + this.AddJoin(result, outPt, e.Top); } - if (prevE != null && prevE.outIdx >= 0 && (ClipperLib.Clipper.TopX(prevE, pt.Y) == ClipperLib.Clipper.TopX(e, pt.Y)) && this.SlopesEqual(e, prevE, this.m_UseFullRange)) this.AddJoin(e, prevE, -1, -1); + return result; }; ClipperLib.Clipper.prototype.CreateOutRec = function () { var result = new ClipperLib.OutRec(); - result.idx = -1; - result.isHole = false; + result.Idx = -1; + result.IsHole = false; + result.IsOpen = false; result.FirstLeft = null; - result.AppendLink = null; - result.pts = null; - result.bottomPt = null; + result.Pts = null; + result.BottomPt = null; + result.PolyNode = null; + this.m_PolyOuts.push(result); + result.Idx = this.m_PolyOuts.length - 1; return result; }; ClipperLib.Clipper.prototype.AddOutPt = function (e, pt) { - var outRec, op; - var ToFront = (e.side == ClipperLib.EdgeSide.esLeft); - if (e.outIdx < 0) - { - outRec = this.CreateOutRec(); - this.m_PolyOuts.push(outRec); - outRec.idx = this.m_PolyOuts.length - 1; - e.outIdx = outRec.idx; - op = new ClipperLib.OutPt(); - outRec.pts = op; - outRec.bottomPt = op; - op.pt = pt; - op.idx = outRec.idx; - op.next = op; - op.prev = op; - this.SetHoleState(e, outRec); + var ToFront = (e.Side == ClipperLib.EdgeSide.esLeft); + if (e.OutIdx < 0) + { + var outRec = this.CreateOutRec(); + outRec.IsOpen = (e.WindDelta === 0); + var newOp = new ClipperLib.OutPt(); + outRec.Pts = newOp; + newOp.Idx = outRec.Idx; + //newOp.Pt = pt; + newOp.Pt.X = pt.X; + newOp.Pt.Y = pt.Y; + newOp.Next = newOp; + newOp.Prev = newOp; + if (!outRec.IsOpen) + this.SetHoleState(e, outRec); + if (use_xyz) + { + if (ClipperLib.IntPoint.op_Equality(pt, e.Bot)) + { + //newOp.Pt = e.Bot; + newOp.Pt.X = e.Bot.X; + newOp.Pt.Y = e.Bot.Y; + newOp.Pt.Z = e.Bot.Z; + } + else if (ClipperLib.IntPoint.op_Equality(pt, e.Top)) + { + //newOp.Pt = e.Top; + newOp.Pt.X = e.Top.X; + newOp.Pt.Y = e.Top.Y; + newOp.Pt.Z = e.Top.Z; + } + else + this.SetZ(newOp.Pt, e); + } + e.OutIdx = outRec.Idx; + //nb: do this after SetZ ! + return newOp; } else { - outRec = this.m_PolyOuts[e.outIdx]; - op = outRec.pts; - var op2; - if (ToFront && ClipperLib.ClipperBase.PointsEqual(pt, op.pt) || (!ToFront && ClipperLib.ClipperBase.PointsEqual(pt, op.prev.pt))) return; - op2 = new ClipperLib.OutPt(); - op2.pt = pt; - op2.idx = outRec.idx; - if (op2.pt.Y == outRec.bottomPt.pt.Y && op2.pt.X < outRec.bottomPt.pt.X) outRec.bottomPt = op2; - op2.next = op; - op2.prev = op.prev; - op2.prev.next = op2; - op.prev = op2; - if (ToFront) outRec.pts = op2; + var outRec = this.m_PolyOuts[e.OutIdx]; + //OutRec.Pts is the 'Left-most' point & OutRec.Pts.Prev is the 'Right-most' + var op = outRec.Pts; + if (ToFront && ClipperLib.IntPoint.op_Equality(pt, op.Pt)) + return op; + else if (!ToFront && ClipperLib.IntPoint.op_Equality(pt, op.Prev.Pt)) + return op.Prev; + var newOp = new ClipperLib.OutPt(); + newOp.Idx = outRec.Idx; + //newOp.Pt = pt; + newOp.Pt.X = pt.X; + newOp.Pt.Y = pt.Y; + newOp.Next = op; + newOp.Prev = op.Prev; + newOp.Prev.Next = newOp; + op.Prev = newOp; + if (ToFront) + outRec.Pts = newOp; + if (use_xyz) + { + if (ClipperLib.IntPoint.op_Equality(pt, e.Bot)) + { + //newOp.Pt = e.Bot; + newOp.Pt.X = e.Bot.X; + newOp.Pt.Y = e.Bot.Y; + newOp.Pt.Z = e.Bot.Z; + } + else if (ClipperLib.IntPoint.op_Equality(pt, e.Top)) + { + //newOp.Pt = e.Top; + newOp.Pt.X = e.Top.X; + newOp.Pt.Y = e.Top.Y; + newOp.Pt.Z = e.Top.Z; + } + else + this.SetZ(newOp.Pt, e); + } + return newOp; } }; ClipperLib.Clipper.prototype.SwapPoints = function (pt1, pt2) { - var tmp = pt1.Value; - pt1.Value = pt2.Value; - pt2.Value = tmp; + var tmp = new ClipperLib.IntPoint(pt1.Value); + //pt1.Value = pt2.Value; + pt1.Value.X = pt2.Value.X; + pt1.Value.Y = pt2.Value.Y; + //pt2.Value = tmp; + pt2.Value.X = tmp.X; + pt2.Value.Y = tmp.Y; }; - ClipperLib.Clipper.prototype.GetOverlapSegment = function (pt1a, pt1b, pt2a, pt2b, pt1, pt2) + ClipperLib.Clipper.prototype.HorzSegmentsOverlap = function (Pt1a, Pt1b, Pt2a, Pt2b) { - if (ClipperLib.Math_Abs_Int64(pt1a.X - pt1b.X) > ClipperLib.Math_Abs_Int64(pt1a.Y - pt1b.Y)) - { - if (pt1a.X > pt1b.X) - (function () - { - pt1a = { - Value: pt1a - }; - pt1b = { - Value: pt1b - }; - var $res = this.SwapPoints(pt1a, pt1b); - pt1a = pt1a.Value; - pt1b = pt1b.Value; - return $res; - }) - .call(this); - if (pt2a.X > pt2b.X) - (function () - { - pt2a = { - Value: pt2a - }; - pt2b = { - Value: pt2b - }; - var $res = this.SwapPoints(pt2a, pt2b); - pt2a = pt2a.Value; - pt2b = pt2b.Value; - return $res; - }) - .call(this); - if (pt1a.X > pt2a.X) pt1.Value = pt1a; - else pt1.Value = pt2a; - if (pt1b.X < pt2b.X) pt2.Value = pt1b; - else pt2.Value = pt2b; - return pt1.Value.X < pt2.Value.X; - } - else - { - if (pt1a.Y < pt1b.Y) - (function () - { - pt1a = { - Value: pt1a - }; - pt1b = { - Value: pt1b - }; - var $res = this.SwapPoints(pt1a, pt1b); - pt1a = pt1a.Value; - pt1b = pt1b.Value; - return $res; - }) - .call(this); - if (pt2a.Y < pt2b.Y) - (function () - { - pt2a = { - Value: pt2a - }; - pt2b = { - Value: pt2b - }; - var $res = this.SwapPoints(pt2a, pt2b); - pt2a = pt2a.Value; - pt2b = pt2b.Value; - return $res; - }) - .call(this); - if (pt1a.Y < pt2a.Y) pt1.Value = pt1a; - else pt1.Value = pt2a; - if (pt1b.Y > pt2b.Y) pt2.Value = pt1b; - else pt2.Value = pt2b; - return pt1.Value.Y > pt2.Value.Y; - } - }; - ClipperLib.Clipper.prototype.FindSegment = function (pp, pt1, pt2) - { - if (pp.Value == null) return false; - var pp2 = pp.Value; - var pt1a = new ClipperLib.IntPoint(pt1.Value); - var pt2a = new ClipperLib.IntPoint(pt2.Value); - do { - // Timo's comment: for some reason calling SlopesEqual() below uses big integers - // So although coordinates low (eg. 900), big integers is used - if (this.SlopesEqual(pt1a, pt2a, pp.Value.pt, pp.Value.prev.pt, true) && this.SlopesEqual(pt1a, pt2a, pp.Value.pt, true) && this.GetOverlapSegment(pt1a, pt2a, pp.Value.pt, pp.Value.prev.pt, pt1, pt2)) return true; - pp.Value = pp.Value.next; - } - while (pp.Value != pp2); - return false; - }; - ClipperLib.Clipper.prototype.Pt3IsBetweenPt1AndPt2 = function (pt1, pt2, pt3) - { - if (ClipperLib.ClipperBase.PointsEqual(pt1, pt3) || ClipperLib.ClipperBase.PointsEqual(pt2, pt3)) return true; - else if (pt1.X != pt2.X) return (pt1.X < pt3.X) == (pt3.X < pt2.X); - else return (pt1.Y < pt3.Y) == (pt3.Y < pt2.Y); + //precondition: both segments are horizontal + if ((Pt1a.X > Pt2a.X) == (Pt1a.X < Pt2b.X)) + return true; + else if ((Pt1b.X > Pt2a.X) == (Pt1b.X < Pt2b.X)) + return true; + else if ((Pt2a.X > Pt1a.X) == (Pt2a.X < Pt1b.X)) + return true; + else if ((Pt2b.X > Pt1a.X) == (Pt2b.X < Pt1b.X)) + return true; + else if ((Pt1a.X == Pt2a.X) && (Pt1b.X == Pt2b.X)) + return true; + else if ((Pt1a.X == Pt2b.X) && (Pt1b.X == Pt2a.X)) + return true; + else + return false; }; ClipperLib.Clipper.prototype.InsertPolyPtBetween = function (p1, p2, pt) { var result = new ClipperLib.OutPt(); - result.pt = pt; - if (p2 == p1.next) + //result.Pt = pt; + result.Pt.X = pt.X; + result.Pt.Y = pt.Y; + if (p2 == p1.Next) { - p1.next = result; - p2.prev = result; - result.next = p2; - result.prev = p1; + p1.Next = result; + p2.Prev = result; + result.Next = p2; + result.Prev = p1; } else { - p2.next = result; - p1.prev = result; - result.next = p1; - result.prev = p2; + p2.Next = result; + p1.Prev = result; + result.Next = p1; + result.Prev = p2; } return result; }; ClipperLib.Clipper.prototype.SetHoleState = function (e, outRec) { var isHole = false; - var e2 = e.prevInAEL; - while (e2 != null) + var e2 = e.PrevInAEL; + while (e2 !== null) { - if (e2.outIdx >= 0) + if (e2.OutIdx >= 0 && e2.WindDelta != 0) { isHole = !isHole; - if (outRec.FirstLeft == null) outRec.FirstLeft = this.m_PolyOuts[e2.outIdx]; + if (outRec.FirstLeft === null) + outRec.FirstLeft = this.m_PolyOuts[e2.OutIdx]; } - e2 = e2.prevInAEL; + e2 = e2.PrevInAEL; } - if (isHole) outRec.isHole = true; + if (isHole) + outRec.IsHole = true; }; ClipperLib.Clipper.prototype.GetDx = function (pt1, pt2) { - if (pt1.Y == pt2.Y) return ClipperLib.ClipperBase.horizontal; - else return (pt2.X - pt1.X) / (pt2.Y - pt1.Y); + if (pt1.Y == pt2.Y) + return ClipperLib.ClipperBase.horizontal; + else + return (pt2.X - pt1.X) / (pt2.Y - pt1.Y); }; ClipperLib.Clipper.prototype.FirstIsBottomPt = function (btmPt1, btmPt2) { - var p = btmPt1.prev; - while (ClipperLib.ClipperBase.PointsEqual(p.pt, btmPt1.pt) && (p != btmPt1)) - p = p.prev; - var dx1p = ClipperLib.Math_Abs_Double(this.GetDx(btmPt1.pt, p.pt)); - p = btmPt1.next; - while (ClipperLib.ClipperBase.PointsEqual(p.pt, btmPt1.pt) && (p != btmPt1)) - p = p.next; - var dx1n = ClipperLib.Math_Abs_Double(this.GetDx(btmPt1.pt, p.pt)); - p = btmPt2.prev; - while (ClipperLib.ClipperBase.PointsEqual(p.pt, btmPt2.pt) && (p != btmPt2)) - p = p.prev; - var dx2p = ClipperLib.Math_Abs_Double(this.GetDx(btmPt2.pt, p.pt)); - p = btmPt2.next; - while (ClipperLib.ClipperBase.PointsEqual(p.pt, btmPt2.pt) && (p != btmPt2)) - p = p.next; - var dx2n = ClipperLib.Math_Abs_Double(this.GetDx(btmPt2.pt, p.pt)); + var p = btmPt1.Prev; + while ((ClipperLib.IntPoint.op_Equality(p.Pt, btmPt1.Pt)) && (p != btmPt1)) + p = p.Prev; + var dx1p = Math.abs(this.GetDx(btmPt1.Pt, p.Pt)); + p = btmPt1.Next; + while ((ClipperLib.IntPoint.op_Equality(p.Pt, btmPt1.Pt)) && (p != btmPt1)) + p = p.Next; + var dx1n = Math.abs(this.GetDx(btmPt1.Pt, p.Pt)); + p = btmPt2.Prev; + while ((ClipperLib.IntPoint.op_Equality(p.Pt, btmPt2.Pt)) && (p != btmPt2)) + p = p.Prev; + var dx2p = Math.abs(this.GetDx(btmPt2.Pt, p.Pt)); + p = btmPt2.Next; + while ((ClipperLib.IntPoint.op_Equality(p.Pt, btmPt2.Pt)) && (p != btmPt2)) + p = p.Next; + var dx2n = Math.abs(this.GetDx(btmPt2.Pt, p.Pt)); return (dx1p >= dx2p && dx1p >= dx2n) || (dx1n >= dx2p && dx1n >= dx2n); }; ClipperLib.Clipper.prototype.GetBottomPt = function (pp) { var dups = null; - var p = pp.next; + var p = pp.Next; while (p != pp) { - if (p.pt.Y > pp.pt.Y) + if (p.Pt.Y > pp.Pt.Y) { pp = p; dups = null; } - else if (p.pt.Y == pp.pt.Y && p.pt.X <= pp.pt.X) + else if (p.Pt.Y == pp.Pt.Y && p.Pt.X <= pp.Pt.X) { - if (p.pt.X < pp.pt.X) + if (p.Pt.X < pp.Pt.X) { dups = null; pp = p; } else { - if (p.next != pp && p.prev != pp) dups = p; + if (p.Next != pp && p.Prev != pp) + dups = p; } } - p = p.next; + p = p.Next; } - if (dups != null) + if (dups !== null) { + //there appears to be at least 2 vertices at bottomPt so ... while (dups != p) { - if (!this.FirstIsBottomPt(p, dups)) pp = dups; - dups = dups.next; - while (!ClipperLib.ClipperBase.PointsEqual(dups.pt, pp.pt)) - dups = dups.next; + if (!this.FirstIsBottomPt(p, dups)) + pp = dups; + dups = dups.Next; + while (ClipperLib.IntPoint.op_Inequality(dups.Pt, pp.Pt)) + dups = dups.Next; } } return pp; }; ClipperLib.Clipper.prototype.GetLowermostRec = function (outRec1, outRec2) { - var bPt1 = outRec1.bottomPt; - var bPt2 = outRec2.bottomPt; - if (bPt1.pt.Y > bPt2.pt.Y) return outRec1; - else if (bPt1.pt.Y < bPt2.pt.Y) return outRec2; - else if (bPt1.pt.X < bPt2.pt.X) return outRec1; - else if (bPt1.pt.X > bPt2.pt.X) return outRec2; - else if (bPt1.next == bPt1) return outRec2; - else if (bPt2.next == bPt2) return outRec1; - else if (this.FirstIsBottomPt(bPt1, bPt2)) return outRec1; - else return outRec2; + //work out which polygon fragment has the correct hole state ... + if (outRec1.BottomPt === null) + outRec1.BottomPt = this.GetBottomPt(outRec1.Pts); + if (outRec2.BottomPt === null) + outRec2.BottomPt = this.GetBottomPt(outRec2.Pts); + var bPt1 = outRec1.BottomPt; + var bPt2 = outRec2.BottomPt; + if (bPt1.Pt.Y > bPt2.Pt.Y) + return outRec1; + else if (bPt1.Pt.Y < bPt2.Pt.Y) + return outRec2; + else if (bPt1.Pt.X < bPt2.Pt.X) + return outRec1; + else if (bPt1.Pt.X > bPt2.Pt.X) + return outRec2; + else if (bPt1.Next == bPt1) + return outRec2; + else if (bPt2.Next == bPt2) + return outRec1; + else if (this.FirstIsBottomPt(bPt1, bPt2)) + return outRec1; + else + return outRec2; }; ClipperLib.Clipper.prototype.Param1RightOfParam2 = function (outRec1, outRec2) { do { outRec1 = outRec1.FirstLeft; - if (outRec1 == outRec2) return true; + if (outRec1 == outRec2) + return true; } - while (outRec1 != null); + while (outRec1 !== null) return false; }; + ClipperLib.Clipper.prototype.GetOutRec = function (idx) + { + var outrec = this.m_PolyOuts[idx]; + while (outrec != this.m_PolyOuts[outrec.Idx]) + outrec = this.m_PolyOuts[outrec.Idx]; + return outrec; + }; ClipperLib.Clipper.prototype.AppendPolygon = function (e1, e2) { - var outRec1 = this.m_PolyOuts[e1.outIdx]; - var outRec2 = this.m_PolyOuts[e2.outIdx]; + //get the start and ends of both output polygons ... + var outRec1 = this.m_PolyOuts[e1.OutIdx]; + var outRec2 = this.m_PolyOuts[e2.OutIdx]; var holeStateRec; - if (this.Param1RightOfParam2(outRec1, outRec2)) holeStateRec = outRec2; - else if (this.Param1RightOfParam2(outRec2, outRec1)) holeStateRec = outRec1; - else holeStateRec = this.GetLowermostRec(outRec1, outRec2); - var p1_lft = outRec1.pts; - var p1_rt = p1_lft.prev; - var p2_lft = outRec2.pts; - var p2_rt = p2_lft.prev; + if (this.Param1RightOfParam2(outRec1, outRec2)) + holeStateRec = outRec2; + else if (this.Param1RightOfParam2(outRec2, outRec1)) + holeStateRec = outRec1; + else + holeStateRec = this.GetLowermostRec(outRec1, outRec2); + var p1_lft = outRec1.Pts; + var p1_rt = p1_lft.Prev; + var p2_lft = outRec2.Pts; + var p2_rt = p2_lft.Prev; var side; - var i; - if (e1.side == ClipperLib.EdgeSide.esLeft) + //join e2 poly onto e1 poly and delete pointers to e2 ... + if (e1.Side == ClipperLib.EdgeSide.esLeft) { - if (e2.side == ClipperLib.EdgeSide.esLeft) + if (e2.Side == ClipperLib.EdgeSide.esLeft) { + //z y x a b c this.ReversePolyPtLinks(p2_lft); - p2_lft.next = p1_lft; - p1_lft.prev = p2_lft; - p1_rt.next = p2_rt; - p2_rt.prev = p1_rt; - outRec1.pts = p2_rt; + p2_lft.Next = p1_lft; + p1_lft.Prev = p2_lft; + p1_rt.Next = p2_rt; + p2_rt.Prev = p1_rt; + outRec1.Pts = p2_rt; } else { - p2_rt.next = p1_lft; - p1_lft.prev = p2_rt; - p2_lft.prev = p1_rt; - p1_rt.next = p2_lft; - outRec1.pts = p2_lft; + //x y z a b c + p2_rt.Next = p1_lft; + p1_lft.Prev = p2_rt; + p2_lft.Prev = p1_rt; + p1_rt.Next = p2_lft; + outRec1.Pts = p2_lft; } side = ClipperLib.EdgeSide.esLeft; } else { - if (e2.side == ClipperLib.EdgeSide.esRight) + if (e2.Side == ClipperLib.EdgeSide.esRight) { + //a b c z y x this.ReversePolyPtLinks(p2_lft); - p1_rt.next = p2_rt; - p2_rt.prev = p1_rt; - p2_lft.next = p1_lft; - p1_lft.prev = p2_lft; + p1_rt.Next = p2_rt; + p2_rt.Prev = p1_rt; + p2_lft.Next = p1_lft; + p1_lft.Prev = p2_lft; } else { - p1_rt.next = p2_lft; - p2_lft.prev = p1_rt; - p1_lft.prev = p2_rt; - p2_rt.next = p1_lft; + //a b c x y z + p1_rt.Next = p2_lft; + p2_lft.Prev = p1_rt; + p1_lft.Prev = p2_rt; + p2_rt.Next = p1_lft; } side = ClipperLib.EdgeSide.esRight; } + outRec1.BottomPt = null; if (holeStateRec == outRec2) { - outRec1.bottomPt = outRec2.bottomPt; - outRec1.bottomPt.idx = outRec1.idx; - if (outRec2.FirstLeft != outRec1) outRec1.FirstLeft = outRec2.FirstLeft; - outRec1.isHole = outRec2.isHole; - } - outRec2.pts = null; - outRec2.bottomPt = null; - outRec2.AppendLink = outRec1; - var OKIdx = e1.outIdx; - var ObsoleteIdx = e2.outIdx; - e1.outIdx = -1; - e2.outIdx = -1; + if (outRec2.FirstLeft != outRec1) + outRec1.FirstLeft = outRec2.FirstLeft; + outRec1.IsHole = outRec2.IsHole; + } + outRec2.Pts = null; + outRec2.BottomPt = null; + outRec2.FirstLeft = outRec1; + var OKIdx = e1.OutIdx; + var ObsoleteIdx = e2.OutIdx; + e1.OutIdx = -1; + //nb: safe because we only get here via AddLocalMaxPoly + e2.OutIdx = -1; var e = this.m_ActiveEdges; - while (e != null) + while (e !== null) { - if (e.outIdx == ObsoleteIdx) + if (e.OutIdx == ObsoleteIdx) { - e.outIdx = OKIdx; - e.side = side; + e.OutIdx = OKIdx; + e.Side = side; break; } - e = e.nextInAEL; - } - for (i = 0; i < this.m_Joins.length; ++i) - { - if (this.m_Joins[i].poly1Idx == ObsoleteIdx) this.m_Joins[i].poly1Idx = OKIdx; - if (this.m_Joins[i].poly2Idx == ObsoleteIdx) this.m_Joins[i].poly2Idx = OKIdx; - } - for (i = 0; i < this.m_HorizJoins.length; ++i) - { - if (this.m_HorizJoins[i].savedIdx == ObsoleteIdx) this.m_HorizJoins[i].savedIdx = OKIdx; + e = e.NextInAEL; } + outRec2.Idx = outRec1.Idx; }; ClipperLib.Clipper.prototype.ReversePolyPtLinks = function (pp) { - if (pp == null) return; + if (pp === null) + return; var pp1; var pp2; pp1 = pp; do { - pp2 = pp1.next; - pp1.next = pp1.prev; - pp1.prev = pp2; + pp2 = pp1.Next; + pp1.Next = pp1.Prev; + pp1.Prev = pp2; pp1 = pp2; } - while (pp1 != pp); + while (pp1 != pp) }; ClipperLib.Clipper.SwapSides = function (edge1, edge2) { - var side = edge1.side; - edge1.side = edge2.side; - edge2.side = side; + var side = edge1.Side; + edge1.Side = edge2.Side; + edge2.Side = side; }; ClipperLib.Clipper.SwapPolyIndexes = function (edge1, edge2) { - var outIdx = edge1.outIdx; - edge1.outIdx = edge2.outIdx; - edge2.outIdx = outIdx; - }; - ClipperLib.Clipper.prototype.DoEdge1 = function (edge1, edge2, pt) - { - this.AddOutPt(edge1, pt); - ClipperLib.Clipper.SwapSides(edge1, edge2); - ClipperLib.Clipper.SwapPolyIndexes(edge1, edge2); - }; - ClipperLib.Clipper.prototype.DoEdge2 = function (edge1, edge2, pt) - { - this.AddOutPt(edge2, pt); - ClipperLib.Clipper.SwapSides(edge1, edge2); - ClipperLib.Clipper.SwapPolyIndexes(edge1, edge2); - }; - ClipperLib.Clipper.prototype.DoBothEdges = function (edge1, edge2, pt) - { - this.AddOutPt(edge1, pt); - this.AddOutPt(edge2, pt); - ClipperLib.Clipper.SwapSides(edge1, edge2); - ClipperLib.Clipper.SwapPolyIndexes(edge1, edge2); + var outIdx = edge1.OutIdx; + edge1.OutIdx = edge2.OutIdx; + edge2.OutIdx = outIdx; }; - ClipperLib.Clipper.prototype.IntersectEdges = function (e1, e2, pt, protects) + ClipperLib.Clipper.prototype.IntersectEdges = function (e1, e2, pt, protect) { - var e1stops = (ClipperLib.Protects.ipLeft & protects) == 0 && e1.nextInLML == null && e1.xtop == pt.X && e1.ytop == pt.Y; - var e2stops = (ClipperLib.Protects.ipRight & protects) == 0 && e2.nextInLML == null && e2.xtop == pt.X && e2.ytop == pt.Y; - var e1Contributing = (e1.outIdx >= 0); - var e2contributing = (e2.outIdx >= 0); - if (e1.polyType == e2.polyType) + //e1 will be to the left of e2 BELOW the intersection. Therefore e1 is before + //e2 in AEL except when e1 is being inserted at the intersection point ... + var e1stops = !protect && e1.NextInLML === null && + e1.Top.X == pt.X && e1.Top.Y == pt.Y; + var e2stops = !protect && e2.NextInLML === null && + e2.Top.X == pt.X && e2.Top.Y == pt.Y; + var e1Contributing = (e1.OutIdx >= 0); + var e2Contributing = (e2.OutIdx >= 0); + if (use_lines) + { + //if either edge is on an OPEN path ... + if (e1.WindDelta === 0 || e2.WindDelta === 0) + { + //ignore subject-subject open path intersections UNLESS they + //are both open paths, AND they are both 'contributing maximas' ... + if (e1.WindDelta === 0 && e2.WindDelta === 0) + { + if ((e1stops || e2stops) && e1Contributing && e2Contributing) + this.AddLocalMaxPoly(e1, e2, pt); + } + //if intersecting a subj line with a subj poly ... + else if (e1.PolyTyp == e2.PolyTyp && + e1.WindDelta != e2.WindDelta && this.m_ClipType == ClipperLib.ClipType.ctUnion) + { + if (e1.WindDelta === 0) + { + if (e2Contributing) + { + this.AddOutPt(e1, pt); + if (e1Contributing) + e1.OutIdx = -1; + } + } + else + { + if (e1Contributing) + { + this.AddOutPt(e2, pt); + if (e2Contributing) + e2.OutIdx = -1; + } + } + } + else if (e1.PolyTyp != e2.PolyTyp) + { + if ((e1.WindDelta === 0) && Math.abs(e2.WindCnt) == 1 && + (this.m_ClipType != ClipperLib.ClipType.ctUnion || e2.WindCnt2 === 0)) + { + this.AddOutPt(e1, pt); + if (e1Contributing) + e1.OutIdx = -1; + } + else if ((e2.WindDelta === 0) && (Math.abs(e1.WindCnt) == 1) && + (this.m_ClipType != ClipperLib.ClipType.ctUnion || e1.WindCnt2 === 0)) + { + this.AddOutPt(e2, pt); + if (e2Contributing) + e2.OutIdx = -1; + } + } + if (e1stops) + if (e1.OutIdx < 0) + this.DeleteFromAEL(e1); + else + ClipperLib.Error("Error intersecting polylines"); + if (e2stops) + if (e2.OutIdx < 0) + this.DeleteFromAEL(e2); + else + ClipperLib.Error("Error intersecting polylines"); + return; + } + } + //update winding counts... + //assumes that e1 will be to the Right of e2 ABOVE the intersection + if (e1.PolyTyp == e2.PolyTyp) { if (this.IsEvenOddFillType(e1)) { - var oldE1WindCnt = e1.windCnt; - e1.windCnt = e2.windCnt; - e2.windCnt = oldE1WindCnt; + var oldE1WindCnt = e1.WindCnt; + e1.WindCnt = e2.WindCnt; + e2.WindCnt = oldE1WindCnt; } else { - if (e1.windCnt + e2.windDelta == 0) e1.windCnt = -e1.windCnt; - else e1.windCnt += e2.windDelta; - if (e2.windCnt - e1.windDelta == 0) e2.windCnt = -e2.windCnt; - else e2.windCnt -= e1.windDelta; + if (e1.WindCnt + e2.WindDelta === 0) + e1.WindCnt = -e1.WindCnt; + else + e1.WindCnt += e2.WindDelta; + if (e2.WindCnt - e1.WindDelta === 0) + e2.WindCnt = -e2.WindCnt; + else + e2.WindCnt -= e1.WindDelta; } } else { - if (!this.IsEvenOddFillType(e2)) e1.windCnt2 += e2.windDelta; - else e1.windCnt2 = (e1.windCnt2 == 0) ? 1 : 0; - if (!this.IsEvenOddFillType(e1)) e2.windCnt2 -= e1.windDelta; - else e2.windCnt2 = (e2.windCnt2 == 0) ? 1 : 0; + if (!this.IsEvenOddFillType(e2)) + e1.WindCnt2 += e2.WindDelta; + else + e1.WindCnt2 = (e1.WindCnt2 === 0) ? 1 : 0; + if (!this.IsEvenOddFillType(e1)) + e2.WindCnt2 -= e1.WindDelta; + else + e2.WindCnt2 = (e2.WindCnt2 === 0) ? 1 : 0; } var e1FillType, e2FillType, e1FillType2, e2FillType2; - if (e1.polyType == ClipperLib.PolyType.ptSubject) + if (e1.PolyTyp == ClipperLib.PolyType.ptSubject) { e1FillType = this.m_SubjFillType; e1FillType2 = this.m_ClipFillType; @@ -3427,7 +4316,7 @@ e1FillType = this.m_ClipFillType; e1FillType2 = this.m_SubjFillType; } - if (e2.polyType == ClipperLib.PolyType.ptSubject) + if (e2.PolyTyp == ClipperLib.PolyType.ptSubject) { e2FillType = this.m_SubjFillType; e2FillType2 = this.m_ClipFillType; @@ -3440,1444 +4329,2332 @@ var e1Wc, e2Wc; switch (e1FillType) { - case ClipperLib.PolyFillType.pftPositive: - e1Wc = e1.windCnt; - break; - case ClipperLib.PolyFillType.pftNegative: - e1Wc = -e1.windCnt; - break; - default: - e1Wc = ClipperLib.Math_Abs_Int32(e1.windCnt); - break; + case ClipperLib.PolyFillType.pftPositive: + e1Wc = e1.WindCnt; + break; + case ClipperLib.PolyFillType.pftNegative: + e1Wc = -e1.WindCnt; + break; + default: + e1Wc = Math.abs(e1.WindCnt); + break; } switch (e2FillType) { - case ClipperLib.PolyFillType.pftPositive: - e2Wc = e2.windCnt; - break; - case ClipperLib.PolyFillType.pftNegative: - e2Wc = -e2.windCnt; - break; - default: - e2Wc = ClipperLib.Math_Abs_Int32(e2.windCnt); - break; - } - if (e1Contributing && e2contributing) - { - if (e1stops || e2stops || (e1Wc != 0 && e1Wc != 1) || (e2Wc != 0 && e2Wc != 1) || (e1.polyType != e2.polyType && this.m_ClipType != ClipperLib.ClipType.ctXor)) this.AddLocalMaxPoly(e1, e2, pt); - else this.DoBothEdges(e1, e2, pt); + case ClipperLib.PolyFillType.pftPositive: + e2Wc = e2.WindCnt; + break; + case ClipperLib.PolyFillType.pftNegative: + e2Wc = -e2.WindCnt; + break; + default: + e2Wc = Math.abs(e2.WindCnt); + break; + } + if (e1Contributing && e2Contributing) + { + if (e1stops || e2stops || (e1Wc !== 0 && e1Wc != 1) || (e2Wc !== 0 && e2Wc != 1) || + (e1.PolyTyp != e2.PolyTyp && this.m_ClipType != ClipperLib.ClipType.ctXor)) + this.AddLocalMaxPoly(e1, e2, pt); + else + { + this.AddOutPt(e1, pt); + this.AddOutPt(e2, pt); + ClipperLib.Clipper.SwapSides(e1, e2); + ClipperLib.Clipper.SwapPolyIndexes(e1, e2); + } } else if (e1Contributing) { - if ((e2Wc == 0 || e2Wc == 1) && (this.m_ClipType != ClipperLib.ClipType.ctIntersection || e2.polyType == ClipperLib.PolyType.ptSubject || (e2.windCnt2 != 0))) this.DoEdge1(e1, e2, pt); + if (e2Wc === 0 || e2Wc == 1) + { + this.AddOutPt(e1, pt); + ClipperLib.Clipper.SwapSides(e1, e2); + ClipperLib.Clipper.SwapPolyIndexes(e1, e2); + } } - else if (e2contributing) + else if (e2Contributing) { - if ((e1Wc == 0 || e1Wc == 1) && (this.m_ClipType != ClipperLib.ClipType.ctIntersection || e1.polyType == ClipperLib.PolyType.ptSubject || (e1.windCnt2 != 0))) this.DoEdge2(e1, e2, pt); + if (e1Wc === 0 || e1Wc == 1) + { + this.AddOutPt(e2, pt); + ClipperLib.Clipper.SwapSides(e1, e2); + ClipperLib.Clipper.SwapPolyIndexes(e1, e2); + } } - else if ((e1Wc == 0 || e1Wc == 1) && (e2Wc == 0 || e2Wc == 1) && !e1stops && !e2stops) + else if ((e1Wc === 0 || e1Wc == 1) && + (e2Wc === 0 || e2Wc == 1) && !e1stops && !e2stops) { + //neither edge is currently contributing ... var e1Wc2, e2Wc2; switch (e1FillType2) { - case ClipperLib.PolyFillType.pftPositive: - e1Wc2 = e1.windCnt2; - break; - case ClipperLib.PolyFillType.pftNegative: - e1Wc2 = -e1.windCnt2; - break; - default: - e1Wc2 = ClipperLib.Math_Abs_Int32(e1.windCnt2); - break; + case ClipperLib.PolyFillType.pftPositive: + e1Wc2 = e1.WindCnt2; + break; + case ClipperLib.PolyFillType.pftNegative: + e1Wc2 = -e1.WindCnt2; + break; + default: + e1Wc2 = Math.abs(e1.WindCnt2); + break; } switch (e2FillType2) { - case ClipperLib.PolyFillType.pftPositive: - e2Wc2 = e2.windCnt2; - break; - case ClipperLib.PolyFillType.pftNegative: - e2Wc2 = -e2.windCnt2; - break; - default: - e2Wc2 = ClipperLib.Math_Abs_Int32(e2.windCnt2); - break; + case ClipperLib.PolyFillType.pftPositive: + e2Wc2 = e2.WindCnt2; + break; + case ClipperLib.PolyFillType.pftNegative: + e2Wc2 = -e2.WindCnt2; + break; + default: + e2Wc2 = Math.abs(e2.WindCnt2); + break; } - if (e1.polyType != e2.polyType) this.AddLocalMinPoly(e1, e2, pt); - else if (e1Wc == 1 && e2Wc == 1) switch (this.m_ClipType) - { + if (e1.PolyTyp != e2.PolyTyp) + this.AddLocalMinPoly(e1, e2, pt); + else if (e1Wc == 1 && e2Wc == 1) + switch (this.m_ClipType) + { case ClipperLib.ClipType.ctIntersection: - if (e1Wc2 > 0 && e2Wc2 > 0) this.AddLocalMinPoly(e1, e2, pt); + if (e1Wc2 > 0 && e2Wc2 > 0) + this.AddLocalMinPoly(e1, e2, pt); break; case ClipperLib.ClipType.ctUnion: - if (e1Wc2 <= 0 && e2Wc2 <= 0) this.AddLocalMinPoly(e1, e2, pt); + if (e1Wc2 <= 0 && e2Wc2 <= 0) + this.AddLocalMinPoly(e1, e2, pt); break; case ClipperLib.ClipType.ctDifference: - if (((e1.polyType == ClipperLib.PolyType.ptClip) && (e1Wc2 > 0) && (e2Wc2 > 0)) || ((e1.polyType == ClipperLib.PolyType.ptSubject) && (e1Wc2 <= 0) && (e2Wc2 <= 0))) this.AddLocalMinPoly(e1, e2, pt); + if (((e1.PolyTyp == ClipperLib.PolyType.ptClip) && (e1Wc2 > 0) && (e2Wc2 > 0)) || + ((e1.PolyTyp == ClipperLib.PolyType.ptSubject) && (e1Wc2 <= 0) && (e2Wc2 <= 0))) + this.AddLocalMinPoly(e1, e2, pt); break; case ClipperLib.ClipType.ctXor: this.AddLocalMinPoly(e1, e2, pt); break; - } - else ClipperLib.Clipper.SwapSides(e1, e2); + } + else + ClipperLib.Clipper.SwapSides(e1, e2); } - if ((e1stops != e2stops) && ((e1stops && (e1.outIdx >= 0)) || (e2stops && (e2.outIdx >= 0)))) + if ((e1stops != e2stops) && + ((e1stops && (e1.OutIdx >= 0)) || (e2stops && (e2.OutIdx >= 0)))) { ClipperLib.Clipper.SwapSides(e1, e2); ClipperLib.Clipper.SwapPolyIndexes(e1, e2); } - if (e1stops) this.DeleteFromAEL(e1); - if (e2stops) this.DeleteFromAEL(e2); + //finally, delete any non-contributing maxima edges ... + if (e1stops) + this.DeleteFromAEL(e1); + if (e2stops) + this.DeleteFromAEL(e2); }; ClipperLib.Clipper.prototype.DeleteFromAEL = function (e) { - var AelPrev = e.prevInAEL; - var AelNext = e.nextInAEL; - if (AelPrev == null && AelNext == null && (e != this.m_ActiveEdges)) return; - if (AelPrev != null) AelPrev.nextInAEL = AelNext; - else this.m_ActiveEdges = AelNext; - if (AelNext != null) AelNext.prevInAEL = AelPrev; - e.nextInAEL = null; - e.prevInAEL = null; + var AelPrev = e.PrevInAEL; + var AelNext = e.NextInAEL; + if (AelPrev === null && AelNext === null && (e != this.m_ActiveEdges)) + return; + //already deleted + if (AelPrev !== null) + AelPrev.NextInAEL = AelNext; + else + this.m_ActiveEdges = AelNext; + if (AelNext !== null) + AelNext.PrevInAEL = AelPrev; + e.NextInAEL = null; + e.PrevInAEL = null; }; ClipperLib.Clipper.prototype.DeleteFromSEL = function (e) { - var SelPrev = e.prevInSEL; - var SelNext = e.nextInSEL; - if (SelPrev == null && SelNext == null && (e != this.m_SortedEdges)) return; - if (SelPrev != null) SelPrev.nextInSEL = SelNext; - else this.m_SortedEdges = SelNext; - if (SelNext != null) SelNext.prevInSEL = SelPrev; - e.nextInSEL = null; - e.prevInSEL = null; + var SelPrev = e.PrevInSEL; + var SelNext = e.NextInSEL; + if (SelPrev === null && SelNext === null && (e != this.m_SortedEdges)) + return; + //already deleted + if (SelPrev !== null) + SelPrev.NextInSEL = SelNext; + else + this.m_SortedEdges = SelNext; + if (SelNext !== null) + SelNext.PrevInSEL = SelPrev; + e.NextInSEL = null; + e.PrevInSEL = null; }; ClipperLib.Clipper.prototype.UpdateEdgeIntoAEL = function (e) { - if (e.Value.nextInLML == null) ClipperLib.Error("UpdateEdgeIntoAEL: invalid call"); - var AelPrev = e.Value.prevInAEL; - var AelNext = e.Value.nextInAEL; - e.Value.nextInLML.outIdx = e.Value.outIdx; - if (AelPrev != null) AelPrev.nextInAEL = e.Value.nextInLML; - else this.m_ActiveEdges = e.Value.nextInLML; - if (AelNext != null) AelNext.prevInAEL = e.Value.nextInLML; - e.Value.nextInLML.side = e.Value.side; - e.Value.nextInLML.windDelta = e.Value.windDelta; - e.Value.nextInLML.windCnt = e.Value.windCnt; - e.Value.nextInLML.windCnt2 = e.Value.windCnt2; - e.Value = e.Value.nextInLML; - e.Value.prevInAEL = AelPrev; - e.Value.nextInAEL = AelNext; - if (e.Value.dx != ClipperLib.ClipperBase.horizontal) this.InsertScanbeam(e.Value.ytop); - }; - ClipperLib.Clipper.prototype.ProcessHorizontals = function () + if (e.NextInLML === null) + ClipperLib.Error("UpdateEdgeIntoAEL: invalid call"); + var AelPrev = e.PrevInAEL; + var AelNext = e.NextInAEL; + e.NextInLML.OutIdx = e.OutIdx; + if (AelPrev !== null) + AelPrev.NextInAEL = e.NextInLML; + else + this.m_ActiveEdges = e.NextInLML; + if (AelNext !== null) + AelNext.PrevInAEL = e.NextInLML; + e.NextInLML.Side = e.Side; + e.NextInLML.WindDelta = e.WindDelta; + e.NextInLML.WindCnt = e.WindCnt; + e.NextInLML.WindCnt2 = e.WindCnt2; + e = e.NextInLML; + // e.Curr = e.Bot; + e.Curr.X = e.Bot.X; + e.Curr.Y = e.Bot.Y; + e.PrevInAEL = AelPrev; + e.NextInAEL = AelNext; + if (!ClipperLib.ClipperBase.IsHorizontal(e)) + this.InsertScanbeam(e.Top.Y); + return e; + }; + ClipperLib.Clipper.prototype.ProcessHorizontals = function (isTopOfScanbeam) { var horzEdge = this.m_SortedEdges; - while (horzEdge != null) + while (horzEdge !== null) { this.DeleteFromSEL(horzEdge); - this.ProcessHorizontal(horzEdge); + this.ProcessHorizontal(horzEdge, isTopOfScanbeam); horzEdge = this.m_SortedEdges; } }; - ClipperLib.Clipper.prototype.ProcessHorizontal = function (horzEdge) + ClipperLib.Clipper.prototype.GetHorzDirection = function (HorzEdge, $var) { - var Direction; - var horzLeft, horzRight; - if (horzEdge.xcurr < horzEdge.xtop) + if (HorzEdge.Bot.X < HorzEdge.Top.X) { - horzLeft = horzEdge.xcurr; - horzRight = horzEdge.xtop; - Direction = ClipperLib.Direction.dLeftToRight; + $var.Left = HorzEdge.Bot.X; + $var.Right = HorzEdge.Top.X; + $var.Dir = ClipperLib.Direction.dLeftToRight; } else { - horzLeft = horzEdge.xtop; - horzRight = horzEdge.xcurr; - Direction = ClipperLib.Direction.dRightToLeft; - } - var eMaxPair; - if (horzEdge.nextInLML != null) eMaxPair = null; - else eMaxPair = this.GetMaximaPair(horzEdge); - var e = this.GetNextInAEL(horzEdge, Direction); - while (e != null) + $var.Left = HorzEdge.Top.X; + $var.Right = HorzEdge.Bot.X; + $var.Dir = ClipperLib.Direction.dRightToLeft; + } + }; + ClipperLib.Clipper.prototype.PrepareHorzJoins = function (horzEdge, isTopOfScanbeam) + { + //get the last Op for this horizontal edge + //the point may be anywhere along the horizontal ... + var outPt = this.m_PolyOuts[horzEdge.OutIdx].Pts; + if (horzEdge.Side != ClipperLib.EdgeSide.esLeft) + outPt = outPt.Prev; + //First, match up overlapping horizontal edges (eg when one polygon's + //intermediate horz edge overlaps an intermediate horz edge of another, or + //when one polygon sits on top of another) ... + //for (var i = 0, ilen = this.m_GhostJoins.length; i < ilen; ++i) { + // var j = this.m_GhostJoins[i]; + // if (this.HorzSegmentsOverlap(j.OutPt1.Pt, j.OffPt, horzEdge.Bot, horzEdge.Top)) + // this.AddJoin(j.OutPt1, outPt, j.OffPt); + //} + + //Also, since horizontal edges at the top of one SB are often removed from + //the AEL before we process the horizontal edges at the bottom of the next, + //we need to create 'ghost' Join records of 'contrubuting' horizontals that + //we can compare with horizontals at the bottom of the next SB. + if (isTopOfScanbeam) + if (ClipperLib.IntPoint.op_Equality(outPt.Pt, horzEdge.Top)) + this.AddGhostJoin(outPt, horzEdge.Bot); + else + this.AddGhostJoin(outPt, horzEdge.Top); + }; + ClipperLib.Clipper.prototype.ProcessHorizontal = function (horzEdge, isTopOfScanbeam) + { + var $var = {Dir: null, Left: null, Right: null}; + this.GetHorzDirection(horzEdge, $var); + var dir = $var.Dir; + var horzLeft = $var.Left; + var horzRight = $var.Right; + + var eLastHorz = horzEdge, + eMaxPair = null; + while (eLastHorz.NextInLML !== null && ClipperLib.ClipperBase.IsHorizontal(eLastHorz.NextInLML)) + eLastHorz = eLastHorz.NextInLML; + if (eLastHorz.NextInLML === null) + eMaxPair = this.GetMaximaPair(eLastHorz); + for (;;) { - var eNext = this.GetNextInAEL(e, Direction); - if (eMaxPair != null || ((Direction == ClipperLib.Direction.dLeftToRight) && (e.xcurr <= horzRight)) || ((Direction == ClipperLib.Direction.dRightToLeft) && (e.xcurr >= horzLeft))) + var IsLastHorz = (horzEdge == eLastHorz); + var e = this.GetNextInAEL(horzEdge, dir); + while (e !== null) { - if (e.xcurr == horzEdge.xtop && eMaxPair == null) + //Break if we've got to the end of an intermediate horizontal edge ... + //nb: Smaller Dx's are to the right of larger Dx's ABOVE the horizontal. + if (e.Curr.X == horzEdge.Top.X && horzEdge.NextInLML !== null && e.Dx < horzEdge.NextInLML.Dx) + break; + var eNext = this.GetNextInAEL(e, dir); + //saves eNext for later + if ((dir == ClipperLib.Direction.dLeftToRight && e.Curr.X <= horzRight) || (dir == ClipperLib.Direction.dRightToLeft && e.Curr.X >= horzLeft)) { - if (this.SlopesEqual(e, horzEdge.nextInLML, this.m_UseFullRange)) + + if (horzEdge.OutIdx >= 0 && horzEdge.WindDelta != 0) + this.PrepareHorzJoins(horzEdge, isTopOfScanbeam); + + //so far we're still in range of the horizontal Edge but make sure + //we're at the last of consec. horizontals when matching with eMaxPair + if (e == eMaxPair && IsLastHorz) + { + if (dir == ClipperLib.Direction.dLeftToRight) + this.IntersectEdges(horzEdge, e, e.Top, false); + else + this.IntersectEdges(e, horzEdge, e.Top, false); + if (eMaxPair.OutIdx >= 0) + ClipperLib.Error("ProcessHorizontal error"); + return; + } + else if (dir == ClipperLib.Direction.dLeftToRight) + { + var Pt = new ClipperLib.IntPoint(e.Curr.X, horzEdge.Curr.Y); + this.IntersectEdges(horzEdge, e, Pt, true); + } + else { - if (horzEdge.outIdx >= 0 && e.outIdx >= 0) this.AddJoin(horzEdge.nextInLML, e, horzEdge.outIdx, -1); - break; + var Pt = new ClipperLib.IntPoint(e.Curr.X, horzEdge.Curr.Y); + this.IntersectEdges(e, horzEdge, Pt, true); } - else if (e.dx < horzEdge.nextInLML.dx) break; + this.SwapPositionsInAEL(horzEdge, e); } - if (e == eMaxPair) - { - if (Direction == ClipperLib.Direction.dLeftToRight) this.IntersectEdges(horzEdge, e, new ClipperLib.IntPoint(e.xcurr, horzEdge.ycurr), 0); - else this.IntersectEdges(e, horzEdge, new ClipperLib.IntPoint(e.xcurr, horzEdge.ycurr), 0); - if (eMaxPair.outIdx >= 0) ClipperLib.Error("ProcessHorizontal error"); + else if ((dir == ClipperLib.Direction.dLeftToRight && e.Curr.X >= horzRight) || (dir == ClipperLib.Direction.dRightToLeft && e.Curr.X <= horzLeft)) + break; + e = eNext; + } + //end while + if (horzEdge.OutIdx >= 0 && horzEdge.WindDelta !== 0) + this.PrepareHorzJoins(horzEdge, isTopOfScanbeam); + if (horzEdge.NextInLML !== null && ClipperLib.ClipperBase.IsHorizontal(horzEdge.NextInLML)) + { + horzEdge = this.UpdateEdgeIntoAEL(horzEdge); + if (horzEdge.OutIdx >= 0) + this.AddOutPt(horzEdge, horzEdge.Bot); + + var $var = {Dir: dir, Left: horzLeft, Right: horzRight}; + this.GetHorzDirection(horzEdge, $var); + dir = $var.Dir; + horzLeft = $var.Left; + horzRight = $var.Right; + } + else + break; + } + //end for (;;) + if (horzEdge.NextInLML !== null) + { + if (horzEdge.OutIdx >= 0) + { + var op1 = this.AddOutPt(horzEdge, horzEdge.Top); + horzEdge = this.UpdateEdgeIntoAEL(horzEdge); + if (horzEdge.WindDelta === 0) return; - } - else if (e.dx == ClipperLib.ClipperBase.horizontal && !this.IsMinima(e) && !(e.xcurr > e.xtop)) - { - if (Direction == ClipperLib.Direction.dLeftToRight) this.IntersectEdges(horzEdge, e, new ClipperLib.IntPoint(e.xcurr, horzEdge.ycurr), (this.IsTopHorz(horzEdge, e.xcurr)) ? ClipperLib.Protects.ipLeft : ClipperLib.Protects.ipBoth); - else this.IntersectEdges(e, horzEdge, new ClipperLib.IntPoint(e.xcurr, horzEdge.ycurr), (this.IsTopHorz(horzEdge, e.xcurr)) ? ClipperLib.Protects.ipRight : ClipperLib.Protects.ipBoth); - } - else if (Direction == ClipperLib.Direction.dLeftToRight) + //nb: HorzEdge is no longer horizontal here + var ePrev = horzEdge.PrevInAEL; + var eNext = horzEdge.NextInAEL; + if (ePrev !== null && ePrev.Curr.X == horzEdge.Bot.X && + ePrev.Curr.Y == horzEdge.Bot.Y && ePrev.WindDelta !== 0 && + (ePrev.OutIdx >= 0 && ePrev.Curr.Y > ePrev.Top.Y && + ClipperLib.ClipperBase.SlopesEqual(horzEdge, ePrev, this.m_UseFullRange))) { - this.IntersectEdges(horzEdge, e, new ClipperLib.IntPoint(e.xcurr, horzEdge.ycurr), (this.IsTopHorz(horzEdge, e.xcurr)) ? ClipperLib.Protects.ipLeft : ClipperLib.Protects.ipBoth); + var op2 = this.AddOutPt(ePrev, horzEdge.Bot); + this.AddJoin(op1, op2, horzEdge.Top); } - else + else if (eNext !== null && eNext.Curr.X == horzEdge.Bot.X && + eNext.Curr.Y == horzEdge.Bot.Y && eNext.WindDelta !== 0 && + eNext.OutIdx >= 0 && eNext.Curr.Y > eNext.Top.Y && + ClipperLib.ClipperBase.SlopesEqual(horzEdge, eNext, this.m_UseFullRange)) { - this.IntersectEdges(e, horzEdge, new ClipperLib.IntPoint(e.xcurr, horzEdge.ycurr), (this.IsTopHorz(horzEdge, e.xcurr)) ? ClipperLib.Protects.ipRight : ClipperLib.Protects.ipBoth); + var op2 = this.AddOutPt(eNext, horzEdge.Bot); + this.AddJoin(op1, op2, horzEdge.Top); } - this.SwapPositionsInAEL(horzEdge, e); } - else if ((Direction == ClipperLib.Direction.dLeftToRight && e.xcurr > horzRight && horzEdge.nextInSEL == null) || (Direction == ClipperLib.Direction.dRightToLeft && e.xcurr < horzLeft && horzEdge.nextInSEL == null)) break; - e = eNext; + else horzEdge = this.UpdateEdgeIntoAEL(horzEdge); } - if (horzEdge.nextInLML != null) + else if (eMaxPair !== null) { - if (horzEdge.outIdx >= 0) this.AddOutPt(horzEdge, new ClipperLib.IntPoint(horzEdge.xtop, horzEdge.ytop)); - (function () + if (eMaxPair.OutIdx >= 0) { - horzEdge = { - Value: horzEdge - }; - var $res = this.UpdateEdgeIntoAEL(horzEdge); - horzEdge = horzEdge.Value; - return $res; - }) - .call(this); + if (dir == ClipperLib.Direction.dLeftToRight) + this.IntersectEdges(horzEdge, eMaxPair, horzEdge.Top, false); + else + this.IntersectEdges(eMaxPair, horzEdge, horzEdge.Top, false); + if (eMaxPair.OutIdx >= 0) + ClipperLib.Error("ProcessHorizontal error"); + } + else + { + this.DeleteFromAEL(horzEdge); + this.DeleteFromAEL(eMaxPair); + } } else { - if (horzEdge.outIdx >= 0) this.IntersectEdges(horzEdge, eMaxPair, new ClipperLib.IntPoint(horzEdge.xtop, horzEdge.ycurr), ClipperLib.Protects.ipBoth); - this.DeleteFromAEL(eMaxPair); + if (horzEdge.OutIdx >= 0) + this.AddOutPt(horzEdge, horzEdge.Top); this.DeleteFromAEL(horzEdge); } }; - ClipperLib.Clipper.prototype.IsTopHorz = function (horzEdge, XPos) - { - var e = this.m_SortedEdges; - while (e != null) - { - if ((XPos >= Math.min(e.xcurr, e.xtop)) && (XPos <= Math.max(e.xcurr, e.xtop))) return false; - e = e.nextInSEL; - } - return true; - }; ClipperLib.Clipper.prototype.GetNextInAEL = function (e, Direction) { - return Direction == ClipperLib.Direction.dLeftToRight ? e.nextInAEL : e.prevInAEL; + return Direction == ClipperLib.Direction.dLeftToRight ? e.NextInAEL : e.PrevInAEL; }; ClipperLib.Clipper.prototype.IsMinima = function (e) { - return e != null && (e.prev.nextInLML != e) && (e.next.nextInLML != e); + return e !== null && (e.Prev.NextInLML != e) && (e.Next.NextInLML != e); }; ClipperLib.Clipper.prototype.IsMaxima = function (e, Y) { - return (e != null && e.ytop == Y && e.nextInLML == null); + return (e !== null && e.Top.Y == Y && e.NextInLML === null); }; ClipperLib.Clipper.prototype.IsIntermediate = function (e, Y) { - return (e.ytop == Y && e.nextInLML != null); + return (e.Top.Y == Y && e.NextInLML !== null); }; ClipperLib.Clipper.prototype.GetMaximaPair = function (e) { - if (!this.IsMaxima(e.next, e.ytop) || (e.next.xtop != e.xtop)) return e.prev; - else return e.next; + var result = null; + if ((ClipperLib.IntPoint.op_Equality(e.Next.Top, e.Top)) && e.Next.NextInLML === null) + result = e.Next; + else if ((ClipperLib.IntPoint.op_Equality(e.Prev.Top, e.Top)) && e.Prev.NextInLML === null) + result = e.Prev; + if (result !== null && (result.OutIdx == -2 || (result.NextInAEL == result.PrevInAEL && !ClipperLib.ClipperBase.IsHorizontal(result)))) + return null; + return result; }; ClipperLib.Clipper.prototype.ProcessIntersections = function (botY, topY) { - if (this.m_ActiveEdges == null) return true; + if (this.m_ActiveEdges == null) + return true; try { this.BuildIntersectList(botY, topY); - if (this.m_IntersectNodes == null) return true; - if (this.FixupIntersections()) this.ProcessIntersectList(); - else return false; + if (this.m_IntersectList.length == 0) + return true; + if (this.m_IntersectList.length == 1 || this.FixupIntersectionOrder()) + this.ProcessIntersectList(); + else + return false; } catch ($$e2) { this.m_SortedEdges = null; - this.DisposeIntersectNodes(); + this.m_IntersectList.length = 0; ClipperLib.Error("ProcessIntersections error"); } + this.m_SortedEdges = null; return true; }; ClipperLib.Clipper.prototype.BuildIntersectList = function (botY, topY) { - if (this.m_ActiveEdges == null) return; + if (this.m_ActiveEdges === null) + return; + //prepare for sorting ... var e = this.m_ActiveEdges; - e.tmpX = ClipperLib.Clipper.TopX(e, topY); + //console.log(JSON.stringify(JSON.decycle( e ))); this.m_SortedEdges = e; - this.m_SortedEdges.prevInSEL = null; - e = e.nextInAEL; - while (e != null) + while (e !== null) { - e.prevInSEL = e.prevInAEL; - e.prevInSEL.nextInSEL = e; - e.nextInSEL = null; - e.tmpX = ClipperLib.Clipper.TopX(e, topY); - e = e.nextInAEL; + e.PrevInSEL = e.PrevInAEL; + e.NextInSEL = e.NextInAEL; + e.Curr.X = ClipperLib.Clipper.TopX(e, topY); + e = e.NextInAEL; } + //bubblesort ... var isModified = true; - while (isModified && this.m_SortedEdges != null) + while (isModified && this.m_SortedEdges !== null) { isModified = false; e = this.m_SortedEdges; - while (e.nextInSEL != null) + while (e.NextInSEL !== null) { - var eNext = e.nextInSEL; + var eNext = e.NextInSEL; var pt = new ClipperLib.IntPoint(); - if (e.tmpX > eNext.tmpX && (function () - { - pt = { - Value: pt - }; - var $res = this.IntersectPoint(e, eNext, pt); - pt = pt.Value; - return $res; - }) - .call(this)) + //console.log("e.Curr.X: " + e.Curr.X + " eNext.Curr.X" + eNext.Curr.X); + if (e.Curr.X > eNext.Curr.X) { + if (!this.IntersectPoint(e, eNext, pt) && e.Curr.X > eNext.Curr.X + 1) + { + //console.log("e.Curr.X: "+JSON.stringify(JSON.decycle( e.Curr.X ))); + //console.log("eNext.Curr.X+1: "+JSON.stringify(JSON.decycle( eNext.Curr.X+1))); + ClipperLib.Error("Intersection error"); + } if (pt.Y > botY) { pt.Y = botY; - pt.X = ClipperLib.Clipper.TopX(e, pt.Y); + if (Math.abs(e.Dx) > Math.abs(eNext.Dx)) + pt.X = ClipperLib.Clipper.TopX(eNext, botY); + else + pt.X = ClipperLib.Clipper.TopX(e, botY); } - this.AddIntersectNode(e, eNext, pt); + var newNode = new ClipperLib.IntersectNode(); + newNode.Edge1 = e; + newNode.Edge2 = eNext; + //newNode.Pt = pt; + newNode.Pt.X = pt.X; + newNode.Pt.Y = pt.Y; + this.m_IntersectList.push(newNode); this.SwapPositionsInSEL(e, eNext); isModified = true; } - else e = eNext; + else + e = eNext; } - if (e.prevInSEL != null) e.prevInSEL.nextInSEL = null; - else break; + if (e.PrevInSEL !== null) + e.PrevInSEL.NextInSEL = null; + else + break; } this.m_SortedEdges = null; }; - ClipperLib.Clipper.prototype.FixupIntersections = function () + ClipperLib.Clipper.prototype.EdgesAdjacent = function (inode) + { + return (inode.Edge1.NextInSEL == inode.Edge2) || (inode.Edge1.PrevInSEL == inode.Edge2); + }; + ClipperLib.Clipper.IntersectNodeSort = function (node1, node2) { - if (this.m_IntersectNodes.next == null) return true; + //the following typecast is safe because the differences in Pt.Y will + //be limited to the height of the scanbeam. + return (node2.Pt.Y - node1.Pt.Y); + }; + ClipperLib.Clipper.prototype.FixupIntersectionOrder = function () + { + //pre-condition: intersections are sorted bottom-most first. + //Now it's crucial that intersections are made only between adjacent edges, + //so to ensure this the order of intersections may need adjusting ... + this.m_IntersectList.sort(this.m_IntersectNodeComparer); this.CopyAELToSEL(); - var int1 = this.m_IntersectNodes; - var int2 = this.m_IntersectNodes.next; - while (int2 != null) - { - var e1 = int1.edge1; - var e2; - if (e1.prevInSEL == int1.edge2) e2 = e1.prevInSEL; - else if (e1.nextInSEL == int1.edge2) e2 = e1.nextInSEL; - else + var cnt = this.m_IntersectList.length; + for (var i = 0; i < cnt; i++) + { + if (!this.EdgesAdjacent(this.m_IntersectList[i])) { - while (int2 != null) - { - if (int2.edge1.nextInSEL == int2.edge2 || int2.edge1.prevInSEL == int2.edge2) break; - else int2 = int2.next; - } - if (int2 == null) return false; - this.SwapIntersectNodes(int1, int2); - e1 = int1.edge1; - e2 = int1.edge2; + var j = i + 1; + while (j < cnt && !this.EdgesAdjacent(this.m_IntersectList[j])) + j++; + if (j == cnt) + return false; + var tmp = this.m_IntersectList[i]; + this.m_IntersectList[i] = this.m_IntersectList[j]; + this.m_IntersectList[j] = tmp; } - this.SwapPositionsInSEL(e1, e2); - int1 = int1.next; - int2 = int1.next; + this.SwapPositionsInSEL(this.m_IntersectList[i].Edge1, this.m_IntersectList[i].Edge2); } - this.m_SortedEdges = null; - return (int1.edge1.prevInSEL == int1.edge2 || int1.edge1.nextInSEL == int1.edge2); + return true; }; ClipperLib.Clipper.prototype.ProcessIntersectList = function () { - while (this.m_IntersectNodes != null) + for (var i = 0, ilen = this.m_IntersectList.length; i < ilen; i++) { - var iNode = this.m_IntersectNodes.next; - this.IntersectEdges(this.m_IntersectNodes.edge1, this.m_IntersectNodes.edge2, this.m_IntersectNodes.pt, ClipperLib.Protects.ipBoth); - this.SwapPositionsInAEL(this.m_IntersectNodes.edge1, this.m_IntersectNodes.edge2); - this.m_IntersectNodes = null; - this.m_IntersectNodes = iNode; + var iNode = this.m_IntersectList[i]; + this.IntersectEdges(iNode.Edge1, iNode.Edge2, iNode.Pt, true); + this.SwapPositionsInAEL(iNode.Edge1, iNode.Edge2); } + this.m_IntersectList.length = 0; }; /* -------------------------------- Round speedtest: http://jsperf.com/fastest-round -------------------------------- */ - var R1=function(a) { return a < 0 ? Math.ceil(a - 0.5): Math.round(a)}; - var R2=function(a) { return a < 0 ? Math.ceil(a - 0.5): Math.floor(a + 0.5)}; - var R3=function(a) { return a < 0 ? -Math.round(Math.abs(a)): Math.round(a)}; - var R4=function(a) { - if (a < 0) { + var R1 = function (a) + { + return a < 0 ? Math.ceil(a - 0.5) : Math.round(a) + }; + var R2 = function (a) + { + return a < 0 ? Math.ceil(a - 0.5) : Math.floor(a + 0.5) + }; + var R3 = function (a) + { + return a < 0 ? -Math.round(Math.abs(a)) : Math.round(a) + }; + var R4 = function (a) + { + if (a < 0) + { a -= 0.5; - return a < -2147483648 ? Math.ceil(a): a | 0; - } else { + return a < -2147483648 ? Math.ceil(a) : a | 0; + } + else + { a += 0.5; - return a > 2147483647 ? Math.floor(a): a | 0; + return a > 2147483647 ? Math.floor(a) : a | 0; } }; if (browser.msie) ClipperLib.Clipper.Round = R1; else if (browser.chromium) ClipperLib.Clipper.Round = R3; else if (browser.safari) ClipperLib.Clipper.Round = R4; else ClipperLib.Clipper.Round = R2; // eg. browser.chrome || browser.firefox || browser.opera - ClipperLib.Clipper.TopX = function (edge, currentY) { - if (currentY == edge.ytop) return edge.xtop; - return edge.xbot + ClipperLib.Clipper.Round(edge.dx * (currentY - edge.ybot)); + //if (edge.Bot == edge.Curr) alert ("edge.Bot = edge.Curr"); + //if (edge.Bot == edge.Top) alert ("edge.Bot = edge.Top"); + if (currentY == edge.Top.Y) + return edge.Top.X; + return edge.Bot.X + ClipperLib.Clipper.Round(edge.Dx * (currentY - edge.Bot.Y)); }; - ClipperLib.Clipper.prototype.AddIntersectNode = function (e1, e2, pt) - { - var newNode = new ClipperLib.IntersectNode(); - newNode.edge1 = e1; - newNode.edge2 = e2; - newNode.pt = pt; - newNode.next = null; - if (this.m_IntersectNodes == null) this.m_IntersectNodes = newNode; - else if (this.ProcessParam1BeforeParam2(newNode, this.m_IntersectNodes)) - { - newNode.next = this.m_IntersectNodes; - this.m_IntersectNodes = newNode; - } - else - { - var iNode = this.m_IntersectNodes; - while (iNode.next != null && this.ProcessParam1BeforeParam2(iNode.next, newNode)) - iNode = iNode.next; - newNode.next = iNode.next; - iNode.next = newNode; - } - }; - ClipperLib.Clipper.prototype.ProcessParam1BeforeParam2 = function (node1, node2) + ClipperLib.Clipper.prototype.IntersectPoint = function (edge1, edge2, ip) { - var result; - if (node1.pt.Y == node2.pt.Y) + ip.X = 0; + ip.Y = 0; + var b1, b2; + //nb: with very large coordinate values, it's possible for SlopesEqual() to + //return false but for the edge.Dx value be equal due to double precision rounding. + if (ClipperLib.ClipperBase.SlopesEqual(edge1, edge2, this.m_UseFullRange) || edge1.Dx == edge2.Dx) { - if (node1.edge1 == node2.edge1 || node1.edge2 == node2.edge1) + if (edge2.Bot.Y > edge1.Bot.Y) { - result = node2.pt.X > node1.pt.X; - return node2.edge1.dx > 0 ? !result : result; + ip.X = edge2.Bot.X; + ip.Y = edge2.Bot.Y; } - else if (node1.edge1 == node2.edge2 || node1.edge2 == node2.edge2) + else { - result = node2.pt.X > node1.pt.X; - return node2.edge2.dx > 0 ? !result : result; + ip.X = edge1.Bot.X; + ip.Y = edge1.Bot.Y; } - else return node2.pt.X > node1.pt.X; + return false; } - else return node1.pt.Y > node2.pt.Y; - }; - ClipperLib.Clipper.prototype.SwapIntersectNodes = function (int1, int2) - { - var e1 = int1.edge1; - var e2 = int1.edge2; - var p = int1.pt; - int1.edge1 = int2.edge1; - int1.edge2 = int2.edge2; - int1.pt = int2.pt; - int2.edge1 = e1; - int2.edge2 = e2; - int2.pt = p; - }; - ClipperLib.Clipper.prototype.IntersectPoint = function (edge1, edge2, ip) - { - var b1, b2; - if (this.SlopesEqual(edge1, edge2, this.m_UseFullRange)) return false; - else if (edge1.dx == 0) + else if (edge1.Delta.X === 0) { - ip.Value.X = edge1.xbot; - if (edge2.dx == ClipperLib.ClipperBase.horizontal) + ip.X = edge1.Bot.X; + if (ClipperLib.ClipperBase.IsHorizontal(edge2)) { - ip.Value.Y = edge2.ybot; + ip.Y = edge2.Bot.Y; } else { - b2 = edge2.ybot - (edge2.xbot / edge2.dx); - ip.Value.Y = ClipperLib.Clipper.Round(ip.Value.X / edge2.dx + b2); + b2 = edge2.Bot.Y - (edge2.Bot.X / edge2.Dx); + ip.Y = ClipperLib.Clipper.Round(ip.X / edge2.Dx + b2); } } - else if (edge2.dx == 0) + else if (edge2.Delta.X === 0) { - ip.Value.X = edge2.xbot; - if (edge1.dx == ClipperLib.ClipperBase.horizontal) + ip.X = edge2.Bot.X; + if (ClipperLib.ClipperBase.IsHorizontal(edge1)) { - ip.Value.Y = edge1.ybot; + ip.Y = edge1.Bot.Y; } else { - b1 = edge1.ybot - (edge1.xbot / edge1.dx); - ip.Value.Y = ClipperLib.Clipper.Round(ip.Value.X / edge1.dx + b1); + b1 = edge1.Bot.Y - (edge1.Bot.X / edge1.Dx); + ip.Y = ClipperLib.Clipper.Round(ip.X / edge1.Dx + b1); } } else { - b1 = edge1.xbot - edge1.ybot * edge1.dx; - b2 = edge2.xbot - edge2.ybot * edge2.dx; - var q = (b2 - b1) / (edge1.dx - edge2.dx); - ip.Value.Y = ClipperLib.Clipper.Round(q); - if (ClipperLib.Math_Abs_Double(edge1.dx) < ClipperLib.Math_Abs_Double(edge2.dx)) ip.Value.X = ClipperLib.Clipper.Round(edge1.dx * q + b1); - else ip.Value.X = ClipperLib.Clipper.Round(edge2.dx * q + b2); + b1 = edge1.Bot.X - edge1.Bot.Y * edge1.Dx; + b2 = edge2.Bot.X - edge2.Bot.Y * edge2.Dx; + var q = (b2 - b1) / (edge1.Dx - edge2.Dx); + ip.Y = ClipperLib.Clipper.Round(q); + if (Math.abs(edge1.Dx) < Math.abs(edge2.Dx)) + ip.X = ClipperLib.Clipper.Round(edge1.Dx * q + b1); + else + ip.X = ClipperLib.Clipper.Round(edge2.Dx * q + b2); } - if (ip.Value.Y < edge1.ytop || ip.Value.Y < edge2.ytop) + if (ip.Y < edge1.Top.Y || ip.Y < edge2.Top.Y) { - if (edge1.ytop > edge2.ytop) + if (edge1.Top.Y > edge2.Top.Y) { - ip.Value.X = edge1.xtop; - ip.Value.Y = edge1.ytop; - return ClipperLib.Clipper.TopX(edge2, edge1.ytop) < edge1.xtop; + ip.Y = edge1.Top.Y; + ip.X = ClipperLib.Clipper.TopX(edge2, edge1.Top.Y); + return ip.X < edge1.Top.X; } else - { - ip.Value.X = edge2.xtop; - ip.Value.Y = edge2.ytop; - return ClipperLib.Clipper.TopX(edge1, edge2.ytop) > edge2.xtop; - } - } - else return true; - }; - ClipperLib.Clipper.prototype.DisposeIntersectNodes = function () - { - while (this.m_IntersectNodes != null) - { - var iNode = this.m_IntersectNodes.next; - this.m_IntersectNodes = null; - this.m_IntersectNodes = iNode; + ip.Y = edge2.Top.Y; + if (Math.abs(edge1.Dx) < Math.abs(edge2.Dx)) + ip.X = ClipperLib.Clipper.TopX(edge1, ip.Y); + else + ip.X = ClipperLib.Clipper.TopX(edge2, ip.Y); } + return true; }; ClipperLib.Clipper.prototype.ProcessEdgesAtTopOfScanbeam = function (topY) { var e = this.m_ActiveEdges; - var ePrev; - while (e != null) + while (e !== null) { - if (this.IsMaxima(e, topY) && this.GetMaximaPair(e) - .dx != ClipperLib.ClipperBase.horizontal) + //1. process maxima, treating them as if they're 'bent' horizontal edges, + // but exclude maxima with horizontal edges. nb: e can't be a horizontal. + var IsMaximaEdge = this.IsMaxima(e, topY); + if (IsMaximaEdge) + { + var eMaxPair = this.GetMaximaPair(e); + IsMaximaEdge = (eMaxPair === null || !ClipperLib.ClipperBase.IsHorizontal(eMaxPair)); + } + if (IsMaximaEdge) { - ePrev = e.prevInAEL; - this.DoMaxima(e, topY); - if (ePrev == null) e = this.m_ActiveEdges; - else e = ePrev.nextInAEL; + var ePrev = e.PrevInAEL; + this.DoMaxima(e); + if (ePrev === null) + e = this.m_ActiveEdges; + else + e = ePrev.NextInAEL; } else { - if (this.IsIntermediate(e, topY) && e.nextInLML.dx == ClipperLib.ClipperBase.horizontal) + //2. promote horizontal edges, otherwise update Curr.X and Curr.Y ... + if (this.IsIntermediate(e, topY) && ClipperLib.ClipperBase.IsHorizontal(e.NextInLML)) { - if (e.outIdx >= 0) - { - this.AddOutPt(e, new ClipperLib.IntPoint(e.xtop, e.ytop)); - for (var i = 0; i < this.m_HorizJoins.length; ++i) - { - var pt = new ClipperLib.IntPoint(), - pt2 = new ClipperLib.IntPoint(); - var hj = this.m_HorizJoins[i]; - if ((function () - { - pt = { - Value: pt - }; - pt2 = { - Value: pt2 - }; - var $res = this.GetOverlapSegment(new ClipperLib.IntPoint(hj.edge.xbot, hj.edge.ybot), - new ClipperLib.IntPoint(hj.edge.xtop, hj.edge.ytop), - new ClipperLib.IntPoint(e.nextInLML.xbot, e.nextInLML.ybot), - new ClipperLib.IntPoint(e.nextInLML.xtop, e.nextInLML.ytop), pt, pt2); - pt = pt.Value; - pt2 = pt2.Value; - return $res; - }) - .call(this)) this.AddJoin(hj.edge, e.nextInLML, hj.savedIdx, e.outIdx); - } - this.AddHorzJoin(e.nextInLML, e.outIdx); - } - (function () - { - e = { - Value: e - }; - var $res = this.UpdateEdgeIntoAEL(e); - e = e.Value; - return $res; - }) - .call(this); + e = this.UpdateEdgeIntoAEL(e); + if (e.OutIdx >= 0) + this.AddOutPt(e, e.Bot); this.AddEdgeToSEL(e); } else { - e.xcurr = ClipperLib.Clipper.TopX(e, topY); - e.ycurr = topY; + e.Curr.X = ClipperLib.Clipper.TopX(e, topY); + e.Curr.Y = topY; + } + if (this.StrictlySimple) + { + var ePrev = e.PrevInAEL; + if ((e.OutIdx >= 0) && (e.WindDelta !== 0) && ePrev !== null && + (ePrev.OutIdx >= 0) && (ePrev.Curr.X == e.Curr.X) && + (ePrev.WindDelta !== 0)) + { + var op = this.AddOutPt(ePrev, e.Curr); + var op2 = this.AddOutPt(e, e.Curr); + this.AddJoin(op, op2, e.Curr); + //StrictlySimple (type-3) join + } } - e = e.nextInAEL; + e = e.NextInAEL; } } - this.ProcessHorizontals(); + //3. Process horizontals at the Top of the scanbeam ... + this.ProcessHorizontals(true); + //4. Promote intermediate vertices ... e = this.m_ActiveEdges; - while (e != null) + while (e !== null) { if (this.IsIntermediate(e, topY)) { - if (e.outIdx >= 0) this.AddOutPt(e, new ClipperLib.IntPoint(e.xtop, e.ytop)); - (function () - { - e = { - Value: e - }; - var $res = this.UpdateEdgeIntoAEL(e); - e = e.Value; - return $res; - }) - .call(this); - ePrev = e.prevInAEL; - var eNext = e.nextInAEL; - if (ePrev != null && ePrev.xcurr == e.xbot && ePrev.ycurr == e.ybot && e.outIdx >= 0 && ePrev.outIdx >= 0 && ePrev.ycurr > ePrev.ytop && this.SlopesEqual(e, ePrev, this.m_UseFullRange)) + var op = null; + if (e.OutIdx >= 0) + op = this.AddOutPt(e, e.Top); + e = this.UpdateEdgeIntoAEL(e); + //if output polygons share an edge, they'll need joining later ... + var ePrev = e.PrevInAEL; + var eNext = e.NextInAEL; + if (ePrev !== null && ePrev.Curr.X == e.Bot.X && + ePrev.Curr.Y == e.Bot.Y && op !== null && + ePrev.OutIdx >= 0 && ePrev.Curr.Y > ePrev.Top.Y && + ClipperLib.ClipperBase.SlopesEqual(e, ePrev, this.m_UseFullRange) && + (e.WindDelta !== 0) && (ePrev.WindDelta !== 0)) { - this.AddOutPt(ePrev, new ClipperLib.IntPoint(e.xbot, e.ybot)); - this.AddJoin(e, ePrev, -1, -1); + var op2 = this.AddOutPt(ePrev, e.Bot); + this.AddJoin(op, op2, e.Top); } - else if (eNext != null && eNext.xcurr == e.xbot && eNext.ycurr == e.ybot && e.outIdx >= 0 && eNext.outIdx >= 0 && eNext.ycurr > eNext.ytop && this.SlopesEqual(e, eNext, this.m_UseFullRange)) + else if (eNext !== null && eNext.Curr.X == e.Bot.X && + eNext.Curr.Y == e.Bot.Y && op !== null && + eNext.OutIdx >= 0 && eNext.Curr.Y > eNext.Top.Y && + ClipperLib.ClipperBase.SlopesEqual(e, eNext, this.m_UseFullRange) && + (e.WindDelta !== 0) && (eNext.WindDelta !== 0)) { - this.AddOutPt(eNext, new ClipperLib.IntPoint(e.xbot, e.ybot)); - this.AddJoin(e, eNext, -1, -1); + var op2 = this.AddOutPt(eNext, e.Bot); + this.AddJoin(op, op2, e.Top); } } - e = e.nextInAEL; + e = e.NextInAEL; } }; - ClipperLib.Clipper.prototype.DoMaxima = function (e, topY) + ClipperLib.Clipper.prototype.DoMaxima = function (e) { var eMaxPair = this.GetMaximaPair(e); - var X = e.xtop; - var eNext = e.nextInAEL; - while (eNext != eMaxPair) + if (eMaxPair === null) { - if (eNext == null) ClipperLib.Error("DoMaxima error"); - this.IntersectEdges(e, eNext, new ClipperLib.IntPoint(X, topY), ClipperLib.Protects.ipBoth); - eNext = eNext.nextInAEL; + if (e.OutIdx >= 0) + this.AddOutPt(e, e.Top); + this.DeleteFromAEL(e); + return; + } + var eNext = e.NextInAEL; + var use_lines = true; + while (eNext !== null && eNext != eMaxPair) + { + this.IntersectEdges(e, eNext, e.Top, true); + this.SwapPositionsInAEL(e, eNext); + eNext = e.NextInAEL; } - if (e.outIdx < 0 && eMaxPair.outIdx < 0) + if (e.OutIdx == -1 && eMaxPair.OutIdx == -1) { this.DeleteFromAEL(e); this.DeleteFromAEL(eMaxPair); } - else if (e.outIdx >= 0 && eMaxPair.outIdx >= 0) + else if (e.OutIdx >= 0 && eMaxPair.OutIdx >= 0) { - this.IntersectEdges(e, eMaxPair, new ClipperLib.IntPoint(X, topY), ClipperLib.Protects.ipNone); + this.IntersectEdges(e, eMaxPair, e.Top, false); } - else ClipperLib.Error("DoMaxima error"); - }; - ClipperLib.Clipper.ReversePolygons = function (polys) - { - var len = polys.length, - poly; - for (var i = 0; i < len; i++) + else if (use_lines && e.WindDelta === 0) { - if (polys[i] instanceof Array) polys[i].reverse(); + if (e.OutIdx >= 0) + { + this.AddOutPt(e, e.Top); + e.OutIdx = -1; + } + this.DeleteFromAEL(e); + if (eMaxPair.OutIdx >= 0) + { + this.AddOutPt(eMaxPair, e.Top); + eMaxPair.OutIdx = -1; + } + this.DeleteFromAEL(eMaxPair); } + else + ClipperLib.Error("DoMaxima error"); + }; + ClipperLib.Clipper.ReversePaths = function (polys) + { + for (var i = 0, len = polys.length; i < len; i++) + polys[i].reverse(); }; ClipperLib.Clipper.Orientation = function (poly) { - return this.Area(poly) >= 0; + return ClipperLib.Clipper.Area(poly) >= 0; }; ClipperLib.Clipper.prototype.PointCount = function (pts) { - if (pts == null) return 0; + if (pts === null) + return 0; var result = 0; var p = pts; do { result++; - p = p.next; + p = p.Next; } - while (p != pts); + while (p != pts) return result; }; ClipperLib.Clipper.prototype.BuildResult = function (polyg) { ClipperLib.Clear(polyg); - var outRec, len = this.m_PolyOuts.length; - for (var i = 0; i < len; i++) + for (var i = 0, ilen = this.m_PolyOuts.length; i < ilen; i++) { - outRec = this.m_PolyOuts[i]; - if (outRec.pts == null) continue; - var p = outRec.pts; + var outRec = this.m_PolyOuts[i]; + if (outRec.Pts === null) + continue; + var p = outRec.Pts.Prev; var cnt = this.PointCount(p); - if (cnt < 3) continue; - var pg = new ClipperLib.Polygon(cnt); + if (cnt < 2) + continue; + var pg = new Array(cnt); for (var j = 0; j < cnt; j++) { - pg.push(p.pt); - p = p.prev; + pg[j] = p.Pt; + p = p.Prev; } polyg.push(pg); } }; - ClipperLib.Clipper.prototype.BuildResultEx = function (polyg) + ClipperLib.Clipper.prototype.BuildResult2 = function (polytree) { - ClipperLib.Clear(polyg); - var i = 0; - while (i < this.m_PolyOuts.length) + polytree.Clear(); + //add each output polygon/contour to polytree ... + //polytree.m_AllPolys.set_Capacity(this.m_PolyOuts.length); + for (var i = 0, ilen = this.m_PolyOuts.length; i < ilen; i++) { - var outRec = this.m_PolyOuts[i++]; - if (outRec.pts == null) break; - var p = outRec.pts; - var cnt = this.PointCount(p); - if (cnt < 3) continue; - var epg = new ClipperLib.ExPolygon(); - epg.outer = new ClipperLib.Polygon(); - epg.holes = new ClipperLib.Polygons(); + var outRec = this.m_PolyOuts[i]; + var cnt = this.PointCount(outRec.Pts); + if ((outRec.IsOpen && cnt < 2) || (!outRec.IsOpen && cnt < 3)) + continue; + this.FixHoleLinkage(outRec); + var pn = new ClipperLib.PolyNode(); + polytree.m_AllPolys.push(pn); + outRec.PolyNode = pn; + pn.m_polygon.length = cnt; + var op = outRec.Pts.Prev; for (var j = 0; j < cnt; j++) { - epg.outer.push(p.pt); - p = p.prev; + pn.m_polygon[j] = op.Pt; + op = op.Prev; } - while (i < this.m_PolyOuts.length) - { - outRec = this.m_PolyOuts[i]; - if (outRec.pts == null || !outRec.isHole) break; - var pg = new ClipperLib.Polygon(); - p = outRec.pts; - do { - pg.push(p.pt); - p = p.prev; - } - while (p != outRec.pts); - epg.holes.push(pg); - i++; + } + //fixup PolyNode links etc ... + //polytree.m_Childs.set_Capacity(this.m_PolyOuts.length); + for (var i = 0, ilen = this.m_PolyOuts.length; i < ilen; i++) + { + var outRec = this.m_PolyOuts[i]; + if (outRec.PolyNode === null) + continue; + else if (outRec.IsOpen) + { + outRec.PolyNode.IsOpen = true; + polytree.AddChild(outRec.PolyNode); } - polyg.push(epg); + else if (outRec.FirstLeft !== null && outRec.FirstLeft.PolyNode != null) + outRec.FirstLeft.PolyNode.AddChild(outRec.PolyNode); + else + polytree.AddChild(outRec.PolyNode); } }; ClipperLib.Clipper.prototype.FixupOutPolygon = function (outRec) { + //FixupOutPolygon() - removes duplicate points and simplifies consecutive + //parallel edges by removing the middle vertex. var lastOK = null; - outRec.pts = outRec.bottomPt; - var pp = outRec.bottomPt; + outRec.BottomPt = null; + var pp = outRec.Pts; for (;;) { - if (pp.prev == pp || pp.prev == pp.next) + if (pp.Prev == pp || pp.Prev == pp.Next) { this.DisposeOutPts(pp); - outRec.pts = null; - outRec.bottomPt = null; + outRec.Pts = null; return; } - if (ClipperLib.ClipperBase.PointsEqual(pp.pt, pp.next.pt) || this.SlopesEqual(pp.prev.pt, pp.pt, pp.next.pt, this.m_UseFullRange)) + //test for duplicate points and collinear edges ... + if ((ClipperLib.IntPoint.op_Equality(pp.Pt, pp.Next.Pt)) || (ClipperLib.IntPoint.op_Equality(pp.Pt, pp.Prev.Pt)) || + (ClipperLib.ClipperBase.SlopesEqual(pp.Prev.Pt, pp.Pt, pp.Next.Pt, this.m_UseFullRange) && + (!this.PreserveCollinear || !this.Pt2IsBetweenPt1AndPt3(pp.Prev.Pt, pp.Pt, pp.Next.Pt)))) { lastOK = null; var tmp = pp; - if (pp == outRec.bottomPt) outRec.bottomPt = null; - pp.prev.next = pp.next; - pp.next.prev = pp.prev; - pp = pp.prev; + pp.Prev.Next = pp.Next; + pp.Next.Prev = pp.Prev; + pp = pp.Prev; tmp = null; } - else if (pp == lastOK) break; + else if (pp == lastOK) + break; else { - if (lastOK == null) lastOK = pp; - pp = pp.next; - } - } - if (outRec.bottomPt == null) - { - outRec.bottomPt = this.GetBottomPt(pp); - outRec.bottomPt.idx = outRec.idx; - outRec.pts = outRec.bottomPt; - } - }; - ClipperLib.Clipper.prototype.JoinPoints = function (j, p1, p2) - { - p1.Value = null; - p2.Value = null; - var outRec1 = this.m_PolyOuts[j.poly1Idx]; - var outRec2 = this.m_PolyOuts[j.poly2Idx]; - if (outRec1 == null || outRec2 == null) return false; - var pp1a = outRec1.pts; - var pp2a = outRec2.pts; - var pt1 = j.pt2a, - pt2 = j.pt2b; - var pt3 = j.pt1a, - pt4 = j.pt1b; - if (!(function () - { - pp1a = { - Value: pp1a - }; - pt1 = { - Value: pt1 - }; - pt2 = { - Value: pt2 - }; - var $res = this.FindSegment(pp1a, pt1, pt2); - pp1a = pp1a.Value; - pt1 = pt1.Value; - pt2 = pt2.Value; - return $res; - }) - .call(this)) return false; - if (outRec1 == outRec2) - { - pp2a = pp1a.next; - if (!(function () - { - pp2a = { - Value: pp2a - }; - pt3 = { - Value: pt3 - }; - pt4 = { - Value: pt4 - }; - var $res = this.FindSegment(pp2a, pt3, pt4); - pp2a = pp2a.Value; - pt3 = pt3.Value; - pt4 = pt4.Value; - return $res; - }) - .call(this) || (pp2a == pp1a)) return false; - } - else if (!(function () - { - pp2a = { - Value: pp2a - }; - pt3 = { - Value: pt3 - }; - pt4 = { - Value: pt4 - }; - var $res = this.FindSegment(pp2a, pt3, pt4); - pp2a = pp2a.Value; - pt3 = pt3.Value; - pt4 = pt4.Value; - return $res; - }) - .call(this)) return false; - if (!(function () - { - pt1 = { - Value: pt1 - }; - pt2 = { - Value: pt2 - }; - var $res = this.GetOverlapSegment(pt1.Value, pt2.Value, pt3, pt4, pt1, pt2); - pt1 = pt1.Value; - pt2 = pt2.Value; - return $res; - }) - .call(this)) - { - return false; + if (lastOK === null) + lastOK = pp; + pp = pp.Next; + } } - var p3, p4, prev = pp1a.prev; - if (ClipperLib.ClipperBase.PointsEqual(pp1a.pt, pt1)) p1.Value = pp1a; - else if (ClipperLib.ClipperBase.PointsEqual(prev.pt, pt1)) p1.Value = prev; - else p1.Value = this.InsertPolyPtBetween(pp1a, prev, pt1); - if (ClipperLib.ClipperBase.PointsEqual(pp1a.pt, pt2)) p2.Value = pp1a; - else if (ClipperLib.ClipperBase.PointsEqual(prev.pt, pt2)) p2.Value = prev; - else if ((p1.Value == pp1a) || (p1.Value == prev)) p2.Value = this.InsertPolyPtBetween(pp1a, prev, pt2); - else if (this.Pt3IsBetweenPt1AndPt2(pp1a.pt, p1.Value.pt, pt2)) p2.Value = this.InsertPolyPtBetween(pp1a, p1.Value, pt2); - else p2.Value = this.InsertPolyPtBetween(p1.Value, prev, pt2); - prev = pp2a.prev; - if (ClipperLib.ClipperBase.PointsEqual(pp2a.pt, pt1)) p3 = pp2a; - else if (ClipperLib.ClipperBase.PointsEqual(prev.pt, pt1)) p3 = prev; - else p3 = this.InsertPolyPtBetween(pp2a, prev, pt1); - if (ClipperLib.ClipperBase.PointsEqual(pp2a.pt, pt2)) p4 = pp2a; - else if (ClipperLib.ClipperBase.PointsEqual(prev.pt, pt2)) p4 = prev; - else if ((p3 == pp2a) || (p3 == prev)) p4 = this.InsertPolyPtBetween(pp2a, prev, pt2); - else if (this.Pt3IsBetweenPt1AndPt2(pp2a.pt, p3.pt, pt2)) p4 = this.InsertPolyPtBetween(pp2a, p3, pt2); - else p4 = this.InsertPolyPtBetween(p3, prev, pt2); - if (p1.Value.next == p2.Value && p3.prev == p4) - { - p1.Value.next = p3; - p3.prev = p1.Value; - p2.Value.prev = p4; - p4.next = p2.Value; - return true; + outRec.Pts = pp; + }; + ClipperLib.Clipper.prototype.DupOutPt = function (outPt, InsertAfter) + { + var result = new ClipperLib.OutPt(); + //result.Pt = outPt.Pt; + result.Pt.X = outPt.Pt.X; + result.Pt.Y = outPt.Pt.Y; + result.Idx = outPt.Idx; + if (InsertAfter) + { + result.Next = outPt.Next; + result.Prev = outPt; + outPt.Next.Prev = result; + outPt.Next = result; } - else if (p1.Value.prev == p2.Value && p3.next == p4) + else { - p1.Value.prev = p3; - p3.next = p1.Value; - p2.Value.next = p4; - p4.prev = p2.Value; - return true; + result.Prev = outPt.Prev; + result.Next = outPt; + outPt.Prev.Next = result; + outPt.Prev = result; } - else return false; + return result; }; - ClipperLib.Clipper.prototype.FixupJoinRecs = function (j, pt, startIdx) + ClipperLib.Clipper.prototype.GetOverlap = function (a1, a2, b1, b2, $val) { - for (var k = startIdx; k < this.m_Joins.length; k++) + if (a1 < a2) { - var j2 = this.m_Joins[k]; - if (j2.poly1Idx == j.poly1Idx && this.PointIsVertex(j2.pt1a, pt)) j2.poly1Idx = j.poly2Idx; - if (j2.poly2Idx == j.poly1Idx && this.PointIsVertex(j2.pt2a, pt)) j2.poly2Idx = j.poly2Idx; + if (b1 < b2) + { + $val.Left = Math.max(a1, b1); + $val.Right = Math.min(a2, b2); + } + else + { + $val.Left = Math.max(a1, b2); + $val.Right = Math.min(a2, b1); + } } + else + { + if (b1 < b2) + { + $val.Left = Math.max(a2, b1); + $val.Right = Math.min(a1, b2); + } + else + { + $val.Left = Math.max(a2, b2); + $val.Right = Math.min(a1, b1); + } + } + return $val.Left < $val.Right; }; - ClipperLib.Clipper.prototype.JoinCommonEdges = function () + ClipperLib.Clipper.prototype.JoinHorz = function (op1, op1b, op2, op2b, Pt, DiscardLeft) { - var k, orec; - for (var i = 0; i < this.m_Joins.length; i++) + var Dir1 = (op1.Pt.X > op1b.Pt.X ? ClipperLib.Direction.dRightToLeft : ClipperLib.Direction.dLeftToRight); + var Dir2 = (op2.Pt.X > op2b.Pt.X ? ClipperLib.Direction.dRightToLeft : ClipperLib.Direction.dLeftToRight); + if (Dir1 == Dir2) + return false; + //When DiscardLeft, we want Op1b to be on the Left of Op1, otherwise we + //want Op1b to be on the Right. (And likewise with Op2 and Op2b.) + //So, to facilitate this while inserting Op1b and Op2b ... + //when DiscardLeft, make sure we're AT or RIGHT of Pt before adding Op1b, + //otherwise make sure we're AT or LEFT of Pt. (Likewise with Op2b.) + if (Dir1 == ClipperLib.Direction.dLeftToRight) + { + while (op1.Next.Pt.X <= Pt.X && + op1.Next.Pt.X >= op1.Pt.X && op1.Next.Pt.Y == Pt.Y) + op1 = op1.Next; + if (DiscardLeft && (op1.Pt.X != Pt.X)) + op1 = op1.Next; + op1b = this.DupOutPt(op1, !DiscardLeft); + if (ClipperLib.IntPoint.op_Inequality(op1b.Pt, Pt)) + { + op1 = op1b; + //op1.Pt = Pt; + op1.Pt.X = Pt.X; + op1.Pt.Y = Pt.Y; + op1b = this.DupOutPt(op1, !DiscardLeft); + } + } + else { - var j = this.m_Joins[i]; - var p1, p2; - if (!(function () + while (op1.Next.Pt.X >= Pt.X && + op1.Next.Pt.X <= op1.Pt.X && op1.Next.Pt.Y == Pt.Y) + op1 = op1.Next; + if (!DiscardLeft && (op1.Pt.X != Pt.X)) + op1 = op1.Next; + op1b = this.DupOutPt(op1, DiscardLeft); + if (ClipperLib.IntPoint.op_Inequality(op1b.Pt, Pt)) { - p1 = { - Value: p1 - }; - p2 = { - Value: p2 - }; - var $res = this.JoinPoints(j, p1, p2); - p1 = p1.Value; - p2 = p2.Value; - return $res; - }) - .call(this)) continue; - var outRec1 = this.m_PolyOuts[j.poly1Idx]; - var outRec2 = this.m_PolyOuts[j.poly2Idx]; - if (outRec1 == outRec2) + op1 = op1b; + //op1.Pt = Pt; + op1.Pt.X = Pt.X; + op1.Pt.Y = Pt.Y; + op1b = this.DupOutPt(op1, DiscardLeft); + } + } + if (Dir2 == ClipperLib.Direction.dLeftToRight) + { + while (op2.Next.Pt.X <= Pt.X && + op2.Next.Pt.X >= op2.Pt.X && op2.Next.Pt.Y == Pt.Y) + op2 = op2.Next; + if (DiscardLeft && (op2.Pt.X != Pt.X)) + op2 = op2.Next; + op2b = this.DupOutPt(op2, !DiscardLeft); + if (ClipperLib.IntPoint.op_Inequality(op2b.Pt, Pt)) { - outRec1.pts = this.GetBottomPt(p1); - outRec1.bottomPt = outRec1.pts; - outRec1.bottomPt.idx = outRec1.idx; - outRec2 = this.CreateOutRec(); - this.m_PolyOuts.push(outRec2); - outRec2.idx = this.m_PolyOuts.length - 1; - j.poly2Idx = outRec2.idx; - outRec2.pts = this.GetBottomPt(p2); - outRec2.bottomPt = outRec2.pts; - outRec2.bottomPt.idx = outRec2.idx; - if (this.PointInPolygon(outRec2.pts.pt, outRec1.pts, this.m_UseFullRange)) - { - outRec2.isHole = !outRec1.isHole; - outRec2.FirstLeft = outRec1; - this.FixupJoinRecs(j, p2, i + 1); - this.FixupOutPolygon(outRec1); - this.FixupOutPolygon(outRec2); - - if ((outRec2.isHole ^ this.m_ReverseOutput) == (this.Area(outRec2, this.m_UseFullRange) > 0)) - this.ReversePolyPtLinks(outRec2.pts); - } - else if (this.PointInPolygon(outRec1.pts.pt, outRec2.pts, this.m_UseFullRange)) - { - outRec2.isHole = outRec1.isHole; - outRec1.isHole = !outRec2.isHole; - outRec2.FirstLeft = outRec1.FirstLeft; - outRec1.FirstLeft = outRec2; - this.FixupJoinRecs(j, p2, i + 1); - this.FixupOutPolygon(outRec1); - this.FixupOutPolygon(outRec2); - - if ((outRec1.isHole ^ this.m_ReverseOutput) == (this.Area(outRec1, this.m_UseFullRange) > 0)) - this.ReversePolyPtLinks(outRec1.pts); + op2 = op2b; + //op2.Pt = Pt; + op2.Pt.X = Pt.X; + op2.Pt.Y = Pt.Y; + op2b = this.DupOutPt(op2, !DiscardLeft); + } + } + else + { + while (op2.Next.Pt.X >= Pt.X && + op2.Next.Pt.X <= op2.Pt.X && op2.Next.Pt.Y == Pt.Y) + op2 = op2.Next; + if (!DiscardLeft && (op2.Pt.X != Pt.X)) + op2 = op2.Next; + op2b = this.DupOutPt(op2, DiscardLeft); + if (ClipperLib.IntPoint.op_Inequality(op2b.Pt, Pt)) + { + op2 = op2b; + //op2.Pt = Pt; + op2.Pt.X = Pt.X; + op2.Pt.Y = Pt.Y; + op2b = this.DupOutPt(op2, DiscardLeft); + } + } + if ((Dir1 == ClipperLib.Direction.dLeftToRight) == DiscardLeft) + { + op1.Prev = op2; + op2.Next = op1; + op1b.Next = op2b; + op2b.Prev = op1b; + } + else + { + op1.Next = op2; + op2.Prev = op1; + op1b.Prev = op2b; + op2b.Next = op1b; + } + return true; + }; + ClipperLib.Clipper.prototype.JoinPoints = function (j, outRec1, outRec2) + { + var op1 = j.OutPt1, + op1b = new ClipperLib.OutPt(); + var op2 = j.OutPt2, + op2b = new ClipperLib.OutPt(); + //There are 3 kinds of joins for output polygons ... + //1. Horizontal joins where Join.OutPt1 & Join.OutPt2 are a vertices anywhere + //along (horizontal) collinear edges (& Join.OffPt is on the same horizontal). + //2. Non-horizontal joins where Join.OutPt1 & Join.OutPt2 are at the same + //location at the Bottom of the overlapping segment (& Join.OffPt is above). + //3. StrictlySimple joins where edges touch but are not collinear and where + //Join.OutPt1, Join.OutPt2 & Join.OffPt all share the same point. + var isHorizontal = (j.OutPt1.Pt.Y == j.OffPt.Y); + if (isHorizontal && (ClipperLib.IntPoint.op_Equality(j.OffPt, j.OutPt1.Pt)) && (ClipperLib.IntPoint.op_Equality(j.OffPt, j.OutPt2.Pt))) + { + //Strictly Simple join ... + op1b = j.OutPt1.Next; + while (op1b != op1 && (ClipperLib.IntPoint.op_Equality(op1b.Pt, j.OffPt))) + op1b = op1b.Next; + var reverse1 = (op1b.Pt.Y > j.OffPt.Y); + op2b = j.OutPt2.Next; + while (op2b != op2 && (ClipperLib.IntPoint.op_Equality(op2b.Pt, j.OffPt))) + op2b = op2b.Next; + var reverse2 = (op2b.Pt.Y > j.OffPt.Y); + if (reverse1 == reverse2) + return false; + if (reverse1) + { + op1b = this.DupOutPt(op1, false); + op2b = this.DupOutPt(op2, true); + op1.Prev = op2; + op2.Next = op1; + op1b.Next = op2b; + op2b.Prev = op1b; + j.OutPt1 = op1; + j.OutPt2 = op1b; + return true; + } + else + { + op1b = this.DupOutPt(op1, true); + op2b = this.DupOutPt(op2, false); + op1.Next = op2; + op2.Prev = op1; + op1b.Prev = op2b; + op2b.Next = op1b; + j.OutPt1 = op1; + j.OutPt2 = op1b; + return true; + } + } + else if (isHorizontal) + { + //treat horizontal joins differently to non-horizontal joins since with + //them we're not yet sure where the overlapping is. OutPt1.Pt & OutPt2.Pt + //may be anywhere along the horizontal edge. + op1b = op1; + while (op1.Prev.Pt.Y == op1.Pt.Y && op1.Prev != op1b && op1.Prev != op2) + op1 = op1.Prev; + while (op1b.Next.Pt.Y == op1b.Pt.Y && op1b.Next != op1 && op1b.Next != op2) + op1b = op1b.Next; + if (op1b.Next == op1 || op1b.Next == op2) + return false; + //a flat 'polygon' + op2b = op2; + while (op2.Prev.Pt.Y == op2.Pt.Y && op2.Prev != op2b && op2.Prev != op1b) + op2 = op2.Prev; + while (op2b.Next.Pt.Y == op2b.Pt.Y && op2b.Next != op2 && op2b.Next != op1) + op2b = op2b.Next; + if (op2b.Next == op2 || op2b.Next == op1) + return false; + //a flat 'polygon' + //Op1 -. Op1b & Op2 -. Op2b are the extremites of the horizontal edges + + var $val = {Left: null, Right: null}; + if (!this.GetOverlap(op1.Pt.X, op1b.Pt.X, op2.Pt.X, op2b.Pt.X, $val)) + return false; + var Left = $val.Left; + var Right = $val.Right; + + //DiscardLeftSide: when overlapping edges are joined, a spike will created + //which needs to be cleaned up. However, we don't want Op1 or Op2 caught up + //on the discard Side as either may still be needed for other joins ... + var Pt = new ClipperLib.IntPoint(); + var DiscardLeftSide; + if (op1.Pt.X >= Left && op1.Pt.X <= Right) + { + //Pt = op1.Pt; + Pt.X = op1.Pt.X; + Pt.Y = op1.Pt.Y; + DiscardLeftSide = (op1.Pt.X > op1b.Pt.X); + } + else if (op2.Pt.X >= Left && op2.Pt.X <= Right) + { + //Pt = op2.Pt; + Pt.X = op2.Pt.X; + Pt.Y = op2.Pt.Y; + DiscardLeftSide = (op2.Pt.X > op2b.Pt.X); + } + else if (op1b.Pt.X >= Left && op1b.Pt.X <= Right) + { + //Pt = op1b.Pt; + Pt.X = op1b.Pt.X; + Pt.Y = op1b.Pt.Y; + DiscardLeftSide = op1b.Pt.X > op1.Pt.X; + } + else + { + //Pt = op2b.Pt; + Pt.X = op2b.Pt.X; + Pt.Y = op2b.Pt.Y; + DiscardLeftSide = (op2b.Pt.X > op2.Pt.X); + } + j.OutPt1 = op1; + j.OutPt2 = op2; + return this.JoinHorz(op1, op1b, op2, op2b, Pt, DiscardLeftSide); + } + else + { + //nb: For non-horizontal joins ... + // 1. Jr.OutPt1.Pt.Y == Jr.OutPt2.Pt.Y + // 2. Jr.OutPt1.Pt > Jr.OffPt.Y + //make sure the polygons are correctly oriented ... + op1b = op1.Next; + while ((ClipperLib.IntPoint.op_Equality(op1b.Pt, op1.Pt)) && (op1b != op1)) + op1b = op1b.Next; + var Reverse1 = ((op1b.Pt.Y > op1.Pt.Y) || !ClipperLib.ClipperBase.SlopesEqual(op1.Pt, op1b.Pt, j.OffPt, this.m_UseFullRange)); + if (Reverse1) + { + op1b = op1.Prev; + while ((ClipperLib.IntPoint.op_Equality(op1b.Pt, op1.Pt)) && (op1b != op1)) + op1b = op1b.Prev; + if ((op1b.Pt.Y > op1.Pt.Y) || !ClipperLib.ClipperBase.SlopesEqual(op1.Pt, op1b.Pt, j.OffPt, this.m_UseFullRange)) + return false; + } + op2b = op2.Next; + while ((ClipperLib.IntPoint.op_Equality(op2b.Pt, op2.Pt)) && (op2b != op2)) + op2b = op2b.Next; + var Reverse2 = ((op2b.Pt.Y > op2.Pt.Y) || !ClipperLib.ClipperBase.SlopesEqual(op2.Pt, op2b.Pt, j.OffPt, this.m_UseFullRange)); + if (Reverse2) + { + op2b = op2.Prev; + while ((ClipperLib.IntPoint.op_Equality(op2b.Pt, op2.Pt)) && (op2b != op2)) + op2b = op2b.Prev; + if ((op2b.Pt.Y > op2.Pt.Y) || !ClipperLib.ClipperBase.SlopesEqual(op2.Pt, op2b.Pt, j.OffPt, this.m_UseFullRange)) + return false; + } + if ((op1b == op1) || (op2b == op2) || (op1b == op2b) || + ((outRec1 == outRec2) && (Reverse1 == Reverse2))) + return false; + if (Reverse1) + { + op1b = this.DupOutPt(op1, false); + op2b = this.DupOutPt(op2, true); + op1.Prev = op2; + op2.Next = op1; + op1b.Next = op2b; + op2b.Prev = op1b; + j.OutPt1 = op1; + j.OutPt2 = op1b; + return true; + } + else + { + op1b = this.DupOutPt(op1, true); + op2b = this.DupOutPt(op2, false); + op1.Next = op2; + op2.Prev = op1; + op1b.Prev = op2b; + op2b.Next = op1b; + j.OutPt1 = op1; + j.OutPt2 = op1b; + return true; + } + } + }; + ClipperLib.Clipper.GetBounds = function (paths) + { + var i = 0, + cnt = paths.length; + while (i < cnt && paths[i].length == 0) i++; + if (i == cnt) return new ClipperLib.IntRect(0, 0, 0, 0); + var result = new ClipperLib.IntRect(); + result.left = paths[i][0].X; + result.right = result.left; + result.top = paths[i][0].Y; + result.bottom = result.top; + for (; i < cnt; i++) + for (var j = 0, jlen = paths[i].length; j < jlen; j++) + { + if (paths[i][j].X < result.left) result.left = paths[i][j].X; + else if (paths[i][j].X > result.right) result.right = paths[i][j].X; + if (paths[i][j].Y < result.top) result.top = paths[i][j].Y; + else if (paths[i][j].Y > result.bottom) result.bottom = paths[i][j].Y; + } + return result; + } + ClipperLib.Clipper.prototype.GetBounds2 = function (ops) + { + var opStart = ops; + var result = new ClipperLib.IntRect(); + result.left = ops.Pt.X; + result.right = ops.Pt.X; + result.top = ops.Pt.Y; + result.bottom = ops.Pt.Y; + ops = ops.Next; + while (ops != opStart) + { + if (ops.Pt.X < result.left) + result.left = ops.Pt.X; + if (ops.Pt.X > result.right) + result.right = ops.Pt.X; + if (ops.Pt.Y < result.top) + result.top = ops.Pt.Y; + if (ops.Pt.Y > result.bottom) + result.bottom = ops.Pt.Y; + ops = ops.Next; + } + return result; + }; - if (this.m_UsingExPolygons && outRec1.isHole) for (k = 0; k < this.m_PolyOuts.length; ++k) + ClipperLib.Clipper.PointInPolygon = function (pt, path) + { + //returns 0 if false, +1 if true, -1 if pt ON polygon boundary + //http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.88.5498&rep=rep1&type=pdf + var result = 0, + cnt = path.length; + if (cnt < 3) + return 0; + var ip = path[0]; + for (var i = 1; i <= cnt; ++i) + { + var ipNext = (i == cnt ? path[0] : path[i]); + if (ipNext.Y == pt.Y) + { + if ((ipNext.X == pt.X) || (ip.Y == pt.Y && ((ipNext.X > pt.X) == (ip.X < pt.X)))) + return -1; + } + if ((ip.Y < pt.Y) != (ipNext.Y < pt.Y)) + { + if (ip.X >= pt.X) + { + if (ipNext.X > pt.X) + result = 1 - result; + else { - orec = this.m_PolyOuts[k]; - if (orec.isHole && orec.bottomPt != null && orec.FirstLeft == outRec1) orec.FirstLeft = outRec2; + var d = (ip.X - pt.X) * (ipNext.Y - pt.Y) - (ipNext.X - pt.X) * (ip.Y - pt.Y); + if (d == 0) + return -1; + else if ((d > 0) == (ipNext.Y > ip.Y)) + result = 1 - result; } } else { - outRec2.isHole = outRec1.isHole; - outRec2.FirstLeft = outRec1.FirstLeft; - this.FixupJoinRecs(j, p2, i + 1); - this.FixupOutPolygon(outRec1); - this.FixupOutPolygon(outRec2); - - if (this.m_UsingExPolygons && outRec2.pts != null) for (k = 0; k < this.m_PolyOuts.length; ++k) + if (ipNext.X > pt.X) { - orec = this.m_PolyOuts[k]; - if (orec.isHole && orec.bottomPt != null && orec.FirstLeft == outRec1 && this.PointInPolygon(orec.bottomPt.pt, outRec2.pts, this.m_UseFullRange)) orec.FirstLeft = outRec2; + var d = (ip.X - pt.X) * (ipNext.Y - pt.Y) - (ipNext.X - pt.X) * (ip.Y - pt.Y); + if (d == 0) + return -1; + else if ((d > 0) == (ipNext.Y > ip.Y)) + result = 1 - result; } } } - else + ip = ipNext; + } + return result; + }; + + ClipperLib.Clipper.prototype.PointInPolygon = function (pt, op) + { + //returns 0 if false, +1 if true, -1 if pt ON polygon boundary + //http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.88.5498&rep=rep1&type=pdf + var result = 0; + var startOp = op; + for (;;) + { + var poly0x = op.Pt.X, + poly0y = op.Pt.Y; + var poly1x = op.Next.Pt.X, + poly1y = op.Next.Pt.Y; + if (poly1y == pt.Y) + { + if ((poly1x == pt.X) || (poly0y == pt.Y && ((poly1x > pt.X) == (poly0x < pt.X)))) + return -1; + } + if ((poly0y < pt.Y) != (poly1y < pt.Y)) { - if (this.m_UsingExPolygons) for (k = 0; k < this.m_PolyOuts.length; ++k) - if (this.m_PolyOuts[k].isHole && this.m_PolyOuts[k].bottomPt != null && this.m_PolyOuts[k].FirstLeft == outRec2) this.m_PolyOuts[k].FirstLeft = outRec1; - this.FixupOutPolygon(outRec1); - if (outRec1.pts != null) + if (poly0x >= pt.X) { - outRec1.isHole = this.Area(outRec1, this.m_UseFullRange) < 0; - if (outRec1.isHole && outRec1.FirstLeft == null) outRec1.FirstLeft = outRec2.FirstLeft; + if (poly1x > pt.X) + result = 1 - result; + else + { + var d = (poly0x - pt.X) * (poly1y - pt.Y) - (poly1x - pt.X) * (poly0y - pt.Y); + if (d == 0) + return -1; + if ((d > 0) == (poly1y > poly0y)) + result = 1 - result; + } } - var OKIdx = outRec1.idx; - var ObsoleteIdx = outRec2.idx; - outRec2.pts = null; - outRec2.bottomPt = null; - outRec2.AppendLink = outRec1; - for (k = i + 1; k < this.m_Joins.length; k++) + else { - var j2 = this.m_Joins[k]; - if (j2.poly1Idx == ObsoleteIdx) j2.poly1Idx = OKIdx; - if (j2.poly2Idx == ObsoleteIdx) j2.poly2Idx = OKIdx; + if (poly1x > pt.X) + { + var d = (poly0x - pt.X) * (poly1y - pt.Y) - (poly1x - pt.X) * (poly0y - pt.Y); + if (d == 0) + return -1; + if ((d > 0) == (poly1y > poly0y)) + result = 1 - result; + } } } + op = op.Next; + if (startOp == op) + break; } + return result; }; - ClipperLib.Clipper.FullRangeNeeded = function (pts) + + ClipperLib.Clipper.prototype.Poly2ContainsPoly1 = function (outPt1, outPt2) { - var result = false; - for (var i = 0; i < pts.length; i++) { - if (ClipperLib.Math_Abs_Int64(pts[i].X) > ClipperLib.ClipperBase.hiRange || ClipperLib.Math_Abs_Int64(pts[i].Y) > ClipperLib.ClipperBase.hiRange) - ClipperLib.Error("Coordinate exceeds range bounds in FullRangeNeeded()."); - else if (ClipperLib.Math_Abs_Int64(pts[i].X) > ClipperLib.ClipperBase.loRange || ClipperLib.Math_Abs_Int64(pts[i].Y) > ClipperLib.ClipperBase.loRange) - { - result = true; - } + var op = outPt1; + do { + var res = this.PointInPolygon(op.Pt, outPt2); + if (res >= 0) + return res != 0; + op = op.Next; } - return result; + while (op != outPt1) + return true; }; - ClipperLib.Clipper.prototype.Area = ClipperLib.Clipper.Area = function () + ClipperLib.Clipper.prototype.FixupFirstLefts1 = function (OldOutRec, NewOutRec) { - var arg = arguments; - var i, a; - if (arg.length == 1) // function ( poly ) + for (var i = 0, ilen = this.m_PolyOuts.length; i < ilen; i++) { - var poly = arg[0]; - var highI = poly.length - 1; - if (highI < 2) return 0; - if (ClipperLib.Clipper.FullRangeNeeded(poly)) - { - a = new Int128( poly[highI].X + poly[0].X ).multiply( new Int128(poly[0].Y - poly[highI].Y) ); - for (i = 1; i <= highI; ++i) - a = a.add( new Int128( poly[i - 1].X + poly[i].X ).multiply( new Int128( poly[i].Y - poly[i - 1].Y ) ) ); - return parseFloat(a.toString()) / 2; - } - else + var outRec = this.m_PolyOuts[i]; + if (outRec.Pts !== null && outRec.FirstLeft == OldOutRec) { - var area = (poly[highI].X + poly[0].X) * (poly[0].Y - poly[highI].Y); - for (i = 1; i <= highI; ++i) - area += (poly[i - 1].X + poly[i].X) * (poly[i].Y - poly[i -1].Y); - return area / 2; + if (this.Poly2ContainsPoly1(outRec.Pts, NewOutRec.Pts)) + outRec.FirstLeft = NewOutRec; } } - else if (arg.length == 2) // function (outRec, UseFull64BitRange) + }; + ClipperLib.Clipper.prototype.FixupFirstLefts2 = function (OldOutRec, NewOutRec) + { + for (var $i2 = 0, $t2 = this.m_PolyOuts, $l2 = $t2.length, outRec = $t2[$i2]; $i2 < $l2; $i2++, outRec = $t2[$i2]) + if (outRec.FirstLeft == OldOutRec) + outRec.FirstLeft = NewOutRec; + }; + ClipperLib.Clipper.ParseFirstLeft = function (FirstLeft) + { + while (FirstLeft != null && FirstLeft.Pts == null) + FirstLeft = FirstLeft.FirstLeft; + return FirstLeft; + }; + ClipperLib.Clipper.prototype.JoinCommonEdges = function () + { + for (var i = 0, ilen = this.m_Joins.length; i < ilen; i++) { - var outRec = arg[0]; - var UseFull64BitRange = arg[1]; - var op = outRec.pts; - if (op == null) return 0; - if (UseFull64BitRange) + var join = this.m_Joins[i]; + var outRec1 = this.GetOutRec(join.OutPt1.Idx); + var outRec2 = this.GetOutRec(join.OutPt2.Idx); + if (outRec1.Pts == null || outRec2.Pts == null) + continue; + //get the polygon fragment with the correct hole state (FirstLeft) + //before calling JoinPoints() ... + var holeStateRec; + if (outRec1 == outRec2) + holeStateRec = outRec1; + else if (this.Param1RightOfParam2(outRec1, outRec2)) + holeStateRec = outRec2; + else if (this.Param1RightOfParam2(outRec2, outRec1)) + holeStateRec = outRec1; + else + holeStateRec = this.GetLowermostRec(outRec1, outRec2); + + if (!this.JoinPoints(join, outRec1, outRec2)) continue; + + if (outRec1 == outRec2) { - a = new Int128(Int128.ZERO); - do { - a = a.add(new Int128( op.pt.X + op.prev.pt.X ).multiply( new Int128 ( op.prev.pt.Y - op.pt.Y ) ) ); - op = op.next; - } while (op != outRec.pts); - return parseFloat(a.toString()) / 2; // This could be something faster! + //instead of joining two polygons, we've just created a new one by + //splitting one polygon into two. + outRec1.Pts = join.OutPt1; + outRec1.BottomPt = null; + outRec2 = this.CreateOutRec(); + outRec2.Pts = join.OutPt2; + //update all OutRec2.Pts Idx's ... + this.UpdateOutPtIdxs(outRec2); + //We now need to check every OutRec.FirstLeft pointer. If it points + //to OutRec1 it may need to point to OutRec2 instead ... + if (this.m_UsingPolyTree) + for (var j = 0, jlen = this.m_PolyOuts.length; j < jlen - 1; j++) + { + var oRec = this.m_PolyOuts[j]; + if (oRec.Pts == null || ClipperLib.Clipper.ParseFirstLeft(oRec.FirstLeft) != outRec1 || oRec.IsHole == outRec1.IsHole) + continue; + if (this.Poly2ContainsPoly1(oRec.Pts, join.OutPt2)) + oRec.FirstLeft = outRec2; + } + if (this.Poly2ContainsPoly1(outRec2.Pts, outRec1.Pts)) + { + //outRec2 is contained by outRec1 ... + outRec2.IsHole = !outRec1.IsHole; + outRec2.FirstLeft = outRec1; + //fixup FirstLeft pointers that may need reassigning to OutRec1 + if (this.m_UsingPolyTree) + this.FixupFirstLefts2(outRec2, outRec1); + if ((outRec2.IsHole ^ this.ReverseSolution) == (this.Area(outRec2) > 0)) + this.ReversePolyPtLinks(outRec2.Pts); + } + else if (this.Poly2ContainsPoly1(outRec1.Pts, outRec2.Pts)) + { + //outRec1 is contained by outRec2 ... + outRec2.IsHole = outRec1.IsHole; + outRec1.IsHole = !outRec2.IsHole; + outRec2.FirstLeft = outRec1.FirstLeft; + outRec1.FirstLeft = outRec2; + //fixup FirstLeft pointers that may need reassigning to OutRec1 + if (this.m_UsingPolyTree) + this.FixupFirstLefts2(outRec1, outRec2); + if ((outRec1.IsHole ^ this.ReverseSolution) == (this.Area(outRec1) > 0)) + this.ReversePolyPtLinks(outRec1.Pts); + } + else + { + //the 2 polygons are completely separate ... + outRec2.IsHole = outRec1.IsHole; + outRec2.FirstLeft = outRec1.FirstLeft; + //fixup FirstLeft pointers that may need reassigning to OutRec2 + if (this.m_UsingPolyTree) + this.FixupFirstLefts1(outRec1, outRec2); + } } else { - a = 0; - do { - a = a + (op.pt.X + op.prev.pt.X) * (op.prev.pt.Y - op.pt.Y); - op = op.next; - } - while (op != outRec.pts); - return a / 2; + //joined 2 polygons together ... + outRec2.Pts = null; + outRec2.BottomPt = null; + outRec2.Idx = outRec1.Idx; + outRec1.IsHole = holeStateRec.IsHole; + if (holeStateRec == outRec2) + outRec1.FirstLeft = outRec2.FirstLeft; + outRec2.FirstLeft = outRec1; + //fixup FirstLeft pointers that may need reassigning to OutRec1 + if (this.m_UsingPolyTree) + this.FixupFirstLefts2(outRec2, outRec1); } } }; - ClipperLib.Clipper.BuildArc = function (pt, a1, a2, r) + ClipperLib.Clipper.prototype.UpdateOutPtIdxs = function (outrec) { - var steps = Math.sqrt(ClipperLib.Math_Abs_Double(r)) * ClipperLib.Math_Abs_Double(a2 - a1); - - steps = steps / 4; // to avoid overload - - // If you want to make steps independent of scaling factor (scale have to be set), - // the following line does the trick: - // steps = steps / Math.sqrt(scale) * 2; - - // If you want that changing scale factor has some influence to steps, uncomment also the following line: - // It may be desirable, that bigger precision ( = bigger scaling factor) needs more steps. - // steps += Math.pow(scale, 0.2); - - if (steps < 6) steps = 6; - if (steps > 64) steps = ClipperLib.MaxSteps; // to avoid overload - - // if (steps > 1048576) steps = 1048576; // 0x100000 - // if (steps > ClipperLib.MaxSteps) steps = ClipperLib.MaxSteps; // 0x100000 - // Had to change 1048576 to lower value, because when coordinates are near or above lorange, program starts hanging - // Adjust this value to meet your needs, maybe 10 is enough for most purposes - var n = ClipperLib.Cast_Int32(steps); - var result = new ClipperLib.Polygon(); - var da = (a2 - a1) / (n - 1); - var a = a1; - for (var i = 0; i < n; ++i) - { - result.push(new ClipperLib.IntPoint(pt.X + ClipperLib.Clipper.Round(Math.cos(a) * r), pt.Y + ClipperLib.Clipper.Round(Math.sin(a) * r))); - a += da; + var op = outrec.Pts; + do { + op.Idx = outrec.Idx; + op = op.Prev; } - return result; + while (op != outrec.Pts) }; - - ClipperLib.Clipper.GetUnitNormal = function (pt1, pt2) + ClipperLib.Clipper.prototype.DoSimplePolygons = function () { - var dx = (pt2.X - pt1.X); - var dy = (pt2.Y - pt1.Y); - if ((dx == 0) && (dy == 0)) return new ClipperLib.Clipper.DoublePoint(0, 0); - var f = 1 / Math.sqrt(dx * dx + dy * dy); - dx *= f; - dy *= f; - return new ClipperLib.Clipper.DoublePoint(dy, -dx); + var i = 0; + while (i < this.m_PolyOuts.length) + { + var outrec = this.m_PolyOuts[i++]; + var op = outrec.Pts; + if (op === null) + continue; + do //for each Pt in Polygon until duplicate found do ... + { + var op2 = op.Next; + while (op2 != outrec.Pts) + { + if ((ClipperLib.IntPoint.op_Equality(op.Pt, op2.Pt)) && op2.Next != op && op2.Prev != op) + { + //split the polygon into two ... + var op3 = op.Prev; + var op4 = op2.Prev; + op.Prev = op4; + op4.Next = op; + op2.Prev = op3; + op3.Next = op2; + outrec.Pts = op; + var outrec2 = this.CreateOutRec(); + outrec2.Pts = op2; + this.UpdateOutPtIdxs(outrec2); + if (this.Poly2ContainsPoly1(outrec2.Pts, outrec.Pts)) + { + //OutRec2 is contained by OutRec1 ... + outrec2.IsHole = !outrec.IsHole; + outrec2.FirstLeft = outrec; + } + else if (this.Poly2ContainsPoly1(outrec.Pts, outrec2.Pts)) + { + //OutRec1 is contained by OutRec2 ... + outrec2.IsHole = outrec.IsHole; + outrec.IsHole = !outrec2.IsHole; + outrec2.FirstLeft = outrec.FirstLeft; + outrec.FirstLeft = outrec2; + } + else + { + //the 2 polygons are separate ... + outrec2.IsHole = outrec.IsHole; + outrec2.FirstLeft = outrec.FirstLeft; + } + op2 = op; + //ie get ready for the next iteration + } + op2 = op2.Next; + } + op = op.Next; + } + while (op != outrec.Pts) + } }; - ClipperLib.Clipper.prototype.OffsetPolygons = function (poly, delta, jointype, MiterLimit, AutoFix) + ClipperLib.Clipper.Area = function (poly) { - var a = arguments; - if (a.length == 4) AutoFix = true; - else if (a.length == 3) + var cnt = poly.length; + if (cnt < 3) + return 0; + var a = 0; + for (var i = 0, j = cnt - 1; i < cnt; ++i) { - MiterLimit = 2; - AutoFix = true; + a += (poly[j].X + poly[i].X) * (poly[j].Y - poly[i].Y); + j = i; } - else if (a.length == 2) - { - jointype = ClipperLib.JoinType.jtSquare; - MiterLimit = 2; - AutoFix = true; + return -a * 0.5; + }; + ClipperLib.Clipper.prototype.Area = function (outRec) + { + var op = outRec.Pts; + if (op == null) + return 0; + var a = 0; + do { + a = a + (op.Prev.Pt.X + op.Pt.X) * (op.Prev.Pt.Y - op.Pt.Y); + op = op.Next; } - if (isNaN(delta)) ClipperLib.Error("Delta is not a number"); - else if (isNaN(MiterLimit)) ClipperLib.Error("MiterLimit is not a number"); - var result = {}; - new ClipperLib.Clipper.PolyOffsetBuilder(poly, result, delta, jointype, MiterLimit, AutoFix); - if (result.Value) result = result.Value; - else result = [[]]; - return result; + while (op != outRec.Pts) + return a * 0.5; }; - ClipperLib.Clipper.prototype.SimplifyPolygon = function (poly, fillType) + if (use_deprecated) + { + ClipperLib.Clipper.OffsetPaths = function (polys, delta, jointype, endtype, MiterLimit) + { + var result = new ClipperLib.Paths(); + var co = new ClipperLib.ClipperOffset(MiterLimit, MiterLimit); + co.AddPaths(polys, jointype, endtype); + co.Execute(result, delta); + return result; + }; + } + ClipperLib.Clipper.SimplifyPolygon = function (poly, fillType) { - var result = new ClipperLib.Polygons(); - var c = new ClipperLib.Clipper(); - if (c.AddPolygon(poly, ClipperLib.PolyType.ptSubject)) + var result = new Array(); + var c = new ClipperLib.Clipper(0); + c.StrictlySimple = true; + c.AddPath(poly, ClipperLib.PolyType.ptSubject, true); c.Execute(ClipperLib.ClipType.ctUnion, result, fillType, fillType); return result; }; - ClipperLib.Clipper.prototype.SimplifyPolygons = function (polys, fillType) + ClipperLib.Clipper.SimplifyPolygons = function (polys, fillType) { - var result = new ClipperLib.Polygons(); - var c = new ClipperLib.Clipper(); - if(c.AddPolygons(polys, ClipperLib.PolyType.ptSubject)) + if (typeof (fillType) == "undefined") fillType = ClipperLib.PolyFillType.pftEvenOdd; + var result = new Array(); + var c = new ClipperLib.Clipper(0); + c.StrictlySimple = true; + c.AddPaths(polys, ClipperLib.PolyType.ptSubject, true); c.Execute(ClipperLib.ClipType.ctUnion, result, fillType, fillType); return result; }; - var ce = ClipperLib.Clipper; - var ce2 = ClipperLib.ClipperBase; - var p; - if (typeof (Object.getOwnPropertyNames) == 'undefined') + ClipperLib.Clipper.DistanceSqrd = function (pt1, pt2) { - for (p in ce2.prototype) - if (typeof (ce.prototype[p]) == 'undefined' || ce.prototype[p] == Object.prototype[p]) ce.prototype[p] = ce2.prototype[p]; - for (p in ce2) - if (typeof (ce[p]) == 'undefined') ce[p] = ce2[p]; - ce.$baseCtor = ce2; - } - else + var dx = (pt1.X - pt2.X); + var dy = (pt1.Y - pt2.Y); + return (dx * dx + dy * dy); + }; + ClipperLib.Clipper.DistanceFromLineSqrd = function (pt, ln1, ln2) { - var props = Object.getOwnPropertyNames(ce2.prototype); - for (var i = 0; i < props.length; i++) - if (typeof (Object.getOwnPropertyDescriptor(ce.prototype, props[i])) == 'undefined') Object.defineProperty(ce.prototype, props[i], Object.getOwnPropertyDescriptor(ce2.prototype, props[i])); - for (p in ce2) - if (typeof (ce[p]) == 'undefined') ce[p] = ce2[p]; - ce.$baseCtor = ce2; - } - ClipperLib.Clipper.DoublePoint = function (x, y) + //The equation of a line in general form (Ax + By + C = 0) + //given 2 points (x¹,y¹) & (x²,y²) is ... + //(y¹ - y²)x + (x² - x¹)y + (y² - y¹)x¹ - (x² - x¹)y¹ = 0 + //A = (y¹ - y²); B = (x² - x¹); C = (y² - y¹)x¹ - (x² - x¹)y¹ + //perpendicular distance of point (x³,y³) = (Ax³ + By³ + C)/Sqrt(A² + B²) + //see http://en.wikipedia.org/wiki/Perpendicular_distance + var A = ln1.Y - ln2.Y; + var B = ln2.X - ln1.X; + var C = A * ln1.X + B * ln1.Y; + C = A * pt.X + B * pt.Y - C; + return (C * C) / (A * A + B * B); + }; + ClipperLib.Clipper.SlopesNearCollinear = function (pt1, pt2, pt3, distSqrd) { - this.X = x; - this.Y = y; + return ClipperLib.Clipper.DistanceFromLineSqrd(pt2, pt1, pt3) < distSqrd; }; - - ClipperLib.Clipper.PolyOffsetBuilder = function (pts, solution, delta, jointype, MiterLimit, AutoFix) - { - this.pts = null; // Polygons - this.currentPoly = null; // Polygon - this.normals = null; - this.delta = 0; - this.m_R = 0; - this.m_i = 0; - this.m_j = 0; - this.m_k = 0; - this.botPt = null; // This is "this." because it is ref in original c# code - if (delta == 0) - { - solution.Value = pts; - return; - } - this.pts = pts; - this.delta = delta; - var i, j; - //AutoFix - fixes polygon orientation if necessary and removes - //duplicate vertices. Can be set false when you're sure that polygon - //orientation is correct and that there are no duplicate vertices. - if (AutoFix) - { - var Len = this.pts.length, - botI = 0; - while (botI < Len && this.pts[botI].length == 0) botI++; - if (botI == Len) + ClipperLib.Clipper.PointsAreClose = function (pt1, pt2, distSqrd) + { + var dx = pt1.X - pt2.X; + var dy = pt1.Y - pt2.Y; + return ((dx * dx) + (dy * dy) <= distSqrd); + }; + //------------------------------------------------------------------------------ + ClipperLib.Clipper.ExcludeOp = function (op) + { + var result = op.Prev; + result.Next = op.Next; + op.Next.Prev = result; + result.Idx = 0; + return result; + }; + ClipperLib.Clipper.CleanPolygon = function (path, distance) + { + if (typeof (distance) == "undefined") distance = 1.415; + //distance = proximity in units/pixels below which vertices will be stripped. + //Default ~= sqrt(2) so when adjacent vertices or semi-adjacent vertices have + //both x & y coords within 1 unit, then the second vertex will be stripped. + var cnt = path.length; + if (cnt == 0) + return new Array(); + var outPts = new Array(cnt); + for (var i = 0; i < cnt; ++i) + outPts[i] = new ClipperLib.OutPt(); + for (var i = 0; i < cnt; ++i) + { + outPts[i].Pt = path[i]; + outPts[i].Next = outPts[(i + 1) % cnt]; + outPts[i].Next.Prev = outPts[i]; + outPts[i].Idx = 0; + } + var distSqrd = distance * distance; + var op = outPts[0]; + while (op.Idx == 0 && op.Next != op.Prev) + { + if (ClipperLib.Clipper.PointsAreClose(op.Pt, op.Prev.Pt, distSqrd)) { - //solution.Value = new ClipperLib.Polygons(); - return; + op = ClipperLib.Clipper.ExcludeOp(op); + cnt--; } - //botPt: used to find the lowermost (in inverted Y-axis) & leftmost point - //This point (on pts[botI]) must be on an outer polygon ring and if - //its orientation is false (counterclockwise) then assume all polygons - //need reversing ... - this.botPt = this.pts[botI][0]; // This is ported with different logic than other C# refs - // adding botPt to object's property it's accessible through object's - // methods - // => All other ref's are now ported using rather complex object.Value - // technique, which seems to work. - for (i = botI; i < Len; ++i) - { - if (this.UpdateBotPt(this.pts[i][0])) botI = i; - for (j = this.pts[i].length - 1; j > 0; j--) - { - if (ClipperLib.ClipperBase.PointsEqual(this.pts[i][j], this.pts[i][j - 1])) - { - this.pts[i].splice(j, 1); - } - else if (this.UpdateBotPt(this.pts[i][j])) botI = i; - } + else if (ClipperLib.Clipper.PointsAreClose(op.Prev.Pt, op.Next.Pt, distSqrd)) + { + ClipperLib.Clipper.ExcludeOp(op.Next); + op = ClipperLib.Clipper.ExcludeOp(op); + cnt -= 2; + } + else if (ClipperLib.Clipper.SlopesNearCollinear(op.Prev.Pt, op.Pt, op.Next.Pt, distSqrd)) + { + op = ClipperLib.Clipper.ExcludeOp(op); + cnt--; + } + else + { + op.Idx = 1; + op = op.Next; } - if (!ClipperLib.Clipper.Orientation(this.pts[botI])) ClipperLib.Clipper.ReversePolygons(this.pts); } - if (MiterLimit <= 1) MiterLimit = 1; - var RMin = 2 / (MiterLimit * MiterLimit); - this.normals = []; - var deltaSq = delta * delta; - solution.Value = new ClipperLib.Polygons(); - ClipperLib.Clear(solution.Value); - var len; - for (this.m_i = 0; this.m_i < this.pts.length; this.m_i++) + if (cnt < 3) + cnt = 0; + var result = new Array(cnt); + for (var i = 0; i < cnt; ++i) { - len = this.pts[this.m_i].length; - if (len > 1 && this.pts[this.m_i][0].X == this.pts[this.m_i][len - 1].X && - this.pts[this.m_i][0].Y == this.pts[this.m_i][len - 1].Y) + result[i] = new ClipperLib.IntPoint(op.Pt); + op = op.Next; + } + outPts = null; + return result; + }; + ClipperLib.Clipper.CleanPolygons = function (polys, distance) + { + var result = new Array(polys.length); + for (var i = 0, ilen = polys.length; i < ilen; i++) + result[i] = ClipperLib.Clipper.CleanPolygon(polys[i], distance); + return result; + }; + ClipperLib.Clipper.Minkowski = function (pattern, path, IsSum, IsClosed) + { + var delta = (IsClosed ? 1 : 0); + var polyCnt = pattern.length; + var pathCnt = path.length; + var result = new Array(); + if (IsSum) + for (var i = 0; i < pathCnt; i++) { - len--; + var p = new Array(polyCnt); + for (var j = 0, jlen = pattern.length, ip = pattern[j]; j < jlen; j++, ip = pattern[j]) + p[j] = new ClipperLib.IntPoint(path[i].X + ip.X, path[i].Y + ip.Y); + result.push(p); } - if (len == 0 || (len < 3 && delta <= 0)) + else + for (var i = 0; i < pathCnt; i++) { - continue; + var p = new Array(polyCnt); + for (var j = 0, jlen = pattern.length, ip = pattern[j]; j < jlen; j++, ip = pattern[j]) + p[j] = new ClipperLib.IntPoint(path[i].X - ip.X, path[i].Y - ip.Y); + result.push(p); } - else if (len == 1) + var quads = new Array(); + for (var i = 0; i < pathCnt - 1 + delta; i++) + for (var j = 0; j < polyCnt; j++) { - var arc; - arc = ClipperLib.Clipper.BuildArc(this.pts[this.m_i][len - 1], 0, ClipperLib.PI2, delta); - solution.Value.push(arc); - continue; + var quad = new Array(); + quad.push(result[i % pathCnt][j % polyCnt]); + quad.push(result[(i + 1) % pathCnt][j % polyCnt]); + quad.push(result[(i + 1) % pathCnt][(j + 1) % polyCnt]); + quad.push(result[i % pathCnt][(j + 1) % polyCnt]); + if (!ClipperLib.Clipper.Orientation(quad)) + quad.reverse(); + quads.push(quad); } - - //build normals ... - ClipperLib.Clear(this.normals); - for (j = 0; j < len - 1; ++j) - this.normals.push(ClipperLib.Clipper.GetUnitNormal(this.pts[this.m_i][j], this.pts[this.m_i][j + 1])); - this.normals.push(ClipperLib.Clipper.GetUnitNormal(this.pts[this.m_i][len - 1], this.pts[this.m_i][0])); + var c = new ClipperLib.Clipper(0); + c.AddPaths(quads, ClipperLib.PolyType.ptSubject, true); + c.Execute(ClipperLib.ClipType.ctUnion, result, ClipperLib.PolyFillType.pftNonZero, ClipperLib.PolyFillType.pftNonZero); + return result; + }; - this.currentPoly = new ClipperLib.Polygon(); - this.m_k = len - 1; - for (this.m_j = 0; this.m_j < len; ++this.m_j) + ClipperLib.Clipper.MinkowskiSum = function () + { + var a = arguments, + alen = a.length; + if (alen == 3) // MinkowskiSum(Path pattern, path, pathIsClosed) + { + var pattern = a[0], + path = a[1], + pathIsClosed = a[2]; + return ClipperLib.Clipper.Minkowski(pattern, path, true, pathIsClosed); + } + else if (alen == 4) // MinkowskiSum(pattern, paths, pathFillType, pathIsClosed) + { + var pattern = a[0], + paths = a[1], + pathFillType = a[2], + pathIsClosed = a[3]; + var c = new ClipperLib.Clipper(), + tmp; + for (var i = 0, ilen = paths.length; i < ilen; ++i) { - switch (jointype) - { - case ClipperLib.JoinType.jtMiter: - this.m_R = 1 + (this.normals[this.m_j].X * this.normals[this.m_k].X + this.normals[this.m_j].Y * this.normals[this.m_k].Y); - if (this.m_R >= RMin) this.DoMiter(); - else this.DoSquare(MiterLimit); - break; - case ClipperLib.JoinType.jtRound: - this.DoRound(); - break; - case ClipperLib.JoinType.jtSquare: - this.DoSquare(1); - break; - } - this.m_k = this.m_j; + var tmp = ClipperLib.Clipper.Minkowski(pattern, paths[i], true, pathIsClosed); + c.AddPaths(tmp, ClipperLib.PolyType.ptSubject, true); } - solution.Value.push(this.currentPoly); - } - - //finally, clean up untidy corners ... - var clpr = new ClipperLib.Clipper(); - clpr.AddPolygons(solution.Value, ClipperLib.PolyType.ptSubject); - if (delta > 0) - { - clpr.Execute(ClipperLib.ClipType.ctUnion, solution.Value, ClipperLib.PolyFillType.pftPositive, ClipperLib.PolyFillType.pftPositive); + if (pathIsClosed) c.AddPaths(paths, ClipperLib.PolyType.ptClip, true); + var solution = new ClipperLib.Paths(); + c.Execute(ClipperLib.ClipType.ctUnion, solution, pathFillType, pathFillType); + return solution; } - else + }; + + ClipperLib.Clipper.MinkowskiDiff = function (pattern, path, pathIsClosed) + { + return ClipperLib.Clipper.Minkowski(pattern, path, false, pathIsClosed); + }; + + ClipperLib.Clipper.PolyTreeToPaths = function (polytree) + { + var result = new Array(); + //result.set_Capacity(polytree.get_Total()); + ClipperLib.Clipper.AddPolyNodeToPaths(polytree, ClipperLib.Clipper.NodeType.ntAny, result); + return result; + }; + ClipperLib.Clipper.AddPolyNodeToPaths = function (polynode, nt, paths) + { + var match = true; + switch (nt) { - var r = clpr.GetBounds(); - var outer = new ClipperLib.Polygon(); - outer.push(new ClipperLib.IntPoint(r.left - 10, r.bottom + 10)); - outer.push(new ClipperLib.IntPoint(r.right + 10, r.bottom + 10)); - outer.push(new ClipperLib.IntPoint(r.right + 10, r.top - 10)); - outer.push(new ClipperLib.IntPoint(r.left - 10, r.top - 10)); - clpr.AddPolygon(outer, ClipperLib.PolyType.ptSubject); - clpr.Execute(ClipperLib.ClipType.ctUnion, solution.Value, ClipperLib.PolyFillType.pftNegative, ClipperLib.PolyFillType.pftNegative); - if (solution.Value.length > 0) - { - solution.Value.splice(0, 1); - for (i = 0; i < solution.Value.length; i++) - solution.Value[i].reverse(); - } - } + case ClipperLib.Clipper.NodeType.ntOpen: + return; + case ClipperLib.Clipper.NodeType.ntClosed: + match = !polynode.IsOpen; + break; + default: + break; + } + if (polynode.m_polygon.length > 0 && match) + paths.push(polynode.m_polygon); + for (var $i3 = 0, $t3 = polynode.Childs(), $l3 = $t3.length, pn = $t3[$i3]; $i3 < $l3; $i3++, pn = $t3[$i3]) + ClipperLib.Clipper.AddPolyNodeToPaths(pn, nt, paths); + }; + ClipperLib.Clipper.OpenPathsFromPolyTree = function (polytree) + { + var result = new ClipperLib.Paths(); + //result.set_Capacity(polytree.ChildCount()); + for (var i = 0, ilen = polytree.ChildCount(); i < ilen; i++) + if (polytree.Childs()[i].IsOpen) + result.push(polytree.Childs()[i].m_polygon); + return result; }; - //ClipperLib.Clipper.PolyOffsetBuilder.buffLength = 128; - ClipperLib.Clipper.PolyOffsetBuilder.prototype.UpdateBotPt = function (pt) + ClipperLib.Clipper.ClosedPathsFromPolyTree = function (polytree) { - if (pt.Y > this.botPt.Y || (pt.Y == this.botPt.Y && pt.X < this.botPt.X)) + var result = new ClipperLib.Paths(); + //result.set_Capacity(polytree.Total()); + ClipperLib.Clipper.AddPolyNodeToPaths(polytree, ClipperLib.Clipper.NodeType.ntClosed, result); + return result; + }; + Inherit(ClipperLib.Clipper, ClipperLib.ClipperBase); + ClipperLib.Clipper.NodeType = { + ntAny: 0, + ntOpen: 1, + ntClosed: 2 + }; + ClipperLib.ClipperOffset = function (miterLimit, arcTolerance) + { + if (typeof (miterLimit) == "undefined") miterLimit = 2; + if (typeof (arcTolerance) == "undefined") arcTolerance = ClipperLib.ClipperOffset.def_arc_tolerance; + this.m_destPolys = new ClipperLib.Paths(); + this.m_srcPoly = new ClipperLib.Path(); + this.m_destPoly = new ClipperLib.Path(); + this.m_normals = new Array(); + this.m_delta = 0; + this.m_sinA = 0; + this.m_sin = 0; + this.m_cos = 0; + this.m_miterLim = 0; + this.m_StepsPerRad = 0; + this.m_lowest = new ClipperLib.IntPoint(); + this.m_polyNodes = new ClipperLib.PolyNode(); + this.MiterLimit = miterLimit; + this.ArcTolerance = arcTolerance; + this.m_lowest.X = -1; + }; + ClipperLib.ClipperOffset.two_pi = 6.28318530717959; + ClipperLib.ClipperOffset.def_arc_tolerance = 0.25; + ClipperLib.ClipperOffset.prototype.Clear = function () + { + ClipperLib.Clear(this.m_polyNodes.Childs()); + this.m_lowest.X = -1; + }; + ClipperLib.ClipperOffset.Round = ClipperLib.Clipper.Round; + ClipperLib.ClipperOffset.prototype.AddPath = function (path, joinType, endType) + { + var highI = path.length - 1; + if (highI < 0) + return; + var newNode = new ClipperLib.PolyNode(); + newNode.m_jointype = joinType; + newNode.m_endtype = endType; + //strip duplicate points from path and also get index to the lowest point ... + if (endType == ClipperLib.EndType.etClosedLine || endType == ClipperLib.EndType.etClosedPolygon) + while (highI > 0 && ClipperLib.IntPoint.op_Equality(path[0], path[highI])) + highI--; + //newNode.m_polygon.set_Capacity(highI + 1); + newNode.m_polygon.push(path[0]); + var j = 0, + k = 0; + for (var i = 1; i <= highI; i++) + if (ClipperLib.IntPoint.op_Inequality(newNode.m_polygon[j], path[i])) + { + j++; + newNode.m_polygon.push(path[i]); + if (path[i].Y > newNode.m_polygon[k].Y || (path[i].Y == newNode.m_polygon[k].Y && path[i].X < newNode.m_polygon[k].X)) + k = j; + } + if ((endType == ClipperLib.EndType.etClosedPolygon && j < 2) || (endType != ClipperLib.EndType.etClosedPolygon && j < 0)) + return; + this.m_polyNodes.AddChild(newNode); + //if this path's lowest pt is lower than all the others then update m_lowest + if (endType != ClipperLib.EndType.etClosedPolygon) + return; + if (this.m_lowest.X < 0) + this.m_lowest = new ClipperLib.IntPoint(0, k); + else { - this.botPt = pt; - return true; + var ip = this.m_polyNodes.Childs()[this.m_lowest.X].m_polygon[this.m_lowest.Y]; + if (newNode.m_polygon[k].Y > ip.Y || (newNode.m_polygon[k].Y == ip.Y && newNode.m_polygon[k].X < ip.X)) + this.m_lowest = new ClipperLib.IntPoint(this.m_polyNodes.ChildCount() - 1, k); } - else return false; }; - ClipperLib.Clipper.PolyOffsetBuilder.prototype.AddPoint = function (pt) + ClipperLib.ClipperOffset.prototype.AddPaths = function (paths, joinType, endType) { - this.currentPoly.push(pt); + for (var i = 0, ilen = paths.length; i < ilen; i++) + this.AddPath(paths[i], joinType, endType); }; - ClipperLib.Clipper.PolyOffsetBuilder.prototype.DoSquare = function (mul) + ClipperLib.ClipperOffset.prototype.FixOrientations = function () { - var pt1 = new ClipperLib.IntPoint(ClipperLib.Cast_Int64(ClipperLib.Clipper.Round(this.pts[this.m_i][this.m_j].X + this.normals[this.m_k].X * this.delta)), - ClipperLib.Cast_Int64(ClipperLib.Clipper.Round(this.pts[this.m_i][this.m_j].Y + this.normals[this.m_k].Y * this.delta))); - var pt2 = new ClipperLib.IntPoint(ClipperLib.Cast_Int64(ClipperLib.Clipper.Round(this.pts[this.m_i][this.m_j].X + this.normals[this.m_j].X * this.delta)), - ClipperLib.Cast_Int64(ClipperLib.Clipper.Round(this.pts[this.m_i][this.m_j].Y + this.normals[this.m_j].Y * this.delta))); - if ((this.normals[this.m_k].X * this.normals[this.m_j].Y - this.normals[this.m_j].X * this.normals[this.m_k].Y) * this.delta >= 0) + //fixup orientations of all closed paths if the orientation of the + //closed path with the lowermost vertex is wrong ... + if (this.m_lowest.X >= 0 && !ClipperLib.Clipper.Orientation(this.m_polyNodes.Childs()[this.m_lowest.X].m_polygon)) { - var a1 = Math.atan2(this.normals[this.m_k].Y, this.normals[this.m_k].X); - var a2 = Math.atan2(-this.normals[this.m_j].Y, -this.normals[this.m_j].X); - a1 = Math.abs(a2 - a1); - if (a1 > ClipperLib.PI) a1 = ClipperLib.PI2 - a1; - var dx = Math.tan((ClipperLib.PI - a1) / 4) * Math.abs(this.delta * mul); - pt1 = new ClipperLib.IntPoint(ClipperLib.Cast_Int64((pt1.X - this.normals[this.m_k].Y * dx)), - ClipperLib.Cast_Int64((pt1.Y + this.normals[this.m_k].X * dx))); - this.AddPoint(pt1); - pt2 = new ClipperLib.IntPoint(ClipperLib.Cast_Int64((pt2.X + this.normals[this.m_j].Y * dx)), - ClipperLib.Cast_Int64((pt2.Y - this.normals[this.m_j].X * dx))); - this.AddPoint(pt2); + for (var i = 0; i < this.m_polyNodes.ChildCount(); i++) + { + var node = this.m_polyNodes.Childs()[i]; + if (node.m_endtype == ClipperLib.EndType.etClosedPolygon || (node.m_endtype == ClipperLib.EndType.etClosedLine && ClipperLib.Clipper.Orientation(node.m_polygon))) + node.m_polygon.reverse(); + } } else { - this.AddPoint(pt1); - this.AddPoint(this.pts[this.m_i][this.m_j]); - this.AddPoint(pt2); + for (var i = 0; i < this.m_polyNodes.ChildCount(); i++) + { + var node = this.m_polyNodes.Childs()[i]; + if (node.m_endtype == ClipperLib.EndType.etClosedLine && !ClipperLib.Clipper.Orientation(node.m_polygon)) + node.m_polygon.reverse(); + } } }; - ClipperLib.Clipper.PolyOffsetBuilder.prototype.DoMiter = function () + ClipperLib.ClipperOffset.GetUnitNormal = function (pt1, pt2) + { + var dx = (pt2.X - pt1.X); + var dy = (pt2.Y - pt1.Y); + if ((dx == 0) && (dy == 0)) + return new ClipperLib.DoublePoint(0, 0); + var f = 1 / Math.sqrt(dx * dx + dy * dy); + dx *= f; + dy *= f; + return new ClipperLib.DoublePoint(dy, -dx); + }; + ClipperLib.ClipperOffset.prototype.DoOffset = function (delta) { - if ((this.normals[this.m_k].X * this.normals[this.m_j].Y - this.normals[this.m_j].X * this.normals[this.m_k].Y) * this.delta >= 0) + this.m_destPolys = new Array(); + this.m_delta = delta; + //if Zero offset, just copy any CLOSED polygons to m_p and return ... + if (ClipperLib.ClipperBase.near_zero(delta)) { - var q = this.delta / this.m_R; - this.AddPoint(new ClipperLib.IntPoint( - ClipperLib.Cast_Int64( - ClipperLib.Clipper.Round(this.pts[this.m_i][this.m_j].X + (this.normals[this.m_k].X + this.normals[this.m_j].X) * q)), - ClipperLib.Cast_Int64( - ClipperLib.Clipper.Round(this.pts[this.m_i][this.m_j].Y + (this.normals[this.m_k].Y + this.normals[this.m_j].Y) * q)))); + //this.m_destPolys.set_Capacity(this.m_polyNodes.ChildCount); + for (var i = 0; i < this.m_polyNodes.ChildCount(); i++) + { + var node = this.m_polyNodes.Childs()[i]; + if (node.m_endtype == ClipperLib.EndType.etClosedPolygon) + this.m_destPolys.push(node.m_polygon); + } + return; } + //see offset_triginometry3.svg in the documentation folder ... + if (this.MiterLimit > 2) + this.m_miterLim = 2 / (this.MiterLimit * this.MiterLimit); else - { - var pt1 = new ClipperLib.IntPoint(ClipperLib.Cast_Int64(ClipperLib.Clipper.Round(this.pts[this.m_i][this.m_j].X + this.normals[this.m_k].X * this.delta)), - ClipperLib.Cast_Int64(ClipperLib.Clipper.Round(this.pts[this.m_i][this.m_j].Y + this.normals[this.m_k].Y * this.delta))); - var pt2 = new ClipperLib.IntPoint(ClipperLib.Cast_Int64(ClipperLib.Clipper.Round(this.pts[this.m_i][this.m_j].X + this.normals[this.m_j].X * this.delta)), - ClipperLib.Cast_Int64(ClipperLib.Clipper.Round(this.pts[this.m_i][this.m_j].Y + this.normals[this.m_j].Y * this.delta))); - this.AddPoint(pt1); - this.AddPoint(this.pts[this.m_i][this.m_j]); - this.AddPoint(pt2); + this.m_miterLim = 0.5; + var y; + if (this.ArcTolerance <= 0) + y = ClipperLib.ClipperOffset.def_arc_tolerance; + else if (this.ArcTolerance > Math.abs(delta) * ClipperLib.ClipperOffset.def_arc_tolerance) + y = Math.abs(delta) * ClipperLib.ClipperOffset.def_arc_tolerance; + else + y = this.ArcTolerance; + //see offset_triginometry2.svg in the documentation folder ... + var steps = 3.14159265358979 / Math.acos(1 - y / Math.abs(delta)); + this.m_sin = Math.sin(ClipperLib.ClipperOffset.two_pi / steps); + this.m_cos = Math.cos(ClipperLib.ClipperOffset.two_pi / steps); + this.m_StepsPerRad = steps / ClipperLib.ClipperOffset.two_pi; + if (delta < 0) + this.m_sin = -this.m_sin; + //this.m_destPolys.set_Capacity(this.m_polyNodes.ChildCount * 2); + for (var i = 0; i < this.m_polyNodes.ChildCount(); i++) + { + var node = this.m_polyNodes.Childs()[i]; + this.m_srcPoly = node.m_polygon; + var len = this.m_srcPoly.length; + if (len == 0 || (delta <= 0 && (len < 3 || node.m_endtype != ClipperLib.EndType.etClosedPolygon))) + continue; + this.m_destPoly = new Array(); + if (len == 1) + { + if (node.m_jointype == ClipperLib.JoinType.jtRound) + { + var X = 1, + Y = 0; + for (var j = 1; j <= steps; j++) + { + this.m_destPoly.push(new ClipperLib.IntPoint(ClipperLib.ClipperOffset.Round(this.m_srcPoly[0].X + X * delta), ClipperLib.ClipperOffset.Round(this.m_srcPoly[0].Y + Y * delta))); + var X2 = X; + X = X * this.m_cos - this.m_sin * Y; + Y = X2 * this.m_sin + Y * this.m_cos; + } + } + else + { + var X = -1, + Y = -1; + for (var j = 0; j < 4; ++j) + { + this.m_destPoly.push(new ClipperLib.IntPoint(ClipperLib.ClipperOffset.Round(this.m_srcPoly[0].X + X * delta), ClipperLib.ClipperOffset.Round(this.m_srcPoly[0].Y + Y * delta))); + if (X < 0) + X = 1; + else if (Y < 0) + Y = 1; + else + X = -1; + } + } + this.m_destPolys.push(this.m_destPoly); + continue; + } + //build m_normals ... + this.m_normals.length = 0; + //this.m_normals.set_Capacity(len); + for (var j = 0; j < len - 1; j++) + this.m_normals.push(ClipperLib.ClipperOffset.GetUnitNormal(this.m_srcPoly[j], this.m_srcPoly[j + 1])); + if (node.m_endtype == ClipperLib.EndType.etClosedLine || node.m_endtype == ClipperLib.EndType.etClosedPolygon) + this.m_normals.push(ClipperLib.ClipperOffset.GetUnitNormal(this.m_srcPoly[len - 1], this.m_srcPoly[0])); + else + this.m_normals.push(new ClipperLib.DoublePoint(this.m_normals[len - 2])); + if (node.m_endtype == ClipperLib.EndType.etClosedPolygon) + { + var k = len - 1; + for (var j = 0; j < len; j++) + k = this.OffsetPoint(j, k, node.m_jointype); + this.m_destPolys.push(this.m_destPoly); + } + else if (node.m_endtype == ClipperLib.EndType.etClosedLine) + { + var k = len - 1; + for (var j = 0; j < len; j++) + k = this.OffsetPoint(j, k, node.m_jointype); + this.m_destPolys.push(this.m_destPoly); + this.m_destPoly = new Array(); + //re-build m_normals ... + var n = this.m_normals[len - 1]; + for (var j = len - 1; j > 0; j--) + this.m_normals[j] = new ClipperLib.DoublePoint(-this.m_normals[j - 1].X, -this.m_normals[j - 1].Y); + this.m_normals[0] = new ClipperLib.DoublePoint(-n.X, -n.Y); + k = 0; + for (var j = len - 1; j >= 0; j--) + k = this.OffsetPoint(j, k, node.m_jointype); + this.m_destPolys.push(this.m_destPoly); + } + else + { + var k = 0; + for (var j = 1; j < len - 1; ++j) + k = this.OffsetPoint(j, k, node.m_jointype); + var pt1; + if (node.m_endtype == ClipperLib.EndType.etOpenButt) + { + var j = len - 1; + pt1 = new ClipperLib.IntPoint(ClipperLib.ClipperOffset.Round(this.m_srcPoly[j].X + this.m_normals[j].X * delta), ClipperLib.ClipperOffset.Round(this.m_srcPoly[j].Y + this.m_normals[j].Y * delta)); + this.m_destPoly.push(pt1); + pt1 = new ClipperLib.IntPoint(ClipperLib.ClipperOffset.Round(this.m_srcPoly[j].X - this.m_normals[j].X * delta), ClipperLib.ClipperOffset.Round(this.m_srcPoly[j].Y - this.m_normals[j].Y * delta)); + this.m_destPoly.push(pt1); + } + else + { + var j = len - 1; + k = len - 2; + this.m_sinA = 0; + this.m_normals[j] = new ClipperLib.DoublePoint(-this.m_normals[j].X, -this.m_normals[j].Y); + if (node.m_endtype == ClipperLib.EndType.etOpenSquare) + this.DoSquare(j, k); + else + this.DoRound(j, k); + } + //re-build m_normals ... + for (var j = len - 1; j > 0; j--) + this.m_normals[j] = new ClipperLib.DoublePoint(-this.m_normals[j - 1].X, -this.m_normals[j - 1].Y); + this.m_normals[0] = new ClipperLib.DoublePoint(-this.m_normals[1].X, -this.m_normals[1].Y); + k = len - 1; + for (var j = k - 1; j > 0; --j) + k = this.OffsetPoint(j, k, node.m_jointype); + if (node.m_endtype == ClipperLib.EndType.etOpenButt) + { + pt1 = new ClipperLib.IntPoint(ClipperLib.ClipperOffset.Round(this.m_srcPoly[0].X - this.m_normals[0].X * delta), ClipperLib.ClipperOffset.Round(this.m_srcPoly[0].Y - this.m_normals[0].Y * delta)); + this.m_destPoly.push(pt1); + pt1 = new ClipperLib.IntPoint(ClipperLib.ClipperOffset.Round(this.m_srcPoly[0].X + this.m_normals[0].X * delta), ClipperLib.ClipperOffset.Round(this.m_srcPoly[0].Y + this.m_normals[0].Y * delta)); + this.m_destPoly.push(pt1); + } + else + { + k = 1; + this.m_sinA = 0; + if (node.m_endtype == ClipperLib.EndType.etOpenSquare) + this.DoSquare(0, 1); + else + this.DoRound(0, 1); + } + this.m_destPolys.push(this.m_destPoly); + } } }; - ClipperLib.Clipper.PolyOffsetBuilder.prototype.DoRound = function () + ClipperLib.ClipperOffset.prototype.Execute = function () { - var pt1 = new ClipperLib.IntPoint(ClipperLib.Clipper.Round(this.pts[this.m_i][this.m_j].X + this.normals[this.m_k].X * this.delta), - ClipperLib.Clipper.Round(this.pts[this.m_i][this.m_j].Y + this.normals[this.m_k].Y * this.delta)); - var pt2 = new ClipperLib.IntPoint(ClipperLib.Clipper.Round(this.pts[this.m_i][this.m_j].X + this.normals[this.m_j].X * this.delta), - ClipperLib.Clipper.Round(this.pts[this.m_i][this.m_j].Y + this.normals[this.m_j].Y * this.delta)); - this.AddPoint(pt1); - //round off reflex angles (ie > 180 deg) unless almost flat (ie < 10deg). - //cross product normals < 0 . angle > 180 deg. - //dot product normals == 1 . no angle - if ((this.normals[this.m_k].X * this.normals[this.m_j].Y - this.normals[this.m_j].X * this.normals[this.m_k].Y) * this.delta >= 0) + var a = arguments, + ispolytree = a[0] instanceof ClipperLib.PolyTree; + if (!ispolytree) // function (solution, delta) { - if ((this.normals[this.m_j].X * this.normals[this.m_k].X + this.normals[this.m_j].Y * this.normals[this.m_k].Y) < 0.985) + var solution = a[0], + delta = a[1]; + ClipperLib.Clear(solution); + this.FixOrientations(); + this.DoOffset(delta); + //now clean up 'corners' ... + var clpr = new ClipperLib.Clipper(0); + clpr.AddPaths(this.m_destPolys, ClipperLib.PolyType.ptSubject, true); + if (delta > 0) { - var a1 = Math.atan2(this.normals[this.m_k].Y, this.normals[this.m_k].X); - var a2 = Math.atan2(this.normals[this.m_j].Y, this.normals[this.m_j].X); - if (this.delta > 0 && a2 < a1) a2 += ClipperLib.PI2; - else if (this.delta < 0 && a2 > a1) a2 -= ClipperLib.PI2; - var arc = ClipperLib.Clipper.BuildArc(this.pts[this.m_i][this.m_j], a1, a2, this.delta); - for (var m = 0; m < arc.length; m++) - this.AddPoint(arc[m]); + clpr.Execute(ClipperLib.ClipType.ctUnion, solution, ClipperLib.PolyFillType.pftPositive, ClipperLib.PolyFillType.pftPositive); + } + else + { + var r = ClipperLib.Clipper.GetBounds(this.m_destPolys); + var outer = new ClipperLib.Path(); + outer.push(new ClipperLib.IntPoint(r.left - 10, r.bottom + 10)); + outer.push(new ClipperLib.IntPoint(r.right + 10, r.bottom + 10)); + outer.push(new ClipperLib.IntPoint(r.right + 10, r.top - 10)); + outer.push(new ClipperLib.IntPoint(r.left - 10, r.top - 10)); + clpr.AddPath(outer, ClipperLib.PolyType.ptSubject, true); + clpr.ReverseSolution = true; + clpr.Execute(ClipperLib.ClipType.ctUnion, solution, ClipperLib.PolyFillType.pftNegative, ClipperLib.PolyFillType.pftNegative); + if (solution.length > 0) + solution.splice(0, 1); + } + //console.log(JSON.stringify(solution)); + } + else // function (polytree, delta) + { + var solution = a[0], + delta = a[1]; + solution.Clear(); + this.FixOrientations(); + this.DoOffset(delta); + //now clean up 'corners' ... + var clpr = new ClipperLib.Clipper(0); + clpr.AddPaths(this.m_destPolys, ClipperLib.PolyType.ptSubject, true); + if (delta > 0) + { + clpr.Execute(ClipperLib.ClipType.ctUnion, solution, ClipperLib.PolyFillType.pftPositive, ClipperLib.PolyFillType.pftPositive); + } + else + { + var r = ClipperLib.Clipper.GetBounds(this.m_destPolys); + var outer = new ClipperLib.Path(); + outer.push(new ClipperLib.IntPoint(r.left - 10, r.bottom + 10)); + outer.push(new ClipperLib.IntPoint(r.right + 10, r.bottom + 10)); + outer.push(new ClipperLib.IntPoint(r.right + 10, r.top - 10)); + outer.push(new ClipperLib.IntPoint(r.left - 10, r.top - 10)); + clpr.AddPath(outer, ClipperLib.PolyType.ptSubject, true); + clpr.ReverseSolution = true; + clpr.Execute(ClipperLib.ClipType.ctUnion, solution, ClipperLib.PolyFillType.pftNegative, ClipperLib.PolyFillType.pftNegative); + //remove the outer PolyNode rectangle ... + if (solution.ChildCount() == 1 && solution.Childs()[0].ChildCount() > 0) + { + var outerNode = solution.Childs()[0]; + //solution.Childs.set_Capacity(outerNode.ChildCount); + solution.Childs()[0] = outerNode.Childs()[0]; + for (var i = 1; i < outerNode.ChildCount(); i++) + solution.AddChild(outerNode.Childs()[i]); + } + else + solution.Clear(); } } - else this.AddPoint(this.pts[this.m_i][this.m_j]); - this.AddPoint(pt2); }; - ClipperLib.Error = function(message) + ClipperLib.ClipperOffset.prototype.OffsetPoint = function (j, k, jointype) + { + this.m_sinA = (this.m_normals[k].X * this.m_normals[j].Y - this.m_normals[j].X * this.m_normals[k].Y); + if (this.m_sinA < 0.00005 && this.m_sinA > -0.00005) + return k; + else if (this.m_sinA > 1) + this.m_sinA = 1.0; + else if (this.m_sinA < -1) + this.m_sinA = -1.0; + if (this.m_sinA * this.m_delta < 0) + { + this.m_destPoly.push(new ClipperLib.IntPoint(ClipperLib.ClipperOffset.Round(this.m_srcPoly[j].X + this.m_normals[k].X * this.m_delta), + ClipperLib.ClipperOffset.Round(this.m_srcPoly[j].Y + this.m_normals[k].Y * this.m_delta))); + this.m_destPoly.push(new ClipperLib.IntPoint(this.m_srcPoly[j])); + this.m_destPoly.push(new ClipperLib.IntPoint(ClipperLib.ClipperOffset.Round(this.m_srcPoly[j].X + this.m_normals[j].X * this.m_delta), + ClipperLib.ClipperOffset.Round(this.m_srcPoly[j].Y + this.m_normals[j].Y * this.m_delta))); + } + else + switch (jointype) + { + case ClipperLib.JoinType.jtMiter: + { + var r = 1 + (this.m_normals[j].X * this.m_normals[k].X + this.m_normals[j].Y * this.m_normals[k].Y); + if (r >= this.m_miterLim) + this.DoMiter(j, k, r); + else + this.DoSquare(j, k); + break; + } + case ClipperLib.JoinType.jtSquare: + this.DoSquare(j, k); + break; + case ClipperLib.JoinType.jtRound: + this.DoRound(j, k); + break; + } + k = j; + return k; + }; + ClipperLib.ClipperOffset.prototype.DoSquare = function (j, k) + { + var dx = Math.tan(Math.atan2(this.m_sinA, + this.m_normals[k].X * this.m_normals[j].X + this.m_normals[k].Y * this.m_normals[j].Y) / 4); + this.m_destPoly.push(new ClipperLib.IntPoint( + ClipperLib.ClipperOffset.Round(this.m_srcPoly[j].X + this.m_delta * (this.m_normals[k].X - this.m_normals[k].Y * dx)), + ClipperLib.ClipperOffset.Round(this.m_srcPoly[j].Y + this.m_delta * (this.m_normals[k].Y + this.m_normals[k].X * dx)))); + this.m_destPoly.push(new ClipperLib.IntPoint( + ClipperLib.ClipperOffset.Round(this.m_srcPoly[j].X + this.m_delta * (this.m_normals[j].X + this.m_normals[j].Y * dx)), + ClipperLib.ClipperOffset.Round(this.m_srcPoly[j].Y + this.m_delta * (this.m_normals[j].Y - this.m_normals[j].X * dx)))); + }; + ClipperLib.ClipperOffset.prototype.DoMiter = function (j, k, r) + { + var q = this.m_delta / r; + this.m_destPoly.push(new ClipperLib.IntPoint( + ClipperLib.ClipperOffset.Round(this.m_srcPoly[j].X + (this.m_normals[k].X + this.m_normals[j].X) * q), + ClipperLib.ClipperOffset.Round(this.m_srcPoly[j].Y + (this.m_normals[k].Y + this.m_normals[j].Y) * q))); + }; + ClipperLib.ClipperOffset.prototype.DoRound = function (j, k) + { + var a = Math.atan2(this.m_sinA, + this.m_normals[k].X * this.m_normals[j].X + this.m_normals[k].Y * this.m_normals[j].Y); + var steps = ClipperLib.Cast_Int32(ClipperLib.ClipperOffset.Round(this.m_StepsPerRad * Math.abs(a))); + var X = this.m_normals[k].X, + Y = this.m_normals[k].Y, + X2; + for (var i = 0; i < steps; ++i) + { + this.m_destPoly.push(new ClipperLib.IntPoint( + ClipperLib.ClipperOffset.Round(this.m_srcPoly[j].X + X * this.m_delta), + ClipperLib.ClipperOffset.Round(this.m_srcPoly[j].Y + Y * this.m_delta))); + X2 = X; + X = X * this.m_cos - this.m_sin * Y; + Y = X2 * this.m_sin + Y * this.m_cos; + } + this.m_destPoly.push(new ClipperLib.IntPoint( + ClipperLib.ClipperOffset.Round(this.m_srcPoly[j].X + this.m_normals[j].X * this.m_delta), + ClipperLib.ClipperOffset.Round(this.m_srcPoly[j].Y + this.m_normals[j].Y * this.m_delta))); + }; + ClipperLib.Error = function (message) { - try { + try + { throw new Error(message); } - catch(err) { + catch (err) + { alert(err.message); } }; - // Make deep copy of Polygons or Polygon - // so that also IntPoint objects are cloned and not only referenced - // This should be the fastest way - ClipperLib.Clone = function (polygon) + // --------------------------------- + // JS extension by Timo 2013 + ClipperLib.JS = {}; + ClipperLib.JS.AreaOfPolygon = function (poly, scale) { - if (!(polygon instanceof Array)) return []; - if (polygon.length == 0) return []; - else if (polygon.length == 1 && polygon[0].length == 0) return [[]]; - var isPolygons = polygon[0] instanceof Array; - if (!isPolygons) polygon = [polygon]; - var len = polygon.length, plen, i, j, result; - var results = []; - for(i = 0; i < len; i++) + if (!scale) scale = 1; + return ClipperLib.Clipper.Area(poly) / (scale * scale); + }; + ClipperLib.JS.AreaOfPolygons = function (poly, scale) + { + if (!scale) scale = 1; + var area = 0; + for (var i = 0; i < poly.length; i++) { - plen = polygon[i].length; - result = []; - for(j = 0; j < plen; j++) - { - result.push({X: polygon[i][j].X, Y: polygon[i][j].Y}); - } - results.push(result); + area += ClipperLib.Clipper.Area(poly[i]); } - if (!isPolygons) results = results[0]; - return results; + return area / (scale * scale); + }; + ClipperLib.JS.BoundsOfPath = function (path, scale) + { + return ClipperLib.JS.BoundsOfPaths([path], scale); + }; + ClipperLib.JS.BoundsOfPaths = function (paths, scale) + { + if (!scale) scale = 1; + var bounds = ClipperLib.Clipper.GetBounds(paths); + bounds.left /= scale; + bounds.bottom /= scale; + bounds.right /= scale; + bounds.top /= scale; + return bounds; }; - // Clean() joins vertices that are too near each other // and causes distortion to offsetted polygons without cleaning - ClipperLib.Clean = function (polygon, delta) + ClipperLib.JS.Clean = function (polygon, delta) { if (!(polygon instanceof Array)) return []; var isPolygons = polygon[0] instanceof Array; - var polygon = ClipperLib.Clone(polygon); - if (typeof delta != "number" || delta === null) + var polygon = ClipperLib.JS.Clone(polygon); + if (typeof delta != "number" || delta === null) { ClipperLib.Error("Delta is not a number in Clean()."); return polygon; } - if (polygon.length == 0 || (polygon.length == 1 && polygon[0].length == 0) || delta < 0) return polygon; + if (polygon.length === 0 || (polygon.length == 1 && polygon[0].length === 0) || delta < 0) return polygon; if (!isPolygons) polygon = [polygon]; var k_length = polygon.length; var len, poly, result, d, p, j, i; var results = []; - for(var k = 0; k < k_length; k++) + for (var k = 0; k < k_length; k++) { poly = polygon[k]; len = poly.length; - if (len == 0) continue; - else if (len < 3) { + if (len === 0) continue; + else if (len < 3) + { result = poly; results.push(result); continue; @@ -4890,7 +6667,7 @@ for (i = 1; i < len; i++) { if ((poly[i].X - p.X) * (poly[i].X - p.X) + - (poly[i].Y - p.Y) * (poly[i].Y - p.Y) <= d) + (poly[i].Y - p.Y) * (poly[i].Y - p.Y) <= d) continue; result[j] = poly[i]; p = poly[i]; @@ -4898,101 +6675,311 @@ } p = poly[j - 1]; if ((poly[0].X - p.X) * (poly[0].X - p.X) + - (poly[0].Y - p.Y) * (poly[0].Y - p.Y) <= d) + (poly[0].Y - p.Y) * (poly[0].Y - p.Y) <= d) j--; if (j < len) - result.splice(j, len - j); + result.splice(j, len - j); if (result.length) results.push(result); } if (!isPolygons && results.length) results = results[0]; - else if (!isPolygons && results.length == 0) results = []; - else if (isPolygons && results.length ==0) results = [[]]; + else if (!isPolygons && results.length === 0) results = []; + else if (isPolygons && results.length === 0) results = [ + [] + ]; return results; } - + // Make deep copy of Polygons or Polygon + // so that also IntPoint objects are cloned and not only referenced + // This should be the fastest way + ClipperLib.JS.Clone = function (polygon) + { + if (!(polygon instanceof Array)) return []; + if (polygon.length === 0) return []; + else if (polygon.length == 1 && polygon[0].length === 0) return [[]]; + var isPolygons = polygon[0] instanceof Array; + if (!isPolygons) polygon = [polygon]; + var len = polygon.length, + plen, i, j, result; + var results = new Array(len); + for (i = 0; i < len; i++) + { + plen = polygon[i].length; + result = new Array(plen); + for (j = 0; j < plen; j++) + { + result[j] = { + X: polygon[i][j].X, + Y: polygon[i][j].Y + }; + } + results[i] = result; + } + if (!isPolygons) results = results[0]; + return results; + }; // Removes points that doesn't affect much to the visual appearance. - // If middle point is at or under certain distance (tolerance) of the line between + // If middle point is at or under certain distance (tolerance) of the line segment between // start and end point, the middle point is removed. - ClipperLib.Lighten = function (polygon, tolerance) + ClipperLib.JS.Lighten = function (polygon, tolerance) { if (!(polygon instanceof Array)) return []; - if (typeof tolerance != "number" || tolerance === null) { ClipperLib.Error("Tolerance is not a number in Lighten().") - return ClipperLib.Clone(polygon); + return ClipperLib.JS.Clone(polygon); } - if (polygon.length === 0 || (polygon.length==1 && polygon[0].length === 0) || tolerance < 0) + if (polygon.length === 0 || (polygon.length == 1 && polygon[0].length === 0) || tolerance < 0) { - return ClipperLib.Clone(polygon); + return ClipperLib.JS.Clone(polygon); } - - if (!(polygon[0] instanceof Array)) polygon = [polygon]; - var i, j, poly, k, poly2, plen, A, B, P, d, rem, addlast; - var bxax, byay, nL; - var len = polygon.length; - var results = []; - for(i = 0; i < len; i++) - { - poly = polygon[i]; - for (k = 0; k < 1000000; k++) // could be forever loop, but wiser to restrict max repeat count - { - poly2 = []; - plen = poly.length; - // the first have to added to the end, if first and last are not the same - // this way we ensure that also the actual last point can be removed if needed - if (poly[plen-1].X != poly[0].X || poly[plen-1].Y != poly[0].Y) - { - addlast = 1; - poly.push({X:poly[0].X, Y:poly[0].Y}); - plen = poly.length; - } - else addlast = 0; - rem = []; // Indexes of removed points - for(j = 0; j < plen - 2; j++) - { - A = poly[j]; // Start point of line segment - P = poly[j+1]; // Middle point. This is the one to be removed. - B = poly[j+2]; // End point of line segment - bxax = B.X - A.X; - byay = B.Y - A.Y; - d = 0; - if (bxax !== 0 || byay !== 0) // To avoid Nan, when A==P && P==B. And to avoid peaks (A==B && A!=P), which have lenght, but not area. - { - nL = Math.sqrt(bxax * bxax + byay * byay); - // d is the perpendicular distance from P to (infinite) line AB. - d = Math.abs((P.X - A.X) * byay - (P.Y - A.Y) * bxax) / nL; - } - if (d <= tolerance) - { - rem[j+1] = 1; - j++; // when removed, transfer the pointer to the next one - } - } - // add all unremoved points to poly2 - poly2.push({X:poly[0].X, Y:poly[0].Y}); - for(j = 1; j < plen-1; j++) - if (!rem[j]) poly2.push({X:poly[j].X,Y:poly[j].Y}); - poly2.push({X:poly[plen-1].X,Y:poly[plen-1].Y}); - // if the first point was added to the end, remove it - if (addlast) poly.pop(); - // break, if there was not anymore removed points - if (!rem.length) break; - // else continue looping using poly2, to check if there are points to remove - else poly = poly2; - } - plen = poly2.length; - // remove duplicate from end, if needed - if (poly2[plen-1].X == poly2[0].X && poly2[plen-1].Y == poly2[0].Y) - { - poly2.pop(); - } - if (poly2.length > 2) // to avoid two-point-polygons - results.push(poly2); + if (!(polygon[0] instanceof Array)) polygon = [polygon]; + var i, j, poly, k, poly2, plen, A, B, P, d, rem, addlast; + var bxax, byay, l, ax, ay; + var len = polygon.length; + var toleranceSq = tolerance * tolerance; + var results = []; + for (i = 0; i < len; i++) + { + poly = polygon[i]; + plen = poly.length; + if (plen == 0) continue; + for (k = 0; k < 1000000; k++) // could be forever loop, but wiser to restrict max repeat count + { + poly2 = []; + plen = poly.length; + // the first have to added to the end, if first and last are not the same + // this way we ensure that also the actual last point can be removed if needed + if (poly[plen - 1].X != poly[0].X || poly[plen - 1].Y != poly[0].Y) + { + addlast = 1; + poly.push( + { + X: poly[0].X, + Y: poly[0].Y + }); + plen = poly.length; + } + else addlast = 0; + rem = []; // Indexes of removed points + for (j = 0; j < plen - 2; j++) + { + A = poly[j]; // Start point of line segment + P = poly[j + 1]; // Middle point. This is the one to be removed. + B = poly[j + 2]; // End point of line segment + ax = A.X; + ay = A.Y; + bxax = B.X - ax; + byay = B.Y - ay; + if (bxax !== 0 || byay !== 0) // To avoid Nan, when A==P && P==B. And to avoid peaks (A==B && A!=P), which have lenght, but not area. + { + l = ((P.X - ax) * bxax + (P.Y - ay) * byay) / (bxax * bxax + byay * byay); + if (l > 1) + { + ax = B.X; + ay = B.Y; + } + else if (l > 0) + { + ax += bxax * l; + ay += byay * l; + } + } + bxax = P.X - ax; + byay = P.Y - ay; + d = bxax * bxax + byay * byay; + if (d <= toleranceSq) + { + rem[j + 1] = 1; + j++; // when removed, transfer the pointer to the next one + } + } + // add all unremoved points to poly2 + poly2.push( + { + X: poly[0].X, + Y: poly[0].Y + }); + for (j = 1; j < plen - 1; j++) + if (!rem[j]) poly2.push( + { + X: poly[j].X, + Y: poly[j].Y + }); + poly2.push( + { + X: poly[plen - 1].X, + Y: poly[plen - 1].Y + }); + // if the first point was added to the end, remove it + if (addlast) poly.pop(); + // break, if there was not anymore removed points + if (!rem.length) break; + // else continue looping using poly2, to check if there are points to remove + else poly = poly2; + } + plen = poly2.length; + // remove duplicate from end, if needed + if (poly2[plen - 1].X == poly2[0].X && poly2[plen - 1].Y == poly2[0].Y) + { + poly2.pop(); + } + if (poly2.length > 2) // to avoid two-point-polygons + results.push(poly2); } if (!polygon[0] instanceof Array) results = results[0]; - if (typeof (results) == "undefined") results = [[]]; + if (typeof (results) == "undefined") results = [ + [] + ]; return results; } - window.ClipperLib = ClipperLib; -})(window); \ No newline at end of file + ClipperLib.JS.PerimeterOfPath = function (path, closed, scale) + { + if (typeof (path) == "undefined") return 0; + var sqrt = Math.sqrt; + var perimeter = 0.0; + var p1, p2, p1x = 0.0, + p1y = 0.0, + p2x = 0.0, + p2y = 0.0; + var j = path.length; + if (j < 2) return 0; + if (closed) + { + path[j] = path[0]; + j++; + } + while (--j) + { + p1 = path[j]; + p1x = p1.X; + p1y = p1.Y; + p2 = path[j - 1]; + p2x = p2.X; + p2y = p2.Y; + perimeter += sqrt((p1x - p2x) * (p1x - p2x) + (p1y - p2y) * (p1y - p2y)); + } + if (closed) path.pop(); + return perimeter / scale; + }; + ClipperLib.JS.PerimeterOfPaths = function (paths, closed, scale) + { + if (!scale) scale = 1; + var perimeter = 0; + for (var i = 0; i < paths.length; i++) + { + perimeter += ClipperLib.JS.PerimeterOfPath(paths[i], closed, scale); + } + return perimeter; + }; + ClipperLib.JS.ScaleDownPath = function (path, scale) + { + var i, p; + if (!scale) scale = 1; + i = path.length; + while (i--) + { + p = path[i]; + p.X = p.X / scale; + p.Y = p.Y / scale; + } + }; + ClipperLib.JS.ScaleDownPaths = function (paths, scale) + { + var i, j, p, round = Math.round; + if (!scale) scale = 1; + i = paths.length; + while (i--) + { + j = paths[i].length; + while (j--) + { + p = paths[i][j]; + p.X = p.X / scale; + p.Y = p.Y / scale; + } + } + }; + ClipperLib.JS.ScaleUpPath = function (path, scale) + { + var i, p, round = Math.round; + if (!scale) scale = 1; + i = path.length; + while (i--) + { + p = path[i]; + p.X = round(p.X * scale); + p.Y = round(p.Y * scale); + } + }; + ClipperLib.JS.ScaleUpPaths = function (paths, scale) + { + var i, j, p, round = Math.round; + if (!scale) scale = 1; + i = paths.length; + while (i--) + { + j = paths[i].length; + while (j--) + { + p = paths[i][j]; + p.X = round(p.X * scale); + p.Y = round(p.Y * scale); + } + } + }; + ClipperLib.ExPolygons = function () + { + return []; + } + ClipperLib.ExPolygon = function () + { + this.outer = null; + this.holes = null; + }; + ClipperLib.JS.AddOuterPolyNodeToExPolygons = function (polynode, expolygons) + { + var ep = new ClipperLib.ExPolygon(); + ep.outer = polynode.Contour(); + var childs = polynode.Childs(); + var ilen = childs.length; + ep.holes = new Array(ilen); + var node, n, i, j, childs2, jlen; + for (i = 0; i < ilen; i++) + { + node = childs[i]; + ep.holes[i] = node.Contour(); + //Add outer polygons contained by (nested within) holes ... + for (j = 0, childs2 = node.Childs(), jlen = childs2.length; j < jlen; j++) + { + n = childs2[j]; + ClipperLib.JS.AddOuterPolyNodeToExPolygons(n, expolygons); + } + } + expolygons.push(ep); + }; + ClipperLib.JS.ExPolygonsToPaths = function (expolygons) + { + var a, i, alen, ilen; + var paths = new ClipperLib.Paths(); + for (a = 0, alen = expolygons.length; a < alen; a++) + { + paths.push(expolygons[a].outer); + for (i = 0, ilen = expolygons[a].holes.length; i < ilen; i++) + { + paths.push(expolygons[a].holes[i]); + } + } + return paths; + } + ClipperLib.JS.PolyTreeToExPolygons = function (polytree) + { + var expolygons = new ClipperLib.ExPolygons(); + var node, i, childs, ilen; + for (i = 0, childs = polytree.Childs(), ilen = childs.length; i < ilen; i++) + { + node = childs[i]; + ClipperLib.JS.AddOuterPolyNodeToExPolygons(node, expolygons); + } + return expolygons; + }; +})(); \ No newline at end of file