From 5050bc0e400764582cf26d5544c6e48c3c633156 Mon Sep 17 00:00:00 2001 From: casperlamboo Date: Tue, 28 Apr 2015 14:11:41 +0200 Subject: [PATCH] improvement, starting point for collision fix --- slice_test.html | 31 +- src/box.js | 14 +- src/clipper.js | 6985 +++++++++++++++++++++++++++++++++++++++++++++++ src/printer.js | 4 + src/slicer.js | 33 +- src/utils.js | 7 + 6 files changed, 7048 insertions(+), 26 deletions(-) create mode 100755 src/clipper.js diff --git a/slice_test.html b/slice_test.html index 61a7f9f..c1ae082 100644 --- a/slice_test.html +++ b/slice_test.html @@ -28,13 +28,17 @@ doodleBox.onload = function () { var gcode = slicer.getGcode(doodleBox.printer); - var print = $(document.createElement("button")).text("Print").on("click", function () { + var print = $(document.createElement("a")).html("Print").on("click", function () { doodleBox.print(gcode); }); - var stop = $(document.createElement("button")).text("Stop").on("click", function () { + var stop = $(document.createElement("a")).html("Stop").on("click", function () { doodleBox.stop(); }); - $("body").append(print, stop); + var download = $(document.createElement("a")).html("Download").attr({ + download: "test.gcode", + href: "data:text/plain," + encodeURIComponent(gcode.join("\n")) + }); + $("body").append(print, stop, download); }; var scene = new THREE.Scene(); @@ -46,19 +50,18 @@ var camera = new THREE.PerspectiveCamera(75, renderer.domElement.width/renderer. applyMouseControls(renderer, camera, 1000); -var material = new THREE.MeshBasicMaterial({color: 0x000000, wireframe: true}); -var geometry = new THREE.TorusGeometry(20, 10, 20, 20); +var material = new THREE.MeshLambertMaterial({color: 0x000000, wireframe: true}); +var geometry = new THREE.TorusGeometry(20, 10, 4, 6); //var geometry = new THREE.BoxGeometry(10, 10, 10, 1, 1, 1); -//var geometry = new THREE.SphereGeometry(5, 32, 32); +//var geometry = new THREE.SphereGeometry(40, 10, 10); var mesh = new THREE.Mesh(geometry, material); scene.add(mesh); -var slicer = new D3D.Slicer(geometry); +var slicer = new D3D.Slicer().setGeometry(geometry); +//var slices = slicer.slice(200, 0.2); +var slices = slicer.slice(1, 1); -var slices = slicer.slice(200, 0.2); -//document.write(JSON.stringify(slices)); - -/*var canvas = document.getElementById("canvas"); +var canvas = document.getElementById("canvas"); var context = canvas.getContext("2d"); for (var layer = 0; layer < slices.length; layer ++) { @@ -68,16 +71,16 @@ for (var layer = 0; layer < slices.length; layer ++) { var shape = slice[i]; context.beginPath(); - for (var j = 0; j < shape.length; j ++) { + for (var j = 0; j <= shape.length; j ++) { var point = shape[(j % shape.length)]; context.lineTo((point.x-100) * 3 + 100, (point.y-100) * 3 + 100); } - context.closePath(); + //context.closePath(); context.strokeStyle = "rgb(" + Math.round(Math.random()*255) + ", " + Math.round(Math.random()*255) + ", " + Math.round(Math.random()*255) + ")"; context.stroke(); } -}*/ +} (function animate () { requestAnimationFrame(animate); diff --git a/src/box.js b/src/box.js index 7e8899d..ca49c41 100644 --- a/src/box.js +++ b/src/box.js @@ -29,6 +29,12 @@ D3D.Box = function (localIp) { getAPI(self.api + "config/all", function (data) { //self.config = data; + for (var i in data) { + if (i.indexOf("doodle3d") === 0) { + self[i] = data[i]; + } + } + self.printer = new D3D.Printer(data); self.update(); @@ -59,15 +65,15 @@ D3D.Box.prototype.updateState = function () { //que api calls so they don't overload the d3d box getAPI(this.api + "printer/state", function (data) { - self.state = data.state; + self.printer.state = data.state; if (data.state !== "connecting" && data.state !== "disconnected") { getAPI(self.api + "printer/temperature", function (data) { - self.temperature = data; + self.printer.temperature = data; getAPI(self.api + "printer/progress", function (data) { - self.progress = data; + self.printer.progress = data; //finish updating state self.update(); @@ -140,6 +146,6 @@ D3D.Box.prototype.stop = function () { "gcode": finishMove.join("\n") //"gcode": {} }, function (data) { - console.log(data); + console.log("Printer stop command sent"); }); }; \ No newline at end of file diff --git a/src/clipper.js b/src/clipper.js new file mode 100755 index 0000000..22aa676 --- /dev/null +++ b/src/clipper.js @@ -0,0 +1,6985 @@ +// 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 : 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. * + * * + *******************************************************************************/ +/******************************************************************************* + * * + * 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 () +{ + "use strict"; + //use_int32: When enabled 32bit ints are used instead of 64bit ints. This + //improve performance but coordinate values are limited to the range +/- 46340 + var use_int32 = false; + //use_xyz: adds a Z member to IntPoint. Adds a minor cost to performance. + var use_xyz = false; + //UseLines: Enables line clipping. Adds a very minor cost to performance. + var use_lines = true; + //use_deprecated: Enables support for the obsolete OffsetPaths() function + //which has been replace with the ClipperOffset class. + var use_deprecated = false; + + var ClipperLib = {}; + var isNode = false; + if (typeof module !== 'undefined' && module.exports) + { + module.exports = ClipperLib; + isNode = true; + } + else + { + if (typeof (document) !== "undefined") window.ClipperLib = ClipperLib; + else self['ClipperLib'] = ClipperLib; + } + var navigator_appName; + if (!isNode) + { + var nav = navigator.userAgent.toString().toLowerCase(); + navigator_appName = navigator.appName; + } + else + { + var nav = "chrome"; // Node.js uses Chrome's V8 engine + navigator_appName = "Netscape"; // Firefox, Chrome and Safari returns "Netscape", so Node.js should also + } + // Browser test to speedup performance critical functions + var browser = {}; + if (nav.indexOf("chrome") != -1 && nav.indexOf("chromium") == -1) browser.chrome = 1; + else browser.chrome = 0; + if (nav.indexOf("chromium") != -1) browser.chromium = 1; + else browser.chromium = 0; + if (nav.indexOf("safari") != -1 && nav.indexOf("chrome") == -1 && nav.indexOf("chromium") == -1) browser.safari = 1; + else browser.safari = 0; + if (nav.indexOf("firefox") != -1) browser.firefox = 1; + else browser.firefox = 0; + if (nav.indexOf("firefox/17") != -1) browser.firefox17 = 1; + else browser.firefox17 = 0; + if (nav.indexOf("firefox/15") != -1) browser.firefox15 = 1; + else browser.firefox15 = 0; + if (nav.indexOf("firefox/3") != -1) browser.firefox3 = 1; + else browser.firefox3 = 0; + if (nav.indexOf("opera") != -1) browser.opera = 1; + else browser.opera = 0; + if (nav.indexOf("msie 10") != -1) browser.msie10 = 1; + else browser.msie10 = 0; + if (nav.indexOf("msie 9") != -1) browser.msie9 = 1; + else browser.msie9 = 0; + if (nav.indexOf("msie 8") != -1) browser.msie8 = 1; + else browser.msie8 = 0; + if (nav.indexOf("msie 7") != -1) browser.msie7 = 1; + else browser.msie7 = 0; + if (nav.indexOf("msie ") != -1) browser.msie = 1; + else browser.msie = 0; + ClipperLib.biginteger_used = null; + // Copyright (c) 2005 Tom Wu + // All Rights Reserved. + // See "LICENSE" for details. + // Basic JavaScript BN library - subset useful for RSA encryption. + // Bits per digit + var dbits; + // JavaScript engine analysis + var canary = 0xdeadbeefcafe; + var j_lm = ((canary & 0xffffff) == 0xefcafe); + // (public) Constructor + function BigInteger(a, b, c) + { + // This test variable can be removed, + // but at least for performance tests it is useful piece of knowledge + // This is the only ClipperLib related variable in BigInteger library + ClipperLib.biginteger_used = 1; + if (a != null) + if ("number" == typeof a && "undefined" == typeof (b)) this.fromInt(a); // faster conversion + else if ("number" == typeof a) this.fromNumber(a, b, c); + else if (b == null && "string" != typeof a) this.fromString(a, 256); + else this.fromString(a, b); + } + // return new, unset BigInteger + function nbi() + { + return new BigInteger(null); + } + // am: Compute w_j += (x*this_i), propagate carries, + // c is initial carry, returns final carry. + // c < 3*dvalue, x < 2*dvalue, this_i < dvalue + // We need to select the fastest one that works in this environment. + // am1: use a single mult and divide to get the high bits, + // max digit bits should be 26 because + // max internal value = 2*dvalue^2-2*dvalue (< 2^53) + function am1(i, x, w, j, c, n) + { + while (--n >= 0) + { + var v = x * this[i++] + w[j] + c; + c = Math.floor(v / 0x4000000); + w[j++] = v & 0x3ffffff; + } + return c; + } + // am2 avoids a big mult-and-extract completely. + // Max digit bits should be <= 30 because we do bitwise ops + // on values up to 2*hdvalue^2-hdvalue-1 (< 2^31) + function am2(i, x, w, j, c, n) + { + var xl = x & 0x7fff, + xh = x >> 15; + while (--n >= 0) + { + var l = this[i] & 0x7fff; + var h = this[i++] >> 15; + var m = xh * l + h * xl; + l = xl * l + ((m & 0x7fff) << 15) + w[j] + (c & 0x3fffffff); + c = (l >>> 30) + (m >>> 15) + xh * h + (c >>> 30); + w[j++] = l & 0x3fffffff; + } + return c; + } + // Alternately, set max digit bits to 28 since some + // browsers slow down when dealing with 32-bit numbers. + function am3(i, x, w, j, c, n) + { + var xl = x & 0x3fff, + xh = x >> 14; + while (--n >= 0) + { + var l = this[i] & 0x3fff; + var h = this[i++] >> 14; + var m = xh * l + h * xl; + l = xl * l + ((m & 0x3fff) << 14) + w[j] + c; + c = (l >> 28) + (m >> 14) + xh * h; + w[j++] = l & 0xfffffff; + } + return c; + } + if (j_lm && (navigator_appName == "Microsoft Internet Explorer")) + { + BigInteger.prototype.am = am2; + dbits = 30; + } + else if (j_lm && (navigator_appName != "Netscape")) + { + BigInteger.prototype.am = am1; + dbits = 26; + } + else + { // Mozilla/Netscape seems to prefer am3 + BigInteger.prototype.am = am3; + dbits = 28; + } + BigInteger.prototype.DB = dbits; + BigInteger.prototype.DM = ((1 << dbits) - 1); + BigInteger.prototype.DV = (1 << dbits); + var BI_FP = 52; + BigInteger.prototype.FV = Math.pow(2, BI_FP); + BigInteger.prototype.F1 = BI_FP - dbits; + BigInteger.prototype.F2 = 2 * dbits - BI_FP; + // Digit conversions + var BI_RM = "0123456789abcdefghijklmnopqrstuvwxyz"; + var BI_RC = new Array(); + var rr, vv; + rr = "0".charCodeAt(0); + for (vv = 0; vv <= 9; ++vv) BI_RC[rr++] = vv; + rr = "a".charCodeAt(0); + for (vv = 10; vv < 36; ++vv) BI_RC[rr++] = vv; + rr = "A".charCodeAt(0); + for (vv = 10; vv < 36; ++vv) BI_RC[rr++] = vv; + + function int2char(n) + { + return BI_RM.charAt(n); + } + + function intAt(s, i) + { + var c = BI_RC[s.charCodeAt(i)]; + return (c == null) ? -1 : c; + } + // (protected) copy this to r + function bnpCopyTo(r) + { + for (var i = this.t - 1; i >= 0; --i) r[i] = this[i]; + r.t = this.t; + r.s = this.s; + } + // (protected) set from integer value x, -DV <= x < DV + function bnpFromInt(x) + { + this.t = 1; + this.s = (x < 0) ? -1 : 0; + if (x > 0) this[0] = x; + else if (x < -1) this[0] = x + this.DV; + else this.t = 0; + } + // return bigint initialized to value + function nbv(i) + { + var r = nbi(); + r.fromInt(i); + return r; + } + // (protected) set from string and radix + function bnpFromString(s, b) + { + var k; + if (b == 16) k = 4; + else if (b == 8) k = 3; + else if (b == 256) k = 8; // byte array + else if (b == 2) k = 1; + else if (b == 32) k = 5; + else if (b == 4) k = 2; + else + { + this.fromRadix(s, b); + return; + } + this.t = 0; + this.s = 0; + var i = s.length, + mi = false, + sh = 0; + while (--i >= 0) + { + var x = (k == 8) ? s[i] & 0xff : intAt(s, i); + if (x < 0) + { + if (s.charAt(i) == "-") mi = true; + continue; + } + mi = false; + 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; + sh += k; + if (sh >= this.DB) sh -= this.DB; + } + if (k == 8 && (s[0] & 0x80) != 0) + { + this.s = -1; + if (sh > 0) this[this.t - 1] |= ((1 << (this.DB - sh)) - 1) << sh; + } + this.clamp(); + if (mi) BigInteger.ZERO.subTo(this, this); + } + // (protected) clamp off excess high words + function bnpClamp() + { + var c = this.s & this.DM; + while (this.t > 0 && this[this.t - 1] == c)--this.t; + } + // (public) return string representation in given radix + function bnToString(b) + { + if (this.s < 0) return "-" + this.negate().toString(b); + var k; + if (b == 16) k = 4; + else if (b == 8) k = 3; + else if (b == 2) k = 1; + else if (b == 32) k = 5; + else if (b == 4) k = 2; + else return this.toRadix(b); + var km = (1 << k) - 1, + d, m = false, + r = "", + i = this.t; + var p = this.DB - (i * this.DB) % k; + if (i-- > 0) + { + if (p < this.DB && (d = this[i] >> p) > 0) + { + m = true; + r = int2char(d); + } + while (i >= 0) + { + if (p < k) + { + d = (this[i] & ((1 << p) - 1)) << (k - p); + d |= this[--i] >> (p += this.DB - k); + } + else + { + d = (this[i] >> (p -= k)) & km; + if (p <= 0) + { + p += this.DB; + --i; + } + } + if (d > 0) m = true; + if (m) r += int2char(d); + } + } + return m ? r : "0"; + } + // (public) -this + function bnNegate() + { + var r = nbi(); + BigInteger.ZERO.subTo(this, r); + return r; + } + // (public) |this| + function bnAbs() + { + return (this.s < 0) ? this.negate() : this; + } + // (public) return + if this > a, - if this < a, 0 if equal + function bnCompareTo(a) + { + var r = this.s - a.s; + if (r != 0) return r; + 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; + return 0; + } + // returns bit length of the integer x + function nbits(x) + { + var r = 1, + t; + if ((t = x >>> 16) != 0) + { + x = t; + r += 16; + } + if ((t = x >> 8) != 0) + { + x = t; + r += 8; + } + if ((t = x >> 4) != 0) + { + x = t; + r += 4; + } + if ((t = x >> 2) != 0) + { + x = t; + r += 2; + } + if ((t = x >> 1) != 0) + { + x = t; + r += 1; + } + return r; + } + // (public) return the number of bits in "this" + function bnBitLength() + { + if (this.t <= 0) return 0; + return this.DB * (this.t - 1) + nbits(this[this.t - 1] ^ (this.s & this.DM)); + } + // (protected) r = this << n*DB + function bnpDLShiftTo(n, r) + { + var i; + for (i = this.t - 1; i >= 0; --i) r[i + n] = this[i]; + for (i = n - 1; i >= 0; --i) r[i] = 0; + r.t = this.t + n; + r.s = this.s; + } + // (protected) r = this >> n*DB + function bnpDRShiftTo(n, r) + { + for (var i = n; i < this.t; ++i) r[i - n] = this[i]; + r.t = Math.max(this.t - n, 0); + r.s = this.s; + } + // (protected) r = this << n + function bnpLShiftTo(n, r) + { + var bs = n % this.DB; + var cbs = this.DB - bs; + var bm = (1 << cbs) - 1; + var ds = Math.floor(n / this.DB), + c = (this.s << bs) & this.DM, + i; + for (i = this.t - 1; i >= 0; --i) + { + r[i + ds + 1] = (this[i] >> cbs) | c; + c = (this[i] & bm) << bs; + } + for (i = ds - 1; i >= 0; --i) r[i] = 0; + r[ds] = c; + r.t = this.t + ds + 1; + r.s = this.s; + r.clamp(); + } + // (protected) r = this >> n + function bnpRShiftTo(n, r) + { + r.s = this.s; + var ds = Math.floor(n / this.DB); + if (ds >= this.t) + { + r.t = 0; + return; + } + var bs = n % this.DB; + var cbs = this.DB - bs; + var bm = (1 << bs) - 1; + r[0] = this[ds] >> bs; + for (var i = ds + 1; i < this.t; ++i) + { + r[i - ds - 1] |= (this[i] & bm) << cbs; + r[i - ds] = this[i] >> bs; + } + if (bs > 0) r[this.t - ds - 1] |= (this.s & bm) << cbs; + r.t = this.t - ds; + r.clamp(); + } + // (protected) r = this - a + function bnpSubTo(a, r) + { + var i = 0, + c = 0, + m = Math.min(a.t, this.t); + while (i < m) + { + c += this[i] - a[i]; + r[i++] = c & this.DM; + c >>= this.DB; + } + if (a.t < this.t) + { + c -= a.s; + while (i < this.t) + { + c += this[i]; + r[i++] = c & this.DM; + c >>= this.DB; + } + c += this.s; + } + else + { + c += this.s; + while (i < a.t) + { + c -= a[i]; + r[i++] = c & this.DM; + c >>= this.DB; + } + c -= a.s; + } + r.s = (c < 0) ? -1 : 0; + if (c < -1) r[i++] = this.DV + c; + else if (c > 0) r[i++] = c; + r.t = i; + r.clamp(); + } + // (protected) r = this * a, r != this,a (HAC 14.12) + // "this" should be the larger one if appropriate. + function bnpMultiplyTo(a, r) + { + var x = this.abs(), + y = a.abs(); + var i = x.t; + r.t = i + y.t; + while (--i >= 0) r[i] = 0; + 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) BigInteger.ZERO.subTo(r, r); + } + // (protected) r = this^2, r != this (HAC 14.16) + function bnpSquareTo(r) + { + var x = this.abs(); + var i = r.t = 2 * x.t; + while (--i >= 0) r[i] = 0; + for (i = 0; i < x.t - 1; ++i) + { + var c = x.am(i, x[i], r, 2 * i, 0, 1); + if ((r[i + x.t] += x.am(i + 1, 2 * x[i], r, 2 * i + 1, c, x.t - i - 1)) >= x.DV) + { + r[i + x.t] -= x.DV; + r[i + x.t + 1] = 1; + } + } + if (r.t > 0) r[r.t - 1] += x.am(i, x[i], r, 2 * i, 0, 1); + r.s = 0; + r.clamp(); + } + // (protected) divide this by m, quotient and remainder to q, r (HAC 14.20) + // r != q, this != m. q or r may be null. + function bnpDivRemTo(m, q, r) + { + var pm = m.abs(); + if (pm.t <= 0) return; + var pt = this.abs(); + if (pt.t < pm.t) + { + if (q != null) q.fromInt(0); + if (r != null) this.copyTo(r); + return; + } + if (r == null) r = nbi(); + var y = nbi(), + ts = this.s, + ms = m.s; + var nsh = this.DB - nbits(pm[pm.t - 1]); // normalize modulus + if (nsh > 0) + { + pm.lShiftTo(nsh, y); + pt.lShiftTo(nsh, r); + } + else + { + pm.copyTo(y); + pt.copyTo(r); + } + var ys = y.t; + var y0 = y[ys - 1]; + if (y0 == 0) return; + var yt = y0 * (1 << this.F1) + ((ys > 1) ? y[ys - 2] >> this.F2 : 0); + var d1 = this.FV / yt, + d2 = (1 << this.F1) / yt, + e = 1 << this.F2; + var i = r.t, + j = i - ys, + t = (q == null) ? nbi() : q; + y.dlShiftTo(j, t); + if (r.compareTo(t) >= 0) + { + r[r.t++] = 1; + r.subTo(t, r); + } + 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) + { + // Estimate quotient digit + var qd = (r[--i] == y0) ? this.DM : Math.floor(r[i] * d1 + (r[i - 1] + e) * d2); + if ((r[i] += y.am(0, qd, r, j, 0, ys)) < qd) + { // Try it out + y.dlShiftTo(j, t); + r.subTo(t, r); + while (r[i] < --qd) r.subTo(t, r); + } + } + if (q != null) + { + r.drShiftTo(ys, 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) 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(BigInteger.ZERO) > 0) a.subTo(r, r); + return r; + } + // Modular reduction using "classic" algorithm + function Classic(m) + { + this.m = m; + } + + function cConvert(x) + { + if (x.s < 0 || x.compareTo(this.m) >= 0) return x.mod(this.m); + else return x; + } + + function cRevert(x) + { + return x; + } + + function cReduce(x) + { + x.divRemTo(this.m, null, x); + } + + function cMulTo(x, y, r) + { + x.multiplyTo(y, r); + this.reduce(r); + } + + function cSqrTo(x, r) + { + x.squareTo(r); + this.reduce(r); + } + Classic.prototype.convert = cConvert; + Classic.prototype.revert = cRevert; + Classic.prototype.reduce = cReduce; + Classic.prototype.mulTo = cMulTo; + Classic.prototype.sqrTo = cSqrTo; + // (protected) return "-1/this % 2^DB"; useful for Mont. reduction + // justification: + // xy == 1 (mod m) + // xy = 1+km + // xy(2-xy) = (1+km)(1-km) + // x[y(2-xy)] = 1-k^2m^2 + // x[y(2-xy)] == 1 (mod m^2) + // if y is 1/x mod m, then y(2-xy) is 1/x mod m^2 + // should reduce x and y(2-xy) by m^2 at each step to keep size bounded. + // JS multiply "overflows" differently from C/C++, so care is needed here. + function bnpInvDigit() + { + if (this.t < 1) return 0; + var x = this[0]; + if ((x & 1) == 0) return 0; + var y = x & 3; // y == 1/x mod 2^2 + y = (y * (2 - (x & 0xf) * y)) & 0xf; // y == 1/x mod 2^4 + y = (y * (2 - (x & 0xff) * y)) & 0xff; // y == 1/x mod 2^8 + y = (y * (2 - (((x & 0xffff) * y) & 0xffff))) & 0xffff; // y == 1/x mod 2^16 + // last step - calculate inverse mod DV directly; + // assumes 16 < DB <= 32 and assumes ability to handle 48-bit ints + y = (y * (2 - x * y % this.DV)) % this.DV; // y == 1/x mod 2^dbits + // we really want the negative inverse, and -DV < y < DV + return (y > 0) ? this.DV - y : -y; + } + // Montgomery reduction + function Montgomery(m) + { + this.m = m; + this.mp = m.invDigit(); + this.mpl = this.mp & 0x7fff; + this.mph = this.mp >> 15; + this.um = (1 << (m.DB - 15)) - 1; + this.mt2 = 2 * m.t; + } + // xR mod m + function montConvert(x) + { + var r = nbi(); + x.abs().dlShiftTo(this.m.t, r); + r.divRemTo(this.m, null, r); + if (x.s < 0 && r.compareTo(BigInteger.ZERO) > 0) this.m.subTo(r, r); + return r; + } + // x/R mod m + function montRevert(x) + { + var r = nbi(); + x.copyTo(r); + this.reduce(r); + return r; + } + // x = x/R mod m (HAC 14.32) + function montReduce(x) + { + while (x.t <= this.mt2) // pad x so am has enough room later + x[x.t++] = 0; + for (var i = 0; i < this.m.t; ++i) + { + // faster way of calculating u0 = x[i]*mp mod DV + var j = x[i] & 0x7fff; + var u0 = (j * this.mpl + (((j * this.mph + (x[i] >> 15) * this.mpl) & this.um) << 15)) & x.DM; + // use am to combine the multiply-shift-add into one call + j = i + this.m.t; + x[j] += this.m.am(0, u0, x, i, 0, this.m.t); + // propagate carry + while (x[j] >= x.DV) + { + x[j] -= x.DV; + x[++j]++; + } + } + x.clamp(); + x.drShiftTo(this.m.t, x); + if (x.compareTo(this.m) >= 0) x.subTo(this.m, x); + } + // r = "x^2/R mod m"; x != r + function montSqrTo(x, r) + { + x.squareTo(r); + this.reduce(r); + } + // r = "xy/R mod m"; x,y != r + function montMulTo(x, y, r) + { + x.multiplyTo(y, r); + this.reduce(r); + } + Montgomery.prototype.convert = montConvert; + Montgomery.prototype.revert = montRevert; + Montgomery.prototype.reduce = montReduce; + Montgomery.prototype.mulTo = montMulTo; + Montgomery.prototype.sqrTo = montSqrTo; + // (protected) true iff this is even + function bnpIsEven() + { + return ((this.t > 0) ? (this[0] & 1) : this.s) == 0; + } + // (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 BigInteger.ONE; + var r = nbi(), + r2 = nbi(), + g = z.convert(this), + i = nbits(e) - 1; + g.copyTo(r); + while (--i >= 0) + { + z.sqrTo(r, r2); + if ((e & (1 << i)) > 0) z.mulTo(r2, g, r); + else + { + var t = r; + r = r2; + r2 = t; + } + } + return z.revert(r); + } + // (public) this^e % m, 0 <= e < 2^32 + function bnModPowInt(e, m) + { + var z; + if (e < 256 || m.isEven()) z = new Classic(m); + else z = new Montgomery(m); + return this.exp(e, z); + } + // protected + 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 + 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" + 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 BigInteger("0", 10) returns "proper" zero + // Version 1.2: square() API, isProbablePrime fix + // (public) + function bnClone() + { + var r = nbi(); + this.copyTo(r); + return r; + } + // (public) return value as integer + function bnIntValue() + { + if (this.s < 0) + { + if (this.t == 1) return this[0] - this.DV; + else if (this.t == 0) return -1; + } + else if (this.t == 1) return this[0]; + else if (this.t == 0) return 0; + // assumes 16 < DB < 32 + return ((this[1] & ((1 << (32 - this.DB)) - 1)) << this.DB) | this[0]; + } + // (public) return value as byte + function bnByteValue() + { + return (this.t == 0) ? this.s : (this[0] << 24) >> 24; + } + // (public) return value as short (assumes DB>=16) + function bnShortValue() + { + return (this.t == 0) ? this.s : (this[0] << 16) >> 16; + } + // (protected) return x s.t. r^x < DV + function bnpChunkSize(r) + { + return Math.floor(Math.LN2 * this.DB / Math.log(r)); + } + // (public) 0 if this == 0, 1 if this > 0 + function bnSigNum() + { + if (this.s < 0) return -1; + else if (this.t <= 0 || (this.t == 1 && this[0] <= 0)) return 0; + else return 1; + } + // (protected) convert to radix string + function bnpToRadix(b) + { + if (b == null) b = 10; + if (this.signum() == 0 || b < 2 || b > 36) return "0"; + var cs = this.chunkSize(b); + var a = Math.pow(b, cs); + var d = nbv(a), + y = nbi(), + z = nbi(), + r = ""; + this.divRemTo(d, y, z); + while (y.signum() > 0) + { + r = (a + z.intValue()).toString(b).substr(1) + r; + y.divRemTo(d, y, z); + } + return z.intValue().toString(b) + r; + } + // (protected) convert from radix string + function bnpFromRadix(s, b) + { + this.fromInt(0); + if (b == null) b = 10; + var cs = this.chunkSize(b); + var d = Math.pow(b, cs), + mi = false, + j = 0, + w = 0; + for (var i = 0; i < s.length; ++i) + { + var x = intAt(s, i); + if (x < 0) + { + if (s.charAt(i) == "-" && this.signum() == 0) mi = true; + continue; + } + w = b * w + x; + if (++j >= cs) + { + this.dMultiply(d); + this.dAddOffset(w, 0); + j = 0; + w = 0; + } + } + if (j > 0) + { + this.dMultiply(Math.pow(b, j)); + this.dAddOffset(w, 0); + } + if (mi) BigInteger.ZERO.subTo(this, this); + } + // (protected) alternate constructor + function bnpFromNumber(a, b, c) + { + if ("number" == typeof b) + { + // 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(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(BigInteger.ONE.shiftLeft(a - 1), this); + } + } + } + else + { + // new BigInteger(int,RNG) + var x = new Array(), + t = a & 7; + x.length = (a >> 3) + 1; + b.nextBytes(x); + if (t > 0) x[0] &= ((1 << t) - 1); + else x[0] = 0; + this.fromString(x, 256); + } + } + // (public) convert to bigendian byte array + function bnToByteArray() + { + var i = this.t, + 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)); + while (i >= 0) + { + if (p < 8) + { + d = (this[i] & ((1 << p) - 1)) << (8 - p); + d |= this[--i] >> (p += this.DB - 8); + } + else + { + d = (this[i] >> (p -= 8)) & 0xff; + if (p <= 0) + { + p += this.DB; + --i; + } + } + if ((d & 0x80) != 0) d |= -256; + if (k == 0 && (this.s & 0x80) != (d & 0x80))++k; + if (k > 0 || d != this.s) r[k++] = d; + } + } + return r; + } + + function bnEquals(a) + { + return (this.compareTo(a) == 0); + } + + function bnMin(a) + { + return (this.compareTo(a) < 0) ? this : a; + } + + function bnMax(a) + { + return (this.compareTo(a) > 0) ? this : a; + } + // (protected) r = this op a (bitwise) + function bnpBitwiseTo(a, op, r) + { + var i, f, m = Math.min(a.t, this.t); + for (i = 0; i < m; ++i) r[i] = op(this[i], a[i]); + if (a.t < this.t) + { + f = a.s & this.DM; + for (i = m; i < this.t; ++i) r[i] = op(this[i], f); + r.t = this.t; + } + else + { + f = this.s & this.DM; + for (i = m; i < a.t; ++i) r[i] = op(f, a[i]); + r.t = a.t; + } + r.s = op(this.s, a.s); + r.clamp(); + } + // (public) this & a + function op_and(x, y) + { + return x & y; + } + + function bnAnd(a) + { + var r = nbi(); + this.bitwiseTo(a, op_and, r); + return r; + } + // (public) this | a + function op_or(x, y) + { + return x | y; + } + + function bnOr(a) + { + var r = nbi(); + this.bitwiseTo(a, op_or, r); + return r; + } + // (public) this ^ a + function op_xor(x, y) + { + return x ^ y; + } + + function bnXor(a) + { + var r = nbi(); + this.bitwiseTo(a, op_xor, r); + return r; + } + // (public) this & ~a + function op_andnot(x, y) + { + return x & ~y; + } + + function bnAndNot(a) + { + var r = nbi(); + this.bitwiseTo(a, op_andnot, r); + return r; + } + // (public) ~this + function bnNot() + { + var r = nbi(); + for (var i = 0; i < this.t; ++i) r[i] = this.DM & ~this[i]; + r.t = this.t; + r.s = ~this.s; + return r; + } + // (public) this << n + function bnShiftLeft(n) + { + var r = nbi(); + if (n < 0) this.rShiftTo(-n, r); + else this.lShiftTo(n, r); + return r; + } + // (public) this >> n + function bnShiftRight(n) + { + var r = nbi(); + if (n < 0) this.lShiftTo(-n, r); + else this.rShiftTo(n, r); + return r; + } + // return index of lowest 1-bit in x, x < 2^31 + function lbit(x) + { + if (x == 0) return -1; + var r = 0; + if ((x & 0xffff) == 0) + { + x >>= 16; + r += 16; + } + if ((x & 0xff) == 0) + { + x >>= 8; + r += 8; + } + if ((x & 0xf) == 0) + { + x >>= 4; + r += 4; + } + if ((x & 3) == 0) + { + x >>= 2; + r += 2; + } + if ((x & 1) == 0)++r; + return r; + } + // (public) returns index of lowest 1-bit (or -1 if none) + function bnGetLowestSetBit() + { + for (var i = 0; i < this.t; ++i) + if (this[i] != 0) return i * this.DB + lbit(this[i]); + if (this.s < 0) return this.t * this.DB; + return -1; + } + // return number of 1 bits in x + function cbit(x) + { + var r = 0; + while (x != 0) + { + x &= x - 1; + ++r; + } + return r; + } + // (public) return number of set bits + function bnBitCount() + { + var r = 0, + x = this.s & this.DM; + for (var i = 0; i < this.t; ++i) r += cbit(this[i] ^ x); + return r; + } + // (public) true iff nth bit is set + function bnTestBit(n) + { + var j = Math.floor(n / this.DB); + if (j >= this.t) return (this.s != 0); + return ((this[j] & (1 << (n % this.DB))) != 0); + } + // (protected) this op (1<>= this.DB; + } + if (a.t < this.t) + { + c += a.s; + while (i < this.t) + { + c += this[i]; + r[i++] = c & this.DM; + c >>= this.DB; + } + c += this.s; + } + else + { + c += this.s; + while (i < a.t) + { + c += a[i]; + r[i++] = c & this.DM; + c >>= this.DB; + } + c += a.s; + } + r.s = (c < 0) ? -1 : 0; + if (c > 0) r[i++] = c; + else if (c < -1) r[i++] = this.DV + c; + r.t = i; + r.clamp(); + } + // (public) this + a + function bnAdd(a) + { + var r = nbi(); + this.addTo(a, r); + return r; + } + // (public) this - a + function bnSubtract(a) + { + var r = nbi(); + this.subTo(a, r); + return r; + } + // (public) this * a + function bnMultiply(a) + { + var r = nbi(); + this.multiplyTo(a, r); + return r; + } + // (public) this^2 + function bnSquare() + { + var r = nbi(); + this.squareTo(r); + return r; + } + // (public) this / a + function bnDivide(a) + { + var r = nbi(); + this.divRemTo(a, r, null); + return r; + } + // (public) this % a + function bnRemainder(a) + { + var r = nbi(); + this.divRemTo(a, null, r); + return r; + } + // (public) [this/a,this%a] + function bnDivideAndRemainder(a) + { + var q = nbi(), + r = nbi(); + this.divRemTo(a, q, r); + return new Array(q, r); + } + // (protected) this *= n, this >= 0, 1 < n < DV + function bnpDMultiply(n) + { + this[this.t] = this.am(0, n - 1, this, 0, 0, this.t); + ++this.t; + this.clamp(); + } + // (protected) this += n << w words, this >= 0 + function bnpDAddOffset(n, w) + { + if (n == 0) return; + while (this.t <= w) this[this.t++] = 0; + this[w] += n; + while (this[w] >= this.DV) + { + this[w] -= this.DV; + if (++w >= this.t) this[this.t++] = 0; + ++this[w]; + } + } + // A "null" reducer + function NullExp() + {} + + function nNop(x) + { + return x; + } + + function nMulTo(x, y, r) + { + x.multiplyTo(y, r); + } + + function nSqrTo(x, r) + { + x.squareTo(r); + } + NullExp.prototype.convert = nNop; + NullExp.prototype.revert = nNop; + NullExp.prototype.mulTo = nMulTo; + NullExp.prototype.sqrTo = nSqrTo; + // (public) this^e + function bnPow(e) + { + return this.exp(e, new NullExp()); + } + // (protected) r = lower n words of "this * a", a.t <= n + // "this" should be the larger one if appropriate. + function bnpMultiplyLowerTo(a, n, r) + { + var i = Math.min(this.t + a.t, n); + r.s = 0; // assumes a,this >= 0 + r.t = i; + while (i > 0) r[--i] = 0; + var j; + for (j = r.t - this.t; i < j; ++i) r[i + this.t] = this.am(0, a[i], r, i, 0, this.t); + for (j = Math.min(a.t, n); i < j; ++i) this.am(0, a[i], r, i, 0, n - i); + r.clamp(); + } + // (protected) r = "this * a" without lower n words, n > 0 + // "this" should be the larger one if appropriate. + function bnpMultiplyUpperTo(a, n, r) + { + --n; + var i = r.t = this.t + a.t - n; + r.s = 0; // assumes a,this >= 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.clamp(); + r.drShiftTo(1, r); + } + // Barrett modular reduction + function Barrett(m) + { + // setup Barrett + this.r2 = nbi(); + this.q3 = nbi(); + BigInteger.ONE.dlShiftTo(2 * m.t, this.r2); + this.mu = this.r2.divide(m); + this.m = m; + } + + function barrettConvert(x) + { + if (x.s < 0 || x.t > 2 * this.m.t) return x.mod(this.m); + else if (x.compareTo(this.m) < 0) return x; + else + { + var r = nbi(); + x.copyTo(r); + this.reduce(r); + return r; + } + } + + function barrettRevert(x) + { + return x; + } + // x = x mod m (HAC 14.42) + function barrettReduce(x) + { + x.drShiftTo(this.m.t - 1, this.r2); + if (x.t > this.m.t + 1) + { + x.t = this.m.t + 1; + x.clamp(); + } + this.mu.multiplyUpperTo(this.r2, this.m.t + 1, this.q3); + this.m.multiplyLowerTo(this.q3, this.m.t + 1, this.r2); + while (x.compareTo(this.r2) < 0) x.dAddOffset(1, this.m.t + 1); + x.subTo(this.r2, x); + while (x.compareTo(this.m) >= 0) x.subTo(this.m, x); + } + // r = x^2 mod m; x != r + function barrettSqrTo(x, r) + { + x.squareTo(r); + this.reduce(r); + } + // r = x*y mod m; x,y != r + function barrettMulTo(x, y, r) + { + x.multiplyTo(y, r); + this.reduce(r); + } + Barrett.prototype.convert = barrettConvert; + Barrett.prototype.revert = barrettRevert; + Barrett.prototype.reduce = barrettReduce; + Barrett.prototype.mulTo = barrettMulTo; + Barrett.prototype.sqrTo = barrettSqrTo; + // (public) this^e % m (HAC 14.85) + function bnModPow(e, m) + { + var i = e.bitLength(), + k, r = nbv(1), + z; + if (i <= 0) return r; + else if (i < 18) k = 1; + else if (i < 48) k = 3; + 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); + // precomputation + var g = new Array(), + n = 3, + k1 = k - 1, + km = (1 << k) - 1; + g[1] = z.convert(this); + if (k > 1) + { + var g2 = nbi(); + z.sqrTo(g[1], g2); + while (n <= km) + { + g[n] = nbi(); + z.mulTo(g2, g[n - 2], g[n]); + n += 2; + } + } + var j = e.t - 1, + w, is1 = true, + r2 = nbi(), + t; + i = nbits(e[j]) - 1; + while (j >= 0) + { + if (i >= k1) w = (e[j] >> (i - k1)) & km; + else + { + w = (e[j] & ((1 << (i + 1)) - 1)) << (k1 - i); + if (j > 0) w |= e[j - 1] >> (this.DB + i - k1); + } + n = k; + while ((w & 1) == 0) + { + w >>= 1; + --n; + } + if ((i -= n) < 0) + { + i += this.DB; + --j; + } + if (is1) + { // ret == 1, don't bother squaring or multiplying it + g[w].copyTo(r); + is1 = false; + } + else + { + while (n > 1) + { + z.sqrTo(r, r2); + z.sqrTo(r2, r); + n -= 2; + } + if (n > 0) z.sqrTo(r, r2); + else + { + t = r; + r = r2; + r2 = t; + } + z.mulTo(r2, g[w], r); + } + while (j >= 0 && (e[j] & (1 << i)) == 0) + { + z.sqrTo(r, r2); + t = r; + r = r2; + r2 = t; + if (--i < 0) + { + i = this.DB - 1; + --j; + } + } + } + return z.revert(r); + } + // (public) gcd(this,a) (HAC 14.54) + function bnGCD(a) + { + var x = (this.s < 0) ? this.negate() : this.clone(); + var y = (a.s < 0) ? a.negate() : a.clone(); + if (x.compareTo(y) < 0) + { + var t = x; + x = y; + y = t; + } + var i = x.getLowestSetBit(), + g = y.getLowestSetBit(); + if (g < 0) return x; + if (i < g) g = i; + if (g > 0) + { + x.rShiftTo(g, x); + y.rShiftTo(g, y); + } + while (x.signum() > 0) + { + if ((i = x.getLowestSetBit()) > 0) x.rShiftTo(i, x); + if ((i = y.getLowestSetBit()) > 0) y.rShiftTo(i, y); + if (x.compareTo(y) >= 0) + { + x.subTo(y, x); + x.rShiftTo(1, x); + } + else + { + y.subTo(x, y); + y.rShiftTo(1, y); + } + } + if (g > 0) y.lShiftTo(g, y); + return y; + } + // (protected) this % n, n < 2^26 + function bnpModInt(n) + { + 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; + return r; + } + // (public) 1/this % m (HAC 14.61) + function bnModInverse(m) + { + var ac = m.isEven(); + if ((this.isEven() && ac) || m.signum() == 0) return BigInteger.ZERO; + var u = m.clone(), + v = this.clone(); + var a = nbv(1), + b = nbv(0), + c = nbv(0), + d = nbv(1); + while (u.signum() != 0) + { + while (u.isEven()) + { + u.rShiftTo(1, u); + if (ac) + { + if (!a.isEven() || !b.isEven()) + { + a.addTo(this, a); + b.subTo(m, b); + } + a.rShiftTo(1, a); + } + else if (!b.isEven()) b.subTo(m, b); + b.rShiftTo(1, b); + } + while (v.isEven()) + { + v.rShiftTo(1, v); + if (ac) + { + if (!c.isEven() || !d.isEven()) + { + c.addTo(this, c); + d.subTo(m, d); + } + c.rShiftTo(1, c); + } + else if (!d.isEven()) d.subTo(m, d); + d.rShiftTo(1, d); + } + if (u.compareTo(v) >= 0) + { + u.subTo(v, u); + if (ac) a.subTo(c, a); + b.subTo(d, b); + } + else + { + v.subTo(u, v); + if (ac) c.subTo(a, c); + d.subTo(b, d); + } + } + 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; + if (d.signum() < 0) return d.add(m); + else return d; + } + var lowprimes = [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]; + var lplim = (1 << 26) / lowprimes[lowprimes.length - 1]; + // (public) test primality with certainty >= 1-.5^t + function bnIsProbablePrime(t) + { + var i, x = this.abs(); + if (x.t == 1 && x[0] <= lowprimes[lowprimes.length - 1]) + { + for (i = 0; i < lowprimes.length; ++i) + if (x[0] == lowprimes[i]) return true; + return false; + } + if (x.isEven()) return false; + i = 1; + while (i < lowprimes.length) + { + var m = lowprimes[i], + 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; + } + return x.millerRabin(t); + } + // (protected) true if probably prime (HAC 4.24, Miller-Rabin) + function bnpMillerRabin(t) + { + var n1 = this.subtract(BigInteger.ONE); + var k = n1.getLowestSetBit(); + if (k <= 0) return false; + var r = n1.shiftRight(k); + t = (t + 1) >> 1; + if (t > lowprimes.length) t = lowprimes.length; + var a = nbi(); + for (var i = 0; i < t; ++i) + { + //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(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(BigInteger.ONE) == 0) return false; + } + if (y.compareTo(n1) != 0) return false; + } + } + return true; + } + // protected + 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 + 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 + 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 + /* + // 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); + }; + ClipperLib.Math_Max_Int32_Int32 = function (a, b) + { + return Math.max(a, b); + }; + /* + ----------------------------------- + cast_32 speedtest: http://jsperf.com/truncate-float-to-integer/2 + ----------------------------------- + */ + 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; + }; + /* + -------------------------- + cast_64 speedtests: http://jsperf.com/truncate-float-to-integer + Chrome: bitwise_not_floor + Firefox17: toInteger (typeof test) + IE9: bitwise_or_floor + IE7 and IE8: to_parseint + Chromium: to_floor_or_ceil + Firefox3: to_floor_or_ceil + Firefox15: to_floor_or_ceil + Opera: to_floor_or_ceil + Safari: to_floor_or_ceil + -------------------------- + */ + 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; + }; + 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) + { + return parseInt(a, 10); + }; + 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; + }; + // eg. browser.chromium || browser.firefox || browser.opera || browser.safari + 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.PI = 3.141592653589793; + ClipperLib.PI2 = 2 * 3.141592653589793; + ClipperLib.IntPoint = function () + { + var a = arguments, + alen = a.length; + this.X = 0; + this.Y = 0; + if (use_xyz) + { + 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; + } + } + else // if (!use_xyz) + { + 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.IntPoint.op_Equality = function (a, 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 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; + this.right = 0; + this.bottom = 0; + } + }; + ClipperLib.IntRect0 = function () + { + this.left = 0; + this.top = 0; + this.right = 0; + this.bottom = 0; + }; + ClipperLib.IntRect1 = function (ir) + { + this.left = ir.left; + this.top = ir.top; + this.right = ir.right; + this.bottom = ir.bottom; + }; + ClipperLib.IntRect4 = function (l, t, r, b) + { + this.left = l; + this.top = t; + this.right = r; + this.bottom = b; + }; + ClipperLib.ClipType = { + ctIntersection: 0, + ctUnion: 1, + ctDifference: 2, + ctXor: 3 + }; + ClipperLib.PolyType = { + ptSubject: 0, + ptClip: 1 + }; + ClipperLib.PolyFillType = { + pftEvenOdd: 0, + pftNonZero: 1, + pftPositive: 2, + pftNegative: 3 + }; + ClipperLib.JoinType = { + jtSquare: 0, + 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: 0, + esRight: 1 + }; + ClipperLib.Direction = { + dRightToLeft: 0, + dLeftToRight: 1 + }; + ClipperLib.TEdge = function () + { + 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 = 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; + }; + ClipperLib.Scanbeam = function () + { + this.Y = 0; + this.Next = null; + }; + ClipperLib.OutRec = function () + { + this.Idx = 0; + this.IsHole = false; + this.IsOpen = false; + this.FirstLeft = null; + this.Pts = null; + this.BottomPt = null; + this.PolyNode = null; + }; + ClipperLib.OutPt = function () + { + this.Idx = 0; + this.Pt = new ClipperLib.IntPoint(); + this.Next = null; + this.Prev = null; + }; + ClipperLib.Join = function () + { + 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 = 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 2013 september): + // protected const double horizontal = -3.4E+38; + // 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.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 (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.IntPoint.op_Equality(pp2.Pt, pt)) + return true; + pp2 = pp2.Next; + } + while (pp2 != pp) + return false; + }; + ClipperLib.ClipperBase.prototype.PointOnLineSegment = function (pt, linePt1, linePt2, UseFullRange) + { + 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) + { + if (this.PointOnLineSegment(pt, pp2.Pt, pp2.Next.Pt, UseFullRange)) + return true; + pp2 = pp2.Next; + if (pp2 == pp) + break; + } + return false; + }; + ClipperLib.ClipperBase.prototype.SlopesEqual = ClipperLib.ClipperBase.SlopesEqual = function () + { + var a = arguments, + alen = a.length; + var e1, e2, pt1, pt2, pt3, pt4, UseFullRange; + if (alen == 3) // function (e1, e2, UseFullRange) + { + 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 (alen == 4) // function (pt1, pt2, pt3, UseFullRange) + { + 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 // function (pt1, pt2, pt3, pt4, UseFullRange) + { + 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, ilen = this.m_edges.length; i < ilen; ++i) + { + 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) + { + var tmpLm = this.m_MinimaList.Next; + this.m_MinimaList = null; + this.m_MinimaList = tmpLm; + } + this.m_CurrentLM = null; + }; + ClipperLib.ClipperBase.prototype.RangeTest = function (Pt, useFullRange) + { + if (useFullRange.Value) + { + 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()."); + } + else if (Pt.X > ClipperLib.ClipperBase.loRange || Pt.Y > ClipperLib.ClipperBase.loRange || -Pt.X > ClipperLib.ClipperBase.loRange || -Pt.Y > ClipperLib.ClipperBase.loRange) + { + useFullRange.Value = true; + this.RangeTest(Pt, useFullRange); + } + }; + 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) + { + if (e.Curr.Y >= e.Next.Curr.Y) + { + //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.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.PolyTyp = polyType; + }; + ClipperLib.ClipperBase.prototype.FindNextLocMin = function (E) + { + 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.ProcessBound = function (E, IsClockwise) + { + 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 (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 + { + 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 + } + } + if (Result.OutIdx == ClipperLib.ClipperBase.Skip) + { + //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); + } + } + return Result; + }; + ClipperLib.ClipperBase.prototype.AddPath = function (pg, polyType, Closed) + { + if (use_lines) + { + if (!Closed && polyType == ClipperLib.PolyType.ptClip) + ClipperLib.Error("AddPath: Open paths must be subject."); + } + else + { + 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; + } + this.m_edges.push(edges); + var clockwise; + var EMin = null; + for (;;) + { + 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 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) + { + this.m_MinimaList = newLm; + } + else if (newLm.Y >= this.m_MinimaList.Y) + { + 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; + } + }; + ClipperLib.ClipperBase.prototype.PopLocalMinima = function () + { + if (this.m_CurrentLM === null) + return; + this.m_CurrentLM = this.m_CurrentLM.Next; + }; + ClipperLib.ClipperBase.prototype.ReverseHorizontal = function (e) + { + //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; + if (e != null) + { + //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; + } + e = lm.RightBound; + if (e != null) + { + //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; + } + }; + 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_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_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_IntersectList = new Array(); + this.m_IntersectNodeComparer = ClipperLib.MyIntersectNodeSort.Compare; + this.m_ExecuteLocked = 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; + //avoids problems with ClipperBase destructor + this.DisposeAllPolyPts(); + ClipperLib.ClipperBase.prototype.Clear.call(this); + }; + + ClipperLib.Clipper.prototype.DisposeScanbeamList = function () + { + while (this.m_Scanbeam !== null) + { + var sb2 = this.m_Scanbeam.Next; + this.m_Scanbeam = null; + this.m_Scanbeam = sb2; + } + }; + ClipperLib.Clipper.prototype.Reset = function () + { + ClipperLib.ClipperBase.prototype.Reset.call(this); + this.m_Scanbeam = null; + this.m_ActiveEdges = null; + this.m_SortedEdges = null; + + var lm = this.m_MinimaList; + while (lm !== null) + { + this.InsertScanbeam(lm.Y); + lm = lm.Next; + } + }; + ClipperLib.Clipper.prototype.InsertScanbeam = function (Y) + { + if (this.m_Scanbeam === null) + { + this.m_Scanbeam = new ClipperLib.Scanbeam(); + this.m_Scanbeam.Next = null; + this.m_Scanbeam.Y = Y; + } + else if (Y > this.m_Scanbeam.Y) + { + var newSb = new ClipperLib.Scanbeam(); + newSb.Y = Y; + 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; + //ie ignores duplicates + var newSb = new ClipperLib.Scanbeam(); + newSb.Y = Y; + 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_UsingPolyTree = false; + try + { + var succeeded = this.ExecuteInternal(); + //build the return polygons ... + if (succeeded) this.BuildResult(solution); + } + finally + { + this.DisposeAllPolyPts(); + this.m_ExecuteLocked = false; + } + return succeeded; + } + else if (alen == 4 && ispolytree) // function (clipType, polytree, subjFillType, clipFillType) + { + var clipType = a[0], + polytree = a[1], + subjFillType = a[2], + clipFillType = a[3]; + if (this.m_ExecuteLocked) + return false; + this.m_ExecuteLocked = true; + this.m_SubjFillType = subjFillType; + this.m_ClipFillType = clipFillType; + this.m_ClipType = clipType; + this.m_UsingPolyTree = true; + try + { + var succeeded = this.ExecuteInternal(); + //build the return polygons ... + if (succeeded) this.BuildResult2(polytree); + } + finally + { + this.DisposeAllPolyPts(); + this.m_ExecuteLocked = false; + } + return succeeded; + } + else if (alen == 2 && !ispolytree) // function (clipType, solution) + { + var clipType = a[0], + solution = a[1]; + return this.Execute(clipType, solution, ClipperLib.PolyFillType.pftEvenOdd, ClipperLib.PolyFillType.pftEvenOdd); + } + else if (alen == 2 && ispolytree) // function (clipType, polytree) + { + var clipType = a[0], + polytree = a[1]; + return this.Execute(clipType, polytree, ClipperLib.PolyFillType.pftEvenOdd, ClipperLib.PolyFillType.pftEvenOdd); + } + }; + ClipperLib.Clipper.prototype.FixHoleLinkage = function (outRec) + { + //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 () + { + try + { + this.Reset(); + if (this.m_CurrentLM === null) + return false; + var botY = this.PopScanbeam(); + do { + this.InsertLocalMinimaIntoAEL(botY); + ClipperLib.Clear(this.m_GhostJoins); + this.ProcessHorizontals(false); + if (this.m_Scanbeam === null) + break; + var topY = this.PopScanbeam(); + //console.log("botY:" + botY + ", topY:" + topY); + if (!this.ProcessIntersections(botY, topY)) + return false; + this.ProcessEdgesAtTopOfScanbeam(topY); + botY = topY; + } + while (this.m_Scanbeam !== null || this.m_CurrentLM !== null) + //fix orientations ... + for (var i = 0, ilen = this.m_PolyOuts.length; i < ilen; i++) + { + 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(); + 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.Clipper.prototype.PopScanbeam = function () + { + var Y = this.m_Scanbeam.Y; + var sb2 = this.m_Scanbeam; + this.m_Scanbeam = this.m_Scanbeam.Next; + sb2 = null; + return Y; + }; + ClipperLib.Clipper.prototype.DisposeAllPolyPts = function () + { + 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); + outRec = null; + this.m_PolyOuts[index] = null; + }; + ClipperLib.Clipper.prototype.DisposeOutPts = function (pp) + { + if (pp === null) + return; + var tmpPp = null; + pp.Prev.Next = null; + while (pp !== null) + { + tmpPp = pp; + pp = pp.Next; + tmpPp = null; + } + }; + ClipperLib.Clipper.prototype.AddJoin = function (Op1, Op2, OffPt) + { + 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.AddGhostJoin = function (Op, OffPt) + { + 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) + { + while (this.m_CurrentLM !== null && (this.m_CurrentLM.Y == botY)) + { + 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) + { + this.InsertEdgeIntoAEL(lb, null); + this.SetWindingCount(lb); + if (this.IsContributing(lb)) + Op1 = this.AddOutPt(lb, lb.Bot); + this.InsertScanbeam(lb.Top.Y); + } + else + { + 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); + } + if (rb != null) + { + if (ClipperLib.ClipperBase.IsHorizontal(rb)) + this.AddEdgeToSEL(rb); + else + this.InsertScanbeam(rb.Top.Y); + } + 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) + { + for (var i = 0, ilen = this.m_GhostJoins.length; i < ilen; i++) + { + //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.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 && + ClipperLib.ClipperBase.SlopesEqual(rb.PrevInAEL, rb, this.m_UseFullRange) && + rb.WindDelta !== 0 && rb.PrevInAEL.WindDelta !== 0) + { + 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; + } + } + } + }; + ClipperLib.Clipper.prototype.InsertEdgeIntoAEL = function (edge, startEdge) + { + if (this.m_ActiveEdges === null) + { + edge.PrevInAEL = null; + edge.NextInAEL = null; + this.m_ActiveEdges = edge; + } + else if (startEdge === null && this.E2InsertsBeforeE1(this.m_ActiveEdges, edge)) + { + edge.PrevInAEL = null; + edge.NextInAEL = this.m_ActiveEdges; + this.m_ActiveEdges.PrevInAEL = edge; + this.m_ActiveEdges = edge; + } + else + { + 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) + { + 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.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.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.PolyTyp == ClipperLib.PolyType.ptSubject) + { + pft = this.m_SubjFillType; + pft2 = this.m_ClipFillType; + } + else + { + pft = this.m_ClipFillType; + pft2 = this.m_SubjFillType; + } + 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: + return (edge.WindCnt2 !== 0); + case ClipperLib.PolyFillType.pftPositive: + return (edge.WindCnt2 > 0); + default: + 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); + } + 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); + } + 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); + } + else + return true; + } + return true; + }; + ClipperLib.Clipper.prototype.SetWindingCount = function (edge) + { + 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 === 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)) + { + //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 + { + //nonZero, Positive or Negative filling ... + if (e.WindCnt * e.WindDelta < 0) + { + //prev edge is 'decreasing' WindCount (WC) toward zero + //so we're outside the previous polygon ... + if (Math.abs(e.WindCnt) > 1) + { + //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 = (edge.WindDelta === 0 ? 1 : edge.WindDelta); + } + else + { + //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; + //ie get ready to calc WindCnt2 + } + //update WindCnt2 ... + if (this.IsEvenOddAltFillType(edge)) + { + //EvenOdd filling ... + while (e != edge) + { + 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; + } + } + }; + ClipperLib.Clipper.prototype.AddEdgeToSEL = function (edge) + { + //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; + } + else + { + edge.NextInSEL = this.m_SortedEdges; + edge.PrevInSEL = null; + this.m_SortedEdges.PrevInSEL = edge; + this.m_SortedEdges = edge; + } + }; + ClipperLib.Clipper.prototype.CopyAELToSEL = function () + { + var e = this.m_ActiveEdges; + this.m_SortedEdges = e; + while (e !== null) + { + e.PrevInSEL = e.PrevInAEL; + e.NextInSEL = e.NextInAEL; + e = e.NextInAEL; + } + }; + ClipperLib.Clipper.prototype.SwapPositionsInAEL = function (edge1, edge2) + { + //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 + { + 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) + { + 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 + { + 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 (e2.WindDelta == 0) this.AddOutPt(e2, pt); + if (e1.OutIdx == e2.OutIdx) + { + e1.OutIdx = -1; + e2.OutIdx = -1; + } + 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 (ClipperLib.ClipperBase.IsHorizontal(e2) || (e1.Dx > e2.Dx)) + { + 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; + } + else + { + 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 (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); + } + return result; + }; + ClipperLib.Clipper.prototype.CreateOutRec = function () + { + var result = new ClipperLib.OutRec(); + result.Idx = -1; + result.IsHole = false; + result.IsOpen = false; + result.FirstLeft = 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 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 + { + 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 = 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.HorzSegmentsOverlap = function (Pt1a, Pt1b, Pt2a, Pt2b) + { + //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; + 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; + } + else + { + 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) + { + if (e2.OutIdx >= 0 && e2.WindDelta != 0) + { + isHole = !isHole; + if (outRec.FirstLeft === null) + outRec.FirstLeft = this.m_PolyOuts[e2.OutIdx]; + } + e2 = e2.PrevInAEL; + } + 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); + }; + ClipperLib.Clipper.prototype.FirstIsBottomPt = function (btmPt1, btmPt2) + { + 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; + while (p != pp) + { + 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) + { + if (p.Pt.X < pp.Pt.X) + { + dups = null; + pp = p; + } + else + { + if (p.Next != pp && p.Prev != pp) + dups = p; + } + } + p = p.Next; + } + 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.IntPoint.op_Inequality(dups.Pt, pp.Pt)) + dups = dups.Next; + } + } + return pp; + }; + ClipperLib.Clipper.prototype.GetLowermostRec = function (outRec1, 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; + } + 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) + { + //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; + var side; + //join e2 poly onto e1 poly and delete pointers to e2 ... + if (e1.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; + } + else + { + //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) + { + //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; + } + else + { + //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) + { + 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) + { + if (e.OutIdx == ObsoleteIdx) + { + e.OutIdx = OKIdx; + e.Side = side; + break; + } + e = e.NextInAEL; + } + outRec2.Idx = outRec1.Idx; + }; + ClipperLib.Clipper.prototype.ReversePolyPtLinks = function (pp) + { + if (pp === null) + return; + var pp1; + var pp2; + pp1 = pp; + do { + pp2 = pp1.Next; + pp1.Next = pp1.Prev; + pp1.Prev = pp2; + pp1 = pp2; + } + while (pp1 != pp) + }; + ClipperLib.Clipper.SwapSides = function (edge1, edge2) + { + 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.IntersectEdges = function (e1, e2, pt, protect) + { + //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; + } + 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; + } + } + 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; + } + var e1FillType, e2FillType, e1FillType2, e2FillType2; + if (e1.PolyTyp == ClipperLib.PolyType.ptSubject) + { + e1FillType = this.m_SubjFillType; + e1FillType2 = this.m_ClipFillType; + } + else + { + e1FillType = this.m_ClipFillType; + e1FillType2 = this.m_SubjFillType; + } + if (e2.PolyTyp == ClipperLib.PolyType.ptSubject) + { + e2FillType = this.m_SubjFillType; + e2FillType2 = this.m_ClipFillType; + } + else + { + e2FillType = this.m_ClipFillType; + e2FillType2 = this.m_SubjFillType; + } + var e1Wc, e2Wc; + switch (e1FillType) + { + 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 = 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.AddOutPt(e1, pt); + ClipperLib.Clipper.SwapSides(e1, e2); + ClipperLib.Clipper.SwapPolyIndexes(e1, e2); + } + } + else if (e2Contributing) + { + 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) + { + //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 = 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 = Math.abs(e2.WindCnt2); + break; + } + 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); + break; + case ClipperLib.ClipType.ctUnion: + if (e1Wc2 <= 0 && e2Wc2 <= 0) + this.AddLocalMinPoly(e1, e2, pt); + break; + case ClipperLib.ClipType.ctDifference: + 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); + } + if ((e1stops != e2stops) && + ((e1stops && (e1.OutIdx >= 0)) || (e2stops && (e2.OutIdx >= 0)))) + { + ClipperLib.Clipper.SwapSides(e1, e2); + ClipperLib.Clipper.SwapPolyIndexes(e1, 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; + //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; + //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.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) + { + this.DeleteFromSEL(horzEdge); + this.ProcessHorizontal(horzEdge, isTopOfScanbeam); + horzEdge = this.m_SortedEdges; + } + }; + ClipperLib.Clipper.prototype.GetHorzDirection = function (HorzEdge, $var) + { + if (HorzEdge.Bot.X < HorzEdge.Top.X) + { + $var.Left = HorzEdge.Bot.X; + $var.Right = HorzEdge.Top.X; + $var.Dir = ClipperLib.Direction.dLeftToRight; + } + else + { + $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 IsLastHorz = (horzEdge == eLastHorz); + var e = this.GetNextInAEL(horzEdge, dir); + while (e !== 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 (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 + { + var Pt = new ClipperLib.IntPoint(e.Curr.X, horzEdge.Curr.Y); + this.IntersectEdges(e, horzEdge, Pt, true); + } + this.SwapPositionsInAEL(horzEdge, e); + } + 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; + //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))) + { + var op2 = this.AddOutPt(ePrev, horzEdge.Bot); + this.AddJoin(op1, op2, horzEdge.Top); + } + 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)) + { + var op2 = this.AddOutPt(eNext, horzEdge.Bot); + this.AddJoin(op1, op2, horzEdge.Top); + } + } + else horzEdge = this.UpdateEdgeIntoAEL(horzEdge); + } + else if (eMaxPair !== null) + { + if (eMaxPair.OutIdx >= 0) + { + 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.AddOutPt(horzEdge, horzEdge.Top); + this.DeleteFromAEL(horzEdge); + } + }; + ClipperLib.Clipper.prototype.GetNextInAEL = function (e, Direction) + { + 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); + }; + ClipperLib.Clipper.prototype.IsMaxima = function (e, Y) + { + return (e !== null && e.Top.Y == Y && e.NextInLML === null); + }; + ClipperLib.Clipper.prototype.IsIntermediate = function (e, Y) + { + return (e.Top.Y == Y && e.NextInLML !== null); + }; + ClipperLib.Clipper.prototype.GetMaximaPair = function (e) + { + 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; + try + { + this.BuildIntersectList(botY, topY); + 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.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; + //prepare for sorting ... + var e = this.m_ActiveEdges; + //console.log(JSON.stringify(JSON.decycle( e ))); + this.m_SortedEdges = e; + while (e !== null) + { + 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) + { + isModified = false; + e = this.m_SortedEdges; + while (e.NextInSEL !== null) + { + var eNext = e.NextInSEL; + var pt = new ClipperLib.IntPoint(); + //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; + if (Math.abs(e.Dx) > Math.abs(eNext.Dx)) + pt.X = ClipperLib.Clipper.TopX(eNext, botY); + else + pt.X = ClipperLib.Clipper.TopX(e, botY); + } + 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; + } + if (e.PrevInSEL !== null) + e.PrevInSEL.NextInSEL = null; + else + break; + } + this.m_SortedEdges = null; + }; + ClipperLib.Clipper.prototype.EdgesAdjacent = function (inode) + { + return (inode.Edge1.NextInSEL == inode.Edge2) || (inode.Edge1.PrevInSEL == inode.Edge2); + }; + ClipperLib.Clipper.IntersectNodeSort = function (node1, node2) + { + //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 cnt = this.m_IntersectList.length; + for (var i = 0; i < cnt; i++) + { + if (!this.EdgesAdjacent(this.m_IntersectList[i])) + { + 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(this.m_IntersectList[i].Edge1, this.m_IntersectList[i].Edge2); + } + return true; + }; + ClipperLib.Clipper.prototype.ProcessIntersectList = function () + { + for (var i = 0, ilen = this.m_IntersectList.length; i < ilen; i++) + { + 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) + { + a -= 0.5; + return a < -2147483648 ? Math.ceil(a) : a | 0; + } + else + { + a += 0.5; + 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 (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.IntersectPoint = function (edge1, edge2, ip) + { + 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 (edge2.Bot.Y > edge1.Bot.Y) + { + ip.X = edge2.Bot.X; + ip.Y = edge2.Bot.Y; + } + else + { + ip.X = edge1.Bot.X; + ip.Y = edge1.Bot.Y; + } + return false; + } + else if (edge1.Delta.X === 0) + { + ip.X = edge1.Bot.X; + if (ClipperLib.ClipperBase.IsHorizontal(edge2)) + { + ip.Y = edge2.Bot.Y; + } + else + { + b2 = edge2.Bot.Y - (edge2.Bot.X / edge2.Dx); + ip.Y = ClipperLib.Clipper.Round(ip.X / edge2.Dx + b2); + } + } + else if (edge2.Delta.X === 0) + { + ip.X = edge2.Bot.X; + if (ClipperLib.ClipperBase.IsHorizontal(edge1)) + { + ip.Y = edge1.Bot.Y; + } + else + { + b1 = edge1.Bot.Y - (edge1.Bot.X / edge1.Dx); + ip.Y = ClipperLib.Clipper.Round(ip.X / edge1.Dx + b1); + } + } + else + { + 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.Y < edge1.Top.Y || ip.Y < edge2.Top.Y) + { + if (edge1.Top.Y > edge2.Top.Y) + { + ip.Y = edge1.Top.Y; + ip.X = ClipperLib.Clipper.TopX(edge2, edge1.Top.Y); + return ip.X < edge1.Top.X; + } + else + 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; + while (e !== null) + { + //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) + { + var ePrev = e.PrevInAEL; + this.DoMaxima(e); + if (ePrev === null) + e = this.m_ActiveEdges; + else + e = ePrev.NextInAEL; + } + else + { + //2. promote horizontal edges, otherwise update Curr.X and Curr.Y ... + if (this.IsIntermediate(e, topY) && ClipperLib.ClipperBase.IsHorizontal(e.NextInLML)) + { + e = this.UpdateEdgeIntoAEL(e); + if (e.OutIdx >= 0) + this.AddOutPt(e, e.Bot); + this.AddEdgeToSEL(e); + } + else + { + 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; + } + } + //3. Process horizontals at the Top of the scanbeam ... + this.ProcessHorizontals(true); + //4. Promote intermediate vertices ... + e = this.m_ActiveEdges; + while (e !== null) + { + if (this.IsIntermediate(e, topY)) + { + 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)) + { + var op2 = this.AddOutPt(ePrev, e.Bot); + this.AddJoin(op, op2, e.Top); + } + 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)) + { + var op2 = this.AddOutPt(eNext, e.Bot); + this.AddJoin(op, op2, e.Top); + } + } + e = e.NextInAEL; + } + }; + ClipperLib.Clipper.prototype.DoMaxima = function (e) + { + var eMaxPair = this.GetMaximaPair(e); + if (eMaxPair === null) + { + 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 == -1 && eMaxPair.OutIdx == -1) + { + this.DeleteFromAEL(e); + this.DeleteFromAEL(eMaxPair); + } + else if (e.OutIdx >= 0 && eMaxPair.OutIdx >= 0) + { + this.IntersectEdges(e, eMaxPair, e.Top, false); + } + else if (use_lines && e.WindDelta === 0) + { + 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 ClipperLib.Clipper.Area(poly) >= 0; + }; + ClipperLib.Clipper.prototype.PointCount = function (pts) + { + if (pts === null) + return 0; + var result = 0; + var p = pts; + do { + result++; + p = p.Next; + } + while (p != pts) + return result; + }; + ClipperLib.Clipper.prototype.BuildResult = function (polyg) + { + ClipperLib.Clear(polyg); + for (var i = 0, ilen = this.m_PolyOuts.length; i < ilen; i++) + { + var outRec = this.m_PolyOuts[i]; + if (outRec.Pts === null) + continue; + var p = outRec.Pts.Prev; + var cnt = this.PointCount(p); + if (cnt < 2) + continue; + var pg = new Array(cnt); + for (var j = 0; j < cnt; j++) + { + pg[j] = p.Pt; + p = p.Prev; + } + polyg.push(pg); + } + }; + ClipperLib.Clipper.prototype.BuildResult2 = function (polytree) + { + 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]; + 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++) + { + pn.m_polygon[j] = op.Pt; + op = op.Prev; + } + } + //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); + } + 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.BottomPt = null; + var pp = outRec.Pts; + for (;;) + { + if (pp.Prev == pp || pp.Prev == pp.Next) + { + this.DisposeOutPts(pp); + outRec.Pts = null; + return; + } + //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; + pp.Prev.Next = pp.Next; + pp.Next.Prev = pp.Prev; + pp = pp.Prev; + tmp = null; + } + else if (pp == lastOK) + break; + else + { + if (lastOK === null) + lastOK = pp; + pp = pp.Next; + } + } + 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 + { + result.Prev = outPt.Prev; + result.Next = outPt; + outPt.Prev.Next = result; + outPt.Prev = result; + } + return result; + }; + ClipperLib.Clipper.prototype.GetOverlap = function (a1, a2, b1, b2, $val) + { + if (a1 < a2) + { + 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.JoinHorz = function (op1, op1b, op2, op2b, Pt, DiscardLeft) + { + 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 + { + 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); + } + } + 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)) + { + 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; + }; + + 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 + { + 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 + { + if (ipNext.X > pt.X) + { + 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; + } + } + } + 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 (poly0x >= pt.X) + { + 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; + } + } + else + { + 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.prototype.Poly2ContainsPoly1 = function (outPt1, outPt2) + { + var op = outPt1; + do { + var res = this.PointInPolygon(op.Pt, outPt2); + if (res >= 0) + return res != 0; + op = op.Next; + } + while (op != outPt1) + return true; + }; + ClipperLib.Clipper.prototype.FixupFirstLefts1 = function (OldOutRec, NewOutRec) + { + for (var i = 0, ilen = this.m_PolyOuts.length; i < ilen; i++) + { + var outRec = this.m_PolyOuts[i]; + if (outRec.Pts !== null && outRec.FirstLeft == OldOutRec) + { + if (this.Poly2ContainsPoly1(outRec.Pts, NewOutRec.Pts)) + outRec.FirstLeft = NewOutRec; + } + } + }; + 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 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) + { + //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 + { + //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.prototype.UpdateOutPtIdxs = function (outrec) + { + var op = outrec.Pts; + do { + op.Idx = outrec.Idx; + op = op.Prev; + } + while (op != outrec.Pts) + }; + ClipperLib.Clipper.prototype.DoSimplePolygons = function () + { + 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.Area = function (poly) + { + var cnt = poly.length; + if (cnt < 3) + return 0; + var a = 0; + for (var i = 0, j = cnt - 1; i < cnt; ++i) + { + a += (poly[j].X + poly[i].X) * (poly[j].Y - poly[i].Y); + j = i; + } + 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; + } + while (op != outRec.Pts) + return a * 0.5; + }; + 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 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.SimplifyPolygons = function (polys, fillType) + { + 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; + }; + ClipperLib.Clipper.DistanceSqrd = function (pt1, pt2) + { + var dx = (pt1.X - pt2.X); + var dy = (pt1.Y - pt2.Y); + return (dx * dx + dy * dy); + }; + ClipperLib.Clipper.DistanceFromLineSqrd = function (pt, ln1, ln2) + { + //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) + { + return ClipperLib.Clipper.DistanceFromLineSqrd(pt2, pt1, pt3) < distSqrd; + }; + 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)) + { + op = ClipperLib.Clipper.ExcludeOp(op); + cnt--; + } + 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 (cnt < 3) + cnt = 0; + var result = new Array(cnt); + for (var i = 0; i < cnt; ++i) + { + 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++) + { + 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 + for (var i = 0; i < pathCnt; i++) + { + 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); + } + var quads = new Array(); + for (var i = 0; i < pathCnt - 1 + delta; i++) + for (var j = 0; j < polyCnt; j++) + { + 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); + } + 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; + }; + + 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) + { + var tmp = ClipperLib.Clipper.Minkowski(pattern, paths[i], true, pathIsClosed); + c.AddPaths(tmp, ClipperLib.PolyType.ptSubject, true); + } + if (pathIsClosed) c.AddPaths(paths, ClipperLib.PolyType.ptClip, true); + var solution = new ClipperLib.Paths(); + c.Execute(ClipperLib.ClipType.ctUnion, solution, pathFillType, pathFillType); + return solution; + } + }; + + 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) + { + 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.ClosedPathsFromPolyTree = function (polytree) + { + 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 + { + 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); + } + }; + ClipperLib.ClipperOffset.prototype.AddPaths = function (paths, joinType, endType) + { + for (var i = 0, ilen = paths.length; i < ilen; i++) + this.AddPath(paths[i], joinType, endType); + }; + ClipperLib.ClipperOffset.prototype.FixOrientations = function () + { + //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)) + { + 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 + { + 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.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) + { + 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)) + { + //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 + 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.ClipperOffset.prototype.Execute = function () + { + var a = arguments, + ispolytree = a[0] instanceof ClipperLib.PolyTree; + if (!ispolytree) // function (solution, delta) + { + 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) + { + 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(); + } + } + }; + 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 + { + throw new Error(message); + } + catch (err) + { + alert(err.message); + } + }; + // --------------------------------- + // JS extension by Timo 2013 + ClipperLib.JS = {}; + ClipperLib.JS.AreaOfPolygon = function (poly, scale) + { + 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++) + { + area += ClipperLib.Clipper.Area(poly[i]); + } + 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.JS.Clean = function (polygon, delta) + { + if (!(polygon instanceof Array)) return []; + var isPolygons = polygon[0] instanceof Array; + 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 (!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++) + { + poly = polygon[k]; + len = poly.length; + if (len === 0) continue; + else if (len < 3) + { + result = poly; + results.push(result); + continue; + } + result = poly; + d = delta * delta; + //d = Math.floor(c_delta * c_delta); + p = poly[0]; + j = 1; + 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) + continue; + result[j] = poly[i]; + p = poly[i]; + j++; + } + 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) + j--; + if (j < len) + 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 = [ + [] + ]; + 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 segment between + // start and end point, the middle point is removed. + 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.JS.Clone(polygon); + } + if (polygon.length === 0 || (polygon.length == 1 && polygon[0].length === 0) || tolerance < 0) + { + 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, 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 = [ + [] + ]; + return results; + } + 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 diff --git a/src/printer.js b/src/printer.js index 22ba9ff..eb83de5 100644 --- a/src/printer.js +++ b/src/printer.js @@ -7,6 +7,10 @@ D3D.Printer = function (config) { "use strict"; + + this.state = "connecting"; + this.temperature = {}; + this.progress = {}; for (var i in config) { if (i.indexOf("printer") === 0) { diff --git a/src/slicer.js b/src/slicer.js index d03a7cf..9d91cec 100644 --- a/src/slicer.js +++ b/src/slicer.js @@ -8,14 +8,23 @@ * ******************************************************/ -D3D.Slicer = function (geometry) { +D3D.Slicer = function () { + "use strict"; + + this.geometry; + + this.lines = []; + this.lineLookup = {}; +}; +D3D.Slicer.prototype.setGeometry = function (geometry) { "use strict"; this.geometry = geometry; this.geometry.mergeVertices(); - this.lines = []; - this.lineLookup = {}; + this.createLines(); + + return this; }; D3D.Slicer.prototype.addLine = function (a, b) { "use stict"; @@ -52,6 +61,8 @@ D3D.Slicer.prototype.createLines = function () { var c = this.addLine(face.c, face.a); //set connecting lines (based on face) + + this.lines[a].connects.push(b, c); this.lines[b].connects.push(a, c); this.lines[c].connects.push(a, b); @@ -65,8 +76,6 @@ D3D.Slicer.prototype.createLines = function () { D3D.Slicer.prototype.slice = function (height, step) { "use strict"; - this.createLines(); - var slices = []; var plane = new THREE.Plane(); @@ -75,7 +84,6 @@ D3D.Slicer.prototype.slice = function (height, step) { plane.set(new THREE.Vector3(0, -1, 0), z); var slice = []; - slices.push(slice); var intersections = []; @@ -98,7 +106,7 @@ D3D.Slicer.prototype.slice = function (height, step) { var done = []; for (var i = 0; i < intersections.length; i ++) { - if (done.indexOf(i) === -1 && intersections[i]) { + if (intersections[i] && done.indexOf(i) === -1) { var index = i; var shape = []; @@ -113,7 +121,9 @@ D3D.Slicer.prototype.slice = function (height, step) { for (var j = 0; j < connects.length; j ++) { index = connects[j]; - if (done.indexOf(index) === -1 && intersections[index]) { + console.log(j, intersections[index]); + + if (intersections[index] && done.indexOf(index) === -1) { break; } else { @@ -128,6 +138,13 @@ D3D.Slicer.prototype.slice = function (height, step) { } } } + + if (slice.length > 0) { + slices.push(slice); + } + else { + break; + } } return slices; diff --git a/src/utils.js b/src/utils.js index 654867b..f9c90ef 100644 --- a/src/utils.js +++ b/src/utils.js @@ -61,6 +61,13 @@ function getAPI (url, callback) { }); } +function downloadFile (file, data) { + $(document.createElement("a")).attr({ + download: file, + href: "data:text/plain," + data + })[0].click(); +} + Array.prototype.clone = function () { "use strict";