Created
October 14, 2013 09:05
-
-
Save nraynaud/6972980 to your computer and use it in GitHub Desktop.
Testing clipper algo change.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| //instrumented by nraynaud for testing purpose | |
| /******************************************************************************* | |
| * * | |
| * Author : Angus Johnson * | |
| * Version : 5.0.2 * | |
| * Date : 30 December 2012 * | |
| * Website : http://www.angusj.com * | |
| * Copyright : Angus Johnson 2010-2012 * | |
| * * | |
| * License: * | |
| * Use, modification & distribution is subject to Boost Software License Ver 1. * | |
| * http://www.boost.org/LICENSE_1_0.txt * | |
| * * | |
| * Attributions: * | |
| * The code in this library is an extension of Bala Vatti's clipping algorithm: * | |
| * "A generic solution to polygon clipping" * | |
| * Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. * | |
| * http://portal.acm.org/citation.cfm?id=129906 * | |
| * * | |
| * Computer graphics and geometric modeling: implementation and algorithms * | |
| * By Max K. Agoston * | |
| * Springer; 1 edition (January 4, 2005) * | |
| * http://books.google.com/books?q=vatti+clipping+agoston * | |
| * * | |
| * See also: * | |
| * "Polygon Offsetting by Computing Winding Numbers" * | |
| * Paper no. DETC2005-85513 pp. 565-575 * | |
| * ASME 2005 International Design Engineering Technical Conferences * | |
| * and Computers and Information in Engineering Conference (IDETC/CIE2005) * | |
| * September 24�28, 2005 , Long Beach, California, USA * | |
| * http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf * | |
| * * | |
| *******************************************************************************/ | |
| /******************************************************************************* | |
| * * | |
| * Author : Timo * | |
| * Version : 5.0.2.2 * | |
| * Date : 11 September 2013 * | |
| * * | |
| * This is a translation of the C# Clipper library to Javascript. * | |
| * Int128 struct of C# is implemented using JSBN of Tom Wu. * | |
| * Because Javascript lacks support for 64-bit integers, the space * | |
| * is a little more restricted than in C# version. * | |
| * * | |
| * C# version has support for coordinate space: * | |
| * +-4611686018427387903 ( sqrt(2^127 -1)/2 ) * | |
| * while Javascript version has support for space: * | |
| * +-4503599627370495 ( sqrt(2^106 -1)/2 ) * | |
| * * | |
| * Tom Wu's JSBN proved to be the fastest big integer library: * | |
| * http://jsperf.com/big-integer-library-test * | |
| * * | |
| * This class can be made simpler when (if ever) 64-bit integer support comes. * | |
| * * | |
| *******************************************************************************/ | |
| /******************************************************************************* | |
| * * | |
| * Basic JavaScript BN library - subset useful for RSA encryption. * | |
| * http://www-cs-students.stanford.edu/~tjw/jsbn/ * | |
| * Copyright (c) 2005 Tom Wu * | |
| * All Rights Reserved. * | |
| * See "LICENSE" for details: * | |
| * http://www-cs-students.stanford.edu/~tjw/jsbn/LICENSE * | |
| * * | |
| *******************************************************************************/ | |
| (function () { | |
| // "use strict"; | |
| // Browser test to speedup performance critical functions | |
| var nav = navigator.userAgent.toString().toLowerCase(); | |
| 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; | |
| var ClipperLib = {}; | |
| ClipperLib.biginteger_used = null; | |
| // Bits per digit | |
| var dbits; | |
| // JavaScript engine analysis | |
| var canary = 0xdeadbeefcafe; | |
| var j_lm = ((canary & 0xffffff) == 0xefcafe); | |
| // (public) Constructor | |
| function Int128(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 Int128 library | |
| ClipperLib.biginteger_used = 1; | |
| if (a != null) if ("number" == typeof a) { | |
| this.fromString(Math.floor(a) | |
| .toString(), 10); //this.fromNumber(a,b,c); | |
| } | |
| else if (b == null && "string" != typeof a) this.fromString(a, 256); | |
| else { | |
| if (a.indexOf(".") != -1) a = a.substring(0, a.indexOf(".")); | |
| this.fromString(a, b); | |
| } | |
| } | |
| // return new, unset Int128 | |
| function nbi() { | |
| return new Int128(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")) { | |
| Int128.prototype.am = am2; | |
| dbits = 30; | |
| } | |
| else if (j_lm && (navigator.appName != "Netscape")) { | |
| Int128.prototype.am = am1; | |
| dbits = 26; | |
| } | |
| else { // Mozilla/Netscape seems to prefer am3 | |
| Int128.prototype.am = am3; | |
| dbits = 28; | |
| } | |
| Int128.prototype.DB = dbits; | |
| Int128.prototype.DM = ((1 << dbits) - 1); | |
| Int128.prototype.DV = (1 << dbits); | |
| var BI_FP = 52; | |
| Int128.prototype.FV = Math.pow(2, BI_FP); | |
| Int128.prototype.F1 = BI_FP - dbits; | |
| Int128.prototype.F2 = 2 * dbits - BI_FP; | |
| // Digit conversions | |
| var BI_RM = "0123456789abcdefghijklmnopqrstuvwxyz"; | |
| var BI_RC = []; | |
| 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) Int128.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(); | |
| Int128.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) Int128.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); | |
| } | |
| Int128.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) Int128.ZERO.subTo(q, q); | |
| } | |
| r.t = ys; | |
| r.clamp(); | |
| if (nsh > 0) r.rShiftTo(nsh, r); // Denormalize remainder | |
| if (ts < 0) Int128.ZERO.subTo(r, r); | |
| } | |
| // (public) this mod a | |
| function bnMod(a) { | |
| var r = nbi(); | |
| this.abs() | |
| .divRemTo(a, null, r); | |
| if (this.s < 0 && r.compareTo(Int128.ZERO) > 0) a.subTo(r, r); | |
| 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(Int128.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 Int128.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 | |
| Int128.prototype.copyTo = bnpCopyTo; | |
| Int128.prototype.fromInt = bnpFromInt; | |
| Int128.prototype.fromString = bnpFromString; | |
| Int128.prototype.clamp = bnpClamp; | |
| Int128.prototype.dlShiftTo = bnpDLShiftTo; | |
| Int128.prototype.drShiftTo = bnpDRShiftTo; | |
| Int128.prototype.lShiftTo = bnpLShiftTo; | |
| Int128.prototype.rShiftTo = bnpRShiftTo; | |
| Int128.prototype.subTo = bnpSubTo; | |
| Int128.prototype.multiplyTo = bnpMultiplyTo; | |
| Int128.prototype.squareTo = bnpSquareTo; | |
| Int128.prototype.divRemTo = bnpDivRemTo; | |
| Int128.prototype.invDigit = bnpInvDigit; | |
| Int128.prototype.isEven = bnpIsEven; | |
| Int128.prototype.exp = bnpExp; | |
| // public | |
| Int128.prototype.toString = bnToString; | |
| Int128.prototype.negate = bnNegate; | |
| Int128.prototype.abs = bnAbs; | |
| Int128.prototype.compareTo = bnCompareTo; | |
| Int128.prototype.bitLength = bnBitLength; | |
| Int128.prototype.mod = bnMod; | |
| Int128.prototype.modPowInt = bnModPowInt; | |
| // "constants" | |
| Int128.ZERO = nbv(0); | |
| Int128.ONE = nbv(1); | |
| // Copyright (c) 2005-2009 Tom Wu | |
| // All Rights Reserved. | |
| // See "LICENSE" for details. | |
| // Extended JavaScript BN functions, required for RSA private ops. | |
| // Version 1.1: new Int128("0", 10) returns "proper" zero | |
| // Version 1.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) Int128.ZERO.subTo(this, this); | |
| } | |
| // (protected) alternate constructor | |
| function bnpFromNumber(a, b, c) { | |
| if ("number" == typeof b) { | |
| // new Int128(int,int,RNG) | |
| if (a < 2) this.fromInt(1); | |
| else { | |
| this.fromNumber(a, c); | |
| if (!this.testBit(a - 1)) // force MSB set | |
| this.bitwiseTo(Int128.ONE.shiftLeft(a - 1), op_or, this); | |
| if (this.isEven()) this.dAddOffset(1, 0); // force odd | |
| while (!this.isProbablePrime(b)) { | |
| this.dAddOffset(2, 0); | |
| if (this.bitLength() > a) this.subTo(Int128.ONE.shiftLeft(a - 1), this); | |
| } | |
| } | |
| } | |
| else { | |
| // new Int128(int,RNG) | |
| var x = [], | |
| 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 = []; | |
| 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<<n) | |
| function bnpChangeBit(n, op) { | |
| var r = Int128.ONE.shiftLeft(n); | |
| this.bitwiseTo(r, op, r); | |
| return r; | |
| } | |
| // (public) this | (1<<n) | |
| function bnSetBit(n) { | |
| return this.changeBit(n, op_or); | |
| } | |
| // (public) this & ~(1<<n) | |
| function bnClearBit(n) { | |
| return this.changeBit(n, op_andnot); | |
| } | |
| // (public) this ^ (1<<n) | |
| function bnFlipBit(n) { | |
| return this.changeBit(n, op_xor); | |
| } | |
| // (protected) r = this + a | |
| function bnpAddTo(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 > 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(); | |
| Int128.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 = [], | |
| 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 Int128.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(Int128.ONE) != 0) return Int128.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(Int128.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(Int128.ONE) != 0 && y.compareTo(n1) != 0) { | |
| var j = 1; | |
| while (j++ < k && y.compareTo(n1) != 0) { | |
| y = y.modPowInt(2, this); | |
| if (y.compareTo(Int128.ONE) == 0) return false; | |
| } | |
| if (y.compareTo(n1) != 0) return false; | |
| } | |
| } | |
| return true; | |
| } | |
| // protected | |
| Int128.prototype.chunkSize = bnpChunkSize; | |
| Int128.prototype.toRadix = bnpToRadix; | |
| Int128.prototype.fromRadix = bnpFromRadix; | |
| Int128.prototype.fromNumber = bnpFromNumber; | |
| Int128.prototype.bitwiseTo = bnpBitwiseTo; | |
| Int128.prototype.changeBit = bnpChangeBit; | |
| Int128.prototype.addTo = bnpAddTo; | |
| Int128.prototype.dMultiply = bnpDMultiply; | |
| Int128.prototype.dAddOffset = bnpDAddOffset; | |
| Int128.prototype.multiplyLowerTo = bnpMultiplyLowerTo; | |
| Int128.prototype.multiplyUpperTo = bnpMultiplyUpperTo; | |
| Int128.prototype.modInt = bnpModInt; | |
| Int128.prototype.millerRabin = bnpMillerRabin; | |
| // public | |
| Int128.prototype.clone = bnClone; | |
| Int128.prototype.intValue = bnIntValue; | |
| Int128.prototype.byteValue = bnByteValue; | |
| Int128.prototype.shortValue = bnShortValue; | |
| Int128.prototype.signum = bnSigNum; | |
| Int128.prototype.toByteArray = bnToByteArray; | |
| Int128.prototype.equals = bnEquals; | |
| Int128.prototype.min = bnMin; | |
| Int128.prototype.max = bnMax; | |
| Int128.prototype.and = bnAnd; | |
| Int128.prototype.or = bnOr; | |
| Int128.prototype.xor = bnXor; | |
| Int128.prototype.andNot = bnAndNot; | |
| Int128.prototype.not = bnNot; | |
| Int128.prototype.shiftLeft = bnShiftLeft; | |
| Int128.prototype.shiftRight = bnShiftRight; | |
| Int128.prototype.getLowestSetBit = bnGetLowestSetBit; | |
| Int128.prototype.bitCount = bnBitCount; | |
| Int128.prototype.testBit = bnTestBit; | |
| Int128.prototype.setBit = bnSetBit; | |
| Int128.prototype.clearBit = bnClearBit; | |
| Int128.prototype.flipBit = bnFlipBit; | |
| Int128.prototype.add = bnAdd; | |
| Int128.prototype.subtract = bnSubtract; | |
| Int128.prototype.multiply = bnMultiply; | |
| Int128.prototype.divide = bnDivide; | |
| Int128.prototype.remainder = bnRemainder; | |
| Int128.prototype.divideAndRemainder = bnDivideAndRemainder; | |
| Int128.prototype.modPow = bnModPow; | |
| Int128.prototype.modInverse = bnModInverse; | |
| Int128.prototype.pow = bnPow; | |
| Int128.prototype.gcd = bnGCD; | |
| Int128.prototype.isProbablePrime = bnIsProbablePrime; | |
| // JSBN-specific extension | |
| Int128.prototype.square = bnSquare; | |
| // 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: | |
| 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 = 222; // How many steps at maximum in arc in BuildArc() function | |
| ClipperLib.PI = 3.141592653589793; | |
| ClipperLib.PI2 = 2 * 3.141592653589793; | |
| ClipperLib.IntPoint = function () { | |
| var a = arguments; | |
| if (a.length == 1) { | |
| this.X = a[0].X; | |
| this.Y = a[0].Y; | |
| } | |
| if (a.length == 2) { | |
| this.X = a[0]; | |
| this.Y = a[1]; | |
| } | |
| }; | |
| ClipperLib.IntRect = function () { | |
| var a = arguments; | |
| if (a.length == 4) // function (l, t, r, b) | |
| { | |
| var l = a[0], | |
| t = a[1], | |
| r = a[2], | |
| b = a[3]; | |
| this.left = l; | |
| this.top = t; | |
| this.right = r; | |
| this.bottom = b; | |
| } | |
| else { | |
| this.left = 0; | |
| this.top = 0; | |
| this.right = 0; | |
| this.bottom = 0; | |
| } | |
| }; | |
| ClipperLib.Polygon = function () { | |
| return []; | |
| }; | |
| ClipperLib.Polygons = function () { | |
| return []; // Was previously [[]], but caused problems when pushed | |
| }; | |
| ClipperLib.ExPolygons = function () { | |
| var a = []; | |
| a.exPolygons = true; // this is needed to make "overloading" possible in Execute | |
| return a; | |
| } | |
| ClipperLib.ExPolygon = function () { | |
| this.outer = null; | |
| this.holes = null; | |
| }; | |
| 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.EdgeSide = { | |
| esLeft: 1, | |
| esRight: 2 | |
| }; | |
| ClipperLib.Protects = { | |
| ipNone: 0, | |
| ipLeft: 1, | |
| ipRight: 2, | |
| ipBoth: 3 | |
| }; | |
| ClipperLib.Direction = { | |
| dRightToLeft: 0, | |
| dLeftToRight: 1 | |
| }; | |
| ClipperLib.TEdge = function () { | |
| this.xbot = 0; | |
| this.ybot = 0; | |
| this.xcurr = 0; | |
| this.ycurr = 0; | |
| this.xtop = 0; | |
| this.ytop = 0; | |
| this.dx = 0; | |
| this.deltaX = 0; | |
| this.deltaY = 0; | |
| this.tmpX = 0; | |
| this.polyType = ClipperLib.PolyType.ptSubject; | |
| this.side = null; //= ClipperLib.EdgeSide.esNeither; | |
| this.windDelta = 0; | |
| this.windCnt = 0; | |
| this.windCnt2 = 0; | |
| this.outIdx = 0; | |
| this.next = null; | |
| this.prev = null; | |
| this.nextInLML = null; | |
| this.nextInAEL = null; | |
| this.prevInAEL = null; | |
| this.nextInSEL = null; | |
| this.prevInSEL = null; | |
| }; | |
| ClipperLib.IntersectNode = function () { | |
| this.edge1 = null; | |
| this.edge2 = null; | |
| this.pt = null; | |
| this.next = null; | |
| }; | |
| 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.FirstLeft = null; | |
| this.AppendLink = null; | |
| this.pts = null; | |
| this.bottomPt = null; | |
| }; | |
| ClipperLib.OutPt = function () { | |
| this.idx = 0; | |
| this.pt = null; | |
| this.next = null; | |
| this.prev = null; | |
| }; | |
| ClipperLib.JoinRec = function () { | |
| this.pt1a = null; | |
| this.pt1b = null; | |
| this.poly1Idx = 0; | |
| this.pt2a = null; | |
| this.pt2b = null; | |
| this.poly2Idx = 0; | |
| }; | |
| ClipperLib.HorzJoinRec = function () { | |
| this.edge = null; | |
| this.savedIdx = 0; | |
| }; | |
| ClipperLib.ClipperBase = function () { | |
| this.m_MinimaList = null; | |
| this.m_CurrentLM = null; | |
| this.m_edges = [ | |
| [] | |
| ]; // 2-dimensional array | |
| this.m_UseFullRange = false; | |
| }; | |
| // Ranges are in original C# too high for Javascript (in current state 2012 December): | |
| // protected const double horizontal = -3.4E+38; | |
| // internal const Int64 loRange = 0x3FFFFFFF; // = 1073741823 = sqrt(2^63 -1)/2 | |
| // internal const Int64 hiRange = 0x3FFFFFFFFFFFFFFFL; // = 4611686018427387903 = sqrt(2^127 -1)/2 | |
| // So had to adjust them to more suitable: | |
| ClipperLib.ClipperBase.horizontal = -9007199254740992; //-2^53 | |
| ClipperLib.ClipperBase.loRange = 47453132; // sqrt(2^53 -1)/2 | |
| ClipperLib.ClipperBase.hiRange = 4503599627370495; // sqrt(2^106 -1)/2 | |
| // If JS some day supports truly 64-bit integers, then these ranges can be as in C# | |
| // and biginteger library can be more simpler (as then 128bit can be represented as two 64bit numbers) | |
| ClipperLib.ClipperBase.PointsEqual = function (pt1, pt2) { | |
| return (pt1.X == pt2.X && pt1.Y == pt2.Y); | |
| }; | |
| ClipperLib.ClipperBase.prototype.PointIsVertex = function (pt, pp) { | |
| var pp2 = pp; | |
| do { | |
| if (ClipperLib.ClipperBase.PointsEqual(pp2.pt, pt)) return true; | |
| pp2 = pp2.next; | |
| } | |
| while (pp2 != pp); | |
| return false; | |
| }; | |
| ClipperLib.ClipperBase.prototype.PointInPolygon = function (pt, pp, UseFulllongRange) { | |
| var pp2 = pp; | |
| var result = false; | |
| if (UseFulllongRange) { | |
| do { | |
| if ((((pp2.pt.Y <= pt.Y) && (pt.Y < pp2.prev.pt.Y)) || ((pp2.prev.pt.Y <= pt.Y) && (pt.Y < pp2.pt.Y))) && new Int128(pt.X - pp2.pt.X) | |
| .compareTo( | |
| new Int128(pp2.prev.pt.X - pp2.pt.X) | |
| .multiply(new Int128(pt.Y - pp2.pt.Y)) | |
| .divide( | |
| new Int128(pp2.prev.pt.Y - pp2.pt.Y))) < 0) result = !result; | |
| pp2 = pp2.next; | |
| } | |
| while (pp2 != pp); | |
| } | |
| else { | |
| do { | |
| if ((((pp2.pt.Y <= pt.Y) && (pt.Y < pp2.prev.pt.Y)) || ((pp2.prev.pt.Y <= pt.Y) && (pt.Y < pp2.pt.Y))) && (pt.X - pp2.pt.X < (pp2.prev.pt.X - pp2.pt.X) * (pt.Y - pp2.pt.Y) / (pp2.prev.pt.Y - pp2.pt.Y))) result = !result; | |
| pp2 = pp2.next; | |
| } | |
| while (pp2 != pp); | |
| } | |
| return result; | |
| }; | |
| ClipperLib.ClipperBase.prototype.SlopesEqual = ClipperLib.ClipperBase.SlopesEqual = function () { | |
| var a = arguments; | |
| var e1, e2, pt1, pt2, pt3, pt4, UseFullRange; | |
| if (a.length == 3) // function (e1, e2, UseFullRange) | |
| { | |
| e1 = a[0], e2 = a[1], UseFullRange = a[2]; | |
| if (UseFullRange) return new Int128(e1.deltaY) | |
| .multiply(new Int128(e2.deltaX)) | |
| .toString() == new Int128(e1.deltaX) | |
| .multiply(new Int128(e2.deltaY)) | |
| .toString(); | |
| else return (e1.deltaY) * (e2.deltaX) == (e1.deltaX) * (e2.deltaY); | |
| } | |
| else if (a.length == 4) // function (pt1, pt2, pt3, UseFullRange) | |
| { | |
| pt1 = a[0], pt2 = a[1], pt3 = a[2], UseFullRange = a[3]; | |
| if (UseFullRange) return new Int128(pt1.Y - pt2.Y) | |
| .multiply(new Int128(pt2.X - pt3.X)) | |
| .toString() == new Int128(pt1.X - pt2.X) | |
| .multiply(new Int128(pt2.Y - pt3.Y)) | |
| .toString(); | |
| else return (pt1.Y - pt2.Y) * (pt2.X - pt3.X) - (pt1.X - pt2.X) * (pt2.Y - pt3.Y) == 0; | |
| } | |
| else if (a.length == 5) // function (pt1, pt2, pt3, pt4, UseFullRange) | |
| { | |
| pt1 = a[0], pt2 = a[1], pt3 = a[2], pt4 = a[3], UseFullRange = a[4]; | |
| if (UseFullRange) return new Int128(pt1.Y - pt2.Y) | |
| .multiply(new Int128(pt3.X - pt4.X)) | |
| .toString() == new Int128(pt1.X - pt2.X) | |
| .multiply(new Int128(pt3.Y - pt4.Y)) | |
| .toString(); | |
| else return (pt1.Y - pt2.Y) * (pt3.X - pt4.X) - (pt1.X - pt2.X) * (pt3.Y - pt4.Y) == 0; | |
| } | |
| }; | |
| ClipperLib.ClipperBase.prototype.Clear = function () { | |
| this.DisposeLocalMinimaList(); | |
| for (var i = 0; i < this.m_edges.length; ++i) { | |
| for (var j = 0; j < this.m_edges[i].length; ++j) | |
| this.m_edges[i][j] = null; | |
| ClipperLib.Clear(this.m_edges[i]); | |
| } | |
| ClipperLib.Clear(this.m_edges); | |
| this.m_UseFullRange = 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.AddPolygons = function (ppg, polyType) { | |
| var result = false; | |
| var res = false; | |
| if (!(ppg instanceof Array)) return result; | |
| for (var i = 0; i < ppg.length; ++i) { | |
| res = this.AddPolygon(ppg[i], polyType, true); | |
| if (res && res != "exceed") result = true; | |
| else if (res == "exceed") break; | |
| } | |
| if (res == "exceed") ClipperLib.Error("Coordinate exceeds range bounds in AddPolygons()."); | |
| return result; | |
| }; | |
| ClipperLib.ClipperBase.prototype.AddPolygon = function (pg, polyType, multiple) { | |
| if (!(pg instanceof Array)) return false; | |
| var len = pg.length; | |
| if (len < 3) return false; | |
| var p = new ClipperLib.Polygon(); | |
| p.push(new ClipperLib.IntPoint(pg[0].X, pg[0].Y)); | |
| var j = 0; | |
| var i; | |
| var exceed = false; | |
| for (i = 1; i < len; ++i) { | |
| var maxVal; | |
| if (this.m_UseFullRange) maxVal = ClipperLib.ClipperBase.hiRange; | |
| else maxVal = ClipperLib.ClipperBase.loRange; | |
| if (ClipperLib.Math_Abs_Int64(pg[i].X) > maxVal || ClipperLib.Math_Abs_Int64(pg[i].Y) > maxVal) { | |
| if (ClipperLib.Math_Abs_Int64(pg[i].X) > ClipperLib.ClipperBase.hiRange || ClipperLib.Math_Abs_Int64(pg[i].Y) > ClipperLib.ClipperBase.hiRange) { | |
| if (typeof(multiple) != "undefined") return "exceed"; | |
| exceed = true; | |
| break; | |
| } | |
| maxVal = ClipperLib.ClipperBase.hiRange; | |
| this.m_UseFullRange = true; | |
| } | |
| if (ClipperLib.ClipperBase.PointsEqual(p[j], pg[i])) continue; | |
| else if (j > 0 && this.SlopesEqual(p[j - 1], p[j], pg[i], this.m_UseFullRange)) { | |
| if (ClipperLib.ClipperBase.PointsEqual(p[j - 1], pg[i])) j--; | |
| } | |
| else j++; | |
| if (j < p.length) p[j] = pg[i]; | |
| else p.push(new ClipperLib.IntPoint(pg[i].X, pg[i].Y)); | |
| } | |
| if (exceed && typeof(multiple) == "undefined") | |
| ClipperLib.Error("Coordinate exceeds range bounds in AddPolygon()"); | |
| if (j < 2) return false; | |
| len = j + 1; | |
| while (len > 2) { | |
| if (ClipperLib.ClipperBase.PointsEqual(p[j], p[0])) j--; | |
| else if (ClipperLib.ClipperBase.PointsEqual(p[0], p[1]) || this.SlopesEqual(p[j], p[0], p[1], this.m_UseFullRange)) p[0] = p[j--]; | |
| else if (this.SlopesEqual(p[j - 1], p[j], p[0], this.m_UseFullRange)) j--; | |
| else if (this.SlopesEqual(p[0], p[1], p[2], this.m_UseFullRange)) { | |
| for (i = 2; i <= j; ++i) | |
| p[i - 1] = p[i]; | |
| j--; | |
| } | |
| else break; | |
| len--; | |
| } | |
| if (len < 3) return false; | |
| var edges = []; | |
| for (i = 0; i < len; i++) | |
| edges.push(new ClipperLib.TEdge()); | |
| this.m_edges.push(edges); | |
| edges[0].xcurr = p[0].X; | |
| edges[0].ycurr = p[0].Y; | |
| this.InitEdge(edges[len - 1], edges[0], edges[len - 2], p[len - 1], polyType); | |
| for (i = len - 2; i > 0; --i) | |
| this.InitEdge(edges[i], edges[i + 1], edges[i - 1], p[i], polyType); | |
| this.InitEdge(edges[0], edges[1], edges[len - 1], p[0], polyType); | |
| var e = edges[0]; | |
| var eHighest = e; | |
| do { | |
| e.xcurr = e.xbot; | |
| e.ycurr = e.ybot; | |
| if (e.ytop < eHighest.ytop) eHighest = e; | |
| e = e.next; | |
| } | |
| while (e != edges[0]); | |
| if (eHighest.windDelta > 0) eHighest = eHighest.next; | |
| if (eHighest.dx == ClipperLib.ClipperBase.horizontal) eHighest = eHighest.next; | |
| e = eHighest; | |
| do { | |
| e = this.AddBoundsToLML(e); | |
| } | |
| while (e != eHighest); | |
| return true; | |
| }; | |
| ClipperLib.ClipperBase.prototype.InitEdge = function (e, eNext, ePrev, pt, polyType) { | |
| e.next = eNext; | |
| e.prev = ePrev; | |
| e.xcurr = pt.X; | |
| e.ycurr = pt.Y; | |
| if (e.ycurr >= e.next.ycurr) { | |
| e.xbot = e.xcurr; | |
| e.ybot = e.ycurr; | |
| e.xtop = e.next.xcurr; | |
| e.ytop = e.next.ycurr; | |
| e.windDelta = 1; | |
| } | |
| else { | |
| e.xtop = e.xcurr; | |
| e.ytop = e.ycurr; | |
| e.xbot = e.next.xcurr; | |
| e.ybot = e.next.ycurr; | |
| e.windDelta = -1; | |
| } | |
| this.SetDx(e); | |
| e.polyType = polyType; | |
| e.outIdx = -1; | |
| }; | |
| ClipperLib.ClipperBase.prototype.SetDx = function (e) { | |
| e.deltaX = (e.xtop - e.xbot); | |
| e.deltaY = (e.ytop - e.ybot); | |
| if (e.deltaY == 0) e.dx = ClipperLib.ClipperBase.horizontal; | |
| else e.dx = (e.deltaX) / (e.deltaY); | |
| }; | |
| ClipperLib.ClipperBase.prototype.AddBoundsToLML = function (e) { | |
| e.nextInLML = null; | |
| e = e.next; | |
| for (; ;) { | |
| if (e.dx == ClipperLib.ClipperBase.horizontal) { | |
| if (e.next.ytop < e.ytop && e.next.xbot > e.prev.xbot) break; | |
| if (e.xtop != e.prev.xbot) this.SwapX(e); | |
| e.nextInLML = e.prev; | |
| } | |
| else if (e.ycurr == e.prev.ycurr) break; | |
| else e.nextInLML = e.prev; | |
| e = e.next; | |
| } | |
| var newLm = new ClipperLib.LocalMinima(); | |
| newLm.next = null; | |
| newLm.Y = e.prev.ybot; | |
| if (e.dx == ClipperLib.ClipperBase.horizontal) { | |
| if (e.xbot != e.prev.xbot) this.SwapX(e); | |
| newLm.leftBound = e.prev; | |
| newLm.rightBound = e; | |
| } | |
| else if (e.dx < e.prev.dx) { | |
| newLm.leftBound = e.prev; | |
| newLm.rightBound = e; | |
| } | |
| else { | |
| newLm.leftBound = e; | |
| newLm.rightBound = e.prev; | |
| } | |
| newLm.leftBound.side = ClipperLib.EdgeSide.esLeft; | |
| newLm.rightBound.side = ClipperLib.EdgeSide.esRight; | |
| this.InsertLocalMinima(newLm); | |
| for (; ;) { | |
| if (e.next.ytop == e.ytop && e.next.dx != ClipperLib.ClipperBase.horizontal) break; | |
| e.nextInLML = e.next; | |
| e = e.next; | |
| if (e.dx == ClipperLib.ClipperBase.horizontal && e.xbot != e.prev.xtop) this.SwapX(e); | |
| } | |
| return e.next; | |
| }; | |
| 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.SwapX = function (e) { | |
| e.xcurr = e.xtop; | |
| e.xtop = e.xbot; | |
| e.xbot = e.xcurr; | |
| }; | |
| ClipperLib.ClipperBase.prototype.Reset = function () { | |
| this.m_CurrentLM = this.m_MinimaList; | |
| var lm = this.m_MinimaList; | |
| while (lm != null) { | |
| var e = lm.leftBound; | |
| while (e != null) { | |
| e.xcurr = e.xbot; | |
| e.ycurr = e.ybot; | |
| e.side = ClipperLib.EdgeSide.esLeft; | |
| e.outIdx = -1; | |
| e = e.nextInLML; | |
| } | |
| e = lm.rightBound; | |
| while (e != null) { | |
| e.xcurr = e.xbot; | |
| e.ycurr = e.ybot; | |
| e.side = ClipperLib.EdgeSide.esRight; | |
| e.outIdx = -1; | |
| e = e.nextInLML; | |
| } | |
| lm = lm.next; | |
| } | |
| return; | |
| }; | |
| ClipperLib.ClipperBase.prototype.GetBounds = function () { | |
| var result = new ClipperLib.IntRect(); | |
| var lm = this.m_MinimaList; | |
| if (lm == null) return result; | |
| result.left = lm.leftBound.xbot; | |
| result.top = lm.leftBound.ybot; | |
| result.right = lm.leftBound.xbot; | |
| result.bottom = lm.leftBound.ybot; | |
| while (lm != null) { | |
| if (lm.leftBound.ybot > result.bottom) result.bottom = lm.leftBound.ybot; | |
| var e = lm.leftBound; | |
| for (; ;) { | |
| var bottomE = e; | |
| while (e.nextInLML != null) { | |
| if (e.xbot < result.left) result.left = e.xbot; | |
| if (e.xbot > result.right) result.right = e.xbot; | |
| e = e.nextInLML; | |
| } | |
| if (e.xbot < result.left) result.left = e.xbot; | |
| if (e.xbot > result.right) result.right = e.xbot; | |
| if (e.xtop < result.left) result.left = e.xtop; | |
| if (e.xtop > result.right) result.right = e.xtop; | |
| if (e.ytop < result.top) result.top = e.ytop; | |
| if (bottomE == lm.leftBound) e = lm.rightBound; | |
| else break; | |
| } | |
| lm = lm.next; | |
| } | |
| return result; | |
| }; | |
| ClipperLib.Clipper = function () { | |
| this.m_PolyOuts = null; | |
| this.m_ClipType = ClipperLib.ClipType.ctIntersection; | |
| this.m_Scanbeam = null; | |
| this.m_ActiveEdges = null; | |
| this.m_SortedEdges = null; | |
| this.m_IntersectNodes = null; | |
| this.m_ExecuteLocked = false; | |
| this.m_ClipFillType = ClipperLib.PolyFillType.pftEvenOdd; | |
| this.m_SubjFillType = ClipperLib.PolyFillType.pftEvenOdd; | |
| this.m_Joins = null; | |
| this.m_HorizJoins = null; | |
| this.m_ReverseOutput = false; | |
| this.m_UsingExPolygons = false; | |
| ClipperLib.ClipperBase.call(this); | |
| this.m_Scanbeam = null; | |
| this.m_ActiveEdges = null; | |
| this.m_SortedEdges = null; | |
| this.m_IntersectNodes = null; | |
| this.m_ExecuteLocked = false; | |
| this.m_PolyOuts = []; | |
| this.m_Joins = []; | |
| this.m_HorizJoins = []; | |
| this.m_ReverseOutput = false; | |
| this.m_UsingExPolygons = false; | |
| }; | |
| ClipperLib.Clipper.prototype.Clear = function () { | |
| if (this.m_edges.length == 0) return; | |
| 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; | |
| this.DisposeAllPolyPts(); | |
| var lm = this.m_MinimaList; | |
| while (lm != null) { | |
| this.InsertScanbeam(lm.Y); | |
| this.InsertScanbeam(lm.leftBound.ytop); | |
| lm = lm.next; | |
| } | |
| }; | |
| ClipperLib.Clipper.prototype.get_ReverseSolution = function () { | |
| return this.m_ReverseOutput; | |
| }; | |
| ClipperLib.Clipper.prototype.set_ReverseSolution = function (value) { | |
| this.m_ReverseOutput = value; | |
| }; | |
| ClipperLib.Clipper.prototype.InsertScanbeam = function (Y) { | |
| var newSb; | |
| if (this.m_Scanbeam == null) { | |
| this.m_Scanbeam = new ClipperLib.Scanbeam(); | |
| this.m_Scanbeam.next = null; | |
| this.m_Scanbeam.Y = Y; | |
| } | |
| else if (Y > this.m_Scanbeam.Y) { | |
| 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; | |
| newSb = new ClipperLib.Scanbeam(); | |
| newSb.Y = Y; | |
| newSb.next = sb2.next; | |
| sb2.next = newSb; | |
| } | |
| }; | |
| ClipperLib.Clipper.prototype.Execute = function (clipType, solution, subjFillType, clipFillType) { | |
| var succeeded; | |
| if (arguments.length == 2) { | |
| subjFillType = ClipperLib.PolyFillType.pftEvenOdd; | |
| clipFillType = ClipperLib.PolyFillType.pftEvenOdd; | |
| } | |
| if (typeof(solution.exPolygons) == "undefined") // hacky way to test if solution is not exPolygons | |
| { | |
| if (this.m_ExecuteLocked) return false; | |
| this.m_ExecuteLocked = true; | |
| ClipperLib.Clear(solution); | |
| this.m_SubjFillType = subjFillType; | |
| this.m_ClipFillType = clipFillType; | |
| this.m_ClipType = clipType; | |
| this.m_UsingExPolygons = false; | |
| succeeded = this.ExecuteInternal(); | |
| if (succeeded) { | |
| this.BuildResult(solution); | |
| } | |
| this.m_ExecuteLocked = false; | |
| return succeeded; | |
| } | |
| else { | |
| if (this.m_ExecuteLocked) return false; | |
| this.m_ExecuteLocked = true; | |
| ClipperLib.Clear(solution); | |
| this.m_SubjFillType = subjFillType; | |
| this.m_ClipFillType = clipFillType; | |
| this.m_ClipType = clipType; | |
| this.m_UsingExPolygons = true; | |
| succeeded = this.ExecuteInternal(); | |
| if (succeeded) { | |
| this.BuildResultEx(solution); | |
| } | |
| this.m_ExecuteLocked = false; | |
| return succeeded; | |
| } | |
| }; | |
| ClipperLib.Clipper.prototype.PolySort = function (or1, or2) { | |
| if (or1 == or2) return 0; | |
| else if (or1.pts == null || or2.pts == null) { | |
| if ((or1.pts == null) != (or2.pts == null)) { | |
| return or1.pts == null ? 1 : -1; | |
| } | |
| else return 0; | |
| } | |
| var i1, i2; | |
| if (or1.isHole) i1 = or1.FirstLeft.idx; | |
| else i1 = or1.idx; | |
| if (or2.isHole) i2 = or2.FirstLeft.idx; | |
| else i2 = or2.idx; | |
| var result = i1 - i2; | |
| if (result == 0 && (or1.isHole != or2.isHole)) { | |
| return or1.isHole ? 1 : -1; | |
| } | |
| return result; | |
| }; | |
| ClipperLib.Clipper.prototype.FindAppendLinkEnd = function (outRec) { | |
| while (outRec.AppendLink != null) | |
| outRec = outRec.AppendLink; | |
| return outRec; | |
| }; | |
| ClipperLib.Clipper.prototype.FixHoleLinkage = function (outRec) { | |
| var tmp; | |
| if (outRec.bottomPt != null) tmp = this.m_PolyOuts[outRec.bottomPt.idx].FirstLeft; | |
| else tmp = outRec.FirstLeft; | |
| if (outRec == tmp) ClipperLib.Error("HoleLinkage error"); | |
| if (tmp != null) { | |
| if (tmp.AppendLink != null) tmp = this.FindAppendLinkEnd(tmp); | |
| if (tmp == outRec) tmp = null; | |
| else if (tmp.isHole) { | |
| this.FixHoleLinkage(tmp); | |
| tmp = tmp.FirstLeft; | |
| } | |
| } | |
| outRec.FirstLeft = tmp; | |
| if (tmp == null) outRec.isHole = false; | |
| outRec.AppendLink = null; | |
| }; | |
| ClipperLib.Clipper.prototype.ExecuteInternal = function () { | |
| var succeeded; | |
| try { | |
| this.Reset(); | |
| if (this.m_CurrentLM == null) return true; | |
| var botY = this.PopScanbeam(); | |
| do { | |
| this.InsertLocalMinimaIntoAEL(botY); | |
| ClipperLib.Clear(this.m_HorizJoins); | |
| this.ProcessHorizontals(); | |
| var topY = this.PopScanbeam(); | |
| succeeded = this.ProcessIntersections(botY, topY); | |
| if (!succeeded) break; | |
| this.ProcessEdgesAtTopOfScanbeam(topY); | |
| botY = topY; | |
| } | |
| while (this.m_Scanbeam != null); | |
| } | |
| catch ($$e1) { | |
| succeeded = false; | |
| } | |
| if (succeeded) { | |
| var outRec; | |
| for (var i = 0; i < this.m_PolyOuts.length; i++) { | |
| outRec = this.m_PolyOuts[i]; | |
| if (outRec.pts == null) continue; | |
| this.FixupOutPolygon(outRec); | |
| if (outRec.pts == null) continue; | |
| if (outRec.isHole && this.m_UsingExPolygons) this.FixHoleLinkage(outRec); | |
| if ((outRec.isHole ^ this.m_ReverseOutput) == (this.Area(outRec, this.m_UseFullRange) > 0)) | |
| this.ReversePolyPtLinks(outRec.pts); | |
| } | |
| this.JoinCommonEdges(); | |
| if (this.m_UsingExPolygons) this.m_PolyOuts.sort(this.PolySort); | |
| } | |
| ClipperLib.Clear(this.m_Joins); | |
| ClipperLib.Clear(this.m_HorizJoins); | |
| return succeeded; | |
| }; | |
| ClipperLib.Clipper.prototype.PopScanbeam = function () { | |
| var Y = this.m_Scanbeam.Y; | |
| var sb2 = this.m_Scanbeam; | |
| this.m_Scanbeam = this.m_Scanbeam.next; | |
| sb2 = null; | |
| return Y; | |
| }; | |
| ClipperLib.Clipper.prototype.DisposeAllPolyPts = function () { | |
| for (var i = 0; i < this.m_PolyOuts.length; ++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 (e1, e2, e1OutIdx, e2OutIdx) { | |
| var jr = new ClipperLib.JoinRec(); | |
| if (e1OutIdx >= 0) jr.poly1Idx = e1OutIdx; | |
| else jr.poly1Idx = e1.outIdx; | |
| jr.pt1a = new ClipperLib.IntPoint(e1.xcurr, e1.ycurr); | |
| jr.pt1b = new ClipperLib.IntPoint(e1.xtop, e1.ytop); | |
| if (e2OutIdx >= 0) jr.poly2Idx = e2OutIdx; | |
| else jr.poly2Idx = e2.outIdx; | |
| jr.pt2a = new ClipperLib.IntPoint(e2.xcurr, e2.ycurr); | |
| jr.pt2b = new ClipperLib.IntPoint(e2.xtop, e2.ytop); | |
| this.m_Joins.push(jr); | |
| }; | |
| ClipperLib.Clipper.prototype.AddHorzJoin = function (e, idx) { | |
| var hj = new ClipperLib.HorzJoinRec(); | |
| hj.edge = e; | |
| hj.savedIdx = idx; | |
| this.m_HorizJoins.push(hj); | |
| }; | |
| ClipperLib.Clipper.prototype.InsertLocalMinimaIntoAEL = function (botY) { | |
| var pt, pt2; | |
| while (this.m_CurrentLM != null && (this.m_CurrentLM.Y == botY)) { | |
| var lb = this.m_CurrentLM.leftBound; | |
| var rb = this.m_CurrentLM.rightBound; | |
| this.InsertEdgeIntoAEL(lb); | |
| this.InsertScanbeam(lb.ytop); | |
| this.InsertEdgeIntoAEL(rb); | |
| if (this.IsEvenOddFillType(lb)) { | |
| lb.windDelta = 1; | |
| rb.windDelta = 1; | |
| } | |
| else { | |
| rb.windDelta = -lb.windDelta; | |
| } | |
| this.SetWindingCount(lb); | |
| rb.windCnt = lb.windCnt; | |
| rb.windCnt2 = lb.windCnt2; | |
| if (rb.dx == ClipperLib.ClipperBase.horizontal) { | |
| this.AddEdgeToSEL(rb); | |
| this.InsertScanbeam(rb.nextInLML.ytop); | |
| } | |
| else this.InsertScanbeam(rb.ytop); | |
| if (this.IsContributing(lb)) this.AddLocalMinPoly(lb, rb, new ClipperLib.IntPoint(lb.xcurr, this.m_CurrentLM.Y)); | |
| if (rb.outIdx >= 0) { | |
| if (rb.dx == ClipperLib.ClipperBase.horizontal) { | |
| for (var i = 0; i < this.m_HorizJoins.length; i++) { | |
| pt = new ClipperLib.IntPoint(), pt2 = new ClipperLib.IntPoint(); | |
| var hj = this.m_HorizJoins[i]; | |
| if ((function () { | |
| pt = { | |
| Value: pt | |
| }; | |
| pt2 = { | |
| Value: pt2 | |
| }; | |
| var $res = this.GetOverlapSegment(new ClipperLib.IntPoint(hj.edge.xbot, hj.edge.ybot), | |
| new ClipperLib.IntPoint(hj.edge.xtop, hj.edge.ytop), | |
| new ClipperLib.IntPoint(rb.xbot, rb.ybot), | |
| new ClipperLib.IntPoint(rb.xtop, rb.ytop), | |
| pt, pt2); | |
| pt = pt.Value; | |
| pt2 = pt2.Value; | |
| return $res; | |
| }) | |
| .call(this)) this.AddJoin(hj.edge, rb, hj.savedIdx, -1); | |
| } | |
| } | |
| } | |
| if (lb.nextInAEL != rb) { | |
| if (rb.outIdx >= 0 && rb.prevInAEL.outIdx >= 0 && this.SlopesEqual(rb.prevInAEL, rb, this.m_UseFullRange)) this.AddJoin(rb, rb.prevInAEL, -1, -1); | |
| var e = lb.nextInAEL; | |
| pt = new ClipperLib.IntPoint(lb.xcurr, lb.ycurr); | |
| while (e != rb) { | |
| if (e == null) ClipperLib.Error("InsertLocalMinimaIntoAEL: missing rightbound!"); | |
| this.IntersectEdges(rb, e, pt, ClipperLib.Protects.ipNone); | |
| e = e.nextInAEL; | |
| } | |
| } | |
| this.PopLocalMinima(); | |
| } | |
| }; | |
| ClipperLib.Clipper.prototype.InsertEdgeIntoAEL = function (edge) { | |
| edge.prevInAEL = null; | |
| edge.nextInAEL = null; | |
| if (this.m_ActiveEdges == null) { | |
| this.m_ActiveEdges = edge; | |
| } | |
| else if (this.E2InsertsBeforeE1(this.m_ActiveEdges, edge)) { | |
| edge.nextInAEL = this.m_ActiveEdges; | |
| this.m_ActiveEdges.prevInAEL = edge; | |
| this.m_ActiveEdges = edge; | |
| } | |
| else { | |
| var e = this.m_ActiveEdges; | |
| while (e.nextInAEL != null && !this.E2InsertsBeforeE1(e.nextInAEL, edge)) | |
| e = e.nextInAEL; | |
| edge.nextInAEL = e.nextInAEL; | |
| if (e.nextInAEL != null) e.nextInAEL.prevInAEL = edge; | |
| edge.prevInAEL = e; | |
| e.nextInAEL = edge; | |
| } | |
| }; | |
| ClipperLib.Clipper.prototype.E2InsertsBeforeE1 = function (e1, e2) { | |
| return e2.xcurr == e1.xcurr ? e2.dx > e1.dx : e2.xcurr < e1.xcurr; | |
| }; | |
| ClipperLib.Clipper.prototype.IsEvenOddFillType = function (edge) { | |
| if (edge.polyType == ClipperLib.PolyType.ptSubject) return this.m_SubjFillType == ClipperLib.PolyFillType.pftEvenOdd; | |
| else return this.m_ClipFillType == ClipperLib.PolyFillType.pftEvenOdd; | |
| }; | |
| ClipperLib.Clipper.prototype.IsEvenOddAltFillType = function (edge) { | |
| if (edge.polyType == ClipperLib.PolyType.ptSubject) return this.m_ClipFillType == ClipperLib.PolyFillType.pftEvenOdd; | |
| else return this.m_SubjFillType == ClipperLib.PolyFillType.pftEvenOdd; | |
| }; | |
| ClipperLib.Clipper.prototype.IsContributing = function (edge) { | |
| var pft, pft2; | |
| if (edge.polyType == 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: | |
| case ClipperLib.PolyFillType.pftNonZero: | |
| if (ClipperLib.Math_Abs_Int32(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); | |
| } | |
| break; | |
| 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); | |
| } | |
| break; | |
| case ClipperLib.ClipType.ctDifference: | |
| if (edge.polyType == ClipperLib.PolyType.ptSubject) switch (pft2) { | |
| case ClipperLib.PolyFillType.pftEvenOdd: | |
| case ClipperLib.PolyFillType.pftNonZero: | |
| return (edge.windCnt2 == 0); | |
| case ClipperLib.PolyFillType.pftPositive: | |
| return (edge.windCnt2 <= 0); | |
| default: | |
| return (edge.windCnt2 >= 0); | |
| } | |
| 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); | |
| } | |
| } | |
| return true; | |
| }; | |
| ClipperLib.Clipper.prototype.SetWindingCount = function (edge) { | |
| var e = edge.prevInAEL; | |
| while (e != null && e.polyType != edge.polyType) | |
| e = e.prevInAEL; | |
| if (e == null) { | |
| edge.windCnt = edge.windDelta; | |
| edge.windCnt2 = 0; | |
| e = this.m_ActiveEdges; | |
| } | |
| else if (this.IsEvenOddFillType(edge)) { | |
| edge.windCnt = 1; | |
| edge.windCnt2 = e.windCnt2; | |
| e = e.nextInAEL; | |
| } | |
| else { | |
| if (e.windCnt * e.windDelta < 0) { | |
| if (ClipperLib.Math_Abs_Int32(e.windCnt) > 1) { | |
| if (e.windDelta * edge.windDelta < 0) edge.windCnt = e.windCnt; | |
| else edge.windCnt = e.windCnt + edge.windDelta; | |
| } | |
| else edge.windCnt = e.windCnt + e.windDelta + edge.windDelta; | |
| } | |
| else { | |
| if (ClipperLib.Math_Abs_Int32(e.windCnt) > 1 && e.windDelta * edge.windDelta < 0) edge.windCnt = e.windCnt; | |
| else if (e.windCnt + edge.windDelta == 0) edge.windCnt = e.windCnt; | |
| else edge.windCnt = e.windCnt + edge.windDelta; | |
| } | |
| edge.windCnt2 = e.windCnt2; | |
| e = e.nextInAEL; | |
| } | |
| if (this.IsEvenOddAltFillType(edge)) { | |
| while (e != edge) { | |
| edge.windCnt2 = (edge.windCnt2 == 0) ? 1 : 0; | |
| e = e.nextInAEL; | |
| } | |
| } | |
| else { | |
| while (e != edge) { | |
| edge.windCnt2 += e.windDelta; | |
| e = e.nextInAEL; | |
| } | |
| } | |
| }; | |
| ClipperLib.Clipper.prototype.AddEdgeToSEL = function (edge) { | |
| 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; | |
| if (this.m_ActiveEdges == null) return; | |
| this.m_SortedEdges.prevInSEL = null; | |
| e = e.nextInAEL; | |
| while (e != null) { | |
| e.prevInSEL = e.prevInAEL; | |
| e.prevInSEL.nextInSEL = e; | |
| e.nextInSEL = null; | |
| e = e.nextInAEL; | |
| } | |
| }; | |
| ClipperLib.Clipper.prototype.SwapPositionsInAEL = function (edge1, edge2) { | |
| var next, prev; | |
| if (edge1.nextInAEL == null && edge1.prevInAEL == null) return; | |
| if (edge2.nextInAEL == null && edge2.prevInAEL == null) return; | |
| if (edge1.nextInAEL == edge2) { | |
| next = edge2.nextInAEL; | |
| if (next != null) next.prevInAEL = edge1; | |
| prev = edge1.prevInAEL; | |
| if (prev != null) prev.nextInAEL = edge2; | |
| edge2.prevInAEL = prev; | |
| edge2.nextInAEL = edge1; | |
| edge1.prevInAEL = edge2; | |
| edge1.nextInAEL = next; | |
| } | |
| else if (edge2.nextInAEL == edge1) { | |
| next = edge1.nextInAEL; | |
| if (next != null) next.prevInAEL = edge2; | |
| prev = edge2.prevInAEL; | |
| if (prev != null) prev.nextInAEL = edge1; | |
| edge1.prevInAEL = prev; | |
| edge1.nextInAEL = edge2; | |
| edge2.prevInAEL = edge1; | |
| edge2.nextInAEL = next; | |
| } | |
| else { | |
| next = edge1.nextInAEL; | |
| prev = edge1.prevInAEL; | |
| edge1.nextInAEL = edge2.nextInAEL; | |
| if (edge1.nextInAEL != null) edge1.nextInAEL.prevInAEL = edge1; | |
| edge1.prevInAEL = edge2.prevInAEL; | |
| if (edge1.prevInAEL != null) edge1.prevInAEL.nextInAEL = edge1; | |
| edge2.nextInAEL = next; | |
| if (edge2.nextInAEL != null) edge2.nextInAEL.prevInAEL = edge2; | |
| edge2.prevInAEL = prev; | |
| if (edge2.prevInAEL != null) edge2.prevInAEL.nextInAEL = edge2; | |
| } | |
| if (edge1.prevInAEL == null) this.m_ActiveEdges = edge1; | |
| else if (edge2.prevInAEL == null) this.m_ActiveEdges = edge2; | |
| }; | |
| ClipperLib.Clipper.prototype.SwapPositionsInSEL = function (edge1, edge2) { | |
| var next, prev; | |
| if (edge1.nextInSEL == null && edge1.prevInSEL == null) return; | |
| if (edge2.nextInSEL == null && edge2.prevInSEL == null) return; | |
| if (edge1.nextInSEL == edge2) { | |
| next = edge2.nextInSEL; | |
| if (next != null) next.prevInSEL = edge1; | |
| prev = edge1.prevInSEL; | |
| if (prev != null) prev.nextInSEL = edge2; | |
| edge2.prevInSEL = prev; | |
| edge2.nextInSEL = edge1; | |
| edge1.prevInSEL = edge2; | |
| edge1.nextInSEL = next; | |
| } | |
| else if (edge2.nextInSEL == edge1) { | |
| next = edge1.nextInSEL; | |
| if (next != null) next.prevInSEL = edge2; | |
| prev = edge2.prevInSEL; | |
| if (prev != null) prev.nextInSEL = edge1; | |
| edge1.prevInSEL = prev; | |
| edge1.nextInSEL = edge2; | |
| edge2.prevInSEL = edge1; | |
| edge2.nextInSEL = next; | |
| } | |
| else { | |
| next = edge1.nextInSEL; | |
| prev = edge1.prevInSEL; | |
| edge1.nextInSEL = edge2.nextInSEL; | |
| if (edge1.nextInSEL != null) edge1.nextInSEL.prevInSEL = edge1; | |
| edge1.prevInSEL = edge2.prevInSEL; | |
| if (edge1.prevInSEL != null) edge1.prevInSEL.nextInSEL = edge1; | |
| edge2.nextInSEL = next; | |
| if (edge2.nextInSEL != null) edge2.nextInSEL.prevInSEL = edge2; | |
| edge2.prevInSEL = prev; | |
| if (edge2.prevInSEL != null) edge2.prevInSEL.nextInSEL = edge2; | |
| } | |
| if (edge1.prevInSEL == null) this.m_SortedEdges = edge1; | |
| else if (edge2.prevInSEL == null) this.m_SortedEdges = edge2; | |
| }; | |
| ClipperLib.Clipper.prototype.AddLocalMaxPoly = function (e1, e2, pt) { | |
| this.AddOutPt(e1, 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 e, prevE; | |
| if (e2.dx == ClipperLib.ClipperBase.horizontal || (e1.dx > e2.dx)) { | |
| 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 { | |
| 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)) && this.SlopesEqual(e, prevE, this.m_UseFullRange)) this.AddJoin(e, prevE, -1, -1); | |
| }; | |
| ClipperLib.Clipper.prototype.CreateOutRec = function () { | |
| var result = new ClipperLib.OutRec(); | |
| result.idx = -1; | |
| result.isHole = false; | |
| result.FirstLeft = null; | |
| result.AppendLink = null; | |
| result.pts = null; | |
| result.bottomPt = null; | |
| return result; | |
| }; | |
| ClipperLib.Clipper.prototype.AddOutPt = function (e, pt) { | |
| var outRec, op; | |
| var ToFront = (e.side == ClipperLib.EdgeSide.esLeft); | |
| if (e.outIdx < 0) { | |
| outRec = this.CreateOutRec(); | |
| this.m_PolyOuts.push(outRec); | |
| outRec.idx = this.m_PolyOuts.length - 1; | |
| e.outIdx = outRec.idx; | |
| op = new ClipperLib.OutPt(); | |
| outRec.pts = op; | |
| outRec.bottomPt = op; | |
| op.pt = pt; | |
| op.idx = outRec.idx; | |
| op.next = op; | |
| op.prev = op; | |
| this.SetHoleState(e, outRec); | |
| } | |
| else { | |
| outRec = this.m_PolyOuts[e.outIdx]; | |
| op = outRec.pts; | |
| var op2; | |
| if (ToFront && ClipperLib.ClipperBase.PointsEqual(pt, op.pt) || (!ToFront && ClipperLib.ClipperBase.PointsEqual(pt, op.prev.pt))) return; | |
| op2 = new ClipperLib.OutPt(); | |
| op2.pt = pt; | |
| op2.idx = outRec.idx; | |
| if (op2.pt.Y == outRec.bottomPt.pt.Y && op2.pt.X < outRec.bottomPt.pt.X) outRec.bottomPt = op2; | |
| op2.next = op; | |
| op2.prev = op.prev; | |
| op2.prev.next = op2; | |
| op.prev = op2; | |
| if (ToFront) outRec.pts = op2; | |
| } | |
| }; | |
| ClipperLib.Clipper.prototype.SwapPoints = function (pt1, pt2) { | |
| var tmp = pt1.Value; | |
| pt1.Value = pt2.Value; | |
| pt2.Value = tmp; | |
| }; | |
| ClipperLib.Clipper.prototype.GetOverlapSegment = function (pt1a, pt1b, pt2a, pt2b, pt1, pt2) { | |
| if (ClipperLib.Math_Abs_Int64(pt1a.X - pt1b.X) > ClipperLib.Math_Abs_Int64(pt1a.Y - pt1b.Y)) { | |
| if (pt1a.X > pt1b.X) | |
| (function () { | |
| pt1a = { | |
| Value: pt1a | |
| }; | |
| pt1b = { | |
| Value: pt1b | |
| }; | |
| var $res = this.SwapPoints(pt1a, pt1b); | |
| pt1a = pt1a.Value; | |
| pt1b = pt1b.Value; | |
| return $res; | |
| }) | |
| .call(this); | |
| if (pt2a.X > pt2b.X) | |
| (function () { | |
| pt2a = { | |
| Value: pt2a | |
| }; | |
| pt2b = { | |
| Value: pt2b | |
| }; | |
| var $res = this.SwapPoints(pt2a, pt2b); | |
| pt2a = pt2a.Value; | |
| pt2b = pt2b.Value; | |
| return $res; | |
| }) | |
| .call(this); | |
| if (pt1a.X > pt2a.X) pt1.Value = pt1a; | |
| else pt1.Value = pt2a; | |
| if (pt1b.X < pt2b.X) pt2.Value = pt1b; | |
| else pt2.Value = pt2b; | |
| return pt1.Value.X < pt2.Value.X; | |
| } | |
| else { | |
| if (pt1a.Y < pt1b.Y) | |
| (function () { | |
| pt1a = { | |
| Value: pt1a | |
| }; | |
| pt1b = { | |
| Value: pt1b | |
| }; | |
| var $res = this.SwapPoints(pt1a, pt1b); | |
| pt1a = pt1a.Value; | |
| pt1b = pt1b.Value; | |
| return $res; | |
| }) | |
| .call(this); | |
| if (pt2a.Y < pt2b.Y) | |
| (function () { | |
| pt2a = { | |
| Value: pt2a | |
| }; | |
| pt2b = { | |
| Value: pt2b | |
| }; | |
| var $res = this.SwapPoints(pt2a, pt2b); | |
| pt2a = pt2a.Value; | |
| pt2b = pt2b.Value; | |
| return $res; | |
| }) | |
| .call(this); | |
| if (pt1a.Y < pt2a.Y) pt1.Value = pt1a; | |
| else pt1.Value = pt2a; | |
| if (pt1b.Y > pt2b.Y) pt2.Value = pt1b; | |
| else pt2.Value = pt2b; | |
| return pt1.Value.Y > pt2.Value.Y; | |
| } | |
| }; | |
| ClipperLib.Clipper.prototype.FindSegment = function (pp, UseFullInt64Range, pt1, pt2) { | |
| if (pp.Value == null) return false; | |
| var pp2 = pp.Value; | |
| var pt1a = new ClipperLib.IntPoint(pt1.Value); | |
| var pt2a = new ClipperLib.IntPoint(pt2.Value); | |
| do { | |
| // Timo's comment: for some reason calling SlopesEqual() below uses big integers | |
| // So although coordinates are low (eg. 900), big integers are sometimes used. | |
| // => Fixed according to changes in original Clipper ver 5.1.2 (25 February 2013) | |
| if (this.SlopesEqual(pt1a, pt2a, pp.Value.pt, pp.Value.prev.pt, UseFullInt64Range) && this.SlopesEqual(pt1a, pt2a, pp.Value.pt, UseFullInt64Range) && this.GetOverlapSegment(pt1a, pt2a, pp.Value.pt, pp.Value.prev.pt, pt1, pt2)) return true; | |
| pp.Value = pp.Value.next; | |
| } | |
| while (pp.Value != pp2); | |
| return false; | |
| }; | |
| ClipperLib.Clipper.prototype.Pt3IsBetweenPt1AndPt2 = function (pt1, pt2, pt3) { | |
| if (ClipperLib.ClipperBase.PointsEqual(pt1, pt3) || ClipperLib.ClipperBase.PointsEqual(pt2, pt3)) return true; | |
| else if (pt1.X != pt2.X) return (pt1.X < pt3.X) == (pt3.X < pt2.X); | |
| else return (pt1.Y < pt3.Y) == (pt3.Y < pt2.Y); | |
| }; | |
| ClipperLib.Clipper.prototype.InsertPolyPtBetween = function (p1, p2, pt) { | |
| var result = new ClipperLib.OutPt(); | |
| result.pt = pt; | |
| 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) { | |
| 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.ClipperBase.PointsEqual(p.pt, btmPt1.pt) && (p != btmPt1)) | |
| p = p.prev; | |
| var dx1p = ClipperLib.Math_Abs_Double(this.GetDx(btmPt1.pt, p.pt)); | |
| p = btmPt1.next; | |
| while (ClipperLib.ClipperBase.PointsEqual(p.pt, btmPt1.pt) && (p != btmPt1)) | |
| p = p.next; | |
| var dx1n = ClipperLib.Math_Abs_Double(this.GetDx(btmPt1.pt, p.pt)); | |
| p = btmPt2.prev; | |
| while (ClipperLib.ClipperBase.PointsEqual(p.pt, btmPt2.pt) && (p != btmPt2)) | |
| p = p.prev; | |
| var dx2p = ClipperLib.Math_Abs_Double(this.GetDx(btmPt2.pt, p.pt)); | |
| p = btmPt2.next; | |
| while (ClipperLib.ClipperBase.PointsEqual(p.pt, btmPt2.pt) && (p != btmPt2)) | |
| p = p.next; | |
| var dx2n = ClipperLib.Math_Abs_Double(this.GetDx(btmPt2.pt, p.pt)); | |
| 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) { | |
| while (dups != p) { | |
| if (!this.FirstIsBottomPt(p, dups)) pp = dups; | |
| dups = dups.next; | |
| while (!ClipperLib.ClipperBase.PointsEqual(dups.pt, pp.pt)) | |
| dups = dups.next; | |
| } | |
| } | |
| return pp; | |
| }; | |
| ClipperLib.Clipper.prototype.GetLowermostRec = function (outRec1, outRec2) { | |
| var bPt1 = outRec1.bottomPt; | |
| var bPt2 = outRec2.bottomPt; | |
| if (bPt1.pt.Y > bPt2.pt.Y) return outRec1; | |
| else if (bPt1.pt.Y < bPt2.pt.Y) return outRec2; | |
| else if (bPt1.pt.X < bPt2.pt.X) return outRec1; | |
| else if (bPt1.pt.X > bPt2.pt.X) return outRec2; | |
| else if (bPt1.next == bPt1) return outRec2; | |
| else if (bPt2.next == bPt2) return outRec1; | |
| else if (this.FirstIsBottomPt(bPt1, bPt2)) return outRec1; | |
| else return outRec2; | |
| }; | |
| ClipperLib.Clipper.prototype.Param1RightOfParam2 = function (outRec1, outRec2) { | |
| do { | |
| outRec1 = outRec1.FirstLeft; | |
| if (outRec1 == outRec2) return true; | |
| } | |
| while (outRec1 != null); | |
| return false; | |
| }; | |
| ClipperLib.Clipper.prototype.AppendPolygon = function (e1, e2) { | |
| 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; | |
| var i; | |
| if (e1.side == ClipperLib.EdgeSide.esLeft) { | |
| if (e2.side == ClipperLib.EdgeSide.esLeft) { | |
| 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 { | |
| 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) { | |
| 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 { | |
| p1_rt.next = p2_lft; | |
| p2_lft.prev = p1_rt; | |
| p1_lft.prev = p2_rt; | |
| p2_rt.next = p1_lft; | |
| } | |
| side = ClipperLib.EdgeSide.esRight; | |
| } | |
| if (holeStateRec == outRec2) { | |
| outRec1.bottomPt = outRec2.bottomPt; | |
| outRec1.bottomPt.idx = outRec1.idx; | |
| if (outRec2.FirstLeft != outRec1) outRec1.FirstLeft = outRec2.FirstLeft; | |
| outRec1.isHole = outRec2.isHole; | |
| } | |
| outRec2.pts = null; | |
| outRec2.bottomPt = null; | |
| outRec2.AppendLink = outRec1; | |
| var OKIdx = e1.outIdx; | |
| var ObsoleteIdx = e2.outIdx; | |
| e1.outIdx = -1; | |
| e2.outIdx = -1; | |
| var e = this.m_ActiveEdges; | |
| while (e != null) { | |
| if (e.outIdx == ObsoleteIdx) { | |
| e.outIdx = OKIdx; | |
| e.side = side; | |
| break; | |
| } | |
| e = e.nextInAEL; | |
| } | |
| for (i = 0; i < this.m_Joins.length; ++i) { | |
| if (this.m_Joins[i].poly1Idx == ObsoleteIdx) this.m_Joins[i].poly1Idx = OKIdx; | |
| if (this.m_Joins[i].poly2Idx == ObsoleteIdx) this.m_Joins[i].poly2Idx = OKIdx; | |
| } | |
| for (i = 0; i < this.m_HorizJoins.length; ++i) { | |
| if (this.m_HorizJoins[i].savedIdx == ObsoleteIdx) this.m_HorizJoins[i].savedIdx = OKIdx; | |
| } | |
| }; | |
| 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.DoEdge1 = function (edge1, edge2, pt) { | |
| this.AddOutPt(edge1, pt); | |
| ClipperLib.Clipper.SwapSides(edge1, edge2); | |
| ClipperLib.Clipper.SwapPolyIndexes(edge1, edge2); | |
| }; | |
| ClipperLib.Clipper.prototype.DoEdge2 = function (edge1, edge2, pt) { | |
| this.AddOutPt(edge2, pt); | |
| ClipperLib.Clipper.SwapSides(edge1, edge2); | |
| ClipperLib.Clipper.SwapPolyIndexes(edge1, edge2); | |
| }; | |
| ClipperLib.Clipper.prototype.DoBothEdges = function (edge1, edge2, pt) { | |
| this.AddOutPt(edge1, pt); | |
| this.AddOutPt(edge2, pt); | |
| ClipperLib.Clipper.SwapSides(edge1, edge2); | |
| ClipperLib.Clipper.SwapPolyIndexes(edge1, edge2); | |
| }; | |
| ClipperLib.Clipper.prototype.IntersectEdges = function (e1, e2, pt, protects) { | |
| var e1stops = (ClipperLib.Protects.ipLeft & protects) == 0 && e1.nextInLML == null && e1.xtop == pt.X && e1.ytop == pt.Y; | |
| var e2stops = (ClipperLib.Protects.ipRight & protects) == 0 && e2.nextInLML == null && e2.xtop == pt.X && e2.ytop == pt.Y; | |
| var e1Contributing = (e1.outIdx >= 0); | |
| var e2contributing = (e2.outIdx >= 0); | |
| if (e1.polyType == e2.polyType) { | |
| 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.polyType == ClipperLib.PolyType.ptSubject) { | |
| e1FillType = this.m_SubjFillType; | |
| e1FillType2 = this.m_ClipFillType; | |
| } | |
| else { | |
| e1FillType = this.m_ClipFillType; | |
| e1FillType2 = this.m_SubjFillType; | |
| } | |
| if (e2.polyType == 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 = ClipperLib.Math_Abs_Int32(e1.windCnt); | |
| break; | |
| } | |
| switch (e2FillType) { | |
| case ClipperLib.PolyFillType.pftPositive: | |
| e2Wc = e2.windCnt; | |
| break; | |
| case ClipperLib.PolyFillType.pftNegative: | |
| e2Wc = -e2.windCnt; | |
| break; | |
| default: | |
| e2Wc = ClipperLib.Math_Abs_Int32(e2.windCnt); | |
| break; | |
| } | |
| if (e1Contributing && e2contributing) { | |
| if (e1stops || e2stops || (e1Wc != 0 && e1Wc != 1) || (e2Wc != 0 && e2Wc != 1) || (e1.polyType != e2.polyType && this.m_ClipType != ClipperLib.ClipType.ctXor)) this.AddLocalMaxPoly(e1, e2, pt); | |
| else this.DoBothEdges(e1, e2, pt); | |
| } | |
| else if (e1Contributing) { | |
| if ((e2Wc == 0 || e2Wc == 1) && (this.m_ClipType != ClipperLib.ClipType.ctIntersection || e2.polyType == ClipperLib.PolyType.ptSubject || (e2.windCnt2 != 0))) this.DoEdge1(e1, e2, pt); | |
| } | |
| else if (e2contributing) { | |
| if ((e1Wc == 0 || e1Wc == 1) && (this.m_ClipType != ClipperLib.ClipType.ctIntersection || e1.polyType == ClipperLib.PolyType.ptSubject || (e1.windCnt2 != 0))) this.DoEdge2(e1, e2, pt); | |
| } | |
| else if ((e1Wc == 0 || e1Wc == 1) && (e2Wc == 0 || e2Wc == 1) && !e1stops && !e2stops) { | |
| var e1Wc2, e2Wc2; | |
| switch (e1FillType2) { | |
| case ClipperLib.PolyFillType.pftPositive: | |
| e1Wc2 = e1.windCnt2; | |
| break; | |
| case ClipperLib.PolyFillType.pftNegative: | |
| e1Wc2 = -e1.windCnt2; | |
| break; | |
| default: | |
| e1Wc2 = ClipperLib.Math_Abs_Int32(e1.windCnt2); | |
| break; | |
| } | |
| switch (e2FillType2) { | |
| case ClipperLib.PolyFillType.pftPositive: | |
| e2Wc2 = e2.windCnt2; | |
| break; | |
| case ClipperLib.PolyFillType.pftNegative: | |
| e2Wc2 = -e2.windCnt2; | |
| break; | |
| default: | |
| e2Wc2 = ClipperLib.Math_Abs_Int32(e2.windCnt2); | |
| break; | |
| } | |
| if (e1.polyType != e2.polyType) 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.polyType == ClipperLib.PolyType.ptClip) && (e1Wc2 > 0) && (e2Wc2 > 0)) || ((e1.polyType == 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); | |
| } | |
| if (e1stops) this.DeleteFromAEL(e1); | |
| if (e2stops) this.DeleteFromAEL(e2); | |
| }; | |
| ClipperLib.Clipper.prototype.DeleteFromAEL = function (e) { | |
| var AelPrev = e.prevInAEL; | |
| var AelNext = e.nextInAEL; | |
| if (AelPrev == null && AelNext == null && (e != this.m_ActiveEdges)) return; | |
| if (AelPrev != null) AelPrev.nextInAEL = AelNext; | |
| else this.m_ActiveEdges = AelNext; | |
| if (AelNext != null) AelNext.prevInAEL = AelPrev; | |
| e.nextInAEL = null; | |
| e.prevInAEL = null; | |
| }; | |
| ClipperLib.Clipper.prototype.DeleteFromSEL = function (e) { | |
| var SelPrev = e.prevInSEL; | |
| var SelNext = e.nextInSEL; | |
| if (SelPrev == null && SelNext == null && (e != this.m_SortedEdges)) return; | |
| if (SelPrev != null) SelPrev.nextInSEL = SelNext; | |
| else this.m_SortedEdges = SelNext; | |
| if (SelNext != null) SelNext.prevInSEL = SelPrev; | |
| e.nextInSEL = null; | |
| e.prevInSEL = null; | |
| }; | |
| ClipperLib.Clipper.prototype.UpdateEdgeIntoAEL = function (e) { | |
| if (e.Value.nextInLML == null) ClipperLib.Error("UpdateEdgeIntoAEL: invalid call"); | |
| var AelPrev = e.Value.prevInAEL; | |
| var AelNext = e.Value.nextInAEL; | |
| e.Value.nextInLML.outIdx = e.Value.outIdx; | |
| if (AelPrev != null) AelPrev.nextInAEL = e.Value.nextInLML; | |
| else this.m_ActiveEdges = e.Value.nextInLML; | |
| if (AelNext != null) AelNext.prevInAEL = e.Value.nextInLML; | |
| e.Value.nextInLML.side = e.Value.side; | |
| e.Value.nextInLML.windDelta = e.Value.windDelta; | |
| e.Value.nextInLML.windCnt = e.Value.windCnt; | |
| e.Value.nextInLML.windCnt2 = e.Value.windCnt2; | |
| e.Value = e.Value.nextInLML; | |
| e.Value.prevInAEL = AelPrev; | |
| e.Value.nextInAEL = AelNext; | |
| if (e.Value.dx != ClipperLib.ClipperBase.horizontal) this.InsertScanbeam(e.Value.ytop); | |
| }; | |
| ClipperLib.Clipper.prototype.ProcessHorizontals = function () { | |
| var horzEdge = this.m_SortedEdges; | |
| while (horzEdge != null) { | |
| this.DeleteFromSEL(horzEdge); | |
| this.ProcessHorizontal(horzEdge); | |
| horzEdge = this.m_SortedEdges; | |
| } | |
| }; | |
| ClipperLib.Clipper.prototype.ProcessHorizontal = function (horzEdge) { | |
| var Direction; | |
| var horzLeft, horzRight; | |
| if (horzEdge.xcurr < horzEdge.xtop) { | |
| horzLeft = horzEdge.xcurr; | |
| horzRight = horzEdge.xtop; | |
| Direction = ClipperLib.Direction.dLeftToRight; | |
| } | |
| else { | |
| horzLeft = horzEdge.xtop; | |
| horzRight = horzEdge.xcurr; | |
| Direction = ClipperLib.Direction.dRightToLeft; | |
| } | |
| var eMaxPair; | |
| if (horzEdge.nextInLML != null) eMaxPair = null; | |
| else eMaxPair = this.GetMaximaPair(horzEdge); | |
| var e = this.GetNextInAEL(horzEdge, Direction); | |
| while (e != null) { | |
| var eNext = this.GetNextInAEL(e, Direction); | |
| if (eMaxPair != null || ((Direction == ClipperLib.Direction.dLeftToRight) && (e.xcurr <= horzRight)) || ((Direction == ClipperLib.Direction.dRightToLeft) && (e.xcurr >= horzLeft))) { | |
| if (e.xcurr == horzEdge.xtop && eMaxPair == null) { | |
| if (this.SlopesEqual(e, horzEdge.nextInLML, this.m_UseFullRange)) { | |
| if (horzEdge.outIdx >= 0 && e.outIdx >= 0) this.AddJoin(horzEdge.nextInLML, e, horzEdge.outIdx, -1); | |
| break; | |
| } | |
| else if (e.dx < horzEdge.nextInLML.dx) break; | |
| } | |
| if (e == eMaxPair) { | |
| if (Direction == ClipperLib.Direction.dLeftToRight) this.IntersectEdges(horzEdge, e, new ClipperLib.IntPoint(e.xcurr, horzEdge.ycurr), 0); | |
| else this.IntersectEdges(e, horzEdge, new ClipperLib.IntPoint(e.xcurr, horzEdge.ycurr), 0); | |
| if (eMaxPair.outIdx >= 0) ClipperLib.Error("ProcessHorizontal error"); | |
| return; | |
| } | |
| else if (e.dx == ClipperLib.ClipperBase.horizontal && !this.IsMinima(e) && !(e.xcurr > e.xtop)) { | |
| if (Direction == ClipperLib.Direction.dLeftToRight) this.IntersectEdges(horzEdge, e, new ClipperLib.IntPoint(e.xcurr, horzEdge.ycurr), (this.IsTopHorz(horzEdge, e.xcurr)) ? ClipperLib.Protects.ipLeft : ClipperLib.Protects.ipBoth); | |
| else this.IntersectEdges(e, horzEdge, new ClipperLib.IntPoint(e.xcurr, horzEdge.ycurr), (this.IsTopHorz(horzEdge, e.xcurr)) ? ClipperLib.Protects.ipRight : ClipperLib.Protects.ipBoth); | |
| } | |
| else if (Direction == ClipperLib.Direction.dLeftToRight) { | |
| this.IntersectEdges(horzEdge, e, new ClipperLib.IntPoint(e.xcurr, horzEdge.ycurr), (this.IsTopHorz(horzEdge, e.xcurr)) ? ClipperLib.Protects.ipLeft : ClipperLib.Protects.ipBoth); | |
| } | |
| else { | |
| this.IntersectEdges(e, horzEdge, new ClipperLib.IntPoint(e.xcurr, horzEdge.ycurr), (this.IsTopHorz(horzEdge, e.xcurr)) ? ClipperLib.Protects.ipRight : ClipperLib.Protects.ipBoth); | |
| } | |
| this.SwapPositionsInAEL(horzEdge, e); | |
| } | |
| else if ((Direction == ClipperLib.Direction.dLeftToRight && e.xcurr > horzRight && horzEdge.nextInSEL == null) || (Direction == ClipperLib.Direction.dRightToLeft && e.xcurr < horzLeft && horzEdge.nextInSEL == null)) break; | |
| e = eNext; | |
| } | |
| if (horzEdge.nextInLML != null) { | |
| if (horzEdge.outIdx >= 0) this.AddOutPt(horzEdge, new ClipperLib.IntPoint(horzEdge.xtop, horzEdge.ytop)); | |
| (function () { | |
| horzEdge = { | |
| Value: horzEdge | |
| }; | |
| var $res = this.UpdateEdgeIntoAEL(horzEdge); | |
| horzEdge = horzEdge.Value; | |
| return $res; | |
| }) | |
| .call(this); | |
| } | |
| else { | |
| if (horzEdge.outIdx >= 0) this.IntersectEdges(horzEdge, eMaxPair, new ClipperLib.IntPoint(horzEdge.xtop, horzEdge.ycurr), ClipperLib.Protects.ipBoth); | |
| this.DeleteFromAEL(eMaxPair); | |
| this.DeleteFromAEL(horzEdge); | |
| } | |
| }; | |
| ClipperLib.Clipper.prototype.IsTopHorz = function (horzEdge, XPos) { | |
| var e = this.m_SortedEdges; | |
| while (e != null) { | |
| if ((XPos >= Math.min(e.xcurr, e.xtop)) && (XPos <= Math.max(e.xcurr, e.xtop))) return false; | |
| e = e.nextInSEL; | |
| } | |
| return true; | |
| }; | |
| ClipperLib.Clipper.prototype.GetNextInAEL = function (e, Direction) { | |
| return Direction == ClipperLib.Direction.dLeftToRight ? e.nextInAEL : e.prevInAEL; | |
| }; | |
| 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.ytop == Y && e.nextInLML == null); | |
| }; | |
| ClipperLib.Clipper.prototype.IsIntermediate = function (e, Y) { | |
| return (e.ytop == Y && e.nextInLML != null); | |
| }; | |
| ClipperLib.Clipper.prototype.GetMaximaPair = function (e) { | |
| if (!this.IsMaxima(e.next, e.ytop) || (e.next.xtop != e.xtop)) return e.prev; | |
| else return e.next; | |
| }; | |
| ClipperLib.Clipper.prototype.ProcessIntersections = function (botY, topY) { | |
| if (this.m_ActiveEdges == null) return true; | |
| try { | |
| if (this.alternateAlgo) | |
| this.BuildIntersectList2(botY, topY); | |
| else | |
| this.BuildIntersectList2(botY, topY); | |
| if (this.m_IntersectNodes == null) return true; | |
| if (this.FixupIntersections()) this.ProcessIntersectList(); | |
| else return false; | |
| } | |
| catch ($$e2) { | |
| this.m_SortedEdges = null; | |
| this.DisposeIntersectNodes(); | |
| ClipperLib.Error("ProcessIntersections error"); | |
| } | |
| return true; | |
| }; | |
| ClipperLib.Clipper.prototype.BuildIntersectList2 = function (botY, topY) { | |
| if (this.m_ActiveEdges == null) return; | |
| var e = this.m_ActiveEdges; | |
| e.tmpX = ClipperLib.Clipper.TopX(e, topY); | |
| this.m_SortedEdges = e; | |
| this.m_SortedEdges.prevInSEL = null; | |
| e = e.nextInAEL; | |
| while (e != null) { | |
| e.prevInSEL = e.prevInAEL; | |
| e.prevInSEL.nextInSEL = e; | |
| e.nextInSEL = null; | |
| e.tmpX = ClipperLib.Clipper.TopX(e, topY); | |
| e = e.nextInAEL; | |
| } | |
| var isModified = true; | |
| var intersectArray = []; | |
| 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(); | |
| if (e.tmpX > eNext.tmpX && (function () { | |
| pt = { | |
| Value: pt | |
| }; | |
| var $res = this.IntersectPoint(e, eNext, pt); | |
| pt = pt.Value; | |
| return $res; | |
| }) | |
| .call(this)) { | |
| if (pt.Y > botY) { | |
| pt.Y = botY; | |
| pt.X = ClipperLib.Clipper.TopX(e, pt.Y); | |
| } | |
| this.SwapPositionsInSEL(e, eNext); | |
| intersectArray.push(this.CreateIntersectNode(e, eNext, pt)); | |
| isModified = true; | |
| } | |
| else e = eNext; | |
| } | |
| if (e.prevInSEL != null) e.prevInSEL.nextInSEL = null; | |
| else break; | |
| } | |
| this.AddBulkIntersectNodes(intersectArray); | |
| this.m_SortedEdges = null; | |
| }; | |
| ClipperLib.Clipper.prototype.AddBulkIntersectNodes = function (intersectArray) { | |
| intersectArray.sort(function (a, b) { | |
| return ClipperLib.Clipper.prototype.ProcessParam1BeforeParam2(a, b) ? -1 : 1; | |
| }); | |
| var startNode = this.m_IntersectNodes; | |
| for (var i = 0; i < intersectArray.length; i++) { | |
| var newNode = intersectArray[i]; | |
| if (this.m_IntersectNodes == null) this.m_IntersectNodes = newNode; | |
| else if (this.ProcessParam1BeforeParam2(newNode, this.m_IntersectNodes)) { | |
| newNode.next = this.m_IntersectNodes; | |
| this.m_IntersectNodes = newNode; | |
| } | |
| else { | |
| var iNode = startNode; | |
| while (iNode.next != null && this.ProcessParam1BeforeParam2(iNode.next, newNode)) | |
| iNode = iNode.next; | |
| newNode.next = iNode.next; | |
| iNode.next = newNode; | |
| } | |
| startNode = newNode; | |
| } | |
| } | |
| ClipperLib.Clipper.prototype.CreateIntersectNode = function (e1, e2, pt) { | |
| var newNode = new ClipperLib.IntersectNode(); | |
| newNode.edge1 = e1; | |
| newNode.edge2 = e2; | |
| newNode.pt = pt; | |
| newNode.next = null; | |
| return newNode; | |
| } | |
| ClipperLib.Clipper.prototype.BuildIntersectList = function (botY, topY) { | |
| if (this.m_ActiveEdges == null) return; | |
| var e = this.m_ActiveEdges; | |
| e.tmpX = ClipperLib.Clipper.TopX(e, topY); | |
| this.m_SortedEdges = e; | |
| this.m_SortedEdges.prevInSEL = null; | |
| e = e.nextInAEL; | |
| while (e != null) { | |
| e.prevInSEL = e.prevInAEL; | |
| e.prevInSEL.nextInSEL = e; | |
| e.nextInSEL = null; | |
| e.tmpX = ClipperLib.Clipper.TopX(e, topY); | |
| e = e.nextInAEL; | |
| } | |
| 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(); | |
| if (e.tmpX > eNext.tmpX && (function () { | |
| pt = { | |
| Value: pt | |
| }; | |
| var $res = this.IntersectPoint(e, eNext, pt); | |
| pt = pt.Value; | |
| return $res; | |
| }) | |
| .call(this)) { | |
| if (pt.Y > botY) { | |
| pt.Y = botY; | |
| pt.X = ClipperLib.Clipper.TopX(e, pt.Y); | |
| } | |
| this.AddIntersectNode(e, eNext, pt); | |
| 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.FixupIntersections = function () { | |
| if (this.m_IntersectNodes.next == null) return true; | |
| this.CopyAELToSEL(); | |
| var int1 = this.m_IntersectNodes; | |
| var int2 = this.m_IntersectNodes.next; | |
| while (int2 != null) { | |
| var e1 = int1.edge1; | |
| var e2; | |
| if (e1.prevInSEL == int1.edge2) e2 = e1.prevInSEL; | |
| else if (e1.nextInSEL == int1.edge2) e2 = e1.nextInSEL; | |
| else { | |
| while (int2 != null) { | |
| if (int2.edge1.nextInSEL == int2.edge2 || int2.edge1.prevInSEL == int2.edge2) break; | |
| else int2 = int2.next; | |
| } | |
| if (int2 == null) return false; | |
| this.SwapIntersectNodes(int1, int2); | |
| e1 = int1.edge1; | |
| e2 = int1.edge2; | |
| } | |
| this.SwapPositionsInSEL(e1, e2); | |
| int1 = int1.next; | |
| int2 = int1.next; | |
| } | |
| this.m_SortedEdges = null; | |
| return (int1.edge1.prevInSEL == int1.edge2 || int1.edge1.nextInSEL == int1.edge2); | |
| }; | |
| ClipperLib.Clipper.prototype.ProcessIntersectList = function () { | |
| while (this.m_IntersectNodes != null) { | |
| var iNode = this.m_IntersectNodes.next; | |
| this.IntersectEdges(this.m_IntersectNodes.edge1, this.m_IntersectNodes.edge2, this.m_IntersectNodes.pt, ClipperLib.Protects.ipBoth); | |
| this.SwapPositionsInAEL(this.m_IntersectNodes.edge1, this.m_IntersectNodes.edge2); | |
| this.m_IntersectNodes = null; | |
| this.m_IntersectNodes = iNode; | |
| } | |
| }; | |
| /* | |
| -------------------------------- | |
| 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 (currentY == edge.ytop) return edge.xtop; | |
| return edge.xbot + ClipperLib.Clipper.Round(edge.dx * (currentY - edge.ybot)); | |
| }; | |
| ClipperLib.Clipper.prototype.AddIntersectNode = function (e1, e2, pt) { | |
| var newNode = new ClipperLib.IntersectNode(); | |
| newNode.edge1 = e1; | |
| newNode.edge2 = e2; | |
| newNode.pt = pt; | |
| newNode.next = null; | |
| if (this.m_IntersectNodes == null) this.m_IntersectNodes = newNode; | |
| else if (this.ProcessParam1BeforeParam2(newNode, this.m_IntersectNodes)) { | |
| newNode.next = this.m_IntersectNodes; | |
| this.m_IntersectNodes = newNode; | |
| } | |
| else { | |
| var iNode = this.m_IntersectNodes; | |
| while (iNode.next != null && this.ProcessParam1BeforeParam2(iNode.next, newNode)) | |
| iNode = iNode.next; | |
| newNode.next = iNode.next; | |
| iNode.next = newNode; | |
| } | |
| }; | |
| ClipperLib.Clipper.prototype.ProcessParam1BeforeParam2 = function (node1, node2) { | |
| var result; | |
| if (node1.pt.Y == node2.pt.Y) { | |
| if (node1.edge1 == node2.edge1 || node1.edge2 == node2.edge1) { | |
| result = node2.pt.X > node1.pt.X; | |
| return node2.edge1.dx > 0 ? !result : result; | |
| } | |
| else if (node1.edge1 == node2.edge2 || node1.edge2 == node2.edge2) { | |
| result = node2.pt.X > node1.pt.X; | |
| return node2.edge2.dx > 0 ? !result : result; | |
| } | |
| else return node2.pt.X > node1.pt.X; | |
| } | |
| else return node1.pt.Y > node2.pt.Y; | |
| }; | |
| ClipperLib.Clipper.prototype.SwapIntersectNodes = function (int1, int2) { | |
| var e1 = int1.edge1; | |
| var e2 = int1.edge2; | |
| var p = int1.pt; | |
| int1.edge1 = int2.edge1; | |
| int1.edge2 = int2.edge2; | |
| int1.pt = int2.pt; | |
| int2.edge1 = e1; | |
| int2.edge2 = e2; | |
| int2.pt = p; | |
| }; | |
| ClipperLib.Clipper.prototype.IntersectPoint = function (edge1, edge2, ip) { | |
| var b1, b2; | |
| if (this.SlopesEqual(edge1, edge2, this.m_UseFullRange)) return false; | |
| else if (edge1.dx == 0) { | |
| ip.Value.X = edge1.xbot; | |
| if (edge2.dx == ClipperLib.ClipperBase.horizontal) { | |
| ip.Value.Y = edge2.ybot; | |
| } | |
| else { | |
| b2 = edge2.ybot - (edge2.xbot / edge2.dx); | |
| ip.Value.Y = ClipperLib.Clipper.Round(ip.Value.X / edge2.dx + b2); | |
| } | |
| } | |
| else if (edge2.dx == 0) { | |
| ip.Value.X = edge2.xbot; | |
| if (edge1.dx == ClipperLib.ClipperBase.horizontal) { | |
| ip.Value.Y = edge1.ybot; | |
| } | |
| else { | |
| b1 = edge1.ybot - (edge1.xbot / edge1.dx); | |
| ip.Value.Y = ClipperLib.Clipper.Round(ip.Value.X / edge1.dx + b1); | |
| } | |
| } | |
| else { | |
| b1 = edge1.xbot - edge1.ybot * edge1.dx; | |
| b2 = edge2.xbot - edge2.ybot * edge2.dx; | |
| var q = (b2 - b1) / (edge1.dx - edge2.dx); | |
| ip.Value.Y = ClipperLib.Clipper.Round(q); | |
| if (ClipperLib.Math_Abs_Double(edge1.dx) < ClipperLib.Math_Abs_Double(edge2.dx)) ip.Value.X = ClipperLib.Clipper.Round(edge1.dx * q + b1); | |
| else ip.Value.X = ClipperLib.Clipper.Round(edge2.dx * q + b2); | |
| } | |
| if (ip.Value.Y < edge1.ytop || ip.Value.Y < edge2.ytop) { | |
| if (edge1.ytop > edge2.ytop) { | |
| ip.Value.X = edge1.xtop; | |
| ip.Value.Y = edge1.ytop; | |
| return ClipperLib.Clipper.TopX(edge2, edge1.ytop) < edge1.xtop; | |
| } | |
| else { | |
| ip.Value.X = edge2.xtop; | |
| ip.Value.Y = edge2.ytop; | |
| return ClipperLib.Clipper.TopX(edge1, edge2.ytop) > edge2.xtop; | |
| } | |
| } | |
| else return true; | |
| }; | |
| ClipperLib.Clipper.prototype.DisposeIntersectNodes = function () { | |
| while (this.m_IntersectNodes != null) { | |
| var iNode = this.m_IntersectNodes.next; | |
| this.m_IntersectNodes = null; | |
| this.m_IntersectNodes = iNode; | |
| } | |
| }; | |
| ClipperLib.Clipper.prototype.ProcessEdgesAtTopOfScanbeam = function (topY) { | |
| var e = this.m_ActiveEdges; | |
| var ePrev; | |
| while (e != null) { | |
| if (this.IsMaxima(e, topY) && this.GetMaximaPair(e) | |
| .dx != ClipperLib.ClipperBase.horizontal) { | |
| ePrev = e.prevInAEL; | |
| this.DoMaxima(e, topY); | |
| if (ePrev == null) e = this.m_ActiveEdges; | |
| else e = ePrev.nextInAEL; | |
| } | |
| else { | |
| if (this.IsIntermediate(e, topY) && e.nextInLML.dx == ClipperLib.ClipperBase.horizontal) { | |
| if (e.outIdx >= 0) { | |
| this.AddOutPt(e, new ClipperLib.IntPoint(e.xtop, e.ytop)); | |
| for (var i = 0; i < this.m_HorizJoins.length; ++i) { | |
| var pt = new ClipperLib.IntPoint(), | |
| pt2 = new ClipperLib.IntPoint(); | |
| var hj = this.m_HorizJoins[i]; | |
| if ((function () { | |
| pt = { | |
| Value: pt | |
| }; | |
| pt2 = { | |
| Value: pt2 | |
| }; | |
| var $res = this.GetOverlapSegment(new ClipperLib.IntPoint(hj.edge.xbot, hj.edge.ybot), | |
| new ClipperLib.IntPoint(hj.edge.xtop, hj.edge.ytop), | |
| new ClipperLib.IntPoint(e.nextInLML.xbot, e.nextInLML.ybot), | |
| new ClipperLib.IntPoint(e.nextInLML.xtop, e.nextInLML.ytop), pt, pt2); | |
| pt = pt.Value; | |
| pt2 = pt2.Value; | |
| return $res; | |
| }) | |
| .call(this)) this.AddJoin(hj.edge, e.nextInLML, hj.savedIdx, e.outIdx); | |
| } | |
| this.AddHorzJoin(e.nextInLML, e.outIdx); | |
| } | |
| (function () { | |
| e = { | |
| Value: e | |
| }; | |
| var $res = this.UpdateEdgeIntoAEL(e); | |
| e = e.Value; | |
| return $res; | |
| }) | |
| .call(this); | |
| this.AddEdgeToSEL(e); | |
| } | |
| else { | |
| e.xcurr = ClipperLib.Clipper.TopX(e, topY); | |
| e.ycurr = topY; | |
| } | |
| e = e.nextInAEL; | |
| } | |
| } | |
| this.ProcessHorizontals(); | |
| e = this.m_ActiveEdges; | |
| while (e != null) { | |
| if (this.IsIntermediate(e, topY)) { | |
| if (e.outIdx >= 0) this.AddOutPt(e, new ClipperLib.IntPoint(e.xtop, e.ytop)); | |
| (function () { | |
| e = { | |
| Value: e | |
| }; | |
| var $res = this.UpdateEdgeIntoAEL(e); | |
| e = e.Value; | |
| return $res; | |
| }) | |
| .call(this); | |
| ePrev = e.prevInAEL; | |
| var eNext = e.nextInAEL; | |
| if (ePrev != null && ePrev.xcurr == e.xbot && ePrev.ycurr == e.ybot && e.outIdx >= 0 && ePrev.outIdx >= 0 && ePrev.ycurr > ePrev.ytop && this.SlopesEqual(e, ePrev, this.m_UseFullRange)) { | |
| this.AddOutPt(ePrev, new ClipperLib.IntPoint(e.xbot, e.ybot)); | |
| this.AddJoin(e, ePrev, -1, -1); | |
| } | |
| else if (eNext != null && eNext.xcurr == e.xbot && eNext.ycurr == e.ybot && e.outIdx >= 0 && eNext.outIdx >= 0 && eNext.ycurr > eNext.ytop && this.SlopesEqual(e, eNext, this.m_UseFullRange)) { | |
| this.AddOutPt(eNext, new ClipperLib.IntPoint(e.xbot, e.ybot)); | |
| this.AddJoin(e, eNext, -1, -1); | |
| } | |
| } | |
| e = e.nextInAEL; | |
| } | |
| }; | |
| ClipperLib.Clipper.prototype.DoMaxima = function (e, topY) { | |
| var eMaxPair = this.GetMaximaPair(e); | |
| var X = e.xtop; | |
| var eNext = e.nextInAEL; | |
| while (eNext != eMaxPair) { | |
| if (eNext == null) ClipperLib.Error("DoMaxima error"); | |
| this.IntersectEdges(e, eNext, new ClipperLib.IntPoint(X, topY), ClipperLib.Protects.ipBoth); | |
| eNext = eNext.nextInAEL; | |
| } | |
| if (e.outIdx < 0 && eMaxPair.outIdx < 0) { | |
| this.DeleteFromAEL(e); | |
| this.DeleteFromAEL(eMaxPair); | |
| } | |
| else if (e.outIdx >= 0 && eMaxPair.outIdx >= 0) { | |
| this.IntersectEdges(e, eMaxPair, new ClipperLib.IntPoint(X, topY), ClipperLib.Protects.ipNone); | |
| } | |
| else ClipperLib.Error("DoMaxima error"); | |
| }; | |
| ClipperLib.Clipper.ReversePolygons = function (polys) { | |
| var len = polys.length, | |
| poly; | |
| for (var i = 0; i < len; i++) { | |
| if (polys[i] instanceof Array) polys[i].reverse(); | |
| } | |
| }; | |
| ClipperLib.Clipper.Orientation = function (poly) { | |
| return this.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); | |
| var outRec, len = this.m_PolyOuts.length; | |
| for (var i = 0; i < len; i++) { | |
| outRec = this.m_PolyOuts[i]; | |
| if (outRec.pts == null) continue; | |
| var p = outRec.pts; | |
| var cnt = this.PointCount(p); | |
| if (cnt < 3) continue; | |
| var pg = new ClipperLib.Polygon(cnt); | |
| for (var j = 0; j < cnt; j++) { | |
| //pg.push(p.pt); | |
| pg.push(new ClipperLib.IntPoint(p.pt.X, p.pt.Y)); // Have to create new point, because the point can be a reference to other point | |
| p = p.prev; | |
| } | |
| polyg.push(pg); | |
| } | |
| }; | |
| ClipperLib.Clipper.prototype.BuildResultEx = function (polyg) { | |
| ClipperLib.Clear(polyg); | |
| var i = 0; | |
| while (i < this.m_PolyOuts.length) { | |
| var outRec = this.m_PolyOuts[i++]; | |
| if (outRec.pts == null) break; | |
| var p = outRec.pts; | |
| var cnt = this.PointCount(p); | |
| if (cnt < 3) continue; | |
| var epg = new ClipperLib.ExPolygon(); | |
| epg.outer = new ClipperLib.Polygon(); | |
| epg.holes = new ClipperLib.Polygons(); | |
| for (var j = 0; j < cnt; j++) { | |
| //epg.outer.push(p.pt); | |
| epg.outer.push(new ClipperLib.IntPoint(p.pt.X, p.pt.Y)); // Have to create new point, because the point can be a reference to other point | |
| p = p.prev; | |
| } | |
| while (i < this.m_PolyOuts.length) { | |
| outRec = this.m_PolyOuts[i]; | |
| if (outRec.pts == null || !outRec.isHole) break; | |
| var pg = new ClipperLib.Polygon(); | |
| p = outRec.pts; | |
| do { | |
| //pg.push(p.pt); | |
| pg.push(new ClipperLib.IntPoint(p.pt.X, p.pt.Y)); // Have to create new point, because the point can be a reference to other point | |
| p = p.prev; | |
| } | |
| while (p != outRec.pts); | |
| epg.holes.push(pg); | |
| i++; | |
| } | |
| polyg.push(epg); | |
| } | |
| }; | |
| ClipperLib.Clipper.prototype.FixupOutPolygon = function (outRec) { | |
| var lastOK = null; | |
| outRec.pts = outRec.bottomPt; | |
| var pp = outRec.bottomPt; | |
| for (; ;) { | |
| if (pp.prev == pp || pp.prev == pp.next) { | |
| this.DisposeOutPts(pp); | |
| outRec.pts = null; | |
| outRec.bottomPt = null; | |
| return; | |
| } | |
| if (ClipperLib.ClipperBase.PointsEqual(pp.pt, pp.next.pt) || this.SlopesEqual(pp.prev.pt, pp.pt, pp.next.pt, this.m_UseFullRange)) { | |
| lastOK = null; | |
| var tmp = pp; | |
| if (pp == outRec.bottomPt) outRec.bottomPt = null; | |
| pp.prev.next = pp.next; | |
| pp.next.prev = pp.prev; | |
| pp = pp.prev; | |
| tmp = null; | |
| } | |
| else if (pp == lastOK) break; | |
| else { | |
| if (lastOK == null) lastOK = pp; | |
| pp = pp.next; | |
| } | |
| } | |
| if (outRec.bottomPt == null) { | |
| outRec.bottomPt = this.GetBottomPt(pp); | |
| outRec.bottomPt.idx = outRec.idx; | |
| outRec.pts = outRec.bottomPt; | |
| } | |
| }; | |
| ClipperLib.Clipper.prototype.JoinPoints = function (j, p1, p2) { | |
| p1.Value = null; | |
| p2.Value = null; | |
| var outRec1 = this.m_PolyOuts[j.poly1Idx]; | |
| var outRec2 = this.m_PolyOuts[j.poly2Idx]; | |
| if (outRec1 == null || outRec2 == null) return false; | |
| var pp1a = outRec1.pts; | |
| var pp2a = outRec2.pts; | |
| var pt1 = j.pt2a, | |
| pt2 = j.pt2b; | |
| var pt3 = j.pt1a, | |
| pt4 = j.pt1b; | |
| if (!(function () { | |
| pp1a = { | |
| Value: pp1a | |
| }; | |
| pt1 = { | |
| Value: pt1 | |
| }; | |
| pt2 = { | |
| Value: pt2 | |
| }; | |
| var $res = this.FindSegment(pp1a, this.m_UseFullRange, pt1, pt2); | |
| pp1a = pp1a.Value; | |
| pt1 = pt1.Value; | |
| pt2 = pt2.Value; | |
| return $res; | |
| }) | |
| .call(this)) return false; | |
| if (outRec1 == outRec2) { | |
| pp2a = pp1a.next; | |
| if (!(function () { | |
| pp2a = { | |
| Value: pp2a | |
| }; | |
| pt3 = { | |
| Value: pt3 | |
| }; | |
| pt4 = { | |
| Value: pt4 | |
| }; | |
| var $res = this.FindSegment(pp2a, this.m_UseFullRange, pt3, pt4); | |
| pp2a = pp2a.Value; | |
| pt3 = pt3.Value; | |
| pt4 = pt4.Value; | |
| return $res; | |
| }) | |
| .call(this) || (pp2a == pp1a)) return false; | |
| } | |
| else if (!(function () { | |
| pp2a = { | |
| Value: pp2a | |
| }; | |
| pt3 = { | |
| Value: pt3 | |
| }; | |
| pt4 = { | |
| Value: pt4 | |
| }; | |
| var $res = this.FindSegment(pp2a, this.m_UseFullRange, pt3, pt4); | |
| pp2a = pp2a.Value; | |
| pt3 = pt3.Value; | |
| pt4 = pt4.Value; | |
| return $res; | |
| }) | |
| .call(this)) return false; | |
| if (!(function () { | |
| pt1 = { | |
| Value: pt1 | |
| }; | |
| pt2 = { | |
| Value: pt2 | |
| }; | |
| var $res = this.GetOverlapSegment(pt1.Value, pt2.Value, pt3, pt4, pt1, pt2); | |
| pt1 = pt1.Value; | |
| pt2 = pt2.Value; | |
| return $res; | |
| }) | |
| .call(this)) { | |
| return false; | |
| } | |
| var p3, p4, prev = pp1a.prev; | |
| if (ClipperLib.ClipperBase.PointsEqual(pp1a.pt, pt1)) p1.Value = pp1a; | |
| else if (ClipperLib.ClipperBase.PointsEqual(prev.pt, pt1)) p1.Value = prev; | |
| else p1.Value = this.InsertPolyPtBetween(pp1a, prev, pt1); | |
| if (ClipperLib.ClipperBase.PointsEqual(pp1a.pt, pt2)) p2.Value = pp1a; | |
| else if (ClipperLib.ClipperBase.PointsEqual(prev.pt, pt2)) p2.Value = prev; | |
| else if ((p1.Value == pp1a) || (p1.Value == prev)) p2.Value = this.InsertPolyPtBetween(pp1a, prev, pt2); | |
| else if (this.Pt3IsBetweenPt1AndPt2(pp1a.pt, p1.Value.pt, pt2)) p2.Value = this.InsertPolyPtBetween(pp1a, p1.Value, pt2); | |
| else p2.Value = this.InsertPolyPtBetween(p1.Value, prev, pt2); | |
| prev = pp2a.prev; | |
| if (ClipperLib.ClipperBase.PointsEqual(pp2a.pt, pt1)) p3 = pp2a; | |
| else if (ClipperLib.ClipperBase.PointsEqual(prev.pt, pt1)) p3 = prev; | |
| else p3 = this.InsertPolyPtBetween(pp2a, prev, pt1); | |
| if (ClipperLib.ClipperBase.PointsEqual(pp2a.pt, pt2)) p4 = pp2a; | |
| else if (ClipperLib.ClipperBase.PointsEqual(prev.pt, pt2)) p4 = prev; | |
| else if ((p3 == pp2a) || (p3 == prev)) p4 = this.InsertPolyPtBetween(pp2a, prev, pt2); | |
| else if (this.Pt3IsBetweenPt1AndPt2(pp2a.pt, p3.pt, pt2)) p4 = this.InsertPolyPtBetween(pp2a, p3, pt2); | |
| else p4 = this.InsertPolyPtBetween(p3, prev, pt2); | |
| if (p1.Value.next == p2.Value && p3.prev == p4) { | |
| p1.Value.next = p3; | |
| p3.prev = p1.Value; | |
| p2.Value.prev = p4; | |
| p4.next = p2.Value; | |
| return true; | |
| } | |
| else if (p1.Value.prev == p2.Value && p3.next == p4) { | |
| p1.Value.prev = p3; | |
| p3.next = p1.Value; | |
| p2.Value.next = p4; | |
| p4.prev = p2.Value; | |
| return true; | |
| } | |
| else return false; | |
| }; | |
| ClipperLib.Clipper.prototype.FixupJoinRecs = function (j, pt, startIdx) { | |
| for (var k = startIdx; k < this.m_Joins.length; k++) { | |
| var j2 = this.m_Joins[k]; | |
| if (j2.poly1Idx == j.poly1Idx && this.PointIsVertex(j2.pt1a, pt)) j2.poly1Idx = j.poly2Idx; | |
| if (j2.poly2Idx == j.poly1Idx && this.PointIsVertex(j2.pt2a, pt)) j2.poly2Idx = j.poly2Idx; | |
| } | |
| }; | |
| ClipperLib.Clipper.prototype.JoinCommonEdges = function () { | |
| var k, orec; | |
| for (var i = 0; i < this.m_Joins.length; i++) { | |
| var j = this.m_Joins[i]; | |
| var p1, p2; | |
| if (!(function () { | |
| p1 = { | |
| Value: p1 | |
| }; | |
| p2 = { | |
| Value: p2 | |
| }; | |
| var $res = this.JoinPoints(j, p1, p2); | |
| p1 = p1.Value; | |
| p2 = p2.Value; | |
| return $res; | |
| }) | |
| .call(this)) continue; | |
| var outRec1 = this.m_PolyOuts[j.poly1Idx]; | |
| var outRec2 = this.m_PolyOuts[j.poly2Idx]; | |
| if (outRec1 == outRec2) { | |
| outRec1.pts = this.GetBottomPt(p1); | |
| outRec1.bottomPt = outRec1.pts; | |
| outRec1.bottomPt.idx = outRec1.idx; | |
| outRec2 = this.CreateOutRec(); | |
| this.m_PolyOuts.push(outRec2); | |
| outRec2.idx = this.m_PolyOuts.length - 1; | |
| j.poly2Idx = outRec2.idx; | |
| outRec2.pts = this.GetBottomPt(p2); | |
| outRec2.bottomPt = outRec2.pts; | |
| outRec2.bottomPt.idx = outRec2.idx; | |
| if (this.PointInPolygon(outRec2.pts.pt, outRec1.pts, this.m_UseFullRange)) { | |
| outRec2.isHole = !outRec1.isHole; | |
| outRec2.FirstLeft = outRec1; | |
| this.FixupJoinRecs(j, p2, i + 1); | |
| this.FixupOutPolygon(outRec1); | |
| this.FixupOutPolygon(outRec2); | |
| if ((outRec2.isHole ^ this.m_ReverseOutput) == (this.Area(outRec2, this.m_UseFullRange) > 0)) | |
| this.ReversePolyPtLinks(outRec2.pts); | |
| } | |
| else if (this.PointInPolygon(outRec1.pts.pt, outRec2.pts, this.m_UseFullRange)) { | |
| outRec2.isHole = outRec1.isHole; | |
| outRec1.isHole = !outRec2.isHole; | |
| outRec2.FirstLeft = outRec1.FirstLeft; | |
| outRec1.FirstLeft = outRec2; | |
| this.FixupJoinRecs(j, p2, i + 1); | |
| this.FixupOutPolygon(outRec1); | |
| this.FixupOutPolygon(outRec2); | |
| if ((outRec1.isHole ^ this.m_ReverseOutput) == (this.Area(outRec1, this.m_UseFullRange) > 0)) | |
| this.ReversePolyPtLinks(outRec1.pts); | |
| if (this.m_UsingExPolygons && outRec1.isHole) for (k = 0; k < this.m_PolyOuts.length; ++k) { | |
| orec = this.m_PolyOuts[k]; | |
| if (orec.isHole && orec.bottomPt != null && orec.FirstLeft == outRec1) orec.FirstLeft = outRec2; | |
| } | |
| } | |
| else { | |
| outRec2.isHole = outRec1.isHole; | |
| outRec2.FirstLeft = outRec1.FirstLeft; | |
| this.FixupJoinRecs(j, p2, i + 1); | |
| this.FixupOutPolygon(outRec1); | |
| this.FixupOutPolygon(outRec2); | |
| if (this.m_UsingExPolygons && outRec2.pts != null) for (k = 0; k < this.m_PolyOuts.length; ++k) { | |
| orec = this.m_PolyOuts[k]; | |
| if (orec.isHole && orec.bottomPt != null && orec.FirstLeft == outRec1 && this.PointInPolygon(orec.bottomPt.pt, outRec2.pts, this.m_UseFullRange)) orec.FirstLeft = outRec2; | |
| } | |
| } | |
| } | |
| else { | |
| if (this.m_UsingExPolygons) for (k = 0; k < this.m_PolyOuts.length; ++k) | |
| if (this.m_PolyOuts[k].isHole && this.m_PolyOuts[k].bottomPt != null && this.m_PolyOuts[k].FirstLeft == outRec2) this.m_PolyOuts[k].FirstLeft = outRec1; | |
| this.FixupOutPolygon(outRec1); | |
| if (outRec1.pts != null) { | |
| outRec1.isHole = this.Area(outRec1, this.m_UseFullRange) < 0; | |
| if (outRec1.isHole && outRec1.FirstLeft == null) outRec1.FirstLeft = outRec2.FirstLeft; | |
| } | |
| var OKIdx = outRec1.idx; | |
| var ObsoleteIdx = outRec2.idx; | |
| outRec2.pts = null; | |
| outRec2.bottomPt = null; | |
| outRec2.AppendLink = outRec1; | |
| for (k = i + 1; k < this.m_Joins.length; k++) { | |
| var j2 = this.m_Joins[k]; | |
| if (j2.poly1Idx == ObsoleteIdx) j2.poly1Idx = OKIdx; | |
| if (j2.poly2Idx == ObsoleteIdx) j2.poly2Idx = OKIdx; | |
| } | |
| } | |
| } | |
| }; | |
| ClipperLib.Clipper.FullRangeNeeded = function (pts) { | |
| var result = false; | |
| for (var i = 0; i < pts.length; i++) { | |
| if (ClipperLib.Math_Abs_Int64(pts[i].X) > ClipperLib.ClipperBase.hiRange || ClipperLib.Math_Abs_Int64(pts[i].Y) > ClipperLib.ClipperBase.hiRange) | |
| ClipperLib.Error("Coordinate exceeds range bounds in FullRangeNeeded()."); | |
| else if (ClipperLib.Math_Abs_Int64(pts[i].X) > ClipperLib.ClipperBase.loRange || ClipperLib.Math_Abs_Int64(pts[i].Y) > ClipperLib.ClipperBase.loRange) { | |
| result = true; | |
| } | |
| } | |
| return result; | |
| }; | |
| ClipperLib.Clipper.prototype.Area = ClipperLib.Clipper.Area = function () { | |
| var arg = arguments; | |
| var i, a; | |
| if (arg.length == 1) // function ( poly ) | |
| { | |
| var poly = arg[0]; | |
| var highI = poly.length - 1; | |
| if (highI < 2) return 0; | |
| if (ClipperLib.Clipper.FullRangeNeeded(poly)) { | |
| a = new Int128(poly[highI].X + poly[0].X).multiply(new Int128(poly[0].Y - poly[highI].Y)); | |
| for (i = 1; i <= highI; ++i) | |
| a = a.add(new Int128(poly[i - 1].X + poly[i].X).multiply(new Int128(poly[i].Y - poly[i - 1].Y))); | |
| return parseFloat(a.toString()) / 2; | |
| } | |
| else { | |
| var area = (poly[highI].X + poly[0].X) * (poly[0].Y - poly[highI].Y); | |
| for (i = 1; i <= highI; ++i) | |
| area += (poly[i - 1].X + poly[i].X) * (poly[i].Y - poly[i - 1].Y); | |
| return area / 2; | |
| } | |
| } | |
| else if (arg.length == 2) // function (outRec, UseFull64BitRange) | |
| { | |
| var outRec = arg[0]; | |
| var UseFull64BitRange = arg[1]; | |
| var op = outRec.pts; | |
| if (op == null) return 0; | |
| if (UseFull64BitRange) { | |
| a = new Int128(Int128.ZERO); | |
| do { | |
| a = a.add(new Int128(op.pt.X + op.prev.pt.X).multiply(new Int128(op.prev.pt.Y - op.pt.Y))); | |
| op = op.next; | |
| } while (op != outRec.pts); | |
| return parseFloat(a.toString()) / 2; // This could be something faster! | |
| } | |
| else { | |
| a = 0; | |
| do { | |
| a = a + (op.pt.X + op.prev.pt.X) * (op.prev.pt.Y - op.pt.Y); | |
| op = op.next; | |
| } | |
| while (op != outRec.pts); | |
| return a / 2; | |
| } | |
| } | |
| }; | |
| ClipperLib.Clipper.BuildArc = function (pt, a1, a2, r) { | |
| var steps = Math.sqrt(ClipperLib.Math_Abs_Double(r)) * ClipperLib.Math_Abs_Double(a2 - a1); | |
| steps = steps / 4; // to avoid overload | |
| // If you want to make steps independent of scaling factor (scale have to be set), | |
| // the following line does the trick: | |
| // steps = steps / Math.sqrt(scale) * 2; | |
| // If you want that changing scale factor has some influence to steps, uncomment also the following line: | |
| // It may be desirable, that bigger precision ( = bigger scaling factor) needs more steps. | |
| // steps += Math.pow(scale, 0.2); | |
| if (steps < 6) steps = 6; | |
| if (steps > ClipperLib.MaxSteps) steps = ClipperLib.MaxSteps; // to avoid overload | |
| // if (steps > 1048576) steps = 1048576; // 0x100000 | |
| // if (steps > ClipperLib.MaxSteps) steps = ClipperLib.MaxSteps; // 0x100000 | |
| // Had to change 1048576 to lower value, because when coordinates are near or above lorange, program starts hanging | |
| // Adjust this value to meet your needs, maybe 10 is enough for most purposes | |
| var n = ClipperLib.Cast_Int32(steps); | |
| var result = new ClipperLib.Polygon(); | |
| var da = (a2 - a1) / (n - 1); | |
| var a = a1; | |
| for (var i = 0; i < n; ++i) { | |
| result.push(new ClipperLib.IntPoint(pt.X + ClipperLib.Clipper.Round(Math.cos(a) * r), pt.Y + ClipperLib.Clipper.Round(Math.sin(a) * r))); | |
| a += da; | |
| } | |
| return result; | |
| }; | |
| ClipperLib.Clipper.GetUnitNormal = function (pt1, pt2) { | |
| var dx = (pt2.X - pt1.X); | |
| var dy = (pt2.Y - pt1.Y); | |
| if ((dx == 0) && (dy == 0)) return new ClipperLib.Clipper.DoublePoint(0, 0); | |
| var f = 1 / Math.sqrt(dx * dx + dy * dy); | |
| dx *= f; | |
| dy *= f; | |
| return new ClipperLib.Clipper.DoublePoint(dy, -dx); | |
| }; | |
| ClipperLib.Clipper.prototype.OffsetPolygons = function (poly, delta, jointype, MiterLimit, AutoFix) { | |
| var a = arguments; | |
| if (a.length == 4) AutoFix = true; | |
| else if (a.length == 3) { | |
| MiterLimit = 2; | |
| AutoFix = true; | |
| } | |
| else if (a.length == 2) { | |
| jointype = ClipperLib.JoinType.jtSquare; | |
| MiterLimit = 2; | |
| AutoFix = true; | |
| } | |
| if (isNaN(delta)) ClipperLib.Error("Delta is not a number"); | |
| else if (isNaN(MiterLimit)) ClipperLib.Error("MiterLimit is not a number"); | |
| var result = {}; | |
| new ClipperLib.Clipper.PolyOffsetBuilder(poly, result, delta, jointype, MiterLimit, AutoFix); | |
| if (result.Value) result = result.Value; | |
| else result = [ | |
| [] | |
| ]; | |
| return result; | |
| }; | |
| ClipperLib.Clipper.prototype.SimplifyPolygon = function (poly, fillType) { | |
| var result = new ClipperLib.Polygons(); | |
| var c = new ClipperLib.Clipper(); | |
| if (c.AddPolygon(poly, ClipperLib.PolyType.ptSubject)) | |
| c.Execute(ClipperLib.ClipType.ctUnion, result, fillType, fillType); | |
| return result; | |
| }; | |
| ClipperLib.Clipper.prototype.SimplifyPolygons = function (polys, fillType) { | |
| var result = new ClipperLib.Polygons(); | |
| var c = new ClipperLib.Clipper(); | |
| if (c.AddPolygons(polys, ClipperLib.PolyType.ptSubject)) | |
| c.Execute(ClipperLib.ClipType.ctUnion, result, fillType, fillType); | |
| return result; | |
| }; | |
| var ce = ClipperLib.Clipper; | |
| var ce2 = ClipperLib.ClipperBase; | |
| var p; | |
| if (typeof (Object.getOwnPropertyNames) == 'undefined') { | |
| 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.Clipper.DoublePoint = function (x, y) { | |
| this.X = x; | |
| this.Y = y; | |
| }; | |
| ClipperLib.Clipper.PolyOffsetBuilder = function (pts, solution, delta, jointype, MiterLimit, AutoFix) { | |
| this.pts = null; // Polygons | |
| this.currentPoly = null; // Polygon | |
| this.normals = null; | |
| this.delta = 0; | |
| this.m_R = 0; | |
| this.m_i = 0; | |
| this.m_j = 0; | |
| this.m_k = 0; | |
| this.botPt = null; // This is "this." because it is ref in original c# code | |
| if (delta == 0) { | |
| solution.Value = pts; | |
| return; | |
| } | |
| this.pts = pts; | |
| this.delta = delta; | |
| var i, j; | |
| //AutoFix - fixes polygon orientation if necessary and removes | |
| //duplicate vertices. Can be set false when you're sure that polygon | |
| //orientation is correct and that there are no duplicate vertices. | |
| if (AutoFix) { | |
| var Len = this.pts.length, | |
| botI = 0; | |
| while (botI < Len && this.pts[botI].length == 0) botI++; | |
| if (botI == Len) { | |
| //solution.Value = new ClipperLib.Polygons(); | |
| return; | |
| } | |
| //botPt: used to find the lowermost (in inverted Y-axis) & leftmost point | |
| //This point (on pts[botI]) must be on an outer polygon ring and if | |
| //its orientation is false (counterclockwise) then assume all polygons | |
| //need reversing ... | |
| this.botPt = this.pts[botI][0]; // This is ported with different logic than other C# refs | |
| // adding botPt to object's property it's accessible through object's | |
| // methods | |
| // => All other ref's are now ported using rather complex object.Value | |
| // technique, which seems to work. | |
| for (i = botI; i < Len; ++i) { | |
| if (this.UpdateBotPt(this.pts[i][0])) botI = i; | |
| for (j = this.pts[i].length - 1; j > 0; j--) { | |
| if (ClipperLib.ClipperBase.PointsEqual(this.pts[i][j], this.pts[i][j - 1])) { | |
| this.pts[i].splice(j, 1); | |
| } | |
| else if (this.UpdateBotPt(this.pts[i][j])) botI = i; | |
| } | |
| } | |
| if (!ClipperLib.Clipper.Orientation(this.pts[botI])) ClipperLib.Clipper.ReversePolygons(this.pts); | |
| } | |
| if (MiterLimit <= 1) MiterLimit = 1; | |
| var RMin = 2 / (MiterLimit * MiterLimit); | |
| this.normals = []; | |
| var deltaSq = delta * delta; | |
| solution.Value = new ClipperLib.Polygons(); | |
| //ClipperLib.Clear(solution.Value); | |
| var len; | |
| for (this.m_i = 0; this.m_i < this.pts.length; this.m_i++) { | |
| len = this.pts[this.m_i].length; | |
| if (len > 1 && this.pts[this.m_i][0].X == this.pts[this.m_i][len - 1].X && | |
| this.pts[this.m_i][0].Y == this.pts[this.m_i][len - 1].Y) { | |
| len--; | |
| } | |
| if (len == 0 || (len < 3 && delta <= 0)) { | |
| continue; | |
| } | |
| else if (len == 1) { | |
| var arc; | |
| arc = ClipperLib.Clipper.BuildArc(this.pts[this.m_i][len - 1], 0, ClipperLib.PI2, delta); | |
| solution.Value.push(arc); | |
| continue; | |
| } | |
| //build normals ... | |
| ClipperLib.Clear(this.normals); | |
| for (j = 0; j < len - 1; ++j) | |
| this.normals.push(ClipperLib.Clipper.GetUnitNormal(this.pts[this.m_i][j], this.pts[this.m_i][j + 1])); | |
| this.normals.push(ClipperLib.Clipper.GetUnitNormal(this.pts[this.m_i][len - 1], this.pts[this.m_i][0])); | |
| this.currentPoly = new ClipperLib.Polygon(); | |
| this.m_k = len - 1; | |
| for (this.m_j = 0; this.m_j < len; ++this.m_j) { | |
| switch (jointype) { | |
| case ClipperLib.JoinType.jtMiter: | |
| this.m_R = 1 + (this.normals[this.m_j].X * this.normals[this.m_k].X + this.normals[this.m_j].Y * this.normals[this.m_k].Y); | |
| if (this.m_R >= RMin) this.DoMiter(); | |
| else this.DoSquare(MiterLimit); | |
| break; | |
| case ClipperLib.JoinType.jtRound: | |
| this.DoRound(); | |
| break; | |
| case ClipperLib.JoinType.jtSquare: | |
| this.DoSquare(1); | |
| break; | |
| } | |
| this.m_k = this.m_j; | |
| } | |
| solution.Value.push(this.currentPoly); | |
| } | |
| //finally, clean up untidy corners ... | |
| var clpr = new ClipperLib.Clipper(); | |
| clpr.AddPolygons(solution.Value, ClipperLib.PolyType.ptSubject); | |
| if (delta > 0) { | |
| clpr.Execute(ClipperLib.ClipType.ctUnion, solution.Value, ClipperLib.PolyFillType.pftPositive, ClipperLib.PolyFillType.pftPositive); | |
| } | |
| else { | |
| var r = clpr.GetBounds(); | |
| var outer = new ClipperLib.Polygon(); | |
| outer.push(new ClipperLib.IntPoint(r.left - 10, r.bottom + 10)); | |
| outer.push(new ClipperLib.IntPoint(r.right + 10, r.bottom + 10)); | |
| outer.push(new ClipperLib.IntPoint(r.right + 10, r.top - 10)); | |
| outer.push(new ClipperLib.IntPoint(r.left - 10, r.top - 10)); | |
| clpr.AddPolygon(outer, ClipperLib.PolyType.ptSubject); | |
| clpr.Execute(ClipperLib.ClipType.ctUnion, solution.Value, ClipperLib.PolyFillType.pftNegative, ClipperLib.PolyFillType.pftNegative); | |
| if (solution.Value.length > 0) { | |
| solution.Value.splice(0, 1); | |
| for (i = 0; i < solution.Value.length; i++) | |
| solution.Value[i].reverse(); | |
| } | |
| } | |
| }; | |
| //ClipperLib.Clipper.PolyOffsetBuilder.buffLength = 128; | |
| ClipperLib.Clipper.PolyOffsetBuilder.prototype.UpdateBotPt = function (pt) { | |
| if (pt.Y > this.botPt.Y || (pt.Y == this.botPt.Y && pt.X < this.botPt.X)) { | |
| this.botPt = pt; | |
| return true; | |
| } | |
| else return false; | |
| }; | |
| ClipperLib.Clipper.PolyOffsetBuilder.prototype.AddPoint = function (pt) { | |
| this.currentPoly.push(pt); | |
| }; | |
| ClipperLib.Clipper.PolyOffsetBuilder.prototype.DoSquare = function (mul) { | |
| var pt1 = new ClipperLib.IntPoint(ClipperLib.Cast_Int64(ClipperLib.Clipper.Round(this.pts[this.m_i][this.m_j].X + this.normals[this.m_k].X * this.delta)), | |
| ClipperLib.Cast_Int64(ClipperLib.Clipper.Round(this.pts[this.m_i][this.m_j].Y + this.normals[this.m_k].Y * this.delta))); | |
| var pt2 = new ClipperLib.IntPoint(ClipperLib.Cast_Int64(ClipperLib.Clipper.Round(this.pts[this.m_i][this.m_j].X + this.normals[this.m_j].X * this.delta)), | |
| ClipperLib.Cast_Int64(ClipperLib.Clipper.Round(this.pts[this.m_i][this.m_j].Y + this.normals[this.m_j].Y * this.delta))); | |
| if ((this.normals[this.m_k].X * this.normals[this.m_j].Y - this.normals[this.m_j].X * this.normals[this.m_k].Y) * this.delta >= 0) { | |
| var a1 = Math.atan2(this.normals[this.m_k].Y, this.normals[this.m_k].X); | |
| var a2 = Math.atan2(-this.normals[this.m_j].Y, -this.normals[this.m_j].X); | |
| a1 = Math.abs(a2 - a1); | |
| if (a1 > ClipperLib.PI) a1 = ClipperLib.PI2 - a1; | |
| var dx = Math.tan((ClipperLib.PI - a1) / 4) * Math.abs(this.delta * mul); | |
| pt1 = new ClipperLib.IntPoint(ClipperLib.Cast_Int64((pt1.X - this.normals[this.m_k].Y * dx)), | |
| ClipperLib.Cast_Int64((pt1.Y + this.normals[this.m_k].X * dx))); | |
| this.AddPoint(pt1); | |
| pt2 = new ClipperLib.IntPoint(ClipperLib.Cast_Int64((pt2.X + this.normals[this.m_j].Y * dx)), | |
| ClipperLib.Cast_Int64((pt2.Y - this.normals[this.m_j].X * dx))); | |
| this.AddPoint(pt2); | |
| } | |
| else { | |
| this.AddPoint(pt1); | |
| this.AddPoint(this.pts[this.m_i][this.m_j]); | |
| this.AddPoint(pt2); | |
| } | |
| }; | |
| ClipperLib.Clipper.PolyOffsetBuilder.prototype.DoMiter = function () { | |
| if ((this.normals[this.m_k].X * this.normals[this.m_j].Y - this.normals[this.m_j].X * this.normals[this.m_k].Y) * this.delta >= 0) { | |
| var q = this.delta / this.m_R; | |
| this.AddPoint(new ClipperLib.IntPoint( | |
| ClipperLib.Cast_Int64( | |
| ClipperLib.Clipper.Round(this.pts[this.m_i][this.m_j].X + (this.normals[this.m_k].X + this.normals[this.m_j].X) * q)), | |
| ClipperLib.Cast_Int64( | |
| ClipperLib.Clipper.Round(this.pts[this.m_i][this.m_j].Y + (this.normals[this.m_k].Y + this.normals[this.m_j].Y) * q)))); | |
| } | |
| else { | |
| var pt1 = new ClipperLib.IntPoint(ClipperLib.Cast_Int64(ClipperLib.Clipper.Round(this.pts[this.m_i][this.m_j].X + this.normals[this.m_k].X * this.delta)), | |
| ClipperLib.Cast_Int64(ClipperLib.Clipper.Round(this.pts[this.m_i][this.m_j].Y + this.normals[this.m_k].Y * this.delta))); | |
| var pt2 = new ClipperLib.IntPoint(ClipperLib.Cast_Int64(ClipperLib.Clipper.Round(this.pts[this.m_i][this.m_j].X + this.normals[this.m_j].X * this.delta)), | |
| ClipperLib.Cast_Int64(ClipperLib.Clipper.Round(this.pts[this.m_i][this.m_j].Y + this.normals[this.m_j].Y * this.delta))); | |
| this.AddPoint(pt1); | |
| this.AddPoint(this.pts[this.m_i][this.m_j]); | |
| this.AddPoint(pt2); | |
| } | |
| }; | |
| ClipperLib.Clipper.PolyOffsetBuilder.prototype.DoRound = function () { | |
| var pt1 = new ClipperLib.IntPoint(ClipperLib.Clipper.Round(this.pts[this.m_i][this.m_j].X + this.normals[this.m_k].X * this.delta), | |
| ClipperLib.Clipper.Round(this.pts[this.m_i][this.m_j].Y + this.normals[this.m_k].Y * this.delta)); | |
| var pt2 = new ClipperLib.IntPoint(ClipperLib.Clipper.Round(this.pts[this.m_i][this.m_j].X + this.normals[this.m_j].X * this.delta), | |
| ClipperLib.Clipper.Round(this.pts[this.m_i][this.m_j].Y + this.normals[this.m_j].Y * this.delta)); | |
| this.AddPoint(pt1); | |
| //round off reflex angles (ie > 180 deg) unless almost flat (ie < 10deg). | |
| //cross product normals < 0 . angle > 180 deg. | |
| //dot product normals == 1 . no angle | |
| if ((this.normals[this.m_k].X * this.normals[this.m_j].Y - this.normals[this.m_j].X * this.normals[this.m_k].Y) * this.delta >= 0) { | |
| if ((this.normals[this.m_j].X * this.normals[this.m_k].X + this.normals[this.m_j].Y * this.normals[this.m_k].Y) < 0.985) { | |
| var a1 = Math.atan2(this.normals[this.m_k].Y, this.normals[this.m_k].X); | |
| var a2 = Math.atan2(this.normals[this.m_j].Y, this.normals[this.m_j].X); | |
| if (this.delta > 0 && a2 < a1) a2 += ClipperLib.PI2; | |
| else if (this.delta < 0 && a2 > a1) a2 -= ClipperLib.PI2; | |
| var arc = ClipperLib.Clipper.BuildArc(this.pts[this.m_i][this.m_j], a1, a2, this.delta); | |
| for (var m = 0; m < arc.length; m++) | |
| this.AddPoint(arc[m]); | |
| } | |
| } | |
| else this.AddPoint(this.pts[this.m_i][this.m_j]); | |
| this.AddPoint(pt2); | |
| }; | |
| ClipperLib.Error = function (message) { | |
| try { | |
| throw new Error(message); | |
| } | |
| catch (err) { | |
| alert(err.message); | |
| } | |
| }; | |
| // Make deep copy of Polygons or Polygon | |
| // so that also IntPoint objects are cloned and not only referenced | |
| // This should be the fastest way | |
| ClipperLib.Clone = function (polygon) { | |
| if (!(polygon instanceof Array)) return []; | |
| if (polygon.length == 0) return []; | |
| else if (polygon.length == 1 && polygon[0].length == 0) return [ | |
| [] | |
| ]; | |
| var isPolygons = polygon[0] instanceof Array; | |
| if (!isPolygons) polygon = [polygon]; | |
| var len = polygon.length, plen, i, j, result; | |
| var results = []; | |
| for (i = 0; i < len; i++) { | |
| plen = polygon[i].length; | |
| result = []; | |
| for (j = 0; j < plen; j++) { | |
| result.push({X: polygon[i][j].X, Y: polygon[i][j].Y}); | |
| } | |
| results.push(result); | |
| } | |
| if (!isPolygons) results = results[0]; | |
| return results; | |
| }; | |
| // Clean() joins vertices that are too near each other | |
| // and causes distortion to offsetted polygons without cleaning | |
| ClipperLib.Clean = function (polygon, delta) { | |
| if (!(polygon instanceof Array)) return []; | |
| var isPolygons = polygon[0] instanceof Array; | |
| var polygon = ClipperLib.Clone(polygon); | |
| if (typeof delta != "number" || delta === null) { | |
| 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; | |
| } | |
| // Removes points that doesn't affect much to the visual appearance. | |
| // If middle point is at or under certain distance (tolerance) of the line between | |
| // start and end point, the middle point is removed. | |
| ClipperLib.Lighten = function (polygon, tolerance) { | |
| if (!(polygon instanceof Array)) return []; | |
| if (typeof tolerance != "number" || tolerance === null) { | |
| ClipperLib.Error("Tolerance is not a number in Lighten().") | |
| return ClipperLib.Clone(polygon); | |
| } | |
| if (polygon.length === 0 || (polygon.length == 1 && polygon[0].length === 0) || tolerance < 0) { | |
| return ClipperLib.Clone(polygon); | |
| } | |
| if (!(polygon[0] instanceof Array)) polygon = [polygon]; | |
| var i, j, poly, k, poly2, plen, A, B, P, d, rem, addlast; | |
| var bxax, byay, nL; | |
| var len = polygon.length; | |
| var results = []; | |
| for (i = 0; i < len; i++) { | |
| poly = polygon[i]; | |
| for (k = 0; k < 1000000; k++) // could be forever loop, but wiser to restrict max repeat count | |
| { | |
| poly2 = []; | |
| plen = poly.length; | |
| // the first have to added to the end, if first and last are not the same | |
| // this way we ensure that also the actual last point can be removed if needed | |
| if (poly[plen - 1].X != poly[0].X || poly[plen - 1].Y != poly[0].Y) { | |
| addlast = 1; | |
| poly.push({X: poly[0].X, Y: poly[0].Y}); | |
| plen = poly.length; | |
| } | |
| else addlast = 0; | |
| rem = []; // Indexes of removed points | |
| for (j = 0; j < plen - 2; j++) { | |
| A = poly[j]; // Start point of line segment | |
| P = poly[j + 1]; // Middle point. This is the one to be removed. | |
| B = poly[j + 2]; // End point of line segment | |
| bxax = B.X - A.X; | |
| byay = B.Y - A.Y; | |
| d = 0; | |
| if (bxax !== 0 || byay !== 0) // To avoid Nan, when A==P && P==B. And to avoid peaks (A==B && A!=P), which have lenght, but not area. | |
| { | |
| nL = Math.sqrt(bxax * bxax + byay * byay); | |
| // d is the perpendicular distance from P to (infinite) line AB. | |
| d = Math.abs((P.X - A.X) * byay - (P.Y - A.Y) * bxax) / nL; | |
| } | |
| if (d <= tolerance) { | |
| rem[j + 1] = 1; | |
| j++; // when removed, transfer the pointer to the next one | |
| } | |
| } | |
| // add all unremoved points to poly2 | |
| poly2.push({X: poly[0].X, Y: poly[0].Y}); | |
| for (j = 1; j < plen - 1; j++) | |
| if (!rem[j]) poly2.push({X: poly[j].X, Y: poly[j].Y}); | |
| poly2.push({X: poly[plen - 1].X, Y: poly[plen - 1].Y}); | |
| // if the first point was added to the end, remove it | |
| if (addlast) poly.pop(); | |
| // break, if there was not anymore removed points | |
| if (!rem.length) break; | |
| // else continue looping using poly2, to check if there are points to remove | |
| else poly = poly2; | |
| } | |
| plen = poly2.length; | |
| // remove duplicate from end, if needed | |
| if (poly2[plen - 1].X == poly2[0].X && poly2[plen - 1].Y == poly2[0].Y) { | |
| poly2.pop(); | |
| } | |
| if (poly2.length > 2) // to avoid two-point-polygons | |
| results.push(poly2); | |
| } | |
| if (!polygon[0] instanceof Array) results = results[0]; | |
| if (typeof (results) == "undefined") results = [ | |
| [] | |
| ]; | |
| return results; | |
| } | |
| if (typeof(document) !== "undefined") window.ClipperLib = ClipperLib; | |
| else self['ClipperLib'] = ClipperLib; | |
| })(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <script src="clipper_unminified.js"></script> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.0/raphael-min.js"></script> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script> | |
| <script> | |
| /* | |
| * jQuery repeatedclick v1.0.5 | |
| * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) | |
| * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses. | |
| * Written by: Alexandr Zykov <[email protected]> | |
| */ | |
| if (typeof(jQuery) != "undefined") | |
| jQuery.fn.hold = function (h, j) { | |
| var c = jQuery.extend({duration: 350, speed: 0.85, min: 50}, j); | |
| "undefined" === typeof jQuery.repeatedEvents && (jQuery.repeatedEvents = []); | |
| jQuery.repeatedEvents.push(h); | |
| var k = jQuery.repeatedEvents.length - 1, d, e; | |
| return this.each(function () { | |
| d = function (f, b, a) { | |
| var g = this; | |
| jQuery.repeatedEvents[f].call(g, a); | |
| e = setTimeout(function () { | |
| d.call(g, f, b > c.min ? b * c.speed : b, a) | |
| }, b) | |
| }; | |
| jQuery(this).mousedown(function (a) { | |
| d.call(this, k, c.duration, a) | |
| }); | |
| var a = function () { | |
| "undefined" !== typeof e && clearInterval(e) | |
| }; | |
| jQuery(this).mouseout(a); | |
| jQuery(this).mouseup(a) | |
| }) | |
| }; | |
| </script> | |
| <script> | |
| /* | |
| * TotalStorage v. 1.1.2 | |
| * Copyright (c) 2012 Jared Novack & Upstatement (upstatement.com) | |
| * Dual licensed under the MIT and GPL licenses: | |
| * http://www.opensource.org/licenses/mit-license.php | |
| * http://www.gnu.org/licenses/gpl.html | |
| * @author Jared Novack/[email protected] | |
| * @url http://upstatement.com/blog/2012/01/jquery-local-storage-done-right-and-easy/ | |
| */ | |
| if (typeof(jQuery) != "undefined") | |
| (function (c) { | |
| var e = window.localStorage, f; | |
| f = "undefined" == typeof e || "undefined" == typeof window.JSON ? !1 : !0; | |
| c.totalStorage = function (b, a) { | |
| return c.totalStorage.impl.init(b, a) | |
| }; | |
| c.totalStorage.setItem = function (b, a) { | |
| return c.totalStorage.impl.setItem(b, a) | |
| }; | |
| c.totalStorage.getItem = function (b) { | |
| return c.totalStorage.impl.getItem(b) | |
| }; | |
| c.totalStorage.getAll = function () { | |
| return c.totalStorage.impl.getAll() | |
| }; | |
| c.totalStorage.deleteItem = function (b) { | |
| return c.totalStorage.impl.deleteItem(b) | |
| }; | |
| c.totalStorage.impl = {init: function (b, a) { | |
| return"undefined" != typeof a ? this.setItem(b, a) : this.getItem(b) | |
| }, setItem: function (b, a) { | |
| if (!f)try { | |
| return c.cookie(b, a), a | |
| } catch (d) { | |
| console.log("Local Storage not supported by this browser. Install the cookie plugin on your site to take advantage of the same functionality. You can get it at https://github.com/carhartl/jquery-cookie") | |
| } | |
| var g = JSON.stringify(a); | |
| e.setItem(b, g); | |
| return this.parseResult(g) | |
| }, getItem: function (b) { | |
| if (!f)try { | |
| return this.parseResult(c.cookie(b)) | |
| } catch (a) { | |
| return null | |
| } | |
| return this.parseResult(e.getItem(b)) | |
| }, | |
| deleteItem: function (b) { | |
| if (!f)try { | |
| return c.cookie(b, null), !0 | |
| } catch (a) { | |
| return!1 | |
| } | |
| e.removeItem(b); | |
| return!0 | |
| }, getAll: function () { | |
| var b = []; | |
| if (f)for (d in e)d.length && b.push({key: d, value: this.parseResult(e.getItem(d))}); else try { | |
| for (var a = document.cookie.split(";"), d = 0; d < a.length; d++) { | |
| var g = a[d].split("=")[0]; | |
| b.push({key: g, value: this.parseResult(c.cookie(g))}) | |
| } | |
| } catch (h) { | |
| return null | |
| } | |
| return b | |
| }, parseResult: function (b) { | |
| var a; | |
| try { | |
| a = JSON.parse(b), "true" == a && (a = !0), "false" == a && (a = !1), parseFloat(a) == a && "object" != typeof a && | |
| (a = parseFloat(a)) | |
| } catch (c) { | |
| } | |
| return a | |
| }} | |
| })(jQuery); | |
| </script> | |
| <meta charset=utf-8/> | |
| <title>Javascript Clipper Main Demo</title> | |
| <style> | |
| .textarea_hide_buttons { | |
| margin-bottom: 6px; | |
| margin-top: 4px; | |
| } | |
| body { | |
| background-color: #F8F8F8; | |
| } | |
| p { | |
| margin: 0px; | |
| padding: 0px; | |
| } | |
| th { | |
| background-color: #DDDDDD; | |
| font-weight: bold; | |
| padding: 0px; | |
| } | |
| body, th, td, input, legend, fieldset, p, b, button, select, textarea { | |
| font-size: 13px; | |
| font-family: Arial, Helvetica, sans-serif; | |
| } | |
| #p, #_p { | |
| background-color: #ffffff; | |
| border: 2px solid #AAAAAA; | |
| } | |
| .svg_mypath { | |
| stroke: black; | |
| stroke-width: 1; | |
| fill: black; | |
| fill-opacity: 0.3; | |
| } | |
| #p1, #_p1 { | |
| fill: #00009C; | |
| fill-opacity: 0.06; | |
| stroke: #D3D3DA; | |
| stroke-opacity: 1.0; | |
| stroke-width: 0.8; | |
| } | |
| #p2, #_p2 { | |
| fill: #9C0000; | |
| fill-opacity: 0.06; | |
| stroke: #FFA07A; | |
| stroke-opacity: 1.0; | |
| stroke-width: 0.8; | |
| } | |
| #p3, #_p3 { | |
| fill: #80ff9C; | |
| fill-opacity: 0.5; | |
| stroke: #003300; | |
| stroke-opacity: 1.0; | |
| stroke-width: 0.8; | |
| } | |
| #svg_source_textarea { | |
| width: 1215px; | |
| height: 300px; | |
| } | |
| #benchmark_exports_textarea { | |
| width: 1215px; | |
| height: 300px; | |
| } | |
| div#svgcontainer { | |
| margin-right: 2px; | |
| margin-top: 6px; | |
| } | |
| div#benchmark_div_outer { | |
| width: 100%; | |
| } | |
| div#benchmark_div { | |
| width: 100%; | |
| } | |
| #benchmark_fieldset { | |
| width: 367px; | |
| min-width: 367px; | |
| height: 350px; | |
| } | |
| .buttons { | |
| display: inline-block; | |
| padding: 0px; | |
| padding-left: 12px; | |
| padding-right: 12px; | |
| background-color: #666666; | |
| color: white; | |
| border-radius: 10px; | |
| text-align: center; | |
| line-height: 20px; | |
| font-size: 13px; | |
| cursor: pointer; | |
| margin-bottom: 2px; | |
| margin-top: 2px; | |
| font-family: Helvetica, Arial, sans-serif; | |
| white-space: nowrap; | |
| } | |
| LEGEND { | |
| font-weight: bold; | |
| } | |
| FIELDSET { | |
| background-color: #EEEEEE; | |
| /* | |
| border-radius: 4px 4px 7px 7px; | |
| -moz-border-radius: 4px 4px 7px 7px; | |
| -webkit-border-radius: 4px 4px 7px 7px; | |
| */ | |
| border: 2px solid #AAAAAA; | |
| white-space: nowrap; | |
| } | |
| table { | |
| border: 0px; | |
| } | |
| .subpolylinks { | |
| cursor: pointer; | |
| font-size: 13px; | |
| padding: 2px; | |
| } | |
| .subpolylinks:hover { | |
| background-color: #BBBBBB; | |
| } | |
| .subpolylinks_disabled { | |
| font-size: 13px; | |
| padding: 2px; | |
| } | |
| /* To prevent odd text resizing in mobile Safari */ | |
| @media only screen and (max-device-width: 480px) { | |
| .body, p, fieldset, td, th { | |
| -webkit-text-size-adjust: 100% | |
| } | |
| } | |
| .polygon_explorer { | |
| text-align: left; | |
| border-collapse: collapse; | |
| white-space: normal; | |
| width: 461px; | |
| } | |
| .polygon_explorer th { | |
| font-weight: bold; | |
| } | |
| .polygon_explorer > tr > th, .polygon_explorer > tr > td, | |
| .polygon_explorer > tbody > tr > th, .polygon_explorer > tbody > tr > td { | |
| border: 1px solid #666666; | |
| padding: 3px 4px 3px 4px; | |
| vertical-align: top; | |
| } | |
| .polygons_fieldset { | |
| border-collapse: collapse; | |
| width: 100%; | |
| } | |
| .polygons_fieldset td { | |
| vertical-align: top; | |
| white-space: nowrap; | |
| padding: 0px; | |
| padding-right: 10px; | |
| } | |
| .polygons_fieldset input[type=radio] { | |
| margin-left: 0px; | |
| } | |
| .random { | |
| border-collapse: collapse; | |
| width: 100%; | |
| } | |
| .random td { | |
| padding: 0px; | |
| padding-right: 10px; | |
| } | |
| .random_inputs { | |
| width: 25px | |
| } | |
| .random th { | |
| padding: 0px; | |
| padding-right: | |
| } | |
| .random th { | |
| background-color: transparent; | |
| } | |
| .random_new_th { | |
| background-color: transparent; | |
| } | |
| .random_new_th button { | |
| margin: 0px; | |
| } | |
| .random button, .random input { | |
| margin-bottom: 0px; | |
| margin-top: 0px; | |
| } | |
| #subj_polygon_count { | |
| margin-bottom: 2px; | |
| } | |
| .filltype_cliptype { | |
| border-collapse: collapse; | |
| } | |
| .filltype_cliptype td { | |
| vertical-align: top; | |
| padding: 0px; | |
| } | |
| .offset_outer { | |
| border-collapse: collapse; | |
| width: 100%; | |
| } | |
| .offset_outer td { | |
| vertical-align: top; | |
| white-space: nowrap; | |
| padding: 0px; | |
| } | |
| .offset_outer input[type=radio] { | |
| margin-left: 0px; | |
| } | |
| .offset_inner input[type=text] { | |
| margin-bottom: 2px; | |
| } | |
| .offset_inner { | |
| border-collapse: collapse; | |
| } | |
| .offset_inner td { | |
| vertical-align: top; | |
| height: 100%; | |
| } | |
| #custom_polygons_fieldset { | |
| display: none; | |
| } | |
| #random_polygons_fieldset { | |
| display: none; | |
| } | |
| #misc_fieldset { | |
| min-height: 81px; | |
| } | |
| .custom_polygon_input { | |
| width: 285px; | |
| height: 15px; | |
| padding: 2px; | |
| line-height: 15px; | |
| } | |
| #polygon_explorer_string_inp { | |
| margin: 0px; | |
| height: 15px; | |
| width: 323px; | |
| padding: 2px; | |
| line-height: 15px; | |
| } | |
| #custom_polygon_inputs { | |
| display: block; | |
| } | |
| #custom_poly_message_box_green { | |
| display: none; | |
| opacity: 0; | |
| position: absolute; | |
| top: 0px; | |
| left: 0px; | |
| height: 0px; | |
| width: 200px; | |
| vertical-align: middle; | |
| text-align: center; | |
| border: 2px solid #106712; | |
| background-color: #A3F1A5; | |
| padding: 8px; | |
| padding-left: 11px; | |
| padding-right: 11px; | |
| border-radius: 6px; | |
| font-size: 16px; | |
| z-index: 2; | |
| } | |
| .custom_polygon_inputs_table { | |
| border-collapse: collapse; | |
| } | |
| #help_builtin_polygon_sets, #help_output_format, #help_custom_polygon { | |
| padding-left: 7px; | |
| padding-right: 7px; | |
| } | |
| /* | |
| #outertable > tbody > tr > td { | |
| border:1px solid red; | |
| } | |
| */ | |
| </style> | |
| </head> | |
| <body> | |
| <table id="outertable" style="margin-bottom:5px"> | |
| <tr> | |
| <td id="td0" style="vertical-align:top;width:353px;min-width:353px"> | |
| <FIELDSET id="PolygonsFieldset"> | |
| <LEGEND title="Builtin polygons">Polygons</LEGEND> | |
| <table class="polygons_fieldset"> | |
| <tr> | |
| <td> | |
| <input type="radio" name="polygons" value="0">Arrows | |
| <br> | |
| <input type="radio" name="polygons" value="1">Texts | |
| <br> | |
| <input type="radio" name="polygons" value="2">Rects | |
| <br> | |
| </td> | |
| <td> | |
| <input type="radio" name="polygons" value="3">Same | |
| <br> | |
| <input type="radio" name="polygons" value="4" title="Random rectangles">RandRects | |
| <br> | |
| <input type="radio" name="polygons" value="5" title="Random polygons">Random | |
| <br> | |
| </td> | |
| <td> | |
| <input type="radio" name="polygons" value="6">Star & rect | |
| <br> | |
| <input type="radio" name="polygons" value="7">Spiral | |
| <br> | |
| <input type="radio" name="polygons" value="8">Grid & star | |
| <br> | |
| </td> | |
| <td style="padding-right:0px"> | |
| <input type="radio" name="polygons" value="9">Glyph | |
| <br> | |
| <input type="radio" name="polygons" value="10" title="Your own custom polygon sets">Custom | |
| <br> | |
| </td> | |
| </tr> | |
| </table> | |
| </FIELDSET> | |
| <FIELDSET id="custom_polygons_fieldset" style="position:relative"> | |
| <LEGEND>Your own custom polygons</LEGEND> | |
| <select name="custom_polygons_select" | |
| id="custom_polygons_select" title="Saved custom polygon sets"></select> | |
| <select name="sample_custom_polygon" id="sample_custom_polygon" title="Get Builtin polygon set"> | |
| <option value="">- Builtin -</option> | |
| <option value="0">Arrows</option> | |
| <option value="1">Texts</option> | |
| <option value="2">Rects</option> | |
| <option value="3">Same</option> | |
| <option value="4">RandRects</option> | |
| <option value="5">Random</option> | |
| <option value="6">Star & rect</option> | |
| <option value="7">Spiral</option> | |
| <option value="8">Grid & star</option> | |
| <option value="9">Glyph</option> | |
| </select> | |
| <button id="help_builtin_polygon_sets" title="Get help about Builtin polygon sets">?</button> | |
| <div id="custom_polygon_inputs"> | |
| <table class="custom_polygon_inputs_table"> | |
| <tr> | |
| <td> | |
| <b title="Here you can type or paste custom subject polygons. Click the '?' button below to get help about string formats.">Subj</b> | |
| </td> | |
| <td> | |
| <textarea class="custom_polygon_input" id="custom_polygon_subj" value=''></textarea> | |
| </td> | |
| </tr> | |
| <tr> | |
| <td> | |
| <b title="Here you can type or paste custom clip polygons. Click the '?' button below to get help about string formats.">Clip</b> | |
| </td> | |
| <td> | |
| <textarea class="custom_polygon_input" id="custom_polygon_clip" value=''></textarea> | |
| </td> | |
| </tr> | |
| </table> | |
| <button id="save_custom_polygon" title="Update polygon set">Save</button> | |
| <button id="add_as_new_custom_polygon" title="Save as a new custom polygon set">Save as new</button> | |
| <button id="remove_custom_polygon" title="Remove selected custom polygon set">Del</button> | |
| <button id="removeall_custom_polygon" title="Remove all custom polygon sets">Del all</button> | |
| <button id="help_custom_polygon" title="Get help about custom polygons">?</button> | |
| </div> | |
| <div id="custom_poly_message_box_green"></div> | |
| </FIELDSET> | |
| <FIELDSET id="random_polygons_fieldset"> | |
| <LEGEND>Random polygon settings</LEGEND> | |
| <table class="random"> | |
| <thead> | |
| <tr> | |
| <th class="random_new_th"> | |
| <button id="generate_random_polygons" title="Generate random polygons">New</button> | |
| </th> | |
| <th>Polygon count</th> | |
| <th style="padding-right:0px">Point count</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| <tr> | |
| <td><b>Subject</b> | |
| </td> | |
| <td> | |
| <button id="subj_polygon_count_minus"> - </button> | |
| <input class="random_inputs" type="text" id="subj_polygon_count" | |
| value="1"> | |
| <button id="subj_polygon_count_plus"> + </button> | |
| </td> | |
| <td style="padding-right:0px"> | |
| <button id="subj_point_count_minus"> - </button> | |
| <input class="random_inputs" type="text" id="subj_point_count" | |
| value="1"> | |
| <button id="subj_point_count_plus"> + </button> | |
| </td> | |
| </tr> | |
| <tr> | |
| <td><b>Clip</b> | |
| </td> | |
| <td> | |
| <button id="clip_polygon_count_minus"> - </button> | |
| <input class="random_inputs" type="text" id="clip_polygon_count" | |
| value="1"> | |
| <button id="clip_polygon_count_plus"> + </button> | |
| </td> | |
| <td style="padding-right:0px"> | |
| <button id="clip_point_count_minus"> - </button> | |
| <input class="random_inputs" type="text" id="clip_point_count" | |
| value="1"> | |
| <button id="clip_point_count_plus"> + </button> | |
| </td> | |
| </tr> | |
| </table> | |
| </FIELDSET> | |
| <table id="filltype_cliptype_table" class="filltype_cliptype" style="width:100%"> | |
| <tr> | |
| <td> | |
| <FIELDSET> | |
| <LEGEND>Subject FillType</LEGEND> | |
| <input type="radio" name="subject_fillType" value="0">EvenOdd | |
| <input type="radio" name="subject_fillType" value="1">NonZero | |
| </FIELDSET> | |
| </td> | |
| <td> | |
| <FIELDSET> | |
| <LEGEND>Clip FillType</LEGEND> | |
| <input type="radio" name="clip_fillType" value="0">EvenOdd | |
| <input type="radio" name="clip_fillType" value="1">NonZero | |
| </FIELDSET> | |
| </td> | |
| </tr> | |
| </table> | |
| <FIELDSET id="ClipTypeFieldset"> | |
| <LEGEND>Clip type (operation)</LEGEND> | |
| <p> | |
| <input type="radio" name="clipType" value="">No | |
| <input type="radio" name="clipType" value="0">Intersect | |
| <input type="radio" name="clipType" value="1">Union | |
| <input type="radio" name="clipType" value="2">Difference | |
| <input type="radio" name="clipType" value="3" checked>Xor</p> | |
| </FIELDSET> | |
| <FIELDSET id="CleaningFieldset"> | |
| <LEGEND>Cleaning and simplifying</LEGEND> | |
| <p> | |
| <b>Clean</b><input type="checkbox" id="clean" | |
| title="Clean. Joins sequential vertices if they are almost coincident. This is needed to prevent polygon breakage when offsetting. Not included in original Clipper, yet. This operation is done before offsetting." | |
| value="1"> | |
| <input style="width:28px" type="text" value="" id="cleandelta" | |
| title="Delta. The distance used for Clean. Sequential vertices are joined if they are at or below this distance of each other."> | |
| <b>Simplify</b><input type="checkbox" id="Simplify" | |
| title="Simplify. Convert complex (self-intersecting) polygon to simple ones. Produces multiple polygons, if there are self-intersections. Note! Not work in self-intersections that are too small, you have to remove too-near-vertices using Clean feature. This operation is done before offsetting." | |
| value="1"> | |
| <b>Lighten</b><input type="checkbox" id="lighten" | |
| title="Lighten. Removes unnecessary vertices. Not included in original Clipper. This operation is done after offsetting." | |
| value="1"> | |
| <input style="width:28px;" type="text" value="" id="lighten_distance" | |
| title="Tolerance. The distance that is used when lightening polygon. Removes middle vertex of three sequential vertices, if the middle vertex is under (or at) this distance of the line between start and end vertex."> | |
| </p> | |
| </FIELDSET> | |
| <FIELDSET id="OffsettingFieldset"> | |
| <LEGEND>Offsetting</LEGEND> | |
| <table class="offset_outer"> | |
| <tr> | |
| <td style="padding-right:6px;"> | |
| <b><span style="display:block;padding-bottom:3px">Polygon</span></b> | |
| <input type="radio" name="offsettable_poly" value="1">Subject | |
| <br> | |
| <input type="radio" name="offsettable_poly" value="2">Clip | |
| <br> | |
| <input type="radio" id="offsettable_poly3" name="offsettable_poly" value="3" checked>Solution | |
| </td> | |
| <td style="padding-right:6px;"> | |
| <b><span style="display:block;padding-bottom:3px">JoinType</span></b> | |
| <input type="radio" name="joinType" value="0" checked>Square | |
| <br> | |
| <input type="radio" name="joinType" value="1">Round | |
| <br> | |
| <input type="radio" name="joinType" value="2">Miter | |
| <br> | |
| </td> | |
| <td style="padding-right:0px"> | |
| <table class="offset_inner"> | |
| <tr> | |
| <td> | |
| <b>Delta</b> | |
| <button id="delta_minus"> - </button> | |
| <input type="text" id="delta" value="" style="width:50px"> | |
| <button id="delta_plus"> + </button> | |
| </td> | |
| </tr> | |
| <tr> | |
| <td><b>MiterLimit</b> | |
| <button id="miterLimit_minus"> - </button> | |
| <input type="text" id="miterLimit" value="" style="width:20px"> | |
| <button id="miterLimit_plus"> + </button> | |
| </td> | |
| </tr> | |
| <tr> | |
| <td style="padding-bottom: 0px; padding-top: 3px;"> | |
| <b title="AutoFix">AutoFix</b> | |
| <input type="checkbox" id="AutoFix" | |
| title="AutoFix. Fixes polygon orientation if necessary and removes duplicate vertices. Can be set false when you are sure that polygon orientation is correct and that there are no duplicate vertices." | |
| value="1"> | |
| </td> | |
| </tr> | |
| </table> | |
| </td> | |
| </tr> | |
| </table> | |
| </FIELDSET> | |
| <FIELDSET id="ScaleFieldset"> | |
| <LEGEND>Scale</LEGEND> | |
| <p style="width:200px;"> | |
| <button id="scale_minus"> - </button> | |
| <input type="text" id="scale" value="" style="width:80px"> | |
| <button id="scale_plus"> + </button> | |
| <span id="scaletest"></span> <b>Bigints used:</b> | |
| <span | |
| id="biginteger_used"></span> | |
| </p> | |
| </FIELDSET> | |
| <FIELDSET id="misc_fieldset"> | |
| <LEGEND>Misc</LEGEND> | |
| <button id="show_svg_source" title="Show current SVG source in textarea at the bottom of the page">Show SVG source | |
| B | |
| </button> | |
| <b>Bevel</b> | |
| <input type="checkbox" id="bevel" | |
| value="1" title="Uncheck this to speedup drawing"> | |
| <b>Explorer</b> | |
| <input type="checkbox" id="sub_poly_links_update" | |
| value="1" title="Enable/disable Polygon Explorer"> | |
| <br> | |
| <button id="benchmark1" title="Execute Normal benchmark">Run NB</button> | |
| <button id="benchmark1b" title="Execute Normal benchmark 5x">Run NB 5x</button> | |
| <button id="benchmark2" title="Execute Big Integer benchmark">Run BIB</button> | |
| <button id="benchmark2b" title="Execute Big Integer benchmark 5x">Run BIB 5x</button> | |
| </FIELDSET> | |
| </td> | |
| <td id="td1" style="vertical-align:top;width:506px;"> | |
| <div id="svgcontainer"></div> | |
| <FIELDSET id="polygon_explorer_fieldset"> | |
| <LEGEND>Polygon explorer</LEGEND> | |
| <div id="polygon_explorer_div" style="padding-left:1px;/*overflow-y:auto;overflow-x:visible;max-height:300px*/"> | |
| <table class="polygon_explorer"> | |
| <tr> | |
| <td colspan="4" style="padding:0px;white-space:nowrap; border:0px"> | |
| <table style="padding:0px;border-collapse:collapse"> | |
| <tr> | |
| <td style="padding:0px;vertical-align:middle"> | |
| <textarea id="polygon_explorer_string_inp" | |
| title="When you click the numbers below, here comes coordinates of the clicked polygon (or polygons) as string." | |
| value="">Click below to see coordinates here!</textarea> | |
| </td> | |
| <td style="padding:0px;vertical-align:middle"> | |
| <select id="output_format" title="Output format" style="width:73px;margin-left:5px"> | |
| <option value="0">Clipper</option> | |
| <option value="1">Plain</option> | |
| <option value="2">SVG</option> | |
| </select> <b>Ex</b> <input type="checkbox" id="ExPolygons" | |
| value="1" title="Show solution as ExPolygons"> | |
| </td> | |
| </tr> | |
| </table> | |
| </td> | |
| </tr> | |
| <tr> | |
| <th style="white-space:nowrap; width:31px;" title="Polygon type">Type</th> | |
| <th style="white-space:nowrap; width:35px;" title="Count of subpolygons">Polys</th> | |
| <th style="white-space:nowrap; width:40px;" | |
| title="Count of points in all sub polygons. Hover numbers below to see the whole multi-polygon highlighted. Click them to see coordinates as strings and the area of multi-polygon (sum of areas of sub polygons)."> | |
| Points | |
| </th> | |
| <th style="white-space:nowrap;" | |
| title="Count of points in sub polygons. Hover numbers below to see sub polygons highlighted. Click to see coordinates as strings and the area of sub polygon." | |
| >Points in subpolygons | |
| </th> | |
| </tr> | |
| <tr> | |
| <td id="subject_box" title="Subject"> | |
| <b>Subj</b> | |
| </td> | |
| <td> | |
| <span id="subj_subpolygons"></span> | |
| </td> | |
| <td> | |
| <span id="subj_points_total" class="subpolylinks" | |
| onclick="popup_path(undefined,'1')" | |
| onmouseover="show_path(undefined,'1')" onmouseout="hide_path()" | |
| ></span> | |
| </td> | |
| <td> | |
| <span id="subj_points_in_subpolygons"></span> | |
| </td> | |
| </tr> | |
| <tr> | |
| <td id="clip_box" title="Clip"> | |
| <b>Clip</b> | |
| </td> | |
| <td> | |
| <span id="clip_subpolygons"> | |
| </span> | |
| </td> | |
| <td><span id="clip_points_total" class="subpolylinks" | |
| onclick="popup_path(undefined,'2')" | |
| onmouseover="show_path(undefined,'2')" onmouseout="hide_path()" | |
| ></span> | |
| </td> | |
| <td> | |
| <span id="clip_points_in_subpolygons"></span> | |
| </td> | |
| </tr> | |
| <tr> | |
| <td id="solution_box" title="Solution"> | |
| <b>Solu</b> | |
| </td> | |
| <td> | |
| <span id="solution_subpolygons"></span> | |
| </td> | |
| <td> | |
| <span id="solution_points_total" class="subpolylinks" | |
| onclick="popup_path(undefined,'3')" | |
| onmouseover="show_path(undefined,'3')" onmouseout="hide_path()" | |
| ></span> | |
| </td> | |
| <td> | |
| <span id="solution_points_in_subpolygons"> | |
| </span> | |
| </td> | |
| </tr> | |
| <tr> | |
| <td> | |
| <b>Total</b> | |
| </td> | |
| <td> | |
| <b><span id="all_subpolygons"></span></b> | |
| </td> | |
| <td> | |
| <b><span id="points_total"></span></b> | |
| </td> | |
| <td style="padding-bottom:0px;padding-top:0px;text-align:right;white-space:nowrap"> | |
| <div id="area" style="padding:0px;display:inline-block;width:90%;text-align:left" | |
| title="Click above numbers to see the area of sub polygons." | |
| >Area: Click above to see polygon area! | |
| </div> | |
| <button id="help_output_format" title="Get help about output format">?</button> | |
| </td> | |
| </tr> | |
| </table> | |
| </div> | |
| </FIELDSET> | |
| </td> | |
| <td id="td2" style="width:353px;vertical-align:top;"> | |
| <FIELDSET id="benchmark_fieldset_f" style="min-height:517px"> | |
| <LEGEND id="#benchmark_fieldset">Benchmark</LEGEND> | |
| <div id="benchmark_div_outer"> | |
| <div id="benchmark_div"></div> | |
| <div id="benchmark_multiple_table_cont"></div> | |
| </div> | |
| </FIELDSET> | |
| </td> | |
| <td id="td3" style="vertical-align:top;"> | |
| <div id="svg_source_container2"></div> | |
| </td> | |
| </tr> | |
| </table> | |
| <div id="svg_source_container"></div> | |
| <div id="benchmark_exports_container"></div> | |
| <script> | |
| //var scale = 10000000000000; | |
| var scale = 100; | |
| var global_do_not_round_and_scale = false; | |
| var scale_addition = 100; | |
| var polygons_default = 1; | |
| var joinType = 0; | |
| var miterLimit = 2.0; | |
| var delta = -1; | |
| var AutoFix = true; | |
| var ExPolygons = false; | |
| var SolutionPolygonClicked = false; | |
| var SolutionPolygonString = ""; | |
| var Simplify = true; | |
| var clean = true; | |
| var cleandelta_default = 0.1; | |
| var cleandelta = cleandelta_default; | |
| var lighten = true; | |
| var lighten_distance_default = 0.1; | |
| var lighten_distance = lighten_distance_default; | |
| var offsettable_poly = 3; | |
| var clipType = 3; | |
| var subject_fillType = 1; | |
| var clip_fillType = 1; | |
| var ss, cc, sss, cpr, sss2, cpr2, p, ok = 1; | |
| var off_result; | |
| var SVG = {}, p, p1, p2, p3; | |
| var random_subj; | |
| var random_clip; | |
| var rnd_sett_defaults = {}; | |
| rnd_sett_defaults.rect = {}; | |
| rnd_sett_defaults.norm = {}; | |
| rnd_sett_defaults.rect.default = { | |
| clip_polygon_count: 1, | |
| clip_point_count: 4, | |
| subj_polygon_count: 2, | |
| subj_point_count: 8 | |
| }; | |
| rnd_sett_defaults.rect.min = { | |
| clip_polygon_count: 1, | |
| clip_point_count: 4, | |
| subj_polygon_count: 1, | |
| subj_point_count: 4 | |
| }; | |
| rnd_sett_defaults.rect.max = { | |
| clip_polygon_count: 100, | |
| clip_point_count: 100, | |
| subj_polygon_count: 100, | |
| subj_point_count: 100 | |
| }; | |
| rnd_sett_defaults.norm.default = { | |
| clip_polygon_count: 1, | |
| clip_point_count: 3, | |
| subj_polygon_count: 2, | |
| subj_point_count: 8 | |
| }; | |
| rnd_sett_defaults.norm.min = { | |
| clip_polygon_count: 1, | |
| clip_point_count: 3, | |
| subj_polygon_count: 1, | |
| subj_point_count: 3 | |
| }; | |
| rnd_sett_defaults.norm.max = { | |
| clip_polygon_count: 100, | |
| clip_point_count: 100, | |
| subj_polygon_count: 100, | |
| subj_point_count: 100 | |
| }; | |
| rnd_sett_defaults.current = "norm"; | |
| var bevel = 0; | |
| var mypath = null; | |
| var scaled_paths = []; | |
| var subj_points_total = 0; | |
| var clip_points_total = 0; | |
| var solution_points_total = 0; | |
| var subj_subpolygons = 0; | |
| var clip_subpolygons = 0; | |
| var solution_subpolygons = 0; | |
| var bench; | |
| var sub_poly_links_update = 1; | |
| var benchmark1_globals = {}; | |
| var browserg; | |
| var repeat_times; | |
| var repeat = 0; | |
| var clicked_benchmark_button_id; | |
| var benchmark_exports = ""; | |
| var benchmark_running = 0; | |
| var benchmark_automatic_click = 0; | |
| // default_custom_subject_polygon = '[[{"X":90.58,"Y":144.44},{"X":84.83,"Y":141.55},{"X":84.84,"Y":141.56},{"X":79.29,"Y":137.67},{"X":73.79,"Y":132.61},{"X":73.8,"Y":132.62},{"X":67.31,"Y":125.14},{"X":63.22,"Y":119.64},{"X":98.93,"Y":130.63},{"X":98.91,"Y":130.61},{"X":99.02,"Y":130.66},{"X":99.02,"Y":130.65},{"X":97.46,"Y":134.4},{"X":93.93,"Y":139.6},{"X":90.86,"Y":144.22},{"X":90.87,"Y":144.21}]]'; | |
| // The Phantom image | |
| var default_custom_subject_polygon = '[[{"X":-29.42,"Y":106.23},{"X":-26.7,"Y":105.45},{"X":-11.55,"Y":101.73},{"X":0.12,"Y":99.72},{"X":9.69,"Y":98.94},{"X":17.63,"Y":99.17},{"X":24.29,"Y":100.23},{"X":23.93,"Y":92.91},{"X":24.08,"Y":81.36},{"X":25.12,"Y":70.62},{"X":26.99,"Y":60.59},{"X":29.66,"Y":51.22},{"X":33.11,"Y":42.43},{"X":37.31,"Y":34.29},{"X":42.16,"Y":26.9},{"X":47.77,"Y":20.05},{"X":53.98,"Y":13.98},{"X":60.82,"Y":8.61},{"X":68.2,"Y":4.01},{"X":76.04,"Y":0.24},{"X":84.37,"Y":-2.73},{"X":92.97,"Y":-4.8},{"X":101.89,"Y":-5.98},{"X":110.88,"Y":-6.23},{"X":120,"Y":-5.53},{"X":128.99,"Y":-3.88},{"X":137.63,"Y":-1.36},{"X":145.94,"Y":2.06},{"X":153.87,"Y":6.32},{"X":161.29,"Y":11.4},{"X":168.03,"Y":17.14},{"X":174.22,"Y":23.66},{"X":179.7,"Y":30.8},{"X":184.49,"Y":38.59},{"X":188.54,"Y":46.95},{"X":191.9,"Y":56.1},{"X":194.44,"Y":65.81},{"X":196.16,"Y":76.18},{"X":197,"Y":87.09},{"X":196.94,"Y":98.79},{"X":195.88,"Y":111.18},{"X":207.51,"Y":112.86},{"X":220.6,"Y":115.46},{"X":233.18,"Y":118.91},{"X":245.2,"Y":123.18},{"X":256.72,"Y":128.26},{"X":267.97,"Y":134.26},{"X":278.87,"Y":141.16},{"X":289.73,"Y":149.16},{"X":300.72,"Y":158.47},{"X":312.07,"Y":169.38},{"X":312.06,"Y":169.37},{"X":336.14,"Y":177.32},{"X":355.16,"Y":183.81},{"X":367.78,"Y":189},{"X":378.1,"Y":194.19},{"X":387.04,"Y":199.69},{"X":395.07,"Y":205.73},{"X":400.6,"Y":210.78},{"X":405.18,"Y":216.34},{"X":409.93,"Y":223.8},{"X":420.35,"Y":243.91},{"X":425.91,"Y":253.51},{"X":431.07,"Y":260.69},{"X":436.35,"Y":266.43},{"X":436.34,"Y":266.42},{"X":443.69,"Y":272.81},{"X":469.07,"Y":291.49},{"X":477.42,"Y":298.56},{"X":483.68,"Y":305.01},{"X":487.79,"Y":310.56},{"X":497.28,"Y":326.73},{"X":502.53,"Y":334.76},{"X":511.53,"Y":345.81},{"X":337.97,"Y":345.86},{"X":315.38,"Y":321.1},{"X":303.25,"Y":308.84},{"X":303.26,"Y":308.85},{"X":292.72,"Y":299.47},{"X":292.69,"Y":299.5},{"X":290.88,"Y":300.26},{"X":290.89,"Y":300.25},{"X":283.26,"Y":304.12},{"X":283.27,"Y":304.11},{"X":275.98,"Y":308.87},{"X":268.76,"Y":314.74},{"X":261.42,"Y":322.02},{"X":261.43,"Y":322.01},{"X":253.55,"Y":331.26},{"X":242.73,"Y":345.84},{"X":-29.42,"Y":345.86}],[{"X":127.69,"Y":269.76},{"X":127.68,"Y":269.77},{"X":133.88,"Y":268.63},{"X":133.87,"Y":268.64},{"X":139.54,"Y":266.65},{"X":139.53,"Y":266.66},{"X":144.66,"Y":263.86},{"X":144.65,"Y":263.87},{"X":149.4,"Y":260.18},{"X":149.39,"Y":260.19},{"X":153.81,"Y":255.5},{"X":158.06,"Y":249.51},{"X":162.29,"Y":241.74},{"X":166.81,"Y":231.1},{"X":166.8,"Y":231.11},{"X":176.87,"Y":202.06},{"X":182.04,"Y":188.77},{"X":182.04,"Y":188.78},{"X":182.02,"Y":188.77},{"X":181.74,"Y":188.64},{"X":181.75,"Y":188.65},{"X":179.85,"Y":188.27},{"X":178.29,"Y":188.8},{"X":178.3,"Y":188.79},{"X":176.74,"Y":190.4},{"X":176.75,"Y":190.39},{"X":173.98,"Y":196.05},{"X":173.14,"Y":196.87},{"X":172.34,"Y":196.57},{"X":171.01,"Y":194.4},{"X":174.86,"Y":188.74},{"X":180.8,"Y":179.01},{"X":185.49,"Y":169.61},{"X":185.48,"Y":169.62},{"X":188.88,"Y":160.79},{"X":188.87,"Y":160.8},{"X":191.15,"Y":152.4},{"X":191.14,"Y":152.41},{"X":192.39,"Y":144.4},{"X":192.38,"Y":144.41},{"X":192.65,"Y":136.76},{"X":192.65,"Y":136.77},{"X":191.95,"Y":129.43},{"X":191.96,"Y":129.44},{"X":190.36,"Y":122.45},{"X":189.2,"Y":126.33},{"X":186.64,"Y":132.72},{"X":183.5,"Y":138.19},{"X":179.85,"Y":142.75},{"X":175.82,"Y":146.34},{"X":171.49,"Y":149.01},{"X":166.88,"Y":150.79},{"X":162.01,"Y":151.66},{"X":157.07,"Y":151.61},{"X":152.08,"Y":150.61},{"X":147.07,"Y":148.62},{"X":142.21,"Y":145.63},{"X":137.56,"Y":141.62},{"X":133.27,"Y":136.62},{"X":129.46,"Y":130.68},{"X":129.03,"Y":130.72},{"X":129.04,"Y":130.71},{"X":124.06,"Y":131.87},{"X":124.07,"Y":131.86},{"X":119.96,"Y":133.69},{"X":116.46,"Y":136.26},{"X":116.47,"Y":136.25},{"X":113.27,"Y":139.82},{"X":107.23,"Y":149.32},{"X":104.14,"Y":153.18},{"X":101,"Y":155.72},{"X":97.49,"Y":157.4},{"X":93.11,"Y":158.38},{"X":88.02,"Y":158.61},{"X":83.43,"Y":157.91},{"X":79.22,"Y":156.35},{"X":75,"Y":153.77},{"X":70.26,"Y":149.63},{"X":60.57,"Y":138.77},{"X":54.38,"Y":132.43},{"X":54.39,"Y":132.44},{"X":49.11,"Y":128.26},{"X":43.79,"Y":125.19},{"X":43.54,"Y":126.2},{"X":43.55,"Y":126.19},{"X":43.13,"Y":133.69},{"X":43.13,"Y":133.68},{"X":43.66,"Y":140.71},{"X":43.65,"Y":140.7},{"X":45.09,"Y":147.46},{"X":45.08,"Y":147.45},{"X":47.49,"Y":154.24},{"X":47.48,"Y":154.23},{"X":51.23,"Y":161.76},{"X":63.98,"Y":182.09},{"X":67.55,"Y":189.18},{"X":69.73,"Y":195.4},{"X":70.92,"Y":201.58},{"X":71.16,"Y":207.87},{"X":70.43,"Y":214.58},{"X":68.63,"Y":221.85},{"X":68.56,"Y":221.68},{"X":68.68,"Y":203.83},{"X":68.68,"Y":203.84},{"X":68.16,"Y":197.1},{"X":68.17,"Y":197.11},{"X":67.05,"Y":192.75},{"X":67.06,"Y":192.76},{"X":65.5,"Y":189.85},{"X":65.51,"Y":189.86},{"X":63.49,"Y":187.86},{"X":60.94,"Y":186.59},{"X":60.95,"Y":186.6},{"X":57.55,"Y":186.01},{"X":57.78,"Y":189.41},{"X":57.77,"Y":189.4},{"X":59.32,"Y":201.46},{"X":59.31,"Y":201.45},{"X":61.68,"Y":212.76},{"X":61.67,"Y":212.75},{"X":64.76,"Y":223.11},{"X":64.75,"Y":223.1},{"X":68.47,"Y":232.45},{"X":72.73,"Y":240.71},{"X":77.44,"Y":247.84},{"X":82.45,"Y":253.81},{"X":82.44,"Y":253.8},{"X":87.82,"Y":258.8},{"X":93.45,"Y":262.8},{"X":93.44,"Y":262.79},{"X":99.44,"Y":265.89},{"X":99.43,"Y":265.88},{"X":105.67,"Y":268.07},{"X":105.66,"Y":268.06},{"X":112.58,"Y":269.42},{"X":112.57,"Y":269.41},{"X":120.65,"Y":270.08}],[{"X":121.49,"Y":240.71},{"X":118.05,"Y":239.68},{"X":115.54,"Y":238.06},{"X":113.86,"Y":235.97},{"X":112.96,"Y":233.43},{"X":112.86,"Y":230.29},{"X":112.9,"Y":230.33},{"X":114.24,"Y":231.32},{"X":114.23,"Y":231.31},{"X":117.3,"Y":232.52},{"X":117.29,"Y":232.51},{"X":122.05,"Y":233.17},{"X":132.1,"Y":233.03},{"X":140.49,"Y":233.06},{"X":139.8,"Y":234.49},{"X":137.79,"Y":236.7},{"X":134.63,"Y":238.72},{"X":130.41,"Y":240.25},{"X":125.83,"Y":240.94}],[{"X":90.76,"Y":219.11},{"X":91.11,"Y":218.28},{"X":92.59,"Y":216.41},{"X":95.22,"Y":214.69},{"X":99.6,"Y":213.14},{"X":106.53,"Y":211.9},{"X":117,"Y":211.2},{"X":143.09,"Y":211.3},{"X":151.49,"Y":210.93},{"X":151.31,"Y":211.49},{"X":149.72,"Y":213.46},{"X":147.43,"Y":214.88},{"X":143.97,"Y":215.82},{"X":138.58,"Y":216.09},{"X":117.5,"Y":214.37},{"X":117.51,"Y":214.38},{"X":108.81,"Y":214.35},{"X":101.81,"Y":215.22},{"X":101.82,"Y":215.21},{"X":95.89,"Y":216.85},{"X":95.9,"Y":216.84},{"X":90.72,"Y":219.22}],[{"X":131.9,"Y":198.38},{"X":131.72,"Y":195.25},{"X":132.38,"Y":192.96},{"X":133.78,"Y":191.21},{"X":136.84,"Y":189.34},{"X":140.91,"Y":187.09},{"X":142.52,"Y":185.35},{"X":143.3,"Y":183.24},{"X":143.3,"Y":183.25},{"X":143.22,"Y":183.13},{"X":141.54,"Y":180.12},{"X":139.65,"Y":174.9},{"X":138.32,"Y":168.14},{"X":137.62,"Y":158.87},{"X":137.76,"Y":144.77},{"X":142.77,"Y":159.79},{"X":145.49,"Y":170.4},{"X":146.69,"Y":178.4},{"X":146.78,"Y":184.55},{"X":146.02,"Y":189.28},{"X":144.58,"Y":192.89},{"X":142.54,"Y":195.66},{"X":139.94,"Y":197.69},{"X":136.5,"Y":199.14},{"X":132.22,"Y":199.86}],[{"X":108.17,"Y":188.43},{"X":106.35,"Y":187.41},{"X":105.13,"Y":185.72},{"X":104.56,"Y":183.25},{"X":104.61,"Y":183.31},{"X":104.81,"Y":183.53},{"X":104.8,"Y":183.52},{"X":106.39,"Y":184.64},{"X":106.38,"Y":184.63},{"X":108.44,"Y":184.92},{"X":114,"Y":183.64},{"X":116.83,"Y":183.49},{"X":119.09,"Y":184.26},{"X":121.14,"Y":185.99},{"X":120.5,"Y":186.31},{"X":114.98,"Y":188.22},{"X":110.98,"Y":188.8}],[{"X":163.89,"Y":141.31},{"X":166.4,"Y":137.22},{"X":174.87,"Y":127.72},{"X":177.63,"Y":123.62},{"X":179.28,"Y":119.59},{"X":179.28,"Y":119.6},{"X":179.24,"Y":119.6},{"X":179.25,"Y":119.59},{"X":176.7,"Y":119.79},{"X":176.71,"Y":119.78},{"X":170.65,"Y":120.87},{"X":170.66,"Y":120.86},{"X":164.55,"Y":122.95},{"X":164.56,"Y":122.94},{"X":148.93,"Y":130.62},{"X":148.93,"Y":130.61},{"X":149.21,"Y":132.61},{"X":149.2,"Y":132.6},{"X":150.57,"Y":136.41},{"X":152.55,"Y":139.41},{"X":152.54,"Y":139.4},{"X":155.11,"Y":141.71},{"X":155.1,"Y":141.7},{"X":158.43,"Y":143.41},{"X":158.42,"Y":143.4},{"X":162.62,"Y":144.5}],[{"X":90.87,"Y":144.21},{"X":90.86,"Y":144.22},{"X":93.93,"Y":139.6},{"X":97.47,"Y":134.4},{"X":99.02,"Y":130.65},{"X":99.02,"Y":130.66},{"X":98.92,"Y":130.62},{"X":98.93,"Y":130.63},{"X":63.23,"Y":119.65},{"X":67.31,"Y":125.14},{"X":73.8,"Y":132.62},{"X":73.79,"Y":132.61},{"X":79.3,"Y":137.68},{"X":84.84,"Y":141.56},{"X":84.83,"Y":141.55},{"X":90.59,"Y":144.44}],[{"X":128.47,"Y":127.41},{"X":128.46,"Y":127.42},{"X":134.55,"Y":125.73},{"X":134.54,"Y":125.74},{"X":141.57,"Y":122.7},{"X":141.56,"Y":122.71},{"X":154.9,"Y":115.21},{"X":164.19,"Y":110.32},{"X":171.16,"Y":107.57},{"X":177.57,"Y":106},{"X":183.83,"Y":105.4},{"X":190.12,"Y":105.74},{"X":182.19,"Y":101.42},{"X":182.2,"Y":101.43},{"X":175.96,"Y":98.85},{"X":175.97,"Y":98.86},{"X":170.13,"Y":97.45},{"X":170.14,"Y":97.46},{"X":164,"Y":96.98},{"X":157.13,"Y":97.46},{"X":157.08,"Y":97.55},{"X":156.47,"Y":98.59},{"X":153.09,"Y":103.35},{"X":149.58,"Y":106.83},{"X":145.83,"Y":109.3},{"X":141.79,"Y":110.89},{"X":137.21,"Y":111.67},{"X":131.97,"Y":111.55},{"X":125.78,"Y":110.38},{"X":117.8,"Y":107.78},{"X":92.32,"Y":96.78},{"X":92.33,"Y":96.79},{"X":83.94,"Y":93.95},{"X":83.95,"Y":93.96},{"X":77.47,"Y":92.65},{"X":77.48,"Y":92.66},{"X":72.09,"Y":92.45},{"X":67.49,"Y":93.16},{"X":67.5,"Y":93.15},{"X":63.28,"Y":94.72},{"X":63.29,"Y":94.71},{"X":59.49,"Y":97.13},{"X":55.9,"Y":100.53},{"X":55.91,"Y":100.52},{"X":52.52,"Y":105.09},{"X":49.25,"Y":111.18},{"X":52.77,"Y":110.06},{"X":58.91,"Y":108.7},{"X":64.9,"Y":108.31},{"X":70.86,"Y":108.86},{"X":77.27,"Y":110.42},{"X":84.53,"Y":113.27},{"X":105.04,"Y":123.87},{"X":105.03,"Y":123.86},{"X":111.74,"Y":126.51},{"X":111.73,"Y":126.5},{"X":117.29,"Y":127.77},{"X":117.28,"Y":127.76},{"X":122.82,"Y":128.06}]]'; | |
| // The Phantom Scull Ring | |
| var default_custom_clip_polygon = '[[{"X":385.77,"Y":166.95},{"X":386.68,"Y":182.21},{"X":386.15,"Y":187.13},{"X":384.96,"Y":190.99},{"X":382.95,"Y":194.32},{"X":379.91,"Y":197.42},{"X":375.8,"Y":200.17},{"X":370.59,"Y":202.47},{"X":364.37,"Y":204.13},{"X":357.67,"Y":204.95},{"X":350.93,"Y":204.85},{"X":344.71,"Y":203.87},{"X":339.59,"Y":202.19},{"X":335.52,"Y":199.95},{"X":332.61,"Y":197.37},{"X":330.67,"Y":194.53},{"X":329.62,"Y":191.41},{"X":329.4,"Y":187.83},{"X":330.02,"Y":183.99},{"X":331.34,"Y":181.14},{"X":333.27,"Y":179.05},{"X":336.11,"Y":177.4},{"X":340.71,"Y":176.05},{"X":352.22,"Y":173.98},{"X":356.3,"Y":172.51},{"X":361.25,"Y":169.4},{"X":370.03,"Y":163.12},{"X":373.94,"Y":161.22},{"X":377.01,"Y":160.6},{"X":379.53,"Y":160.94},{"X":381.9,"Y":162.19},{"X":384.3,"Y":164.67},{"X":385.77,"Y":166.93},{"X":385.77,"Y":166.95}],[{"X":270.77,"Y":163.84},{"X":273.88,"Y":163.4},{"X":276.36,"Y":163.91},{"X":280.12,"Y":166.08},{"X":285.42,"Y":169.2},{"X":291.02,"Y":171.33},{"X":299.79,"Y":173.33},{"X":307.68,"Y":175.13},{"X":311.52,"Y":176.78},{"X":314.01,"Y":178.71},{"X":315.73,"Y":181.15},{"X":316.83,"Y":184.41},{"X":317.2,"Y":188.89},{"X":316.7,"Y":192.44},{"X":315.33,"Y":195.6},{"X":313.05,"Y":198.56},{"X":309.77,"Y":201.19},{"X":305.39,"Y":203.45},{"X":299.95,"Y":205.12},{"X":293.7,"Y":205.99},{"X":287.12,"Y":205.96},{"X":280.75,"Y":205},{"X":275.02,"Y":203.24},{"X":270.05,"Y":200.77},{"X":266.08,"Y":197.81},{"X":263.1,"Y":194.47},{"X":260.95,"Y":190.7},{"X":259.68,"Y":186.54},{"X":259.29,"Y":181.92},{"X":259.86,"Y":176.86},{"X":261.36,"Y":171.68},{"X":263.13,"Y":168.56},{"X":265.48,"Y":166.25},{"X":268.64,"Y":164.55},{"X":270.77,"Y":163.84},{"X":270.77,"Y":163.84}],[{"X":303.94,"Y":227.57},{"X":303.74,"Y":222.63},{"X":304.44,"Y":218.35},{"X":305.94,"Y":214.59},{"X":308.24,"Y":211.27},{"X":311.35,"Y":208.46},{"X":315.13,"Y":206.32},{"X":319.55,"Y":204.91},{"X":324.31,"Y":204.37},{"X":329.09,"Y":204.77},{"X":333.4,"Y":206.03},{"X":337.08,"Y":208.03},{"X":339.99,"Y":210.61},{"X":342.11,"Y":213.69},{"X":343.43,"Y":217.19},{"X":343.94,"Y":221.2},{"X":343.59,"Y":225.38},{"X":342.53,"Y":228.01},{"X":340.69,"Y":230.17},{"X":337.79,"Y":232.02},{"X":333.81,"Y":233.34},{"X":328.46,"Y":234.03},{"X":322.02,"Y":233.83},{"X":315.23,"Y":232.68},{"X":309.37,"Y":230.81},{"X":305.39,"Y":228.73},{"X":303.99,"Y":227.61},{"X":303.94,"Y":227.57}],[{"X":421.16,"Y":64.34},{"X":421.16,"Y":64.45},{"X":422.7,"Y":96.81},{"X":424.28,"Y":149.29},{"X":425.26,"Y":216.18},{"X":425.57,"Y":327.02},{"X":220.65,"Y":327.02},{"X":220.41,"Y":312.05},{"X":219.69,"Y":230.46},{"X":219.9,"Y":144.66},{"X":221.01,"Y":65.92},{"X":233.74,"Y":65.34},{"X":264.63,"Y":64.78},{"X":325.38,"Y":65.31},{"X":363.93,"Y":65.31},{"X":393.46,"Y":64.48},{"X":420.05,"Y":62.8},{"X":420.8,"Y":62.92},{"X":421.16,"Y":64.34}],[{"X":404.57,"Y":215.13},{"X":404.58,"Y":215.08},{"X":405.83,"Y":205.87},{"X":406.55,"Y":194.49},{"X":406.3,"Y":183.7},{"X":405.16,"Y":173.57},{"X":403.17,"Y":164.03},{"X":400.34,"Y":155.01},{"X":396.73,"Y":146.61},{"X":392.34,"Y":138.76},{"X":387.23,"Y":131.54},{"X":381.47,"Y":125.02},{"X":375.05,"Y":119.16},{"X":368.04,"Y":114.01},{"X":360.39,"Y":109.58},{"X":352.3,"Y":105.99},{"X":343.88,"Y":103.28},{"X":335.22,"Y":101.47},{"X":326.42,"Y":100.59},{"X":317.57,"Y":100.64},{"X":308.77,"Y":101.63},{"X":300.3,"Y":103.53},{"X":292.06,"Y":106.33},{"X":284.32,"Y":109.94},{"X":277.01,"Y":114.36},{"X":270.11,"Y":119.65},{"X":263.83,"Y":125.67},{"X":258.83,"Y":131.66},{"X":253.44,"Y":139.77},{"X":248.35,"Y":149.13},{"X":244.11,"Y":158.88},{"X":241,"Y":168.28},{"X":239,"Y":177.21},{"X":238.09,"Y":185.4},{"X":238.17,"Y":192.9},{"X":239.13,"Y":199.57},{"X":240.91,"Y":205.61},{"X":243.45,"Y":211.04},{"X":246.09,"Y":214.67},{"X":249.63,"Y":217.9},{"X":254.56,"Y":221.04},{"X":270.07,"Y":228.23},{"X":275.76,"Y":231.57},{"X":279.77,"Y":234.93},{"X":282.82,"Y":238.64},{"X":285.08,"Y":242.87},{"X":286.61,"Y":247.72},{"X":287.31,"Y":253.67},{"X":287.19,"Y":274.82},{"X":287.8,"Y":281.39},{"X":288.94,"Y":285.57},{"X":290.57,"Y":288.43},{"X":292.56,"Y":290.24},{"X":295.1,"Y":291.3},{"X":295.83,"Y":283.75},{"X":296,"Y":265.6},{"X":296.31,"Y":250.76},{"X":297.29,"Y":241.67},{"X":299.29,"Y":240.75},{"X":303.08,"Y":239.94},{"X":307.26,"Y":240},{"X":307.66,"Y":242.7},{"X":309.02,"Y":256.43},{"X":310.57,"Y":291.19},{"X":311.49,"Y":291.54},{"X":314.95,"Y":293.31},{"X":316.6,"Y":294.82},{"X":316.6,"Y":295.52},{"X":315.35,"Y":295.94},{"X":315.99,"Y":296.02},{"X":317.2,"Y":295.47},{"X":318.2,"Y":293.99},{"X":319.04,"Y":290.81},{"X":319.39,"Y":284.37},{"X":318.41,"Y":262.35},{"X":318.37,"Y":251.85},{"X":319.08,"Y":245.75},{"X":320.27,"Y":242.05},{"X":321.29,"Y":240.77},{"X":323.08,"Y":240.08},{"X":328.27,"Y":240},{"X":329.31,"Y":244.13},{"X":330.19,"Y":252.03},{"X":330.63,"Y":273.92},{"X":331.25,"Y":284.25},{"X":332.33,"Y":289.99},{"X":333.7,"Y":293.31},{"X":335.26,"Y":295.12},{"X":337.01,"Y":295.94},{"X":337.81,"Y":294.94},{"X":339.33,"Y":291.52},{"X":340.32,"Y":286.68},{"X":340.61,"Y":279.64},{"X":339.38,"Y":260.18},{"X":339.31,"Y":251.49},{"X":340.05,"Y":245.84},{"X":341.39,"Y":241.87},{"X":342.36,"Y":240.75},{"X":344.31,"Y":240.04},{"X":351.49,"Y":240},{"X":351.67,"Y":240.9},{"X":352.45,"Y":248.35},{"X":352.58,"Y":274.14},{"X":353.27,"Y":282.2},{"X":354.5,"Y":287.31},{"X":356.13,"Y":290.68},{"X":358.13,"Y":292.86},{"X":359.27,"Y":289.89},{"X":360.62,"Y":284.2},{"X":361.32,"Y":276.58},{"X":361.16,"Y":258.89},{"X":361.45,"Y":247.8},{"X":365.47,"Y":242.21},{"X":370.89,"Y":236.4},{"X":377.72,"Y":230.54},{"X":385.94,"Y":224.78},{"X":395.31,"Y":219.41},{"X":404.57,"Y":215.13}]]'; | |
| var output_format = 1; | |
| var ClipperLib_MaxSteps_original; // this hold the original ClipperLib.MaxSteps value | |
| // during benchmarks, ClipperLib.MaxSteps is set to 10 | |
| // to make benchmarks comparable | |
| var ismousedown = false; // Not yet in use | |
| var bench_glob = []; | |
| var bench_elapsed_time = 0; | |
| window.onerror = function (message, url, linenumber) { | |
| console.log("JavaScript error: " + message + " on line " + linenumber + " for " + url); | |
| } | |
| window.onload = function () { | |
| "use strict"; | |
| if (typeof (jQuery) == "undefined") alert("Failed to load jQuery. Please reload the page. If this fails repeatedly, please check the path of the jQuery library or use an own copy of jQuery."); | |
| else if (typeof (Raphael) == "undefined") alert("Failed to load Raphael. Please reload the page. If this fails repeatedly, please check the path of the Raphael library or use an own copy of Raphael."); | |
| else if (typeof (ClipperLib) == "undefined") alert("Failed to load ClipperLib. Please check that ClipperLib is loaded correctly."); | |
| else { | |
| ClipperLib_MaxSteps_original = ClipperLib.MaxSteps; | |
| browserg = get_browser(); | |
| bench = new benchmark("bench"); | |
| p = SVG.create(); | |
| main(); | |
| } | |
| } | |
| function get_browser() { | |
| var nav = navigator.userAgent.toString().toLowerCase(); | |
| 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; | |
| for (var i in browser) { | |
| if (browser[i] === 1 && !i.match(/[0-9]/)) browser["browser"] = i; | |
| } | |
| browser.version = $.browser.version; | |
| return browser; | |
| } | |
| // Helper tool to piece together Raphael's paths into strings again | |
| Array.prototype.flatten || (Array.prototype.flatten = function () { | |
| return this.reduce(function (a, b) { | |
| return a.concat('function' === typeof b.flatten ? b.flatten() : b); | |
| }, []); | |
| }); | |
| /* Normalizes inputted polygon string | |
| Input can be JSON-stringified version of the following: | |
| - Array of arrays of points [[{"X":10,"Y":10},{"X":10,"Y":10},{"X":10,"Y":10}][{"X":10,"Y":10},{"X":10,"Y":10},{"X":10,"Y":10}]] | |
| - Array of points [{"X":10,"Y":10},{"X":10,"Y":10},{"X":10,"Y":10}] | |
| - The aboves in lowercase | |
| - Array of x,y-coordinates eg. [0, 10, 20, 30, 40, 50] or [0 10 20 30 40 50] or [0 10, 20 30, 40 50]; | |
| - The above without [] | |
| - SVG path string with commands MLVHZ and mlvhz | |
| Returns normalized Clipper Polygons object stringified or false in failure | |
| */ | |
| function normalize_clipper_poly(polystr, quiet) { | |
| if (typeof (polystr) != "string") return false; | |
| polystr = polystr.trim(); | |
| var np, txt; | |
| if (polystr.substr(0, 1).toUpperCase() === "M") { | |
| np = svgpath_to_clipper_polygons(polystr); | |
| if (np === false) { | |
| txt = "Unable to parse SVG path string.\n"; | |
| txt += "Click OK to continue.\n"; | |
| if (!quiet) alert(txt); | |
| return false; | |
| } | |
| else return JSON.stringify(np); | |
| } | |
| polystr = polystr.replace(/[\s,]+/g, ","); | |
| if (polystr.substr(0, 1) !== "[") polystr = "[" + polystr; | |
| if (polystr.substr(-1, 1) !== "]") polystr = polystr + "]"; | |
| try { | |
| var poly = JSON.parse(polystr); | |
| } | |
| catch (err) { | |
| txt = "Unable to parse polygon string.\n"; | |
| txt += "Error: " + err.message + "\n"; | |
| txt += "Click OK to continue.\n"; | |
| if (!quiet) alert(txt); | |
| return false; | |
| } | |
| // if only points without "X" and "Y" | |
| var temp_n = [], i; | |
| if (isArray(poly) && poly.length && typeof (poly[0]) == "number") { | |
| var len = poly.length; | |
| for (i = 0; i < len; i = i + 2) { | |
| temp_n.push({ | |
| X: poly[i], | |
| Y: poly[i + 1] | |
| }); | |
| } | |
| poly = temp_n; | |
| } | |
| // if an array of array of points without "X" and "Y" | |
| var temp_n2 = [], i, j, len, len2; | |
| if (isArray(poly) && poly.length && isArray(poly[0]) && typeof(poly[0][0]) != "undefined" && | |
| typeof(poly[0][0].X) == "undefined" && | |
| typeof(poly[0][0].x) == "undefined") { | |
| len2 = poly.length; | |
| for (j = 0; j < len2; j++) { | |
| temp_n = []; | |
| len = poly[j].length; | |
| for (i = 0; i < len; i = i + 2) { | |
| temp_n.push({ | |
| X: poly[j][i], | |
| Y: poly[j][i + 1] | |
| }); | |
| } | |
| temp_n2.push(temp_n); | |
| } | |
| poly = temp_n2; | |
| } | |
| // if not array of arrays, convert to array of arrays | |
| if (isArray(poly) && poly.length > 0 && !isArray(poly[0])) poly = [poly]; | |
| var pp, n = [ | |
| [] | |
| ], | |
| m, pm, x, y; | |
| np = [ | |
| [] | |
| ]; | |
| for (i = 0, m = poly.length; i < m; i++) { | |
| np[i] = []; | |
| for (j = 0, pm = poly[i].length; j < pm; j++) { | |
| pp = {}; | |
| y = null; | |
| x = null; | |
| if (typeof (poly[i][j].X) != "undefined" && !isNaN(Number(poly[i][j].X))) x = Number(poly[i][j].X); | |
| else if (typeof (poly[i][j].x) != "undefined" && !isNaN(Number(poly[i][j].x))) x = Number(poly[i][j].x); | |
| if (typeof (poly[i][j].Y) != "undefined" && !isNaN(Number(poly[i][j].Y))) y = Number(poly[i][j].Y); | |
| else if (typeof (poly[i][j].y) != "undefined" && !isNaN(Number(poly[i][j].y))) y = Number(poly[i][j].y); | |
| if (y !== null && x !== null) { | |
| pp.X = x; | |
| pp.Y = y; | |
| np[i].push(pp); | |
| } | |
| else { | |
| txt = "Unable to parse polygon string.\n"; | |
| txt += "Error: Coordinates are not in a right form.\n"; | |
| txt += "Click OK to continue.\n"; | |
| if (!quiet) alert(txt); | |
| return false; | |
| } | |
| ; | |
| } | |
| } | |
| return JSON.stringify(np); | |
| } | |
| // helper function for normalize_clipper_poly() | |
| function svgpath_to_clipper_polygons(d) { | |
| var arr; | |
| d = d.trim(); | |
| arr = Raphael.parsePathString(d); // str to array | |
| arr = Raphael._pathToAbsolute(arr); // mahvstcsqz -> uppercase | |
| var str = arr.flatten().join(" "); | |
| var paths = str.replace(/M/g, '|M').split("|"); | |
| var k, polygons_arr = [], | |
| polygon_arr = []; | |
| for (k = 0; k < paths.length; k++) { | |
| if (paths[k].trim() === "") continue; | |
| arr = Raphael.parsePathString(paths[k].trim()); | |
| polygon_arr = []; | |
| var i = 0, | |
| j, m = arr.length, | |
| letter = "", | |
| x = 0, | |
| y = 0, | |
| pt = {}, | |
| subpath_start = {}; | |
| subpath_start.x = ""; | |
| subpath_start.y = ""; | |
| for (; i < m; i++) { | |
| letter = arr[i][0].toUpperCase(); | |
| if (letter != "M" && letter != "L" && letter != "Z") continue; | |
| if (letter != "Z") { | |
| for (j = 1; j < arr[i].length; j = j + 2) { | |
| if (letter == "V") y = arr[i][j]; | |
| else if (letter == "H") x = arr[i][j]; | |
| else { | |
| x = arr[i][j]; | |
| y = arr[i][j + 1]; | |
| } | |
| pt = {}; | |
| pt.X = null; | |
| pt.Y = null; | |
| if (typeof (x) != "undefined" && !isNaN(Number(x))) pt.X = Number(x); | |
| if (typeof (y) != "undefined" && !isNaN(Number(y))) pt.Y = Number(y); | |
| if (pt.X !== null && pt.Y !== null) { | |
| polygon_arr.push(pt); | |
| } | |
| else { | |
| return false; | |
| } | |
| } | |
| } | |
| if ((letter != "Z" && subpath_start.x === "") || letter == "M") { | |
| subpath_start.x = x; | |
| subpath_start.y = y; | |
| } | |
| if (letter == "Z") { | |
| x = subpath_start.x; | |
| y = subpath_start.y; | |
| } | |
| } | |
| polygons_arr.push(polygon_arr); | |
| } | |
| return polygons_arr; | |
| } | |
| function format_output(polystr, ExPolygonsOrNot) { | |
| var txt, returnAsExPolygons = false; | |
| if (typeof(polystr) != "string" || polystr === "") return ""; | |
| if (ExPolygonsOrNot == "ExPolygons" && ExPolygons) returnAsExPolygons = true; | |
| try { | |
| var poly = JSON.parse(polystr); | |
| } | |
| catch (err) { | |
| txt = "Unable to parse polygon for output.\n"; | |
| txt += "Error: " + err.message + "\n"; | |
| txt += "Click OK to continue.\n"; | |
| alert(txt); | |
| return ""; | |
| } | |
| var i, j, m, n, newpolystr = ""; | |
| if (output_format === 0) // Clipper | |
| { | |
| //return polystr; | |
| } | |
| else if (output_format == 1) // Plain | |
| { | |
| m = poly.length; | |
| for (i = 0; i < m; i++) { | |
| newpolystr += "["; | |
| n = poly[i].length; | |
| for (j = 0; j < n; j++) { | |
| newpolystr += poly[i][j].X + "," + poly[i][j].Y; | |
| if (j !== n - 1) newpolystr += ", "; | |
| } | |
| newpolystr += "]"; | |
| if (i !== m - 1) newpolystr += ","; | |
| } | |
| //return "[" + newpolystr + "]"; | |
| polystr = "[" + newpolystr + "]"; | |
| } | |
| else if (output_format == 2) // SVG | |
| { | |
| m = poly.length; | |
| for (i = 0; i < m; i++) { | |
| n = poly[i].length; | |
| for (j = 0; j < n; j++) { | |
| if (j === 0) newpolystr += "M"; | |
| else newpolystr += "L"; | |
| newpolystr += poly[i][j].X + "," + poly[i][j].Y; | |
| if (j !== n - 1) newpolystr += " "; | |
| } | |
| newpolystr += "Z"; | |
| if (i !== m - 1) newpolystr += " "; | |
| } | |
| if (newpolystr.trim() == "Z") newpolystr = ""; | |
| //return newpolystr; | |
| polystr = newpolystr; | |
| } | |
| if (!returnAsExPolygons) return polystr; | |
| else { | |
| if (!cpr) cpr = new ClipperLib.Clipper(); | |
| else cpr.Clear(); | |
| cpr.AddPolygons(poly, ClipperLib.PolyType.ptSubject); | |
| sss = new ClipperLib.ExPolygons(); | |
| cpr.Execute(clipType, sss, subject_fillType, clip_fillType); | |
| polystr = JSON.stringify(sss); | |
| return polystr; | |
| } | |
| /* | |
| else if (output_format == 3) // ExPolygons | |
| { | |
| m = poly.length; | |
| for(i = 0; i < m; i++) | |
| { | |
| newpolystr += "["; | |
| n = poly[i].length; | |
| for(j = 0; j < n; j++) | |
| { | |
| newpolystr += poly[i][j].X + "," + poly[i][j].Y; | |
| if (j !== n - 1) newpolystr += ", "; | |
| } | |
| newpolystr += "]"; | |
| if (i !== m - 1) newpolystr += ","; | |
| } | |
| return "[" + newpolystr + "]"; | |
| } | |
| */ | |
| } | |
| ; | |
| function get_gb_and_arrow() { | |
| var gb_poly = '[[{"X":189,"Y":154},{"X":192,"Y":155},{"X":193,"Y":154},{"X":194,"Y":151},{"X":194,"Y":150},{"X":196,"Y":151},{"X":199,"Y":151},{"X":200,"Y":150},{"X":203,"Y":149},{"X":204,"Y":149},{"X":205,"Y":149},{"X":206,"Y":149},{"X":209,"Y":150},{"X":211,"Y":149},{"X":212,"Y":149},{"X":212,"Y":152},{"X":212,"Y":155},{"X":213,"Y":156},{"X":213,"Y":156},{"X":213,"Y":159},{"X":214,"Y":159},{"X":216,"Y":161},{"X":216,"Y":161},{"X":216,"Y":163},{"X":217,"Y":163},{"X":218,"Y":166},{"X":218,"Y":166},{"X":217,"Y":163},{"X":217,"Y":162},{"X":218,"Y":162},{"X":218,"Y":163},{"X":220,"Y":165},{"X":218,"Y":167},{"X":218,"Y":167},{"X":215,"Y":168},{"X":214,"Y":170},{"X":214,"Y":171},{"X":215,"Y":170},{"X":217,"Y":168},{"X":220,"Y":168},{"X":221,"Y":168},{"X":222,"Y":170},{"X":223,"Y":172},{"X":223,"Y":174},{"X":224,"Y":176},{"X":223,"Y":178},{"X":223,"Y":179},{"X":223,"Y":179},{"X":223,"Y":180},{"X":222,"Y":180},{"X":222,"Y":180},{"X":221,"Y":178},{"X":222,"Y":177},{"X":222,"Y":174},{"X":221,"Y":174},{"X":220,"Y":172},{"X":218,"Y":172},{"X":218,"Y":174},{"X":220,"Y":174},{"X":218,"Y":174},{"X":220,"Y":176},{"X":220,"Y":178},{"X":217,"Y":180},{"X":221,"Y":179},{"X":221,"Y":180},{"X":222,"Y":181},{"X":221,"Y":182},{"X":218,"Y":183},{"X":218,"Y":183},{"X":217,"Y":182},{"X":214,"Y":184},{"X":214,"Y":187},{"X":214,"Y":189},{"X":211,"Y":191},{"X":210,"Y":191},{"X":210,"Y":190},{"X":209,"Y":189},{"X":207,"Y":189},{"X":206,"Y":189},{"X":207,"Y":189},{"X":209,"Y":191},{"X":209,"Y":191},{"X":210,"Y":191},{"X":207,"Y":192},{"X":205,"Y":191},{"X":204,"Y":191},{"X":204,"Y":191},{"X":204,"Y":192},{"X":204,"Y":194},{"X":204,"Y":195},{"X":206,"Y":195},{"X":206,"Y":198},{"X":206,"Y":199},{"X":206,"Y":200},{"X":206,"Y":202},{"X":206,"Y":203},{"X":210,"Y":206},{"X":209,"Y":207},{"X":207,"Y":211},{"X":209,"Y":212},{"X":210,"Y":212},{"X":210,"Y":213},{"X":207,"Y":212},{"X":207,"Y":213},{"X":206,"Y":213},{"X":207,"Y":215},{"X":209,"Y":216},{"X":209,"Y":218},{"X":210,"Y":222},{"X":209,"Y":226},{"X":211,"Y":226},{"X":209,"Y":231},{"X":207,"Y":232},{"X":206,"Y":234},{"X":205,"Y":237},{"X":205,"Y":240},{"X":202,"Y":246},{"X":201,"Y":247},{"X":200,"Y":247},{"X":200,"Y":247},{"X":200,"Y":248},{"X":201,"Y":249},{"X":201,"Y":248},{"X":201,"Y":250},{"X":202,"Y":251},{"X":201,"Y":254},{"X":200,"Y":254},{"X":201,"Y":253},{"X":199,"Y":254},{"X":199,"Y":253},{"X":199,"Y":253},{"X":196,"Y":254},{"X":194,"Y":251},{"X":192,"Y":253},{"X":193,"Y":251},{"X":192,"Y":251},{"X":192,"Y":251},{"X":191,"Y":251},{"X":192,"Y":253},{"X":191,"Y":253},{"X":190,"Y":254},{"X":189,"Y":255},{"X":189,"Y":255},{"X":190,"Y":254},{"X":190,"Y":253},{"X":190,"Y":251},{"X":189,"Y":251},{"X":189,"Y":249},{"X":189,"Y":251},{"X":189,"Y":253},{"X":189,"Y":253},{"X":188,"Y":255},{"X":185,"Y":254},{"X":187,"Y":253},{"X":185,"Y":253},{"X":185,"Y":253},{"X":185,"Y":254},{"X":184,"Y":254},{"X":183,"Y":254},{"X":179,"Y":254},{"X":177,"Y":255},{"X":177,"Y":255},{"X":176,"Y":255},{"X":174,"Y":255},{"X":176,"Y":256},{"X":177,"Y":257},{"X":176,"Y":257},{"X":176,"Y":258},{"X":172,"Y":258},{"X":172,"Y":260},{"X":171,"Y":260},{"X":170,"Y":260},{"X":169,"Y":259},{"X":169,"Y":260},{"X":168,"Y":260},{"X":169,"Y":261},{"X":167,"Y":262},{"X":166,"Y":264},{"X":166,"Y":264},{"X":162,"Y":265},{"X":161,"Y":264},{"X":161,"Y":264},{"X":162,"Y":262},{"X":163,"Y":261},{"X":160,"Y":262},{"X":158,"Y":261},{"X":158,"Y":262},{"X":159,"Y":262},{"X":160,"Y":264},{"X":160,"Y":265},{"X":160,"Y":265},{"X":158,"Y":267},{"X":157,"Y":267},{"X":156,"Y":267},{"X":155,"Y":267},{"X":155,"Y":267},{"X":154,"Y":268},{"X":154,"Y":270},{"X":154,"Y":269},{"X":151,"Y":269},{"X":150,"Y":272},{"X":149,"Y":272},{"X":149,"Y":270},{"X":147,"Y":270},{"X":147,"Y":272},{"X":146,"Y":272},{"X":146,"Y":272},{"X":144,"Y":270},{"X":143,"Y":272},{"X":141,"Y":270},{"X":141,"Y":272},{"X":140,"Y":272},{"X":140,"Y":271},{"X":140,"Y":273},{"X":139,"Y":273},{"X":139,"Y":273},{"X":139,"Y":273},{"X":138,"Y":272},{"X":138,"Y":273},{"X":137,"Y":273},{"X":137,"Y":273},{"X":136,"Y":272},{"X":137,"Y":270},{"X":136,"Y":269},{"X":136,"Y":270},{"X":135,"Y":270},{"X":134,"Y":270},{"X":134,"Y":272},{"X":133,"Y":272},{"X":133,"Y":272},{"X":132,"Y":272},{"X":132,"Y":270},{"X":129,"Y":272},{"X":130,"Y":272},{"X":128,"Y":272},{"X":128,"Y":271},{"X":127,"Y":272},{"X":127,"Y":271},{"X":127,"Y":271},{"X":129,"Y":269},{"X":132,"Y":269},{"X":134,"Y":267},{"X":134,"Y":267},{"X":128,"Y":269},{"X":127,"Y":269},{"X":128,"Y":268},{"X":129,"Y":267},{"X":136,"Y":266},{"X":136,"Y":266},{"X":136,"Y":265},{"X":136,"Y":265},{"X":135,"Y":264},{"X":135,"Y":262},{"X":135,"Y":264},{"X":134,"Y":266},{"X":126,"Y":266},{"X":126,"Y":267},{"X":124,"Y":267},{"X":123,"Y":267},{"X":122,"Y":267},{"X":122,"Y":267},{"X":122,"Y":267},{"X":122,"Y":266},{"X":123,"Y":267},{"X":123,"Y":266},{"X":123,"Y":265},{"X":123,"Y":265},{"X":125,"Y":265},{"X":126,"Y":264},{"X":125,"Y":264},{"X":125,"Y":262},{"X":127,"Y":261},{"X":127,"Y":262},{"X":127,"Y":261},{"X":129,"Y":261},{"X":128,"Y":261},{"X":130,"Y":259},{"X":133,"Y":259},{"X":134,"Y":259},{"X":132,"Y":259},{"X":129,"Y":260},{"X":128,"Y":260},{"X":127,"Y":260},{"X":127,"Y":260},{"X":122,"Y":262},{"X":122,"Y":261},{"X":121,"Y":261},{"X":121,"Y":260},{"X":122,"Y":259},{"X":121,"Y":258},{"X":119,"Y":259},{"X":118,"Y":260},{"X":118,"Y":258},{"X":117,"Y":258},{"X":117,"Y":256},{"X":119,"Y":256},{"X":121,"Y":256},{"X":119,"Y":255},{"X":119,"Y":255},{"X":121,"Y":254},{"X":121,"Y":254},{"X":126,"Y":251},{"X":127,"Y":250},{"X":127,"Y":251},{"X":127,"Y":251},{"X":128,"Y":249},{"X":129,"Y":249},{"X":132,"Y":249},{"X":128,"Y":248},{"X":127,"Y":248},{"X":127,"Y":249},{"X":126,"Y":248},{"X":123,"Y":249},{"X":122,"Y":248},{"X":121,"Y":248},{"X":119,"Y":248},{"X":121,"Y":249},{"X":121,"Y":249},{"X":118,"Y":248},{"X":119,"Y":249},{"X":117,"Y":249},{"X":116,"Y":248},{"X":116,"Y":248},{"X":116,"Y":247},{"X":117,"Y":247},{"X":116,"Y":246},{"X":117,"Y":246},{"X":117,"Y":247},{"X":118,"Y":247},{"X":118,"Y":245},{"X":119,"Y":245},{"X":121,"Y":244},{"X":123,"Y":244},{"X":123,"Y":245},{"X":125,"Y":246},{"X":126,"Y":244},{"X":125,"Y":243},{"X":126,"Y":242},{"X":126,"Y":244},{"X":127,"Y":246},{"X":129,"Y":246},{"X":129,"Y":246},{"X":129,"Y":246},{"X":132,"Y":246},{"X":132,"Y":245},{"X":128,"Y":245},{"X":128,"Y":243},{"X":129,"Y":244},{"X":129,"Y":243},{"X":130,"Y":240},{"X":128,"Y":239},{"X":128,"Y":239},{"X":133,"Y":238},{"X":134,"Y":237},{"X":135,"Y":238},{"X":135,"Y":237},{"X":134,"Y":237},{"X":134,"Y":237},{"X":135,"Y":235},{"X":136,"Y":235},{"X":138,"Y":236},{"X":138,"Y":235},{"X":141,"Y":236},{"X":147,"Y":234},{"X":147,"Y":235},{"X":148,"Y":235},{"X":149,"Y":234},{"X":151,"Y":234},{"X":151,"Y":234},{"X":154,"Y":234},{"X":150,"Y":233},{"X":149,"Y":233},{"X":149,"Y":232},{"X":148,"Y":231},{"X":147,"Y":231},{"X":147,"Y":232},{"X":144,"Y":235},{"X":143,"Y":234},{"X":141,"Y":236},{"X":140,"Y":235},{"X":141,"Y":234},{"X":139,"Y":234},{"X":137,"Y":234},{"X":137,"Y":233},{"X":136,"Y":232},{"X":137,"Y":234},{"X":134,"Y":234},{"X":134,"Y":235},{"X":129,"Y":234},{"X":128,"Y":234},{"X":132,"Y":234},{"X":135,"Y":231},{"X":136,"Y":229},{"X":138,"Y":229},{"X":138,"Y":227},{"X":139,"Y":226},{"X":139,"Y":224},{"X":141,"Y":223},{"X":140,"Y":223},{"X":138,"Y":223},{"X":138,"Y":223},{"X":139,"Y":222},{"X":143,"Y":217},{"X":146,"Y":217},{"X":147,"Y":217},{"X":147,"Y":217},{"X":149,"Y":217},{"X":147,"Y":217},{"X":149,"Y":217},{"X":149,"Y":217},{"X":150,"Y":217},{"X":150,"Y":217},{"X":150,"Y":216},{"X":151,"Y":215},{"X":150,"Y":215},{"X":149,"Y":215},{"X":149,"Y":214},{"X":150,"Y":213},{"X":150,"Y":213},{"X":150,"Y":212},{"X":148,"Y":212},{"X":146,"Y":213},{"X":139,"Y":213},{"X":138,"Y":213},{"X":138,"Y":211},{"X":138,"Y":211},{"X":137,"Y":213},{"X":137,"Y":213},{"X":137,"Y":212},{"X":137,"Y":211},{"X":137,"Y":210},{"X":137,"Y":210},{"X":138,"Y":210},{"X":137,"Y":209},{"X":137,"Y":210},{"X":137,"Y":209},{"X":137,"Y":209},{"X":137,"Y":209},{"X":137,"Y":209},{"X":136,"Y":210},{"X":135,"Y":210},{"X":134,"Y":209},{"X":133,"Y":209},{"X":133,"Y":207},{"X":133,"Y":207},{"X":135,"Y":206},{"X":135,"Y":206},{"X":135,"Y":206},{"X":135,"Y":206},{"X":133,"Y":206},{"X":133,"Y":206},{"X":133,"Y":206},{"X":133,"Y":206},{"X":132,"Y":206},{"X":132,"Y":207},{"X":129,"Y":206},{"X":128,"Y":205},{"X":127,"Y":206},{"X":127,"Y":205},{"X":126,"Y":205},{"X":127,"Y":203},{"X":129,"Y":204},{"X":128,"Y":203},{"X":128,"Y":202},{"X":127,"Y":201},{"X":126,"Y":201},{"X":128,"Y":201},{"X":128,"Y":200},{"X":132,"Y":201},{"X":129,"Y":198},{"X":134,"Y":198},{"X":134,"Y":196},{"X":134,"Y":194},{"X":136,"Y":193},{"X":138,"Y":194},{"X":139,"Y":193},{"X":138,"Y":193},{"X":139,"Y":192},{"X":138,"Y":192},{"X":139,"Y":191},{"X":139,"Y":191},{"X":139,"Y":190},{"X":136,"Y":190},{"X":135,"Y":190},{"X":134,"Y":190},{"X":134,"Y":190},{"X":134,"Y":188},{"X":136,"Y":189},{"X":136,"Y":189},{"X":136,"Y":189},{"X":136,"Y":187},{"X":136,"Y":187},{"X":136,"Y":188},{"X":136,"Y":187},{"X":136,"Y":187},{"X":135,"Y":184},{"X":135,"Y":183},{"X":136,"Y":183},{"X":135,"Y":183},{"X":136,"Y":182},{"X":135,"Y":182},{"X":135,"Y":183},{"X":134,"Y":182},{"X":134,"Y":182},{"X":134,"Y":182},{"X":135,"Y":181},{"X":134,"Y":181},{"X":135,"Y":180},{"X":134,"Y":180},{"X":135,"Y":179},{"X":135,"Y":179},{"X":133,"Y":178},{"X":133,"Y":179},{"X":133,"Y":179},{"X":133,"Y":180},{"X":132,"Y":180},{"X":132,"Y":180},{"X":132,"Y":181},{"X":132,"Y":182},{"X":130,"Y":182},{"X":132,"Y":178},{"X":132,"Y":178},{"X":130,"Y":177},{"X":132,"Y":177},{"X":132,"Y":177},{"X":134,"Y":174},{"X":134,"Y":177},{"X":135,"Y":178},{"X":135,"Y":179},{"X":134,"Y":178},{"X":134,"Y":178},{"X":136,"Y":179},{"X":136,"Y":177},{"X":136,"Y":178},{"X":136,"Y":177},{"X":136,"Y":177},{"X":136,"Y":176},{"X":136,"Y":174},{"X":138,"Y":176},{"X":143,"Y":176},{"X":144,"Y":178},{"X":146,"Y":177},{"X":147,"Y":177},{"X":147,"Y":178},{"X":148,"Y":179},{"X":148,"Y":179},{"X":148,"Y":180},{"X":148,"Y":180},{"X":149,"Y":181},{"X":149,"Y":181},{"X":151,"Y":178},{"X":154,"Y":178},{"X":156,"Y":180},{"X":157,"Y":179},{"X":157,"Y":179},{"X":158,"Y":180},{"X":160,"Y":181},{"X":161,"Y":181},{"X":160,"Y":180},{"X":160,"Y":180},{"X":161,"Y":179},{"X":162,"Y":179},{"X":161,"Y":178},{"X":161,"Y":178},{"X":162,"Y":178},{"X":162,"Y":178},{"X":161,"Y":177},{"X":160,"Y":178},{"X":159,"Y":177},{"X":161,"Y":176},{"X":161,"Y":174},{"X":161,"Y":174},{"X":163,"Y":174},{"X":162,"Y":173},{"X":163,"Y":172},{"X":163,"Y":173},{"X":167,"Y":173},{"X":168,"Y":171},{"X":168,"Y":170},{"X":170,"Y":168},{"X":170,"Y":168},{"X":171,"Y":168},{"X":170,"Y":168},{"X":168,"Y":168},{"X":167,"Y":168},{"X":167,"Y":168},{"X":167,"Y":168},{"X":166,"Y":168},{"X":163,"Y":170},{"X":163,"Y":168},{"X":166,"Y":168},{"X":166,"Y":168},{"X":163,"Y":168},{"X":163,"Y":168},{"X":161,"Y":168},{"X":160,"Y":168},{"X":157,"Y":166},{"X":158,"Y":165},{"X":157,"Y":165},{"X":158,"Y":165},{"X":158,"Y":163},{"X":160,"Y":162},{"X":165,"Y":163},{"X":163,"Y":162},{"X":162,"Y":162},{"X":163,"Y":162},{"X":166,"Y":163},{"X":162,"Y":160},{"X":163,"Y":160},{"X":167,"Y":160},{"X":166,"Y":159},{"X":166,"Y":159},{"X":167,"Y":159},{"X":168,"Y":159},{"X":167,"Y":159},{"X":166,"Y":158},{"X":166,"Y":159},{"X":165,"Y":158},{"X":165,"Y":156},{"X":167,"Y":156},{"X":166,"Y":156},{"X":165,"Y":154},{"X":166,"Y":154},{"X":166,"Y":152},{"X":167,"Y":154},{"X":167,"Y":152},{"X":168,"Y":152},{"X":168,"Y":152},{"X":168,"Y":152},{"X":168,"Y":151},{"X":169,"Y":150},{"X":171,"Y":150},{"X":171,"Y":150},{"X":171,"Y":150},{"X":171,"Y":151},{"X":172,"Y":150},{"X":174,"Y":150},{"X":174,"Y":148},{"X":174,"Y":147},{"X":176,"Y":148},{"X":176,"Y":149},{"X":177,"Y":149},{"X":177,"Y":150},{"X":177,"Y":150},{"X":178,"Y":150},{"X":177,"Y":150},{"X":178,"Y":150},{"X":178,"Y":150},{"X":178,"Y":149},{"X":177,"Y":149},{"X":177,"Y":147},{"X":178,"Y":147},{"X":178,"Y":147},{"X":178,"Y":150},{"X":179,"Y":150},{"X":180,"Y":151},{"X":180,"Y":152},{"X":181,"Y":151},{"X":180,"Y":148},{"X":180,"Y":150},{"X":180,"Y":149},{"X":180,"Y":148},{"X":179,"Y":148},{"X":179,"Y":147},{"X":180,"Y":147},{"X":181,"Y":146},{"X":182,"Y":147},{"X":182,"Y":149},{"X":183,"Y":151},{"X":183,"Y":151},{"X":183,"Y":152},{"X":181,"Y":155},{"X":182,"Y":155},{"X":182,"Y":155},{"X":181,"Y":155},{"X":180,"Y":157},{"X":182,"Y":155},{"X":184,"Y":154},{"X":184,"Y":154},{"X":184,"Y":151},{"X":183,"Y":148},{"X":184,"Y":148},{"X":183,"Y":147},{"X":184,"Y":147},{"X":184,"Y":147},{"X":184,"Y":147},{"X":187,"Y":146},{"X":187,"Y":146},{"X":187,"Y":147},{"X":189,"Y":147},{"X":187,"Y":146},{"X":187,"Y":145},{"X":187,"Y":145},{"X":187,"Y":144},{"X":185,"Y":144},{"X":185,"Y":144},{"X":189,"Y":144},{"X":190,"Y":145},{"X":191,"Y":146},{"X":195,"Y":148},{"X":195,"Y":149},{"X":191,"Y":151}],[{"X":236,"Y":34},{"X":237,"Y":35},{"X":238,"Y":35},{"X":239,"Y":35},{"X":239,"Y":36},{"X":240,"Y":35},{"X":239,"Y":35},{"X":239,"Y":35},{"X":240,"Y":36},{"X":242,"Y":37},{"X":242,"Y":37},{"X":240,"Y":40},{"X":242,"Y":38},{"X":242,"Y":37},{"X":243,"Y":37},{"X":243,"Y":36},{"X":245,"Y":36},{"X":246,"Y":37},{"X":247,"Y":38},{"X":246,"Y":40},{"X":247,"Y":37},{"X":249,"Y":37},{"X":250,"Y":38},{"X":250,"Y":38},{"X":250,"Y":37},{"X":251,"Y":37},{"X":251,"Y":36},{"X":253,"Y":37},{"X":254,"Y":36},{"X":254,"Y":36},{"X":256,"Y":36},{"X":257,"Y":36},{"X":260,"Y":36},{"X":262,"Y":36},{"X":262,"Y":36},{"X":262,"Y":36},{"X":264,"Y":35},{"X":266,"Y":36},{"X":266,"Y":35},{"X":265,"Y":34},{"X":266,"Y":33},{"X":266,"Y":34},{"X":269,"Y":34},{"X":269,"Y":35},{"X":271,"Y":34},{"X":271,"Y":35},{"X":270,"Y":38},{"X":270,"Y":39},{"X":271,"Y":39},{"X":270,"Y":44},{"X":269,"Y":44},{"X":268,"Y":46},{"X":266,"Y":47},{"X":264,"Y":51},{"X":258,"Y":53},{"X":257,"Y":55},{"X":256,"Y":56},{"X":254,"Y":58},{"X":254,"Y":60},{"X":253,"Y":60},{"X":251,"Y":61},{"X":250,"Y":61},{"X":250,"Y":62},{"X":253,"Y":62},{"X":254,"Y":61},{"X":255,"Y":61},{"X":255,"Y":62},{"X":256,"Y":62},{"X":257,"Y":60},{"X":258,"Y":61},{"X":256,"Y":63},{"X":254,"Y":66},{"X":254,"Y":66},{"X":254,"Y":66},{"X":253,"Y":66},{"X":250,"Y":67},{"X":248,"Y":66},{"X":248,"Y":68},{"X":246,"Y":69},{"X":246,"Y":70},{"X":249,"Y":67},{"X":253,"Y":67},{"X":253,"Y":66},{"X":254,"Y":67},{"X":251,"Y":69},{"X":251,"Y":69},{"X":251,"Y":70},{"X":250,"Y":71},{"X":250,"Y":72},{"X":249,"Y":71},{"X":249,"Y":72},{"X":249,"Y":73},{"X":250,"Y":73},{"X":250,"Y":73},{"X":250,"Y":73},{"X":253,"Y":71},{"X":253,"Y":70},{"X":253,"Y":69},{"X":256,"Y":69},{"X":260,"Y":68},{"X":262,"Y":68},{"X":264,"Y":66},{"X":266,"Y":64},{"X":271,"Y":68},{"X":275,"Y":66},{"X":276,"Y":67},{"X":277,"Y":67},{"X":283,"Y":68},{"X":284,"Y":68},{"X":286,"Y":67},{"X":287,"Y":68},{"X":289,"Y":67},{"X":291,"Y":67},{"X":291,"Y":68},{"X":292,"Y":67},{"X":293,"Y":68},{"X":293,"Y":69},{"X":293,"Y":70},{"X":294,"Y":73},{"X":294,"Y":73},{"X":294,"Y":74},{"X":295,"Y":74},{"X":290,"Y":81},{"X":289,"Y":84},{"X":290,"Y":86},{"X":287,"Y":91},{"X":287,"Y":93},{"X":286,"Y":96},{"X":283,"Y":97},{"X":283,"Y":97},{"X":283,"Y":101},{"X":283,"Y":101},{"X":281,"Y":102},{"X":282,"Y":103},{"X":281,"Y":105},{"X":278,"Y":107},{"X":277,"Y":108},{"X":276,"Y":108},{"X":275,"Y":108},{"X":273,"Y":108},{"X":272,"Y":108},{"X":271,"Y":108},{"X":267,"Y":111},{"X":266,"Y":112},{"X":267,"Y":112},{"X":268,"Y":111},{"X":275,"Y":108},{"X":276,"Y":110},{"X":276,"Y":111},{"X":276,"Y":112},{"X":276,"Y":112},{"X":277,"Y":113},{"X":279,"Y":113},{"X":280,"Y":116},{"X":278,"Y":117},{"X":276,"Y":117},{"X":273,"Y":117},{"X":272,"Y":117},{"X":269,"Y":121},{"X":269,"Y":122},{"X":267,"Y":123},{"X":265,"Y":124},{"X":261,"Y":123},{"X":258,"Y":123},{"X":258,"Y":124},{"X":259,"Y":124},{"X":262,"Y":124},{"X":265,"Y":125},{"X":266,"Y":124},{"X":267,"Y":125},{"X":269,"Y":125},{"X":270,"Y":126},{"X":271,"Y":126},{"X":272,"Y":125},{"X":273,"Y":125},{"X":275,"Y":124},{"X":275,"Y":123},{"X":276,"Y":122},{"X":279,"Y":122},{"X":280,"Y":124},{"X":280,"Y":124},{"X":282,"Y":124},{"X":286,"Y":127},{"X":289,"Y":127},{"X":289,"Y":127},{"X":290,"Y":129},{"X":292,"Y":133},{"X":294,"Y":136},{"X":295,"Y":136},{"X":295,"Y":137},{"X":295,"Y":137},{"X":295,"Y":138},{"X":298,"Y":138},{"X":298,"Y":138},{"X":299,"Y":140},{"X":300,"Y":141},{"X":300,"Y":144},{"X":299,"Y":146},{"X":300,"Y":147},{"X":300,"Y":149},{"X":301,"Y":152},{"X":301,"Y":154},{"X":303,"Y":158},{"X":304,"Y":160},{"X":304,"Y":161},{"X":306,"Y":166},{"X":306,"Y":168},{"X":309,"Y":169},{"X":308,"Y":169},{"X":309,"Y":170},{"X":309,"Y":172},{"X":309,"Y":172},{"X":309,"Y":172},{"X":309,"Y":171},{"X":312,"Y":172},{"X":315,"Y":173},{"X":319,"Y":176},{"X":321,"Y":176},{"X":322,"Y":177},{"X":322,"Y":178},{"X":323,"Y":180},{"X":325,"Y":182},{"X":326,"Y":183},{"X":327,"Y":187},{"X":331,"Y":188},{"X":328,"Y":189},{"X":327,"Y":191},{"X":330,"Y":195},{"X":333,"Y":200},{"X":335,"Y":204},{"X":335,"Y":206},{"X":335,"Y":206},{"X":335,"Y":205},{"X":335,"Y":204},{"X":333,"Y":204},{"X":333,"Y":204},{"X":332,"Y":204},{"X":330,"Y":204},{"X":327,"Y":201},{"X":326,"Y":201},{"X":324,"Y":202},{"X":322,"Y":203},{"X":320,"Y":202},{"X":319,"Y":203},{"X":319,"Y":203},{"X":321,"Y":203},{"X":322,"Y":203},{"X":323,"Y":203},{"X":326,"Y":202},{"X":327,"Y":203},{"X":328,"Y":204},{"X":332,"Y":206},{"X":333,"Y":206},{"X":333,"Y":207},{"X":334,"Y":210},{"X":336,"Y":210},{"X":336,"Y":211},{"X":337,"Y":212},{"X":339,"Y":217},{"X":341,"Y":220},{"X":339,"Y":222},{"X":336,"Y":225},{"X":335,"Y":229},{"X":334,"Y":229},{"X":334,"Y":229},{"X":335,"Y":229},{"X":336,"Y":229},{"X":336,"Y":231},{"X":338,"Y":233},{"X":341,"Y":232},{"X":342,"Y":234},{"X":342,"Y":233},{"X":342,"Y":232},{"X":343,"Y":227},{"X":344,"Y":226},{"X":346,"Y":226},{"X":347,"Y":226},{"X":350,"Y":226},{"X":352,"Y":226},{"X":352,"Y":226},{"X":353,"Y":226},{"X":354,"Y":226},{"X":353,"Y":225},{"X":354,"Y":225},{"X":355,"Y":225},{"X":356,"Y":226},{"X":360,"Y":226},{"X":363,"Y":229},{"X":368,"Y":233},{"X":368,"Y":233},{"X":370,"Y":236},{"X":370,"Y":238},{"X":370,"Y":240},{"X":370,"Y":243},{"X":370,"Y":245},{"X":368,"Y":249},{"X":368,"Y":251},{"X":368,"Y":255},{"X":365,"Y":257},{"X":364,"Y":261},{"X":361,"Y":259},{"X":361,"Y":260},{"X":359,"Y":260},{"X":358,"Y":260},{"X":363,"Y":260},{"X":360,"Y":264},{"X":361,"Y":264},{"X":361,"Y":262},{"X":363,"Y":264},{"X":363,"Y":264},{"X":360,"Y":266},{"X":358,"Y":267},{"X":357,"Y":266},{"X":356,"Y":266},{"X":355,"Y":266},{"X":354,"Y":267},{"X":353,"Y":268},{"X":350,"Y":268},{"X":352,"Y":269},{"X":352,"Y":269},{"X":354,"Y":267},{"X":355,"Y":268},{"X":355,"Y":269},{"X":355,"Y":272},{"X":354,"Y":272},{"X":353,"Y":272},{"X":354,"Y":272},{"X":354,"Y":272},{"X":354,"Y":272},{"X":355,"Y":272},{"X":356,"Y":272},{"X":353,"Y":276},{"X":349,"Y":276},{"X":348,"Y":277},{"X":346,"Y":277},{"X":345,"Y":277},{"X":344,"Y":278},{"X":345,"Y":278},{"X":346,"Y":277},{"X":347,"Y":277},{"X":350,"Y":277},{"X":352,"Y":278},{"X":350,"Y":279},{"X":349,"Y":279},{"X":348,"Y":279},{"X":347,"Y":279},{"X":348,"Y":280},{"X":349,"Y":280},{"X":350,"Y":280},{"X":350,"Y":280},{"X":350,"Y":279},{"X":352,"Y":280},{"X":352,"Y":279},{"X":352,"Y":278},{"X":355,"Y":279},{"X":356,"Y":281},{"X":355,"Y":281},{"X":353,"Y":281},{"X":355,"Y":281},{"X":361,"Y":280},{"X":367,"Y":279},{"X":366,"Y":281},{"X":365,"Y":282},{"X":366,"Y":284},{"X":366,"Y":288},{"X":365,"Y":289},{"X":363,"Y":290},{"X":361,"Y":290},{"X":360,"Y":291},{"X":358,"Y":292},{"X":357,"Y":293},{"X":357,"Y":295},{"X":354,"Y":295},{"X":352,"Y":297},{"X":350,"Y":298},{"X":344,"Y":300},{"X":343,"Y":302},{"X":342,"Y":303},{"X":337,"Y":302},{"X":333,"Y":300},{"X":322,"Y":302},{"X":322,"Y":303},{"X":321,"Y":302},{"X":321,"Y":304},{"X":320,"Y":304},{"X":317,"Y":302},{"X":319,"Y":302},{"X":317,"Y":301},{"X":317,"Y":301},{"X":316,"Y":301},{"X":315,"Y":301},{"X":316,"Y":301},{"X":317,"Y":302},{"X":314,"Y":302},{"X":315,"Y":302},{"X":315,"Y":301},{"X":314,"Y":301},{"X":314,"Y":303},{"X":313,"Y":303},{"X":313,"Y":303},{"X":313,"Y":301},{"X":313,"Y":300},{"X":311,"Y":301},{"X":312,"Y":302},{"X":312,"Y":303},{"X":309,"Y":301},{"X":308,"Y":299},{"X":305,"Y":298},{"X":305,"Y":299},{"X":306,"Y":299},{"X":309,"Y":302},{"X":308,"Y":303},{"X":305,"Y":303},{"X":304,"Y":304},{"X":301,"Y":304},{"X":299,"Y":304},{"X":300,"Y":304},{"X":300,"Y":304},{"X":298,"Y":304},{"X":297,"Y":304},{"X":295,"Y":306},{"X":295,"Y":304},{"X":293,"Y":304},{"X":292,"Y":306},{"X":293,"Y":305},{"X":293,"Y":306},{"X":295,"Y":306},{"X":295,"Y":308},{"X":295,"Y":308},{"X":295,"Y":309},{"X":294,"Y":310},{"X":293,"Y":310},{"X":292,"Y":310},{"X":290,"Y":309},{"X":284,"Y":309},{"X":284,"Y":309},{"X":283,"Y":310},{"X":284,"Y":311},{"X":283,"Y":312},{"X":283,"Y":311},{"X":280,"Y":308},{"X":277,"Y":305},{"X":275,"Y":305},{"X":272,"Y":306},{"X":270,"Y":306},{"X":268,"Y":306},{"X":265,"Y":310},{"X":264,"Y":309},{"X":262,"Y":306},{"X":262,"Y":310},{"X":261,"Y":312},{"X":261,"Y":314},{"X":260,"Y":315},{"X":260,"Y":316},{"X":261,"Y":317},{"X":261,"Y":317},{"X":260,"Y":317},{"X":259,"Y":319},{"X":259,"Y":319},{"X":258,"Y":320},{"X":258,"Y":321},{"X":258,"Y":322},{"X":256,"Y":323},{"X":255,"Y":322},{"X":255,"Y":322},{"X":254,"Y":322},{"X":253,"Y":320},{"X":251,"Y":319},{"X":249,"Y":319},{"X":247,"Y":319},{"X":247,"Y":317},{"X":246,"Y":317},{"X":246,"Y":317},{"X":247,"Y":317},{"X":246,"Y":319},{"X":245,"Y":319},{"X":244,"Y":317},{"X":243,"Y":317},{"X":240,"Y":317},{"X":239,"Y":319},{"X":236,"Y":319},{"X":235,"Y":319},{"X":235,"Y":317},{"X":234,"Y":319},{"X":233,"Y":322},{"X":231,"Y":321},{"X":229,"Y":322},{"X":228,"Y":322},{"X":228,"Y":324},{"X":227,"Y":324},{"X":228,"Y":324},{"X":227,"Y":324},{"X":227,"Y":321},{"X":227,"Y":321},{"X":226,"Y":322},{"X":227,"Y":323},{"X":226,"Y":324},{"X":227,"Y":324},{"X":226,"Y":324},{"X":226,"Y":325},{"X":225,"Y":325},{"X":226,"Y":326},{"X":226,"Y":327},{"X":226,"Y":328},{"X":224,"Y":328},{"X":224,"Y":331},{"X":223,"Y":328},{"X":222,"Y":326},{"X":221,"Y":326},{"X":217,"Y":324},{"X":216,"Y":324},{"X":216,"Y":325},{"X":215,"Y":326},{"X":213,"Y":327},{"X":212,"Y":326},{"X":213,"Y":325},{"X":212,"Y":323},{"X":213,"Y":324},{"X":216,"Y":321},{"X":218,"Y":322},{"X":220,"Y":321},{"X":221,"Y":321},{"X":222,"Y":320},{"X":223,"Y":317},{"X":225,"Y":316},{"X":225,"Y":315},{"X":227,"Y":315},{"X":228,"Y":313},{"X":227,"Y":312},{"X":228,"Y":311},{"X":228,"Y":311},{"X":229,"Y":310},{"X":229,"Y":310},{"X":231,"Y":309},{"X":234,"Y":309},{"X":234,"Y":306},{"X":235,"Y":305},{"X":236,"Y":304},{"X":238,"Y":303},{"X":238,"Y":299},{"X":239,"Y":295},{"X":240,"Y":295},{"X":243,"Y":295},{"X":244,"Y":295},{"X":246,"Y":294},{"X":247,"Y":293},{"X":245,"Y":291},{"X":246,"Y":291},{"X":246,"Y":290},{"X":246,"Y":290},{"X":255,"Y":288},{"X":259,"Y":288},{"X":260,"Y":288},{"X":262,"Y":289},{"X":264,"Y":290},{"X":266,"Y":290},{"X":268,"Y":289},{"X":270,"Y":289},{"X":271,"Y":288},{"X":271,"Y":288},{"X":271,"Y":286},{"X":272,"Y":286},{"X":272,"Y":282},{"X":275,"Y":281},{"X":277,"Y":279},{"X":278,"Y":279},{"X":283,"Y":271},{"X":284,"Y":270},{"X":284,"Y":269},{"X":284,"Y":270},{"X":283,"Y":270},{"X":278,"Y":276},{"X":275,"Y":277},{"X":272,"Y":278},{"X":272,"Y":278},{"X":272,"Y":278},{"X":271,"Y":278},{"X":269,"Y":279},{"X":269,"Y":280},{"X":268,"Y":280},{"X":268,"Y":281},{"X":267,"Y":282},{"X":264,"Y":282},{"X":260,"Y":281},{"X":258,"Y":279},{"X":257,"Y":279},{"X":255,"Y":278},{"X":254,"Y":275},{"X":250,"Y":275},{"X":250,"Y":275},{"X":250,"Y":277},{"X":247,"Y":277},{"X":247,"Y":277},{"X":246,"Y":278},{"X":244,"Y":277},{"X":244,"Y":276},{"X":245,"Y":275},{"X":246,"Y":275},{"X":246,"Y":275},{"X":248,"Y":275},{"X":249,"Y":273},{"X":249,"Y":272},{"X":248,"Y":273},{"X":247,"Y":272},{"X":244,"Y":272},{"X":244,"Y":272},{"X":243,"Y":271},{"X":243,"Y":270},{"X":244,"Y":271},{"X":243,"Y":269},{"X":243,"Y":268},{"X":243,"Y":269},{"X":242,"Y":269},{"X":242,"Y":270},{"X":237,"Y":270},{"X":236,"Y":272},{"X":235,"Y":272},{"X":234,"Y":273},{"X":233,"Y":275},{"X":231,"Y":275},{"X":228,"Y":273},{"X":228,"Y":272},{"X":227,"Y":272},{"X":227,"Y":272},{"X":228,"Y":272},{"X":228,"Y":272},{"X":228,"Y":272},{"X":229,"Y":272},{"X":233,"Y":271},{"X":229,"Y":270},{"X":226,"Y":270},{"X":226,"Y":272},{"X":225,"Y":270},{"X":224,"Y":269},{"X":225,"Y":269},{"X":226,"Y":268},{"X":227,"Y":268},{"X":227,"Y":266},{"X":225,"Y":266},{"X":223,"Y":265},{"X":223,"Y":262},{"X":225,"Y":264},{"X":225,"Y":262},{"X":227,"Y":261},{"X":227,"Y":260},{"X":229,"Y":261},{"X":231,"Y":261},{"X":232,"Y":261},{"X":232,"Y":260},{"X":233,"Y":261},{"X":234,"Y":260},{"X":234,"Y":259},{"X":235,"Y":259},{"X":236,"Y":257},{"X":236,"Y":257},{"X":236,"Y":256},{"X":240,"Y":256},{"X":243,"Y":254},{"X":244,"Y":254},{"X":246,"Y":253},{"X":248,"Y":250},{"X":249,"Y":248},{"X":250,"Y":244},{"X":251,"Y":243},{"X":253,"Y":242},{"X":250,"Y":242},{"X":248,"Y":240},{"X":250,"Y":237},{"X":251,"Y":235},{"X":250,"Y":236},{"X":249,"Y":235},{"X":248,"Y":235},{"X":248,"Y":234},{"X":249,"Y":232},{"X":248,"Y":231},{"X":243,"Y":231},{"X":242,"Y":232},{"X":242,"Y":233},{"X":242,"Y":234},{"X":240,"Y":235},{"X":238,"Y":233},{"X":238,"Y":234},{"X":236,"Y":233},{"X":236,"Y":234},{"X":236,"Y":233},{"X":236,"Y":232},{"X":238,"Y":231},{"X":240,"Y":229},{"X":244,"Y":226},{"X":244,"Y":223},{"X":245,"Y":223},{"X":245,"Y":223},{"X":248,"Y":220},{"X":249,"Y":218},{"X":250,"Y":218},{"X":254,"Y":217},{"X":255,"Y":217},{"X":254,"Y":216},{"X":256,"Y":216},{"X":257,"Y":217},{"X":259,"Y":217},{"X":265,"Y":215},{"X":266,"Y":215},{"X":266,"Y":216},{"X":270,"Y":220},{"X":270,"Y":217},{"X":268,"Y":214},{"X":268,"Y":214},{"X":271,"Y":212},{"X":271,"Y":213},{"X":273,"Y":216},{"X":276,"Y":217},{"X":277,"Y":217},{"X":278,"Y":216},{"X":277,"Y":216},{"X":275,"Y":216},{"X":272,"Y":213},{"X":270,"Y":210},{"X":272,"Y":204},{"X":276,"Y":202},{"X":271,"Y":202},{"X":270,"Y":199},{"X":271,"Y":195},{"X":275,"Y":194},{"X":275,"Y":195},{"X":276,"Y":193},{"X":275,"Y":193},{"X":275,"Y":193},{"X":275,"Y":193},{"X":273,"Y":192},{"X":275,"Y":190},{"X":276,"Y":190},{"X":276,"Y":189},{"X":276,"Y":187},{"X":276,"Y":185},{"X":275,"Y":185},{"X":273,"Y":188},{"X":272,"Y":188},{"X":271,"Y":185},{"X":270,"Y":189},{"X":269,"Y":191},{"X":269,"Y":191},{"X":267,"Y":189},{"X":267,"Y":188},{"X":267,"Y":188},{"X":268,"Y":188},{"X":268,"Y":183},{"X":267,"Y":187},{"X":266,"Y":187},{"X":264,"Y":183},{"X":264,"Y":181},{"X":259,"Y":176},{"X":260,"Y":172},{"X":261,"Y":170},{"X":262,"Y":168},{"X":264,"Y":167},{"X":264,"Y":166},{"X":265,"Y":163},{"X":266,"Y":162},{"X":266,"Y":163},{"X":267,"Y":163},{"X":267,"Y":162},{"X":267,"Y":162},{"X":267,"Y":161},{"X":268,"Y":160},{"X":270,"Y":161},{"X":271,"Y":161},{"X":271,"Y":160},{"X":270,"Y":160},{"X":270,"Y":160},{"X":271,"Y":160},{"X":271,"Y":159},{"X":269,"Y":159},{"X":267,"Y":160},{"X":264,"Y":160},{"X":264,"Y":160},{"X":262,"Y":160},{"X":261,"Y":160},{"X":261,"Y":160},{"X":261,"Y":160},{"X":261,"Y":163},{"X":260,"Y":163},{"X":258,"Y":162},{"X":257,"Y":163},{"X":256,"Y":162},{"X":256,"Y":163},{"X":256,"Y":162},{"X":256,"Y":162},{"X":255,"Y":163},{"X":256,"Y":165},{"X":253,"Y":167},{"X":251,"Y":167},{"X":251,"Y":165},{"X":250,"Y":166},{"X":249,"Y":166},{"X":248,"Y":165},{"X":248,"Y":162},{"X":247,"Y":165},{"X":246,"Y":162},{"X":245,"Y":162},{"X":244,"Y":161},{"X":244,"Y":162},{"X":244,"Y":162},{"X":244,"Y":165},{"X":246,"Y":165},{"X":245,"Y":166},{"X":246,"Y":169},{"X":245,"Y":169},{"X":242,"Y":169},{"X":240,"Y":166},{"X":237,"Y":165},{"X":236,"Y":162},{"X":236,"Y":162},{"X":234,"Y":163},{"X":233,"Y":165},{"X":234,"Y":169},{"X":235,"Y":169},{"X":235,"Y":169},{"X":236,"Y":170},{"X":235,"Y":170},{"X":234,"Y":169},{"X":234,"Y":168},{"X":229,"Y":162},{"X":228,"Y":160},{"X":229,"Y":158},{"X":231,"Y":158},{"X":232,"Y":158},{"X":232,"Y":160},{"X":233,"Y":162},{"X":233,"Y":161},{"X":232,"Y":158},{"X":232,"Y":157},{"X":233,"Y":154},{"X":236,"Y":150},{"X":236,"Y":147},{"X":237,"Y":146},{"X":238,"Y":144},{"X":240,"Y":144},{"X":240,"Y":141},{"X":239,"Y":140},{"X":239,"Y":140},{"X":239,"Y":139},{"X":239,"Y":138},{"X":237,"Y":137},{"X":237,"Y":136},{"X":235,"Y":134},{"X":236,"Y":133},{"X":236,"Y":130},{"X":235,"Y":127},{"X":236,"Y":126},{"X":237,"Y":125},{"X":238,"Y":127},{"X":243,"Y":127},{"X":240,"Y":125},{"X":238,"Y":124},{"X":237,"Y":122},{"X":237,"Y":124},{"X":238,"Y":124},{"X":237,"Y":124},{"X":236,"Y":124},{"X":236,"Y":122},{"X":237,"Y":121},{"X":236,"Y":121},{"X":236,"Y":119},{"X":236,"Y":119},{"X":235,"Y":121},{"X":236,"Y":122},{"X":236,"Y":124},{"X":235,"Y":124},{"X":235,"Y":124},{"X":234,"Y":128},{"X":233,"Y":128},{"X":233,"Y":126},{"X":232,"Y":124},{"X":232,"Y":127},{"X":229,"Y":126},{"X":229,"Y":124},{"X":229,"Y":124},{"X":229,"Y":127},{"X":228,"Y":127},{"X":229,"Y":129},{"X":227,"Y":128},{"X":226,"Y":127},{"X":227,"Y":124},{"X":228,"Y":122},{"X":229,"Y":121},{"X":229,"Y":119},{"X":233,"Y":118},{"X":233,"Y":117},{"X":235,"Y":115},{"X":234,"Y":116},{"X":233,"Y":116},{"X":232,"Y":118},{"X":228,"Y":119},{"X":226,"Y":124},{"X":225,"Y":124},{"X":225,"Y":124},{"X":225,"Y":125},{"X":225,"Y":126},{"X":225,"Y":128},{"X":227,"Y":130},{"X":227,"Y":132},{"X":226,"Y":133},{"X":225,"Y":134},{"X":224,"Y":136},{"X":224,"Y":137},{"X":224,"Y":138},{"X":224,"Y":138},{"X":224,"Y":139},{"X":222,"Y":144},{"X":222,"Y":144},{"X":222,"Y":144},{"X":223,"Y":145},{"X":222,"Y":147},{"X":220,"Y":148},{"X":217,"Y":147},{"X":217,"Y":144},{"X":220,"Y":144},{"X":220,"Y":139},{"X":220,"Y":138},{"X":221,"Y":136},{"X":221,"Y":135},{"X":224,"Y":129},{"X":222,"Y":132},{"X":221,"Y":130},{"X":221,"Y":129},{"X":223,"Y":126},{"X":221,"Y":127},{"X":221,"Y":126},{"X":223,"Y":122},{"X":222,"Y":123},{"X":221,"Y":126},{"X":220,"Y":126},{"X":221,"Y":124},{"X":222,"Y":123},{"X":222,"Y":122},{"X":223,"Y":121},{"X":224,"Y":121},{"X":223,"Y":119},{"X":224,"Y":117},{"X":223,"Y":119},{"X":222,"Y":119},{"X":223,"Y":117},{"X":223,"Y":115},{"X":223,"Y":112},{"X":224,"Y":112},{"X":225,"Y":108},{"X":229,"Y":108},{"X":227,"Y":107},{"X":226,"Y":107},{"X":226,"Y":107},{"X":226,"Y":107},{"X":225,"Y":107},{"X":226,"Y":106},{"X":227,"Y":106},{"X":228,"Y":105},{"X":229,"Y":105},{"X":227,"Y":105},{"X":226,"Y":105},{"X":227,"Y":102},{"X":228,"Y":102},{"X":228,"Y":102},{"X":229,"Y":101},{"X":232,"Y":101},{"X":233,"Y":101},{"X":232,"Y":101},{"X":229,"Y":101},{"X":233,"Y":95},{"X":231,"Y":97},{"X":229,"Y":97},{"X":228,"Y":100},{"X":227,"Y":101},{"X":227,"Y":101},{"X":225,"Y":103},{"X":224,"Y":105},{"X":221,"Y":106},{"X":221,"Y":106},{"X":220,"Y":105},{"X":220,"Y":105},{"X":217,"Y":104},{"X":215,"Y":103},{"X":215,"Y":102},{"X":216,"Y":101},{"X":217,"Y":101},{"X":221,"Y":100},{"X":222,"Y":101},{"X":224,"Y":101},{"X":222,"Y":101},{"X":220,"Y":100},{"X":218,"Y":101},{"X":217,"Y":100},{"X":212,"Y":100},{"X":211,"Y":99},{"X":213,"Y":97},{"X":215,"Y":97},{"X":216,"Y":96},{"X":217,"Y":97},{"X":218,"Y":97},{"X":218,"Y":96},{"X":218,"Y":97},{"X":220,"Y":96},{"X":220,"Y":96},{"X":218,"Y":95},{"X":218,"Y":94},{"X":221,"Y":94},{"X":221,"Y":94},{"X":222,"Y":94},{"X":222,"Y":94},{"X":221,"Y":94},{"X":221,"Y":94},{"X":221,"Y":93},{"X":217,"Y":92},{"X":218,"Y":93},{"X":220,"Y":93},{"X":218,"Y":92},{"X":220,"Y":90},{"X":221,"Y":89},{"X":221,"Y":90},{"X":222,"Y":91},{"X":225,"Y":90},{"X":223,"Y":90},{"X":222,"Y":90},{"X":222,"Y":89},{"X":222,"Y":88},{"X":221,"Y":88},{"X":221,"Y":86},{"X":222,"Y":85},{"X":223,"Y":85},{"X":225,"Y":88},{"X":226,"Y":86},{"X":224,"Y":86},{"X":224,"Y":85},{"X":223,"Y":83},{"X":222,"Y":83},{"X":223,"Y":83},{"X":223,"Y":82},{"X":223,"Y":81},{"X":223,"Y":80},{"X":225,"Y":80},{"X":225,"Y":80},{"X":227,"Y":82},{"X":227,"Y":81},{"X":226,"Y":81},{"X":225,"Y":80},{"X":222,"Y":80},{"X":222,"Y":80},{"X":223,"Y":78},{"X":225,"Y":77},{"X":226,"Y":74},{"X":225,"Y":77},{"X":224,"Y":78},{"X":223,"Y":77},{"X":224,"Y":77},{"X":224,"Y":75},{"X":221,"Y":78},{"X":220,"Y":78},{"X":220,"Y":75},{"X":221,"Y":75},{"X":220,"Y":75},{"X":221,"Y":74},{"X":220,"Y":74},{"X":220,"Y":72},{"X":221,"Y":70},{"X":221,"Y":70},{"X":222,"Y":71},{"X":222,"Y":70},{"X":223,"Y":72},{"X":223,"Y":72},{"X":224,"Y":72},{"X":225,"Y":71},{"X":224,"Y":71},{"X":223,"Y":70},{"X":223,"Y":70},{"X":223,"Y":70},{"X":222,"Y":69},{"X":222,"Y":68},{"X":221,"Y":68},{"X":221,"Y":64},{"X":223,"Y":64},{"X":223,"Y":64},{"X":222,"Y":64},{"X":221,"Y":63},{"X":221,"Y":62},{"X":221,"Y":60},{"X":223,"Y":60},{"X":223,"Y":60},{"X":223,"Y":62},{"X":224,"Y":63},{"X":224,"Y":62},{"X":225,"Y":62},{"X":225,"Y":60},{"X":224,"Y":60},{"X":224,"Y":58},{"X":225,"Y":58},{"X":225,"Y":58},{"X":225,"Y":60},{"X":227,"Y":60},{"X":227,"Y":58},{"X":232,"Y":60},{"X":232,"Y":60},{"X":228,"Y":58},{"X":228,"Y":57},{"X":228,"Y":57},{"X":229,"Y":59},{"X":232,"Y":58},{"X":233,"Y":60},{"X":234,"Y":61},{"X":234,"Y":60},{"X":232,"Y":58},{"X":233,"Y":58},{"X":233,"Y":58},{"X":231,"Y":56},{"X":229,"Y":55},{"X":228,"Y":53},{"X":228,"Y":53},{"X":227,"Y":52},{"X":227,"Y":51},{"X":229,"Y":52},{"X":229,"Y":52},{"X":232,"Y":52},{"X":232,"Y":52},{"X":231,"Y":51},{"X":232,"Y":51},{"X":231,"Y":50},{"X":232,"Y":50},{"X":231,"Y":50},{"X":231,"Y":50},{"X":229,"Y":49},{"X":229,"Y":49},{"X":228,"Y":47},{"X":228,"Y":46},{"X":231,"Y":48},{"X":233,"Y":47},{"X":233,"Y":48},{"X":234,"Y":47},{"X":237,"Y":48},{"X":236,"Y":47},{"X":237,"Y":47},{"X":235,"Y":47},{"X":234,"Y":46},{"X":234,"Y":45},{"X":234,"Y":45},{"X":233,"Y":45},{"X":234,"Y":41},{"X":236,"Y":42},{"X":235,"Y":41},{"X":235,"Y":41},{"X":235,"Y":41},{"X":235,"Y":40},{"X":236,"Y":40},{"X":237,"Y":40},{"X":236,"Y":39},{"X":234,"Y":38},{"X":234,"Y":38},{"X":236,"Y":37}]]'; | |
| var arrow_poly = '[[{"X":246,"Y":146},{"X":313,"Y":146},{"X":290,"Y":168},{"X":335,"Y":191},{"X":290,"Y":235},{"X":268,"Y":191},{"X":246,"Y":213}],[{"X":246,"Y":57},{"X":268,"Y":79},{"X":290,"Y":35},{"X":335,"Y":79},{"X":290,"Y":102},{"X":313,"Y":124},{"X":246,"Y":124}],[{"X":224,"Y":146},{"X":224,"Y":213},{"X":201,"Y":191},{"X":179,"Y":235},{"X":134,"Y":191},{"X":179,"Y":168},{"X":157,"Y":146}],[{"X":179,"Y":102},{"X":134,"Y":79},{"X":179,"Y":35},{"X":201,"Y":79},{"X":224,"Y":57},{"X":224,"Y":124},{"X":157,"Y":124}]]'; | |
| return { | |
| "ss": deserialize_clipper_poly(gb_poly), | |
| "cc": deserialize_clipper_poly(arrow_poly) | |
| } | |
| } | |
| function get_text_polys() { | |
| var text_polygon = '[[{"X":"28.18","Y":"205.95"},{"X":"28.18","Y":"105.07"},{"X":"48.27","Y":"105.07"},{"X":"48.28","Y":"110.23"},{"X":"54.15","Y":"107.22"},{"X":"59.69","Y":"105.39"},{"X":"65.04","Y":"104.66"},{"X":"72.07","Y":"105.08"},{"X":"78.09","Y":"106.31"},{"X":"81.57","Y":"107.68"},{"X":"84.84","Y":"110.05"},{"X":"88.03","Y":"113.63"},{"X":"90.87","Y":"118.22"},{"X":"92.71","Y":"123.12"},{"X":"94.06","Y":"129.73"},{"X":"94.70","Y":"138.25"},{"X":"94.41","Y":"147.24"},{"X":"93.31","Y":"155.42"},{"X":"91.66","Y":"161.49"},{"X":"89.46","Y":"166.25"},{"X":"86.67","Y":"170.32"},{"X":"83.75","Y":"173.13"},{"X":"80.65","Y":"174.92"},{"X":"75.51","Y":"176.56"},{"X":"69.28","Y":"177.50"},{"X":"63.74","Y":"177.55"},{"X":"57.05","Y":"176.72"},{"X":"51.42","Y":"175.15"},{"X":"48.27","Y":"173.79"},{"X":"48.27","Y":"205.95"},{"X":"48.27","Y":"205.95"},{"X":"28.18","Y":"205.95"}],[{"X":"48.28","Y":"120.42"},{"X":"48.38","Y":"164.32"},{"X":"52.31","Y":"166.53"},{"X":"56.14","Y":"167.74"},{"X":"61.35","Y":"168.35"},{"X":"65.96","Y":"167.83"},{"X":"69.49","Y":"166.59"},{"X":"72.20","Y":"164.73"},{"X":"74.29","Y":"162.19"},{"X":"76.19","Y":"157.95"},{"X":"77.71","Y":"152.00"},{"X":"78.63","Y":"144.11"},{"X":"78.66","Y":"136.61"},{"X":"77.89","Y":"129.16"},{"X":"76.59","Y":"124.08"},{"X":"74.84","Y":"120.60"},{"X":"72.55","Y":"118.04"},{"X":"69.77","Y":"116.28"},{"X":"65.91","Y":"115.07"},{"X":"60.69","Y":"114.57"},{"X":"56.85","Y":"115.18"},{"X":"53.10","Y":"116.68"},{"X":"48.91","Y":"119.57"},{"X":"48.28","Y":"120.39"},{"X":"48.28","Y":"120.42"}],[{"X":"132.15","Y":"104.65"},{"X":"139.94","Y":"105.10"},{"X":"146.11","Y":"106.30"},{"X":"151.03","Y":"108.11"},{"X":"155.15","Y":"110.57"},{"X":"158.60","Y":"113.67"},{"X":"161.35","Y":"117.41"},{"X":"163.45","Y":"121.89"},{"X":"165.10","Y":"127.80"},{"X":"166.07","Y":"135.09"},{"X":"166.22","Y":"143.93"},{"X":"165.51","Y":"151.69"},{"X":"164.06","Y":"158.12"},{"X":"162.01","Y":"163.28"},{"X":"159.40","Y":"167.47"},{"X":"156.32","Y":"170.74"},{"X":"152.52","Y":"173.40"},{"X":"147.87","Y":"175.49"},{"X":"142.07","Y":"176.99"},{"X":"134.92","Y":"177.77"},{"X":"127.42","Y":"177.67"},{"X":"120.74","Y":"176.71"},{"X":"115.29","Y":"175.07"},{"X":"110.74","Y":"172.78"},{"X":"106.95","Y":"169.86"},{"X":"103.92","Y":"166.34"},{"X":"101.48","Y":"162.04"},{"X":"99.57","Y":"156.68"},{"X":"98.29","Y":"150.05"},{"X":"97.78","Y":"141.98"},{"X":"98.18","Y":"134.19"},{"X":"99.36","Y":"127.55"},{"X":"101.22","Y":"122.01"},{"X":"103.68","Y":"117.38"},{"X":"106.77","Y":"113.52"},{"X":"110.41","Y":"110.41"},{"X":"114.84","Y":"107.89"},{"X":"120.07","Y":"106.06"},{"X":"126.27","Y":"104.95"},{"X":"132.11","Y":"104.65"},{"X":"132.15","Y":"104.65"}],[{"X":"117.86","Y":"140.93"},{"X":"118.75","Y":"153.65"},{"X":"119.63","Y":"158.05"},{"X":"121.31","Y":"161.73"},{"X":"123.57","Y":"164.71"},{"X":"126.01","Y":"166.43"},{"X":"129.14","Y":"167.44"},{"X":"133.32","Y":"167.70"},{"X":"137.17","Y":"167.11"},{"X":"140.03","Y":"165.83"},{"X":"142.14","Y":"163.92"},{"X":"143.62","Y":"161.33"},{"X":"145.05","Y":"155.97"},{"X":"145.97","Y":"148.23"},{"X":"146.12","Y":"137.89"},{"X":"145.44","Y":"129.22"},{"X":"144.13","Y":"122.91"},{"X":"142.80","Y":"119.58"},{"X":"140.91","Y":"117.26"},{"X":"138.39","Y":"115.66"},{"X":"135.07","Y":"114.75"},{"X":"131.03","Y":"114.64"},{"X":"127.21","Y":"115.38"},{"X":"124.25","Y":"116.82"},{"X":"122.07","Y":"118.86"},{"X":"120.53","Y":"121.59"},{"X":"119.03","Y":"126.93"},{"X":"118.09","Y":"134.21"},{"X":"117.86","Y":"140.81"},{"X":"117.86","Y":"140.93"}],[{"X":"172.03","Y":"177.00"},{"X":"172.03","Y":"76.33"},{"X":"191.91","Y":"76.33"},{"X":"191.91","Y":"177.00"},{"X":"191.91","Y":"177.00"},{"X":"172.03","Y":"177.00"}],[{"X":"224.60","Y":"196.88"},{"X":"221.39","Y":"198.11"},{"X":"215.61","Y":"199.00"},{"X":"208.60","Y":"199.21"},{"X":"204.09","Y":"190.40"},{"X":"211.28","Y":"190.55"},{"X":"214.82","Y":"189.98"},{"X":"217.84","Y":"188.66"},{"X":"220.35","Y":"186.54"},{"X":"222.45","Y":"183.44"},{"X":"224.33","Y":"178.55"},{"X":"225.80","Y":"173.86"},{"X":"195.43","Y":"105.07"},{"X":"215.95","Y":"105.07"},{"X":"235.40","Y":"149.04"},{"X":"252.44","Y":"105.07"},{"X":"265.17","Y":"105.14"},{"X":"233.73","Y":"186.25"},{"X":"231.07","Y":"190.77"},{"X":"228.03","Y":"194.27"},{"X":"224.70","Y":"196.82"},{"X":"224.60","Y":"196.88"}],[{"X":"292.60","Y":"192.98"},{"X":"298.73","Y":"192.56"},{"X":"303.08","Y":"191.46"},{"X":"305.98","Y":"189.91"},{"X":"307.86","Y":"187.98"},{"X":"308.96","Y":"185.54"},{"X":"309.26","Y":"183.06"},{"X":"309.20","Y":"173.81"},{"X":"304.04","Y":"175.83"},{"X":"298.03","Y":"177.13"},{"X":"291.00","Y":"177.63"},{"X":"284.29","Y":"177.05"},{"X":"278.60","Y":"175.70"},{"X":"274.88","Y":"174.11"},{"X":"271.82","Y":"171.71"},{"X":"268.90","Y":"168.11"},{"X":"266.25","Y":"163.24"},{"X":"264.52","Y":"157.97"},{"X":"263.31","Y":"150.88"},{"X":"262.81","Y":"141.60"},{"X":"263.34","Y":"132.62"},{"X":"264.61","Y":"125.23"},{"X":"266.40","Y":"119.67"},{"X":"268.74","Y":"115.31"},{"X":"271.95","Y":"111.17"},{"X":"275.11","Y":"108.42"},{"X":"278.30","Y":"106.76"},{"X":"283.81","Y":"105.35"},{"X":"290.52","Y":"104.67"},{"X":"295.16","Y":"104.90"},{"X":"299.98","Y":"105.99"},{"X":"306.08","Y":"108.50"},{"X":"309.26","Y":"110.23"},{"X":"309.26","Y":"105.06"},{"X":"329.35","Y":"105.06"},{"X":"329.23","Y":"182.37"},{"X":"328.37","Y":"186.78"},{"X":"326.76","Y":"190.49"},{"X":"324.35","Y":"193.73"},{"X":"321.15","Y":"196.44"},{"X":"316.95","Y":"198.74"},{"X":"311.55","Y":"200.55"},{"X":"304.61","Y":"201.78"},{"X":"296.23","Y":"202.25"},{"X":"286.50","Y":"201.75"},{"X":"279.10","Y":"200.55"},{"X":"271.33","Y":"198.26"},{"X":"269.50","Y":"197.28"},{"X":"268.04","Y":"186.11"},{"X":"273.14","Y":"188.62"},{"X":"280.19","Y":"191.16"},{"X":"286.81","Y":"192.48"},{"X":"292.48","Y":"192.97"},{"X":"292.60","Y":"192.98"}],[{"X":"309.26","Y":"120.42"},{"X":"308.13","Y":"119.26"},{"X":"303.52","Y":"116.46"},{"X":"299.14","Y":"114.93"},{"X":"296.63","Y":"114.57"},{"X":"291.87","Y":"115.12"},{"X":"288.19","Y":"116.39"},{"X":"285.33","Y":"118.31"},{"X":"282.99","Y":"121.03"},{"X":"281.19","Y":"124.65"},{"X":"279.81","Y":"130.01"},{"X":"278.92","Y":"137.91"},{"X":"278.88","Y":"145.11"},{"X":"279.69","Y":"152.40"},{"X":"281.18","Y":"158.05"},{"X":"283.08","Y":"162.12"},{"X":"285.18","Y":"164.65"},{"X":"287.89","Y":"166.53"},{"X":"291.41","Y":"167.79"},{"X":"296.02","Y":"168.34"},{"X":"301.23","Y":"167.79"},{"X":"305.10","Y":"166.63"},{"X":"309.03","Y":"164.44"},{"X":"309.26","Y":"164.24"},{"X":"309.26","Y":"164.24"},{"X":"309.26","Y":"120.42"}],[{"X":"369.84","Y":"104.65"},{"X":"377.62","Y":"105.10"},{"X":"383.80","Y":"106.30"},{"X":"388.72","Y":"108.11"},{"X":"392.84","Y":"110.56"},{"X":"396.29","Y":"113.67"},{"X":"399.04","Y":"117.41"},{"X":"401.13","Y":"121.89"},{"X":"402.78","Y":"127.80"},{"X":"403.75","Y":"135.08"},{"X":"403.90","Y":"143.93"},{"X":"403.19","Y":"151.69"},{"X":"401.74","Y":"158.12"},{"X":"399.70","Y":"163.28"},{"X":"397.08","Y":"167.47"},{"X":"394.00","Y":"170.74"},{"X":"390.20","Y":"173.40"},{"X":"385.55","Y":"175.49"},{"X":"379.75","Y":"176.99"},{"X":"372.60","Y":"177.77"},{"X":"365.11","Y":"177.67"},{"X":"358.43","Y":"176.71"},{"X":"352.98","Y":"175.07"},{"X":"348.43","Y":"172.78"},{"X":"344.63","Y":"169.86"},{"X":"341.60","Y":"166.34"},{"X":"339.16","Y":"162.04"},{"X":"337.26","Y":"156.68"},{"X":"335.98","Y":"150.05"},{"X":"335.46","Y":"141.97"},{"X":"335.86","Y":"134.19"},{"X":"337.04","Y":"127.55"},{"X":"338.90","Y":"122.01"},{"X":"341.37","Y":"117.38"},{"X":"344.45","Y":"113.52"},{"X":"348.10","Y":"110.41"},{"X":"352.52","Y":"107.89"},{"X":"357.76","Y":"106.06"},{"X":"363.95","Y":"104.95"},{"X":"369.79","Y":"104.65"},{"X":"369.84","Y":"104.65"}],[{"X":"355.55","Y":"140.93"},{"X":"356.44","Y":"153.65"},{"X":"357.32","Y":"158.05"},{"X":"358.99","Y":"161.73"},{"X":"361.25","Y":"164.71"},{"X":"363.70","Y":"166.43"},{"X":"366.83","Y":"167.44"},{"X":"371.01","Y":"167.70"},{"X":"374.86","Y":"167.11"},{"X":"377.72","Y":"165.83"},{"X":"379.82","Y":"163.92"},{"X":"381.31","Y":"161.33"},{"X":"382.74","Y":"155.97"},{"X":"383.65","Y":"148.23"},{"X":"383.81","Y":"137.89"},{"X":"383.12","Y":"129.22"},{"X":"381.82","Y":"122.91"},{"X":"380.48","Y":"119.58"},{"X":"378.60","Y":"117.26"},{"X":"376.07","Y":"115.66"},{"X":"372.76","Y":"114.75"},{"X":"368.72","Y":"114.64"},{"X":"364.90","Y":"115.38"},{"X":"361.94","Y":"116.82"},{"X":"359.76","Y":"118.86"},{"X":"358.22","Y":"121.60"},{"X":"356.71","Y":"126.93"},{"X":"355.77","Y":"134.21"},{"X":"355.55","Y":"140.81"},{"X":"355.55","Y":"140.93"}],[{"X":"471.82","Y":"121.68"},{"X":"471.82","Y":"177.00"},{"X":"456.69","Y":"177.00"},{"X":"456.58","Y":"122.81"},{"X":"455.77","Y":"120.25"},{"X":"454.20","Y":"118.25"},{"X":"451.80","Y":"116.74"},{"X":"448.06","Y":"115.65"},{"X":"442.84","Y":"115.25"},{"X":"438.30","Y":"116.22"},{"X":"435.19","Y":"117.70"},{"X":"432.97","Y":"119.70"},{"X":"430.36","Y":"123.35"},{"X":"429.69","Y":"124.95"},{"X":"429.69","Y":"177.00"},{"X":"409.38","Y":"177.00"},{"X":"409.38","Y":"105.07"},{"X":"429.69","Y":"105.07"},{"X":"429.70","Y":"110.91"},{"X":"434.67","Y":"107.84"},{"X":"440.16","Y":"105.83"},{"X":"445.62","Y":"104.85"},{"X":"452.21","Y":"104.70"},{"X":"457.71","Y":"105.40"},{"X":"461.98","Y":"106.78"},{"X":"465.33","Y":"108.76"},{"X":"468.28","Y":"111.73"},{"X":"470.35","Y":"115.03"},{"X":"471.52","Y":"118.58"},{"X":"471.82","Y":"121.56"},{"X":"471.82","Y":"121.68"}]]'; | |
| var text_clipping = '[[{"X":"115.54","Y":"216.41"},{"X":"113.01","Y":"217.26"},{"X":"105.79","Y":"218.58"},{"X":"96.36","Y":"219.18"},{"X":"88.73","Y":"218.69"},{"X":"82.86","Y":"217.48"},{"X":"78.40","Y":"215.73"},{"X":"74.91","Y":"213.42"},{"X":"72.12","Y":"210.49"},{"X":"69.88","Y":"206.77"},{"X":"68.13","Y":"201.83"},{"X":"66.93","Y":"195.19"},{"X":"66.45","Y":"186.51"},{"X":"67.03","Y":"179.19"},{"X":"68.36","Y":"173.03"},{"X":"70.25","Y":"168.30"},{"X":"72.58","Y":"164.82"},{"X":"75.62","Y":"161.94"},{"X":"79.35","Y":"159.71"},{"X":"84.02","Y":"158.09"},{"X":"89.93","Y":"157.11"},{"X":"96.97","Y":"156.93"},{"X":"105.18","Y":"157.69"},{"X":"114.58","Y":"159.65"},{"X":"116.44","Y":"168.59"},{"X":"110.93","Y":"166.65"},{"X":"105.50","Y":"165.51"},{"X":"98.32","Y":"165.06"},{"X":"94.16","Y":"165.58"},{"X":"90.96","Y":"166.82"},{"X":"88.62","Y":"168.68"},{"X":"86.96","Y":"171.17"},{"X":"85.31","Y":"175.67"},{"X":"84.32","Y":"181.28"},{"X":"84.15","Y":"188.62"},{"X":"84.83","Y":"196.53"},{"X":"86.18","Y":"202.53"},{"X":"87.64","Y":"205.97"},{"X":"89.52","Y":"208.29"},{"X":"92.02","Y":"209.92"},{"X":"95.18","Y":"210.85"},{"X":"99.07","Y":"210.96"},{"X":"105.48","Y":"210.28"},{"X":"111.84","Y":"208.54"},{"X":"116.32","Y":"206.45"},{"X":"116.62","Y":"206.17"},{"X":"115.55","Y":"216.31"},{"X":"115.54","Y":"216.41"}],[{"X":"120.11","Y":"219.00"},{"X":"120.11","Y":"132.31"},{"X":"137.23","Y":"132.31"},{"X":"137.23","Y":"219.00"},{"X":"137.23","Y":"219.00"},{"X":"120.11","Y":"219.00"}],[{"X":"153.34","Y":"132.13"},{"X":"156.60","Y":"132.56"},{"X":"159.17","Y":"133.77"},{"X":"161.19","Y":"135.77"},{"X":"162.61","Y":"138.40"},{"X":"163.19","Y":"141.48"},{"X":"162.87","Y":"144.75"},{"X":"161.71","Y":"147.51"},{"X":"159.79","Y":"149.80"},{"X":"157.51","Y":"151.22"},{"X":"154.46","Y":"151.96"},{"X":"151.02","Y":"151.84"},{"X":"148.34","Y":"150.92"},{"X":"146.08","Y":"149.20"},{"X":"144.28","Y":"146.63"},{"X":"143.38","Y":"143.78"},{"X":"143.37","Y":"140.64"},{"X":"144.20","Y":"137.77"},{"X":"145.94","Y":"135.15"},{"X":"148.14","Y":"133.37"},{"X":"150.80","Y":"132.38"},{"X":"153.33","Y":"132.13"},{"X":"153.34","Y":"132.13"}],[{"X":"144.58","Y":"219.00"},{"X":"144.58","Y":"157.06"},{"X":"161.88","Y":"157.06"},{"X":"161.88","Y":"219.00"},{"X":"161.88","Y":"219.00"},{"X":"144.58","Y":"219.00"}],[{"X":"168.78","Y":"243.93"},{"X":"168.78","Y":"157.06"},{"X":"186.09","Y":"157.06"},{"X":"186.19","Y":"161.44"},{"X":"191.55","Y":"158.75"},{"X":"196.41","Y":"157.22"},{"X":"201.17","Y":"156.70"},{"X":"207.60","Y":"157.21"},{"X":"212.99","Y":"158.51"},{"X":"216.03","Y":"160.11"},{"X":"219.02","Y":"162.82"},{"X":"222.00","Y":"166.95"},{"X":"223.86","Y":"171.04"},{"X":"225.25","Y":"176.56"},{"X":"226.01","Y":"184.02"},{"X":"225.94","Y":"191.67"},{"X":"225.03","Y":"199.56"},{"X":"223.55","Y":"205.37"},{"X":"221.48","Y":"209.86"},{"X":"218.90","Y":"213.53"},{"X":"216.12","Y":"216.03"},{"X":"213.05","Y":"217.58"},{"X":"207.99","Y":"218.94"},{"X":"201.87","Y":"219.54"},{"X":"195.30","Y":"219.05"},{"X":"189.90","Y":"217.79"},{"X":"186.09","Y":"216.23"},{"X":"186.09","Y":"243.93"},{"X":"186.09","Y":"243.93"},{"X":"168.78","Y":"243.93"}],[{"X":"186.09","Y":"170.27"},{"X":"186.13","Y":"208.05"},{"X":"189.79","Y":"210.09"},{"X":"193.53","Y":"211.16"},{"X":"197.86","Y":"211.53"},{"X":"201.86","Y":"210.97"},{"X":"204.91","Y":"209.73"},{"X":"207.25","Y":"207.87"},{"X":"209.01","Y":"205.27"},{"X":"210.72","Y":"200.64"},{"X":"211.90","Y":"194.30"},{"X":"212.32","Y":"186.82"},{"X":"211.80","Y":"179.19"},{"X":"210.69","Y":"174.07"},{"X":"209.11","Y":"170.68"},{"X":"207.06","Y":"168.30"},{"X":"204.40","Y":"166.63"},{"X":"200.81","Y":"165.59"},{"X":"196.18","Y":"165.25"},{"X":"192.24","Y":"166.16"},{"X":"188.91","Y":"167.87"},{"X":"186.21","Y":"169.99"},{"X":"186.09","Y":"170.26"},{"X":"186.09","Y":"170.27"}],[{"X":"231.09","Y":"243.93"},{"X":"231.09","Y":"157.06"},{"X":"248.39","Y":"157.06"},{"X":"248.50","Y":"161.44"},{"X":"253.86","Y":"158.75"},{"X":"258.72","Y":"157.22"},{"X":"263.48","Y":"156.70"},{"X":"269.91","Y":"157.21"},{"X":"275.30","Y":"158.51"},{"X":"278.34","Y":"160.11"},{"X":"281.33","Y":"162.82"},{"X":"284.31","Y":"166.96"},{"X":"286.17","Y":"171.04"},{"X":"287.56","Y":"176.57"},{"X":"288.32","Y":"184.02"},{"X":"288.25","Y":"191.67"},{"X":"287.34","Y":"199.56"},{"X":"285.86","Y":"205.37"},{"X":"283.79","Y":"209.86"},{"X":"281.21","Y":"213.53"},{"X":"278.43","Y":"216.04"},{"X":"275.35","Y":"217.58"},{"X":"270.29","Y":"218.94"},{"X":"264.18","Y":"219.54"},{"X":"257.60","Y":"219.05"},{"X":"252.21","Y":"217.78"},{"X":"248.40","Y":"216.23"},{"X":"248.40","Y":"243.93"},{"X":"248.40","Y":"243.93"},{"X":"231.09","Y":"243.93"}],[{"X":"248.39","Y":"170.27"},{"X":"248.44","Y":"208.05"},{"X":"252.10","Y":"210.09"},{"X":"255.84","Y":"211.16"},{"X":"260.17","Y":"211.53"},{"X":"264.17","Y":"210.97"},{"X":"267.22","Y":"209.73"},{"X":"269.56","Y":"207.87"},{"X":"271.32","Y":"205.27"},{"X":"273.03","Y":"200.64"},{"X":"274.21","Y":"194.30"},{"X":"274.63","Y":"186.82"},{"X":"274.12","Y":"179.19"},{"X":"273.00","Y":"174.07"},{"X":"271.42","Y":"170.68"},{"X":"269.38","Y":"168.30"},{"X":"266.72","Y":"166.64"},{"X":"263.12","Y":"165.59"},{"X":"258.49","Y":"165.25"},{"X":"254.56","Y":"166.16"},{"X":"251.23","Y":"167.86"},{"X":"248.52","Y":"169.99"},{"X":"248.40","Y":"170.26"},{"X":"248.39","Y":"170.27"}],[{"X":"302.11","Y":"132.13"},{"X":"305.37","Y":"132.56"},{"X":"307.93","Y":"133.77"},{"X":"309.95","Y":"135.77"},{"X":"311.37","Y":"138.40"},{"X":"311.95","Y":"141.48"},{"X":"311.63","Y":"144.75"},{"X":"310.47","Y":"147.51"},{"X":"308.56","Y":"149.80"},{"X":"306.27","Y":"151.22"},{"X":"303.22","Y":"151.96"},{"X":"299.79","Y":"151.84"},{"X":"297.10","Y":"150.92"},{"X":"294.85","Y":"149.20"},{"X":"293.04","Y":"146.63"},{"X":"292.14","Y":"143.78"},{"X":"292.13","Y":"140.64"},{"X":"292.97","Y":"137.77"},{"X":"294.71","Y":"135.15"},{"X":"296.90","Y":"133.37"},{"X":"299.56","Y":"132.38"},{"X":"302.10","Y":"132.13"},{"X":"302.11","Y":"132.13"}],[{"X":"293.34","Y":"219.00"},{"X":"293.34","Y":"157.06"},{"X":"310.65","Y":"157.06"},{"X":"310.65","Y":"219.00"},{"X":"310.65","Y":"219.00"},{"X":"293.34","Y":"219.00"}],[{"X":"371.24","Y":"171.37"},{"X":"371.24","Y":"219.00"},{"X":"358.21","Y":"219.00"},{"X":"358.11","Y":"172.27"},{"X":"357.35","Y":"170.01"},{"X":"355.81","Y":"168.19"},{"X":"353.32","Y":"166.83"},{"X":"349.52","Y":"165.99"},{"X":"345.63","Y":"165.91"},{"X":"341.71","Y":"166.90"},{"X":"339.11","Y":"168.36"},{"X":"336.70","Y":"171.02"},{"X":"335.06","Y":"173.87"},{"X":"334.96","Y":"219.00"},{"X":"317.48","Y":"219.00"},{"X":"317.48","Y":"157.06"},{"X":"334.96","Y":"157.06"},{"X":"335.01","Y":"162.07"},{"X":"339.49","Y":"159.34"},{"X":"344.58","Y":"157.56"},{"X":"349.77","Y":"156.78"},{"X":"355.76","Y":"156.84"},{"X":"360.47","Y":"157.70"},{"X":"364.07","Y":"159.18"},{"X":"366.81","Y":"161.26"},{"X":"369.25","Y":"164.29"},{"X":"370.67","Y":"167.42"},{"X":"371.23","Y":"170.82"},{"X":"371.24","Y":"171.27"},{"X":"371.24","Y":"171.37"}],[{"X":"401.90","Y":"232.76"},{"X":"407.58","Y":"232.33"},{"X":"411.33","Y":"231.29"},{"X":"413.88","Y":"229.75"},{"X":"415.39","Y":"227.89"},{"X":"416.16","Y":"225.48"},{"X":"416.21","Y":"216.25"},{"X":"411.46","Y":"218.08"},{"X":"405.72","Y":"219.22"},{"X":"399.44","Y":"219.51"},{"X":"393.19","Y":"218.76"},{"X":"388.30","Y":"217.32"},{"X":"385.44","Y":"215.70"},{"X":"382.67","Y":"212.97"},{"X":"380.07","Y":"208.95"},{"X":"378.24","Y":"204.52"},{"X":"376.93","Y":"198.51"},{"X":"376.28","Y":"190.44"},{"X":"376.52","Y":"182.65"},{"X":"377.58","Y":"175.38"},{"X":"379.16","Y":"170.07"},{"X":"381.22","Y":"166.08"},{"X":"384.14","Y":"162.28"},{"X":"387.11","Y":"159.77"},{"X":"390.08","Y":"158.35"},{"X":"395.48","Y":"157.12"},{"X":"401.76","Y":"156.70"},{"X":"406.66","Y":"157.40"},{"X":"412.02","Y":"159.31"},{"X":"416.24","Y":"161.51"},{"X":"416.24","Y":"157.06"},{"X":"433.55","Y":"157.06"},{"X":"433.43","Y":"223.84"},{"X":"432.54","Y":"227.93"},{"X":"430.90","Y":"231.30"},{"X":"428.49","Y":"234.16"},{"X":"425.23","Y":"236.55"},{"X":"420.86","Y":"238.51"},{"X":"415.19","Y":"239.93"},{"X":"407.89","Y":"240.69"},{"X":"399.04","Y":"240.53"},{"X":"391.61","Y":"239.58"},{"X":"384.09","Y":"237.51"},{"X":"382.01","Y":"236.48"},{"X":"380.74","Y":"226.84"},{"X":"385.59","Y":"229.19"},{"X":"391.82","Y":"231.36"},{"X":"398.16","Y":"232.49"},{"X":"401.75","Y":"232.75"},{"X":"401.90","Y":"232.76"}],[{"X":"416.25","Y":"170.27"},{"X":"414.97","Y":"169.05"},{"X":"410.96","Y":"166.71"},{"X":"406.83","Y":"165.39"},{"X":"404.44","Y":"165.28"},{"X":"400.31","Y":"165.96"},{"X":"397.15","Y":"167.34"},{"X":"394.75","Y":"169.35"},{"X":"392.82","Y":"172.20"},{"X":"391.40","Y":"176.14"},{"X":"390.35","Y":"182.50"},{"X":"390.04","Y":"190.14"},{"X":"390.67","Y":"197.15"},{"X":"391.97","Y":"202.39"},{"X":"393.71","Y":"206.20"},{"X":"395.76","Y":"208.58"},{"X":"398.42","Y":"210.25"},{"X":"401.86","Y":"211.27"},{"X":"406.04","Y":"211.51"},{"X":"410.92","Y":"210.69"},{"X":"414.49","Y":"209.16"},{"X":"416.25","Y":"208.01"},{"X":"416.25","Y":"170.27"},{"X":"416.25","Y":"170.27"}]]'; | |
| return { | |
| "ss": deserialize_clipper_poly(text_polygon), | |
| "cc": deserialize_clipper_poly(text_clipping) | |
| }; | |
| } | |
| function get_rectangle_polys() { | |
| var rectangle1 = '[[{"X":155,"Y":61.67},{"X":155,"Y":212.7},{"X":288.98,"Y":212.7},{"X":288.98,"Y":61.67},{"X":173.01999999999998,"Y":61.67},{"X":155,"Y":61.67}],[{"X":274.37,"Y":190.77},{"X":171.24,"Y":190.77},{"X":171.24,"Y":81.16},{"X":274.37,"Y":81.16},{"X":274.37,"Y":81.16},{"X":274.37,"Y":190.77}]]'; | |
| var rectangle2 = '[[{"X":217.69,"Y":126.14},{"X":217.69,"Y":277.17},{"X":351.66999999999996,"Y":277.17},{"X":351.66999999999996,"Y":126.14},{"X":253.71,"Y":126.14},{"X":217.69,"Y":126.14}],[{"X":337.05,"Y":255.25},{"X":233.93,"Y":255.25},{"X":233.93,"Y":145.63},{"X":337.05,"Y":145.63},{"X":337.05,"Y":145.63},{"X":337.05,"Y":255.25}]]'; | |
| return { | |
| "ss": deserialize_clipper_poly(rectangle1), | |
| "cc": deserialize_clipper_poly(rectangle2) | |
| }; | |
| } | |
| function get_same_self_intersecting_polys() { | |
| var rectangle1 = '[[{"X":172,"Y":90},{"X":222,"Y":90},{"X":222,"Y":240},{"X":322,"Y":240},{"X":322,"Y":140},{"X":172,"Y":140}]]'; | |
| var rectangle2 = '[[{"X":172,"Y":90},{"X":222,"Y":90},{"X":222,"Y":240},{"X":322,"Y":240},{"X":322,"Y":140},{"X":172,"Y":140}]]'; | |
| return { | |
| "ss": deserialize_clipper_poly(rectangle1), | |
| "cc": deserialize_clipper_poly(rectangle2) | |
| }; | |
| } | |
| function get_star_and_rect() { | |
| var star = '[[{"X":147.47,"Y":312.74},{"X":247.07,"Y":33.510000000000005},{"X":337.66,"Y":312.04},{"X":86.36,"Y":122.7},{"X":404.07,"Y":123.57},{"X":148.11,"Y":312.27},{"X":147.47,"Y":312.74}]]'; | |
| var rectangle1 = '[[{"X":336.36,"Y":261.38},{"X":155.25,"Y":260.49},{"X":155.25,"Y":84.06},{"X":336.36,"Y":84.06},{"X":336.36,"Y":261.38},{"X":336.36,"Y":261.38}]]'; | |
| return { | |
| "ss": deserialize_clipper_poly(star), | |
| "cc": deserialize_clipper_poly(rectangle1) | |
| }; | |
| } | |
| function get_spiral_and_rects() { | |
| var spiral = '[[{"X":222.02,"Y":294.08},{"X":202.56,"Y":292.96},{"X":183.69,"Y":289.52},{"X":165.670002,"Y":283.86},{"X":148.5,"Y":275.99},{"X":132.45999999999998,"Y":266.02},{"X":117.8,"Y":254.11},{"X":104.75,"Y":240.45},{"X":93.72,"Y":225.49},{"X":84.85,"Y":209.5},{"X":78.12,"Y":192.5},{"X":73.73,"Y":175.06},{"X":71.6,"Y":156.89},{"X":71.83,"Y":139.51},{"X":74.33,"Y":123.52},{"X":79.1,"Y":108.05},{"X":86.16,"Y":93.15},{"X":95.270001,"Y":79.41},{"X":106.41,"Y":66.85},{"X":119.19,"Y":55.97},{"X":133.6,"Y":46.79},{"X":149.15,"Y":39.7},{"X":165.51999999999998,"Y":34.82},{"X":182.41,"Y":32.2300004},{"X":199.5,"Y":31.97},{"X":214.94,"Y":34.03},{"X":229.61,"Y":38.33},{"X":243.45,"Y":44.81},{"X":256.13,"Y":53.36},{"X":267.330004,"Y":63.75},{"X":276.63,"Y":75.5},{"X":283.960004,"Y":88.57},{"X":289.03,"Y":102.35},{"X":291.840003,"Y":116.77},{"X":292.340003,"Y":131.44},{"X":290.53999999999996,"Y":144.5},{"X":286.49,"Y":157.05},{"X":280.16999999999996,"Y":168.96},{"X":271.9,"Y":179.61},{"X":261.710004,"Y":188.89},{"X":250.1,"Y":196.31},{"X":237.4,"Y":201.68},{"X":223.99,"Y":204.85},{"X":209.93,"Y":205.76},{"X":197.13,"Y":204.33},{"X":185.11,"Y":200.63},{"X":173.99,"Y":194.74},{"X":164.19,"Y":186.85},{"X":156.22,"Y":177.51},{"X":150.18,"Y":166.82},{"X":146.41,"Y":155.44},{"X":144.89,"Y":143.56},{"X":145.63,"Y":132.5},{"X":148.59,"Y":122.14},{"X":153.8,"Y":112.36},{"X":160.93,"Y":103.88},{"X":169.89,"Y":96.86},{"X":180.09,"Y":91.81},{"X":191.1,"Y":88.95},{"X":202.47,"Y":88.37},{"X":212.8,"Y":90.1},{"X":222.22,"Y":93.95},{"X":230.55,"Y":99.81},{"X":237.37,"Y":107.36},{"X":242.28,"Y":116.28},{"X":244.92,"Y":125.8},{"X":245.3,"Y":135.670002},{"X":243.42,"Y":144.44},{"X":239.27,"Y":152.74},{"X":233.18,"Y":159.73},{"X":225.31,"Y":165.19},{"X":216.34,"Y":168.54},{"X":206.82,"Y":169.6},{"X":198.23,"Y":168.4},{"X":190.26,"Y":164.97},{"X":183.5,"Y":159.55},{"X":178.57999999999998,"Y":152.77},{"X":175.66,"Y":144.92},{"X":175,"Y":136.87},{"X":176.64,"Y":132.74},{"X":180.14,"Y":130},{"X":184.55,"Y":129.420002},{"X":188.66,"Y":131.13},{"X":191.34,"Y":134.670002},{"X":192.98,"Y":143.46},{"X":196.15,"Y":148.15},{"X":201.12,"Y":151.43},{"X":206.97,"Y":152.6},{"X":214.06,"Y":151.54},{"X":220.43,"Y":148.24},{"X":225.15,"Y":143.26},{"X":227.84,"Y":137.26},{"X":228.34,"Y":130.41},{"X":226.58,"Y":122.84},{"X":222.68,"Y":116.12},{"X":216.8,"Y":110.59},{"X":209.61,"Y":106.92},{"X":201.39,"Y":105.35},{"X":191.84,"Y":106.08},{"X":182.76,"Y":109.12},{"X":174.94,"Y":114.11},{"X":168.57999999999998,"Y":120.86},{"X":164.26,"Y":128.730002},{"X":162.09,"Y":137.44},{"X":162.230002,"Y":147.31},{"X":164.64,"Y":157.2},{"X":169.35,"Y":166.56},{"X":176.03,"Y":174.64},{"X":184.51999999999998,"Y":181.28},{"X":194.25,"Y":185.91},{"X":204.75,"Y":188.36},{"X":216.73,"Y":188.57},{"X":228.83,"Y":186.5},{"X":240.34,"Y":182.2},{"X":250.83,"Y":175.81},{"X":259.9,"Y":167.54},{"X":267.03,"Y":157.91},{"X":272.02,"Y":147.35},{"X":274.830004,"Y":136.01},{"X":275.340003,"Y":123.74},{"X":273.580004,"Y":110.68},{"X":269.580004,"Y":98.12},{"X":263.3,"Y":86.19},{"X":255.05,"Y":75.52},{"X":244.91,"Y":66.18},{"X":233.35,"Y":58.67},{"X":220.7,"Y":53.21},{"X":207.02,"Y":49.86},{"X":192.67,"Y":48.8},{"X":176.82999999999998,"Y":50},{"X":161.34,"Y":53.51},{"X":146.540002,"Y":59.27},{"X":132.76,"Y":67.17},{"X":120.53999999999999,"Y":76.85},{"X":109.92,"Y":88.25},{"X":101.32,"Y":100.89},{"X":94.75999999999999,"Y":114.7},{"X":90.52,"Y":129.07},{"X":88.57,"Y":143.93},{"X":88.92,"Y":160.71},{"X":91.63,"Y":177.58},{"X":96.66,"Y":193.91},{"X":103.9,"Y":209.38},{"X":113.37,"Y":223.97},{"X":124.72,"Y":237.14},{"X":137.93,"Y":248.89},{"X":152.59,"Y":258.78999999999996},{"X":168.44,"Y":266.65},{"X":185.18,"Y":272.34},{"X":202.83,"Y":275.85},{"X":221.07,"Y":277.07},{"X":241.43,"Y":276},{"X":261.24,"Y":272.63},{"X":280.52,"Y":266.98},{"X":299,"Y":259.12},{"X":316.43,"Y":249.14},{"X":332.56,"Y":237.17},{"X":346.94,"Y":223.57},{"X":359.59,"Y":208.35},{"X":370.15,"Y":191.97},{"X":378.51,"Y":174.7},{"X":384.69,"Y":156.54},{"X":388.59,"Y":137.75},{"X":390.64,"Y":115.98},{"X":393.21,"Y":112.35},{"X":397.26,"Y":110.5},{"X":401.4,"Y":110.83},{"X":405.08,"Y":113.33},{"X":407,"Y":117.34},{"X":406.15,"Y":134.39},{"X":402.81,"Y":154.2},{"X":397.18,"Y":173.48},{"X":389.22,"Y":192.25},{"X":379.12,"Y":209.96},{"X":366.88,"Y":226.64},{"X":352.8,"Y":241.8},{"X":336.88,"Y":255.47},{"X":319.51,"Y":267.26},{"X":300.93,"Y":277.02},{"X":281.38,"Y":284.66},{"X":260.81,"Y":290.13},{"X":239.75,"Y":293.26},{"X":222.07,"Y":294.08},{"X":222.02,"Y":294.08}]]'; | |
| var rectangle1 = '[[{"X":210.91,"Y":210.89},{"X":107.65,"Y":210.65},{"X":107.65,"Y":27.58},{"X":210.91,"Y":27.75},{"X":210.91,"Y":36.45},{"X":210.91,"Y":210.89}],[{"X":304.24,"Y":323.13},{"X":210.91,"Y":322.86},{"X":210.91,"Y":70.9},{"X":304.24,"Y":70.91},{"X":304.24,"Y":148.61},{"X":304.24,"Y":323.13}],[{"X":407.5,"Y":254.59},{"X":304.24,"Y":254.35},{"X":304.24,"Y":71.28},{"X":407.5,"Y":71.28},{"X":407.5,"Y":71.28},{"X":407.5,"Y":254.59}]]'; | |
| return { | |
| "ss": deserialize_clipper_poly(spiral), | |
| "cc": deserialize_clipper_poly(rectangle1) | |
| }; | |
| } | |
| function get_rounded_grid_and_star() { | |
| var rounded_grid = '[[{"X":346.2,"Y":66.31},{"X":332.04,"Y":53.81},{"X":316.43,"Y":43.18},{"X":299.87,"Y":34.73995},{"X":282.39,"Y":28.44},{"X":263.95,"Y":24.33},{"X":245.15,"Y":22.56002},{"X":226.27,"Y":23.13},{"X":207.6,"Y":26.04},{"X":189.74,"Y":31.18},{"X":172.6702,"Y":38.53},{"X":156.65,"Y":47.95},{"X":141.69,"Y":59.49},{"X":128.2902,"Y":72.8},{"X":116.65,"Y":87.67},{"X":107.11,"Y":103.62},{"X":99.64,"Y":120.65},{"X":94.37,"Y":138.47},{"X":91.33,"Y":157.11},{"X":90.63,"Y":175.99},{"X":92.2599,"Y":194.8},{"X":96.2401,"Y":213.27},{"X":102.42,"Y":230.8},{"X":110.74,"Y":247.42},{"X":121.26,"Y":263.1},{"X":133.6702,"Y":277.35},{"X":147.74,"Y":289.95},{"X":163.2698,"Y":300.69},{"X":179.77,"Y":309.25},{"X":197.21,"Y":315.68},{"X":215.62,"Y":319.92},{"X":234.41,"Y":321.81},{"X":253.29,"Y":321.38},{"X":271.98,"Y":318.6},{"X":289.87,"Y":313.58},{"X":307,"Y":306.36},{"X":323.09,"Y":297.05},{"X":338.12,"Y":285.62},{"X":351.62,"Y":272.4},{"X":363.37,"Y":257.61},{"X":373.02,"Y":241.73},{"X":380.61,"Y":224.76},{"X":386,"Y":206.97},{"X":389.18,"Y":188.35},{"X":390.01,"Y":169.48},{"X":388.5,"Y":150.65},{"X":384.65,"Y":132.16},{"X":378.61,"Y":114.59},{"X":370.4,"Y":97.91},{"X":359.99,"Y":82.15},{"X":347.69,"Y":67.82},{"X":346.21,"Y":66.32},{"X":346.2,"Y":66.31}],[{"X":375.03,"Y":172.21},{"X":373.83,"Y":190.15},{"X":370.54,"Y":206.57},{"X":362.32,"Y":203.86},{"X":365.47,"Y":187.07},{"X":366.32,"Y":169.7},{"X":364.79,"Y":152.38},{"X":360.96,"Y":135.73},{"X":354.9,"Y":119.76},{"X":346.72,"Y":104.76},{"X":336.39,"Y":90.77},{"X":324.26,"Y":78.32},{"X":320.42,"Y":74.77},{"X":325.57,"Y":67.97},{"X":338.69,"Y":80.27},{"X":350.08,"Y":94.19},{"X":359.52,"Y":109.5},{"X":366.74,"Y":125.65},{"X":371.75,"Y":142.61},{"X":374.49,"Y":160.09},{"X":375.03,"Y":172.07},{"X":375.03,"Y":172.21}],[{"X":129.2698,"Y":172.21},{"X":130.44,"Y":156.06},{"X":133.9598,"Y":140.26},{"X":139.75,"Y":125.15},{"X":147.69,"Y":111.05},{"X":157.6,"Y":98.25},{"X":169.15,"Y":87.09},{"X":174.15,"Y":94.2},{"X":163.0798,"Y":105.17},{"X":153.78,"Y":117.67},{"X":146.4802,"Y":131.44},{"X":141.37,"Y":146.17},{"X":138.57,"Y":161.5},{"X":138.13,"Y":177.08},{"X":140.05,"Y":192.54},{"X":140.87,"Y":196.58},{"X":132.61,"Y":199.24},{"X":129.82,"Y":183.3},{"X":129.2698,"Y":172.22},{"X":129.2698,"Y":172.21}],[{"X":327.6,"Y":172.21},{"X":326.42,"Y":186.54},{"X":325.15,"Y":191.85},{"X":317.08,"Y":189.04},{"X":318.84,"Y":175.67},{"X":318.28,"Y":162.2},{"X":315.43,"Y":149.02},{"X":310.34,"Y":136.54},{"X":303.19,"Y":125.11},{"X":293.99,"Y":114.85},{"X":292.48,"Y":113.21},{"X":297.61,"Y":106.36},{"X":307.64,"Y":116.67},{"X":315.86,"Y":128.4802},{"X":322.02,"Y":141.48},{"X":325.96,"Y":155.31},{"X":327.56,"Y":169.6},{"X":327.6,"Y":172},{"X":327.6,"Y":172.21}],[{"X":153.0198,"Y":172.21},{"X":154.19,"Y":157.87},{"X":157.7098,"Y":143.92},{"X":163.49,"Y":130.75},{"X":171.35,"Y":118.7},{"X":181.07,"Y":108.1},{"X":183.26,"Y":106.49},{"X":188.11,"Y":113.5},{"X":178.65,"Y":123.52},{"X":171.2098,"Y":134.7698},{"X":165.8,"Y":147.12},{"X":162.61,"Y":160.22},{"X":161.7098,"Y":173.67},{"X":163.13,"Y":187.08},{"X":163.36,"Y":189.28},{"X":155.22,"Y":191.74},{"X":153.18,"Y":177.51},{"X":153.0198,"Y":172.41},{"X":153.0198,"Y":172.21}],[{"X":303.92,"Y":172.21},{"X":302.73,"Y":184.43},{"X":294.39,"Y":181.76},{"X":295.2104,"Y":170.41},{"X":293.6597,"Y":159.14},{"X":289.8,"Y":148.43},{"X":283.81,"Y":138.76},{"X":278.44,"Y":132.51},{"X":283.57,"Y":125.63},{"X":291.73,"Y":134.82},{"X":297.98,"Y":145.39},{"X":302.07,"Y":156.97},{"X":303.84,"Y":169.12},{"X":303.92,"Y":172.12},{"X":303.92,"Y":172.21}],[{"X":200.38,"Y":172.21},{"X":201.53,"Y":162.7},{"X":204.93,"Y":153.75},{"X":210.37,"Y":145.87},{"X":211.36,"Y":145.15},{"X":216.39,"Y":152.18},{"X":211.93,"Y":159.27},{"X":209.51,"Y":167.29},{"X":209.16,"Y":174.43},{"X":200.68,"Y":177.07},{"X":200.38,"Y":172.28},{"X":200.38,"Y":172.21}],[{"X":279.9196,"Y":177.17},{"X":271.4,"Y":174.36},{"X":270.87,"Y":166.01},{"X":268.12,"Y":158.1},{"X":264.19,"Y":152.1},{"X":269.5,"Y":145.05},{"X":275.1597,"Y":152.78},{"X":278.81,"Y":161.64},{"X":280.22,"Y":171.11},{"X":279.93,"Y":177.09},{"X":279.9196,"Y":177.17}],[{"X":251.65,"Y":183.77},{"X":247.81,"Y":186.59},{"X":247.94,"Y":182.57},{"X":251.37,"Y":183.68},{"X":251.65,"Y":183.77}],[{"X":252.45,"Y":168.26},{"X":254.89,"Y":165.21},{"X":256.24,"Y":169.49},{"X":252.54,"Y":168.29},{"X":252.45,"Y":168.26}],[{"X":240.31,"Y":159.45},{"X":238.03,"Y":156.14},{"X":242.64,"Y":156.24},{"X":240.35,"Y":159.39},{"X":240.31,"Y":159.45}],[{"X":228.17,"Y":168.26},{"X":224.41,"Y":169.28},{"X":225.81,"Y":165.03},{"X":228.11,"Y":168.18},{"X":228.17,"Y":168.26}],[{"X":232.81,"Y":182.52},{"X":232.69,"Y":186.53},{"X":229.1,"Y":183.73},{"X":232.52,"Y":182.62},{"X":232.81,"Y":182.52}],[{"X":247.81,"Y":202.54},{"X":255.59,"Y":199.43},{"X":262.26,"Y":194.37},{"X":266.99,"Y":188.75},{"X":275.13,"Y":191.69},{"X":269.4604,"Y":199.41},{"X":262.13,"Y":205.57},{"X":253.54,"Y":209.81},{"X":247.81,"Y":211.35},{"X":247.81,"Y":211.35},{"X":247.81,"Y":202.54}],[{"X":252.07,"Y":143.27},{"X":243.96,"Y":141.17},{"X":235.59,"Y":141.32},{"X":228.44,"Y":143.14},{"X":223.38,"Y":136.11},{"X":232.48,"Y":133.12},{"X":242.03,"Y":132.38},{"X":251.47,"Y":133.94},{"X":257.2,"Y":136.21},{"X":252.09,"Y":143.25},{"X":252.07,"Y":143.27}],[{"X":213.83,"Y":188.68},{"X":219.16,"Y":195.14},{"X":226,"Y":199.96},{"X":232.81,"Y":202.54},{"X":232.71,"Y":211.33},{"X":223.6,"Y":208.4},{"X":215.45,"Y":203.37},{"X":208.72,"Y":196.55},{"X":205.5,"Y":191.38},{"X":213.77,"Y":188.7},{"X":213.83,"Y":188.68}],[{"X":232.81,"Y":226.54},{"X":232.63,"Y":235.34},{"X":220.63,"Y":232.69},{"X":209.38,"Y":227.76},{"X":199.29,"Y":220.77},{"X":190.72,"Y":211.98},{"X":183.98,"Y":201.71},{"X":182.64,"Y":198.8},{"X":190.9,"Y":196.16},{"X":196.92,"Y":205.81},{"X":204.8,"Y":214.02},{"X":214.19,"Y":220.45},{"X":224.71,"Y":224.8},{"X":232.61,"Y":226.52},{"X":232.81,"Y":226.54}],[{"X":247.81,"Y":226.54},{"X":258.86,"Y":223.83},{"X":269.11,"Y":218.89},{"X":278.13,"Y":211.95},{"X":285.53,"Y":203.31},{"X":289.8,"Y":196.15},{"X":298.05,"Y":198.84},{"X":291.8403,"Y":209.44},{"X":283.7104,"Y":218.65},{"X":273.98,"Y":226.14},{"X":262.99,"Y":231.62},{"X":251.15,"Y":234.87},{"X":247.81,"Y":235.36},{"X":247.81,"Y":235.36},{"X":247.81,"Y":226.54}],[{"X":266.2,"Y":123.84},{"X":255.67,"Y":119.53},{"X":244.47,"Y":117.5},{"X":233.1,"Y":117.82},{"X":222.03,"Y":120.47},{"X":214.38,"Y":123.79},{"X":209.48,"Y":116.59},{"X":220.74,"Y":111.68},{"X":232.73,"Y":109.05},{"X":245.01,"Y":108.77},{"X":257.11,"Y":110.86},{"X":268.5804,"Y":115.25},{"X":271.2896,"Y":116.84},{"X":266.35,"Y":123.63},{"X":266.2,"Y":123.84}],[{"X":202.29,"Y":132.6702},{"X":194.93,"Y":141.35},{"X":189.51,"Y":151.35},{"X":186.28,"Y":162.26},{"X":185.4,"Y":173.61},{"X":186.15,"Y":181.89},{"X":177.9,"Y":184.53},{"X":176.7,"Y":172.31},{"X":177.86,"Y":160.08},{"X":181.36,"Y":148.31},{"X":187.07,"Y":137.44},{"X":194.76,"Y":127.86},{"X":197.29,"Y":125.8},{"X":202.24,"Y":132.6},{"X":202.29,"Y":132.6702}],[{"X":168.2,"Y":203.48},{"X":174.63,"Y":215.34},{"X":182.98,"Y":225.92},{"X":193.01,"Y":234.94},{"X":204.42,"Y":242.12},{"X":216.89,"Y":247.24},{"X":230.06,"Y":250.14},{"X":232.81,"Y":250.45},{"X":232.66,"Y":259.23},{"X":218.49,"Y":256.8},{"X":204.91,"Y":252.05},{"X":192.3,"Y":245.13},{"X":180.99,"Y":236.24},{"X":171.2902,"Y":225.63},{"X":163.44,"Y":213.57},{"X":160.07,"Y":206.12},{"X":168.06,"Y":203.53},{"X":168.2,"Y":203.48}],[{"X":247.81,"Y":250.45},{"X":261.07,"Y":248.02},{"X":273.72,"Y":243.35},{"X":285.37,"Y":236.57},{"X":295.7104,"Y":227.91},{"X":304.42,"Y":217.62},{"X":311.26,"Y":206},{"X":312.63,"Y":203.56},{"X":320.65,"Y":206.36},{"X":313.94,"Y":219.08},{"X":305.24,"Y":230.53},{"X":294.7896,"Y":240.41},{"X":282.86,"Y":248.46},{"X":269.78,"Y":254.44},{"X":255.89,"Y":258.1696},{"X":247.81,"Y":259.24},{"X":247.81,"Y":259.24},{"X":247.81,"Y":250.45}],[{"X":280.24,"Y":104.53},{"X":268.0903,"Y":98.68},{"X":255.12,"Y":95},{"X":241.71,"Y":93.61},{"X":228.26,"Y":94.52},{"X":215.16,"Y":97.73},{"X":202.81,"Y":103.15},{"X":200.26,"Y":104.37},{"X":195.31,"Y":97.37},{"X":208.19,"Y":90.97},{"X":221.96,"Y":86.79},{"X":236.22,"Y":84.94},{"X":250.6,"Y":85.45},{"X":264.69,"Y":88.31},{"X":278.12,"Y":93.47},{"X":285.3304,"Y":97.52},{"X":280.4,"Y":104.31},{"X":280.24,"Y":104.53}],[{"X":240.31,"Y":69.85},{"X":224.77,"Y":71.03},{"X":209.88,"Y":74.47},{"X":195.4,"Y":80.25},{"X":186.27,"Y":85.13},{"X":181.44,"Y":78.15},{"X":195.74,"Y":70.57},{"X":211,"Y":65.16},{"X":226.88,"Y":62.04},{"X":243.04,"Y":61.27},{"X":259.15,"Y":62.84},{"X":274.86,"Y":66.7401},{"X":289.8304,"Y":72.9},{"X":299.23,"Y":78.4},{"X":294.06,"Y":85.16},{"X":280.23,"Y":77.97},{"X":265.47,"Y":72.98},{"X":250.12,"Y":70.31},{"X":240.53,"Y":69.85},{"X":240.31,"Y":69.85}],[{"X":145.5798,"Y":210.82},{"X":152.56,"Y":224.76},{"X":161.57,"Y":237.47},{"X":172.4,"Y":248.69},{"X":184.79,"Y":258.14},{"X":198.47,"Y":265.6},{"X":213.13,"Y":270.89},{"X":228.42,"Y":273.88},{"X":232.81,"Y":274.29},{"X":232.64,"Y":282.91},{"X":216.62,"Y":280.63},{"X":201.1,"Y":276.03},{"X":186.42,"Y":269.2},{"X":172.89,"Y":260.32},{"X":160.7902,"Y":249.58},{"X":150.36,"Y":237.2},{"X":141.8298,"Y":223.44},{"X":137.4202,"Y":213.47},{"X":145.41,"Y":210.88},{"X":145.5798,"Y":210.82}],[{"X":247.81,"Y":274.29},{"X":263.22,"Y":271.97},{"X":277.81,"Y":267.44},{"X":291.82,"Y":260.61},{"X":304.63,"Y":251.73},{"X":315.96,"Y":241.02},{"X":325.54,"Y":228.73},{"X":333.15,"Y":215.13},{"X":335.12,"Y":210.85},{"X":343.34,"Y":213.56},{"X":336.21,"Y":228.09},{"X":327.03,"Y":241.42},{"X":316.03,"Y":253.29},{"X":303.43,"Y":263.45},{"X":289.49,"Y":271.68},{"X":274.5,"Y":277.79},{"X":258.78,"Y":281.63},{"X":247.81,"Y":282.92},{"X":247.81,"Y":282.92},{"X":247.81,"Y":274.29}],[{"X":339.67,"Y":196.56},{"X":342.21,"Y":181.18},{"X":342.39,"Y":165.6},{"X":340.21,"Y":150.16},{"X":335.7,"Y":135.25},{"X":328.96,"Y":121.19},{"X":320.17,"Y":108.32},{"X":309.54,"Y":96.93},{"X":306.51,"Y":93.91},{"X":311.67,"Y":87.25},{"X":323.27,"Y":98.54},{"X":333.13,"Y":111.37},{"X":341.03,"Y":125.5},{"X":346.77,"Y":140.63},{"X":350.23,"Y":156.45},{"X":351.35,"Y":172.59},{"X":350.12,"Y":188.73},{"X":347.95,"Y":199.24},{"X":339.68,"Y":196.56},{"X":339.67,"Y":196.56}],[{"X":313.32,"Y":59.03},{"X":307.96,"Y":65.9601},{"X":292.9604,"Y":57.76},{"X":277,"Y":51.68},{"X":260.35,"Y":47.83},{"X":243.33,"Y":46.27},{"X":225.96,"Y":47.04},{"X":209.16,"Y":50.13},{"X":192.93,"Y":55.47},{"X":177.58,"Y":62.97},{"X":172.2698,"Y":65.87},{"X":167.5,"Y":58.9},{"X":182.96,"Y":50.31},{"X":199.42,"Y":43.83},{"X":216.59,"Y":39.58},{"X":234.17,"Y":37.62005},{"X":252.15,"Y":38},{"X":269.63,"Y":40.7},{"X":286.6,"Y":45.68},{"X":302.76,"Y":52.86},{"X":313.12,"Y":58.91},{"X":313.32,"Y":59.03}],[{"X":155.18,"Y":67.87},{"X":160.1,"Y":75.1},{"X":147.49,"Y":87.07},{"X":136.63,"Y":100.65},{"X":127.73,"Y":115.58},{"X":121.08,"Y":131.32},{"X":116.64,"Y":147.82},{"X":114.48,"Y":164.77},{"X":114.66,"Y":182.16},{"X":117.15,"Y":199.06},{"X":118.19,"Y":203.94},{"X":110.01,"Y":206.46},{"X":106.64,"Y":189.1},{"X":105.59,"Y":171.14},{"X":106.92,"Y":153.21},{"X":110.56,"Y":135.9},{"X":116.45,"Y":119.22},{"X":124.48,"Y":103.46},{"X":134.7,"Y":88.66},{"X":146.78,"Y":75.33},{"X":155.07,"Y":67.9601},{"X":155.18,"Y":67.87}],[{"X":114.68,"Y":220.85},{"X":123.06,"Y":218.44},{"X":130.4202,"Y":233.86},{"X":139.99,"Y":248.38},{"X":151.45,"Y":261.4604},{"X":164.59,"Y":272.84},{"X":179.16,"Y":282.33},{"X":194.62,"Y":289.61},{"X":210.92,"Y":294.71},{"X":227.77,"Y":297.55},{"X":232.81,"Y":297.95},{"X":232.52,"Y":306.7},{"X":214.97,"Y":304.53},{"X":197.86,"Y":300.07},{"X":181.48,"Y":293.39},{"X":166.12,"Y":284.61},{"X":151.82,"Y":273.7},{"X":139.0798,"Y":261.01},{"X":128.14,"Y":246.73},{"X":119.31,"Y":231.41},{"X":114.72,"Y":220.97},{"X":114.68,"Y":220.85}],[{"X":247.81,"Y":306.71},{"X":248.04,"Y":297.93},{"X":264.99,"Y":295.74},{"X":281.48,"Y":291.27},{"X":297.2,"Y":284.59},{"X":311.88,"Y":275.84},{"X":325.47,"Y":264.99},{"X":337.44,"Y":252.39},{"X":347.59,"Y":238.27},{"X":355.57,"Y":223.16},{"X":357.93,"Y":218.25},{"X":365.83,"Y":221.11},{"X":358.32,"Y":237.13},{"X":348.6,"Y":252.26},{"X":336.95,"Y":265.97},{"X":323.6,"Y":278.02},{"X":308.77,"Y":288.19},{"X":292.99,"Y":296.19},{"X":276.3,"Y":302.04},{"X":258.98,"Y":305.63},{"X":247.94,"Y":306.71},{"X":247.81,"Y":306.71}]]'; | |
| var star = '[[{"X":240.31,"Y":12.26},{"X":292.45,"Y":117.59},{"X":408.23,"Y":134.69},{"X":324.43,"Y":216.63},{"X":344.16,"Y":332.1},{"X":240.06,"Y":277.63},{"X":136.4,"Y":332},{"X":155.99,"Y":216.19},{"X":72.33,"Y":134.4202},{"X":188.37,"Y":117.5},{"X":240.28,"Y":12.30999},{"X":240.31,"Y":12.26}]]'; | |
| return { | |
| "ss": deserialize_clipper_poly(rounded_grid), | |
| "cc": deserialize_clipper_poly(star) | |
| }; | |
| } | |
| function get_glyph_and_grid() { | |
| var glyph = '[[{"X":105.65,"Y":51.37},{"X":105.65,"Y":295.26},{"X":395.09,"Y":295.26},{"X":395.01,"Y":51.37},{"X":108.51,"Y":51.37},{"X":105.65,"Y":51.37}],[{"X":380.09,"Y":89.4799},{"X":349.33,"Y":89.4799},{"X":349.33,"Y":66.37},{"X":380.09,"Y":66.37},{"X":380.09,"Y":66.37},{"X":380.09,"Y":89.4799}],[{"X":257.87,"Y":127.7},{"X":257.87,"Y":104.48},{"X":288.63,"Y":104.48},{"X":288.44,"Y":127.7},{"X":283.0396,"Y":127.7},{"X":257.87,"Y":127.7}],[{"X":288.63,"Y":142.7},{"X":288.63,"Y":165.81},{"X":257.87,"Y":165.81},{"X":257.87,"Y":142.7},{"X":257.87,"Y":142.7},{"X":288.63,"Y":142.7}],[{"X":242.87,"Y":127.7},{"X":212.11,"Y":127.7},{"X":212.11,"Y":104.48},{"X":242.87,"Y":104.48},{"X":242.87,"Y":104.48},{"X":242.87,"Y":127.7}],[{"X":242.87,"Y":142.7},{"X":242.87,"Y":165.81},{"X":212.11,"Y":165.81},{"X":212.11,"Y":142.7},{"X":212.11,"Y":142.7},{"X":242.87,"Y":142.7}],[{"X":197.11,"Y":165.81},{"X":166.41,"Y":165.81},{"X":166.41,"Y":142.7},{"X":197.11,"Y":142.7},{"X":197.11,"Y":142.7},{"X":197.11,"Y":165.81}],[{"X":197.11,"Y":180.81},{"X":197.11,"Y":203.92},{"X":166.41,"Y":203.92},{"X":166.41,"Y":180.81},{"X":166.41,"Y":180.81},{"X":197.11,"Y":180.81}],[{"X":212.11,"Y":180.81},{"X":242.87,"Y":180.81},{"X":242.87,"Y":203.92},{"X":212.11,"Y":203.92},{"X":212.11,"Y":203.92},{"X":212.11,"Y":180.81}],[{"X":242.87,"Y":218.92},{"X":242.87,"Y":242.14},{"X":212.11,"Y":242.14},{"X":212.11,"Y":218.92},{"X":212.11,"Y":218.92},{"X":242.87,"Y":218.92}],[{"X":257.87,"Y":218.92},{"X":288.63,"Y":218.92},{"X":288.63,"Y":242.14},{"X":257.87,"Y":242.14},{"X":257.87,"Y":242.14},{"X":257.87,"Y":218.92}],[{"X":257.87,"Y":203.92},{"X":257.87,"Y":180.81},{"X":288.63,"Y":180.81},{"X":288.63,"Y":203.92},{"X":288.63,"Y":203.92},{"X":257.87,"Y":203.92}],[{"X":303.63,"Y":180.81},{"X":334.33004,"Y":180.81},{"X":334.33004,"Y":203.92},{"X":303.63,"Y":203.92},{"X":303.63,"Y":203.92},{"X":303.63,"Y":180.81}],[{"X":303.63,"Y":165.81},{"X":303.63,"Y":142.7},{"X":334.33004,"Y":142.7},{"X":334.33004,"Y":165.81},{"X":334.33004,"Y":165.81},{"X":303.63,"Y":165.81}],[{"X":303.63,"Y":127.7},{"X":303.63,"Y":104.48},{"X":334.33004,"Y":104.48},{"X":334.07,"Y":127.7},{"X":328.6696,"Y":127.7},{"X":303.63,"Y":127.7}],[{"X":303.63,"Y":89.4799},{"X":303.63,"Y":66.37},{"X":334.33004,"Y":66.37},{"X":334.15,"Y":89.4799},{"X":316.15,"Y":89.4799},{"X":303.63,"Y":89.4799}],[{"X":288.63,"Y":89.4799},{"X":257.87,"Y":89.4799},{"X":257.87,"Y":66.37},{"X":288.63,"Y":66.37},{"X":288.63,"Y":89.4799},{"X":288.63,"Y":89.4799}],[{"X":242.87,"Y":89.4799},{"X":212.11,"Y":89.4799},{"X":212.11,"Y":66.37},{"X":242.87,"Y":66.37},{"X":242.87,"Y":66.37},{"X":242.87,"Y":89.4799}],[{"X":197.11,"Y":89.4799},{"X":166.41,"Y":89.4799},{"X":166.41,"Y":66.37},{"X":197.11,"Y":66.37},{"X":197.11,"Y":66.37},{"X":197.11,"Y":89.4799}],[{"X":197.11,"Y":104.48},{"X":196.93,"Y":127.7},{"X":166.41,"Y":127.7},{"X":166.41,"Y":104.48},{"X":166.41,"Y":104.48},{"X":197.11,"Y":104.48}],[{"X":151.41,"Y":127.7},{"X":120.65,"Y":127.7},{"X":120.65,"Y":104.48},{"X":151.41,"Y":104.48},{"X":151.41,"Y":104.48},{"X":151.41,"Y":127.7}],[{"X":151.41,"Y":142.7},{"X":151.41,"Y":165.81},{"X":120.65,"Y":165.81},{"X":120.65,"Y":142.7},{"X":120.65,"Y":142.7},{"X":151.41,"Y":142.7}],[{"X":151.41,"Y":180.81},{"X":151.41,"Y":203.92},{"X":120.65,"Y":203.92},{"X":120.65,"Y":180.81},{"X":120.65,"Y":180.81},{"X":151.41,"Y":180.81}],[{"X":151.41,"Y":218.92},{"X":151.41,"Y":242.14},{"X":120.65,"Y":242.14},{"X":120.65,"Y":218.92},{"X":120.65,"Y":218.92},{"X":151.41,"Y":218.92}],[{"X":166.41,"Y":218.92},{"X":197.11,"Y":218.92},{"X":197.11,"Y":242.14},{"X":166.41,"Y":242.14},{"X":166.41,"Y":242.14},{"X":166.41,"Y":218.92}],[{"X":197.11,"Y":257.14},{"X":197.11,"Y":280.26},{"X":166.41,"Y":280.26},{"X":166.41,"Y":257.14},{"X":166.41,"Y":257.14},{"X":197.11,"Y":257.14}],[{"X":212.11,"Y":257.14},{"X":242.87,"Y":257.14},{"X":242.87,"Y":280.26},{"X":212.11,"Y":280.26},{"X":212.11,"Y":280.26},{"X":212.11,"Y":257.14}],[{"X":257.87,"Y":257.14},{"X":288.63,"Y":257.14},{"X":288.63,"Y":280.26},{"X":257.87,"Y":280.26},{"X":257.87,"Y":280.26},{"X":257.87,"Y":257.14}],[{"X":303.63,"Y":257.14},{"X":334.33004,"Y":257.14},{"X":334.33004,"Y":280.26},{"X":303.63,"Y":280.26},{"X":303.63,"Y":280.26},{"X":303.63,"Y":257.14}],[{"X":303.63,"Y":242.14},{"X":303.63,"Y":218.92},{"X":334.33004,"Y":218.92},{"X":334.33004,"Y":242.14},{"X":334.33004,"Y":242.14},{"X":303.63,"Y":242.14}],[{"X":349.33,"Y":218.92},{"X":380.09,"Y":218.92},{"X":380.09,"Y":242.14},{"X":349.33,"Y":242.14},{"X":349.33,"Y":242.14},{"X":349.33,"Y":218.92}],[{"X":349.33,"Y":203.92},{"X":349.33,"Y":180.81},{"X":380.09,"Y":180.81},{"X":380.09,"Y":203.92},{"X":380.09,"Y":203.92},{"X":349.33,"Y":203.92}],[{"X":349.33,"Y":165.81},{"X":349.33,"Y":142.7},{"X":380.09,"Y":142.7},{"X":380.09,"Y":165.81},{"X":380.09,"Y":165.81},{"X":349.33,"Y":165.81}],[{"X":349.33,"Y":127.7},{"X":349.33,"Y":104.48},{"X":380.09,"Y":104.48},{"X":379.9,"Y":127.7},{"X":374.5,"Y":127.7},{"X":349.33,"Y":127.7}],[{"X":151.41,"Y":66.37},{"X":151.12,"Y":89.4799},{"X":120.65,"Y":89.4799},{"X":120.65,"Y":66.37},{"X":120.65,"Y":66.37},{"X":151.41,"Y":66.37}],[{"X":120.65,"Y":257.14},{"X":151.41,"Y":257.14},{"X":151.41,"Y":280.26},{"X":120.65,"Y":280.26},{"X":120.65,"Y":280.26},{"X":120.65,"Y":257.14}],[{"X":349.33,"Y":280.26},{"X":349.33,"Y":257.14},{"X":380.09,"Y":257.14},{"X":380.09,"Y":280.26},{"X":380.09,"Y":280.26},{"X":349.33,"Y":280.26}]]'; | |
| var grid = '[[{"X":305.08004,"Y":241.97},{"X":306,"Y":251.51},{"X":308.18,"Y":256.4},{"X":311.72,"Y":259.09003},{"X":317.31,"Y":260.01},{"X":324.71004,"Y":259.01},{"X":332.45,"Y":255.86},{"X":335.57,"Y":257.5396},{"X":337.6,"Y":260.44},{"X":336.94,"Y":262.33004},{"X":328.27,"Y":268.74},{"X":317.9,"Y":273.4196},{"X":307.94,"Y":275.49},{"X":296.26,"Y":275.23},{"X":286.64,"Y":272.99},{"X":279.7896,"Y":269.31},{"X":274.14,"Y":263.55},{"X":271.6597,"Y":260.21004},{"X":269.2,"Y":261.06},{"X":254.83,"Y":268.51},{"X":242.11,"Y":272.97},{"X":227.59,"Y":275.23},{"X":209.91,"Y":275.48},{"X":197.47,"Y":273.63},{"X":187.91,"Y":270.13},{"X":180.48002,"Y":265.09003},{"X":175.32,"Y":258.88},{"X":172.2098,"Y":251.44},{"X":171.1,"Y":242.23},{"X":172.24,"Y":233.63},{"X":175.49,"Y":226.24},{"X":181,"Y":219.54},{"X":189.42002,"Y":213.3},{"X":201.36,"Y":207.73},{"X":217.23,"Y":203.25},{"X":238.28,"Y":200.1},{"X":265.24,"Y":198.78},{"X":269.37,"Y":198.47},{"X":269.98,"Y":182.93},{"X":268.74,"Y":171.32},{"X":266.05,"Y":163.7098},{"X":261.58004,"Y":157.72},{"X":255.24,"Y":153.24},{"X":247.06,"Y":150.3298},{"X":235.44,"Y":149.13},{"X":224.71,"Y":150.05},{"X":215.91,"Y":153},{"X":210.23002,"Y":156.86},{"X":207.64,"Y":160.85},{"X":207.19,"Y":165.28},{"X":209.34,"Y":169.86},{"X":212.0198,"Y":174.15},{"X":212.14,"Y":177.99},{"X":209.8,"Y":181.78},{"X":204.22,"Y":185.79},{"X":197.62,"Y":187.68},{"X":188.65,"Y":187.43},{"X":182.41,"Y":185.39},{"X":178.45,"Y":181.77},{"X":176.2098,"Y":176.9},{"X":176.03,"Y":170.64},{"X":178.2,"Y":164.13},{"X":183.09,"Y":157.7},{"X":191.04002,"Y":151.36},{"X":202.01,"Y":145.8298},{"X":216.09,"Y":141.57},{"X":232.08,"Y":139.24},{"X":250.07,"Y":139.18},{"X":266.13,"Y":141.23002},{"X":279.05,"Y":145.06},{"X":289.1597,"Y":150.3},{"X":295.9196,"Y":156.19},{"X":300.73,"Y":163.41},{"X":303.85,"Y":172.47},{"X":305.07,"Y":183.78},{"X":305.07,"Y":241.97},{"X":305.08004,"Y":241.97}],[{"X":243.99,"Y":64.95},{"X":255.92,"Y":66.07},{"X":266.21004,"Y":69.28},{"X":274.98,"Y":74.44},{"X":280.65,"Y":80.19},{"X":284.03,"Y":86.85},{"X":285.26,"Y":94.52001},{"X":284.28,"Y":102.84},{"X":281.24,"Y":109.66},{"X":276.0396,"Y":115.43},{"X":267.89,"Y":120.46},{"X":257.68,"Y":123.93},{"X":245.79,"Y":125.33},{"X":232.93,"Y":124.53},{"X":222.21,"Y":121.74},{"X":213.14,"Y":117.11},{"X":207.36,"Y":111.92},{"X":203.7,"Y":105.75},{"X":201.94,"Y":98.18},{"X":202.34,"Y":90.12},{"X":204.86,"Y":83.4},{"X":210.01,"Y":76.81},{"X":217.49,"Y":71.33},{"X":227.17,"Y":67.31},{"X":238.35,"Y":65.2},{"X":243.75,"Y":64.95},{"X":243.99,"Y":64.95}],[{"X":269.99,"Y":212.88},{"X":269.48,"Y":208.76},{"X":266.59003,"Y":208.36},{"X":245.76,"Y":210.86},{"X":230.95,"Y":214.67},{"X":220.9,"Y":219.34},{"X":213.82,"Y":224.85},{"X":209.69,"Y":230.71},{"X":207.92002,"Y":237.03},{"X":208.4,"Y":244.49},{"X":210.86,"Y":250.57},{"X":215.2,"Y":255.08},{"X":221.69,"Y":258.13},{"X":230.57,"Y":259.43},{"X":242.52,"Y":258.58004},{"X":255.27,"Y":255.23},{"X":266.07,"Y":250.04},{"X":269.34003,"Y":247.02},{"X":269.99,"Y":244.81},{"X":269.99,"Y":212.88},{"X":269.99,"Y":212.88}],[{"X":243.63,"Y":73.34},{"X":235.93,"Y":74.4},{"X":230.07,"Y":77.36},{"X":225.65,"Y":82.21001},{"X":223.05,"Y":88.57},{"X":222.41,"Y":96.92},{"X":223.94,"Y":104.53},{"X":227.23,"Y":110.22},{"X":231.99,"Y":114.29},{"X":238.44,"Y":116.65},{"X":246.81,"Y":116.94},{"X":253.73,"Y":115.1},{"X":258.87,"Y":111.5},{"X":262.63,"Y":106.12},{"X":264.65,"Y":98.93},{"X":264.59003,"Y":90.25},{"X":262.47,"Y":83.41},{"X":258.6597,"Y":78.43},{"X":253.37,"Y":75.08},{"X":246.08,"Y":73.43},{"X":243.68,"Y":73.34},{"X":243.63,"Y":73.34}]]'; | |
| return { | |
| "ss": deserialize_clipper_poly(glyph), | |
| "cc": deserialize_clipper_poly(grid) | |
| }; | |
| } | |
| function get_custom_poly() { | |
| var selected_value = $("#custom_polygons_select").val(); | |
| var arr = $.totalStorage('custom_polygons'); | |
| if (selected_value !== "") { | |
| //selected_value = parseInt(selected_value,10); | |
| return { | |
| "ss": deserialize_clipper_poly(arr[selected_value].subj), | |
| "cc": deserialize_clipper_poly(arr[selected_value].clip) | |
| }; | |
| } | |
| //else return { "ss": '[[{"X":0,"Y":0},{"X":1,"Y":1}]]', "cc": '[[{"X":0,"Y":0},{"X":1,"Y":1}]]' }; | |
| else return { | |
| "ss": deserialize_clipper_poly(default_custom_subject_polygon), | |
| "cc": deserialize_clipper_poly(default_custom_clip_polygon) | |
| }; | |
| } | |
| function get_random_polys(which, polygon) { | |
| var point_count, polygon_count; | |
| if (which == "subj") { | |
| point_count = rnd_sett.subj_point_count; | |
| polygon_count = rnd_sett.subj_polygon_count; | |
| } | |
| else if (which == "clip") { | |
| point_count = rnd_sett.clip_point_count; | |
| polygon_count = rnd_sett.clip_polygon_count; | |
| } | |
| if (arguments.length === 1) polygon = parseInt($('input[type="radio"][name="polygons"]:checked').val(), 10); | |
| if (polygon != 4 && polygon != 5) return new ClipperLib.Polygons(); | |
| var svg = $("#p"); | |
| var margin = 10; | |
| rnd_sett.rand_min_x = 0 + margin; | |
| rnd_sett.rand_max_x = parseFloat(svg.attr("width"), 10) - margin; | |
| rnd_sett.rand_min_y = 0 + margin; | |
| rnd_sett.rand_max_y = parseFloat(svg.attr("height"), 10) - margin; | |
| var i, j, pp, np = new ClipperLib.Polygons(), | |
| prev_x = null, | |
| prev_y = null, | |
| horiz_or_vertic = null, | |
| prev_horiz_or_vertic = null; | |
| for (i = 0; i < polygon_count; i++) { | |
| np[i] = new ClipperLib.Polygon(); | |
| for (j = 0; j < point_count; j++) { | |
| pp = new ClipperLib.IntPoint(); | |
| if (polygon == 4) { | |
| horiz_or_vertic = rnd("int", 0, 1); // 0 = horiz, 1 = vertic | |
| if (prev_horiz_or_vertic === horiz_or_vertic) { | |
| if (horiz_or_vertic === 0) horiz_or_vertic = 1; | |
| else if (horiz_or_vertic === 1) horiz_or_vertic = 0; | |
| else horiz_or_vertic = 0; | |
| } | |
| if (horiz_or_vertic === 0) // horiz => y remains same | |
| { | |
| pp.X = round(rnd("float", rnd_sett.rand_min_x, rnd_sett.rand_max_x)); | |
| if (prev_y == null) pp.Y = round(rnd("float", rnd_sett.rand_min_y, rnd_sett.rand_max_y)); | |
| else pp.Y = prev_y; | |
| prev_x = pp.X; | |
| prev_y = pp.Y; | |
| prev_horiz_or_vertic = horiz_or_vertic; | |
| } | |
| else // vertic => x remains same | |
| { | |
| pp.Y = round(rnd("float", rnd_sett.rand_min_y, rnd_sett.rand_max_y)); | |
| if (prev_x == null) pp.X = round(rnd("float", rnd_sett.rand_min_x, rnd_sett.rand_max_x)); | |
| else pp.X = prev_x; | |
| prev_x = pp.X; | |
| prev_y = pp.Y; | |
| prev_horiz_or_vertic = horiz_or_vertic; | |
| } | |
| // last point fix | |
| if (j == point_count - 1 && point_count !== 1) { | |
| if (horiz_or_vertic === 0) // horiz => y remains same | |
| { | |
| pp.X = np[i][0].X; | |
| } | |
| else // vertic => x remains same | |
| { | |
| pp.Y = np[i][0].Y; | |
| } | |
| np[i].push(pp); | |
| } | |
| else np[i].push(pp); | |
| } | |
| else if (polygon == 5) { | |
| pp.X = round(rnd("float", rnd_sett.rand_min_x, rnd_sett.rand_max_x)); | |
| pp.Y = round(rnd("float", rnd_sett.rand_min_y, rnd_sett.rand_max_y)); | |
| np[i].push(pp); | |
| } | |
| } | |
| prev_x = null; | |
| prev_y = null; | |
| horiz_or_vertic = null; | |
| prev_horiz_or_vertic = null; | |
| } | |
| rnd_sett.scale = scale; | |
| return np; | |
| } | |
| function scale_again_random_poly(poly) { | |
| var i, j; | |
| for (i = 0; i < poly.length; i++) { | |
| for (j = 0; j < poly[i].length; j++) { | |
| poly[i][j].X = round(poly[i][j].X / rnd_sett.scale); | |
| poly[i][j].Y = round(poly[i][j].Y / rnd_sett.scale); | |
| } | |
| } | |
| return poly; | |
| } | |
| function rnd(intfloat, Amin, Amax) { | |
| var num; | |
| if (intfloat == "float") num = (Amin + (Amax - Amin) * Math.random()).toFixed(2); | |
| else if (intfloat == "int") num = Math.floor(Amin + (1 + Amax - Amin) * Math.random()); | |
| return num; | |
| } | |
| function get_rect_poly() { | |
| var rect_poly = '[[{"X":100,"Y":100},{"X":200,"Y":100},{"X":200,"Y":200},{"X":100,"Y":200},{"X":100,"Y":100}]]'; | |
| rect_poly = '[[{"X":100,"Y":100},{"X":100,"Y":100},{"X":200,"Y":100},{"X":200,"Y":200},{"X":100,"Y":200},{"X":100,"Y":100}]]'; | |
| return deserialize_clipper_poly(rect_poly); | |
| } | |
| function round(a) { | |
| if (global_do_not_round_and_scale) return a; | |
| else | |
| return Math.floor(a * scale); | |
| } | |
| window.lsk = 0; | |
| function deserialize_clipper_poly(polystr) { | |
| window.lsk++; | |
| var poly = JSON.parse(polystr); | |
| var i, j, pp, n = [ | |
| [] | |
| ], m, pm; | |
| var np = new ClipperLib.Polygons(); | |
| for (i = 0, m = poly.length; i < m; i++) { | |
| np[i] = new ClipperLib.Polygon(); | |
| for (j = 0, pm = poly[i].length; j < pm; j++) { | |
| pp = new ClipperLib.IntPoint(); | |
| if (!isNaN(Number(poly[i][j].X)) && !isNaN(Number(poly[i][j].Y))) { | |
| pp.X = round(Number(poly[i][j].X)); | |
| pp.Y = round(Number(poly[i][j].Y)); | |
| if (benchmark_running) { | |
| if (pp.X > bench.max_point_x) bench.max_point_x = pp.X; | |
| if (pp.Y > bench.max_point_y) bench.max_point_y = pp.Y; | |
| if (pp.X < bench.min_point_x) bench.min_point_x = pp.X; | |
| if (pp.Y < bench.min_point_y) bench.min_point_y = pp.Y; | |
| if (typeof (bench.points["L" + pp.X]) == "undefined") bench.points["L" + pp.X] = scale + ":" + pp.X + ":" + poly[i][j].X; | |
| if (typeof (bench.points["L" + pp.Y]) == "undefined") bench.points["L" + pp.Y] = scale + ":" + pp.Y + ":" + poly[i][j].Y; | |
| } | |
| np[i].push(pp); | |
| } | |
| else return n; | |
| } | |
| } | |
| return np; | |
| } | |
| (function () { | |
| "use strict"; | |
| SVG.create = function () { | |
| p = Raphael("svgcontainer", 500, 350); | |
| p.canvas.setAttribute("id", "p"); | |
| var str = "<filter id='innerbewel' x0='-50%' y0='-50%' width='200%' height='200%' >"; | |
| str += "<feGaussianBlur in='SourceAlpha' stdDeviation='2' result='blur'/>"; | |
| str += "<feOffset dy='3' dx='3'/>"; | |
| str += "<feComposite in2='SourceAlpha' operator='arithmetic'"; | |
| str += " k2='-1' k3='1' result='hlDiff'/>"; | |
| str += "<feFlood flood-color='white' flood-opacity='0.8'/>"; // changed to 1.0 for speed | |
| str += "<feComposite in2='hlDiff' operator='in'/>"; | |
| str += "<feComposite in2='SourceGraphic' operator='over' result='withGlow'/>"; | |
| str += "<feOffset in='blur' dy='-3' dx='-3'/>"; | |
| str += "<feComposite in2='SourceAlpha' operator='arithmetic'"; | |
| str += " k2='-1' k3='1' result='shadowDiff'/>"; | |
| str += "<feFlood flood-color='black' flood-opacity='0.5'/>"; // changed to 1.0 for speed | |
| str += "<feComposite in2='shadowDiff' operator='in'/>"; | |
| str += "<feComposite in2='withGlow' operator='over'/>"; | |
| str += "</filter>"; | |
| var markers = ''; | |
| // Markers to show start, mid and end points of path | |
| markers += '<marker id="StartMarker" viewBox="0 0 10 10" refX="0" refY="5" markerUnits="strokeWidth" markerWidth="10" markerHeight="10" stroke="red" stroke-width="1" fill="none" orient="auto">'; | |
| markers += '<path d="M0,5L10,5M5,0L10,5M5,10L10,5">'; | |
| markers += '</marker>'; | |
| markers += '<marker id="MidMarker" stroke-opacity="1.0" viewBox="-1 -1 12 12" refX="5" refY="5" markerWidth="4" markerHeight="4" stroke="red" stroke-width="1" fill="yellow" stroke-opacity="0.5" fill-opacity="0.5" orient="0" markerUnits="strokeWidth">'; | |
| markers += '<circle cx="5" cy="5" r="5"></circle>'; | |
| markers += '</marker>'; | |
| markers += '<marker id="EndMarker" viewBox="0 0 10 10" refX="5" refY="5" markerUnits="strokeWidth" markerWidth="8" markerHeight="8" stroke="blue" stroke-width="1" fill="none" orient="auto">'; | |
| markers += '<rect x="0" y="0" width="10" height="10"></rect>'; | |
| markers += '</marker>'; | |
| // This second invisible marker is needed to avoid noisy after images in Chrome: | |
| markers += '<marker id="StartMarker2" viewBox="0 0 10 10" refX="0" refY="5" markerUnits="strokeWidth" markerWidth="10" markerHeight="10" stroke="none" fill="none" orient="auto">'; | |
| markers += '<path d="M0,5L10,5M5,0L10,5M5,10L10,5">'; | |
| markers += '</marker>'; | |
| markers += '<marker id="MidMarker2" stroke-opacity="1.0" viewBox="-1 -1 12 12" refX="5" refY="5" markerWidth="4" markerHeight="4" stroke="none" fill="none" orient="0" markerUnits="strokeWidth">'; | |
| markers += '<circle cx="5" cy="5" r="5"></circle>'; | |
| markers += '</marker>'; | |
| markers += '<marker id="EndMarker2" viewBox="0 0 10 10" refX="5" refY="5" markerUnits="strokeWidth" markerWidth="8" markerHeight="8" stroke="none" fill="none" orient="auto">'; | |
| markers += '<rect x="0" y="0" width="10" height="10"></rect>'; | |
| markers += '</marker>'; | |
| $("body").append("<svg id='dummy' style='display:none'><defs>" + str + markers + "</defs></svg>"); | |
| $("#p defs").append($("#innerbewel")); | |
| $("#p defs").append($("#dummy marker")); | |
| $("#dummy").remove(); | |
| return p; | |
| }; | |
| SVG.addpaths = function (a, b, c, a1, b1) { | |
| if (a) { | |
| subj_subpolygons = a.length; | |
| a = this.polys2path(a, "1"); | |
| if (sub_poly_links_update) { | |
| if (typeof(subj_subpolygons) == "undefined") subj_subpolygons = 0; | |
| $("#subj_subpolygons").html(subj_subpolygons); | |
| $("#subj_points_in_subpolygons").html(this.sub_poly_links); | |
| $("#subj_points_total").html(this.total.toString()); | |
| subj_points_total = this.total; | |
| } | |
| } | |
| if (b) { | |
| clip_subpolygons = b.length; | |
| b = this.polys2path(b, "2"); | |
| if (sub_poly_links_update) { | |
| if (typeof(clip_subpolygons) == "undefined") clip_subpolygons = 0; | |
| $("#clip_subpolygons").html(clip_subpolygons); | |
| $("#clip_points_in_subpolygons").html(this.sub_poly_links); | |
| $("#clip_points_total").html(this.total.toString()); | |
| clip_points_total = this.total; | |
| } | |
| } | |
| if (typeof(c) != "undefined" && typeof(c.length) != "undefined") solution_subpolygons = c.length; | |
| else solution_subpolygons = 0; | |
| if (c) { | |
| c = this.polys2path(c, "3"); | |
| if (sub_poly_links_update) { | |
| $("#solution_subpolygons").html(solution_subpolygons); | |
| $("#solution_points_in_subpolygons").html(this.sub_poly_links); | |
| $("#solution_points_total").html(this.total.toString()); | |
| solution_points_total = this.total; | |
| } | |
| } | |
| if (sub_poly_links_update) { | |
| $("#points_total").html((subj_points_total + clip_points_total + solution_points_total).toString()); | |
| if (isNaN(subj_subpolygons)) subj_subpolygons = 0; | |
| else if (isNaN(clip_subpolygons)) clip_subpolygons = 0; | |
| else if (isNaN(solution_subpolygons)) solution_subpolygons = 0; | |
| $("#all_subpolygons").html(subj_subpolygons + clip_subpolygons + solution_subpolygons); | |
| } | |
| if (a) p1 = p.path(a); | |
| if (b) p2 = p.path(b); | |
| if (c) p3 = p.path(c); | |
| if (a) p1.node.setAttribute("id", "p1"); | |
| if (b) p2.node.setAttribute("id", "p2"); | |
| if (c) p3.node.setAttribute("id", "p3"); | |
| if (c && bevel) $("#p3").attr("filter", "url(#innerbewel)"); | |
| else if (c && !bevel) $("#p3").removeAttr("filter"); | |
| if (a) $("#p1").removeAttr("fill stroke"); | |
| if (b) $("#p2").removeAttr("fill stroke"); | |
| if (c) $("#p3").removeAttr("fill stroke"); | |
| var PolyFillType = { | |
| pftEvenOdd: 0, | |
| pftNonZero: 1, | |
| pftPositive: 2, | |
| pftNegative: 3 | |
| }; | |
| if (a) { | |
| if (a1 == PolyFillType.pftEvenOdd) $("#p1").attr("fill-rule", "evenodd"); | |
| else $("#p1").attr("fill-rule", "nonzero"); | |
| } | |
| if (b) { | |
| if (b1 == PolyFillType.pftEvenOdd) $("#p2").attr("fill-rule", "evenodd"); | |
| else $("#p2").attr("fill-rule", "nonzero"); | |
| } | |
| // p.setViewBox(bbox.x, bbox.y, bbox.width, bbox.height, true); | |
| // p.setSize(bbox.width, bbox.height); | |
| }; | |
| SVG.sub_poly_counts = null; | |
| SVG.sub_poly_links = null; | |
| SVG.total = null; | |
| SVG.polys2path = function (a, fr) { | |
| scaled_paths[fr] = []; | |
| var path = "", | |
| i, j, link, d; | |
| this.sub_poly_counts = []; | |
| this.sub_poly_links = ""; | |
| this.total = 0; | |
| if (!scale) scale = 1; | |
| var a_length = a.length; | |
| var a_i_length; | |
| var classi = "subpolylinks"; | |
| for (i = 0; i < a_length; i++) { | |
| this.sub_poly_counts.push(a[i].length); | |
| d = ""; | |
| a_i_length = a[i].length; | |
| for (j = 0; j < a_i_length; j++) { | |
| this.total++; | |
| if (j == 0) { | |
| d += "M"; | |
| } | |
| else { | |
| d += "L"; | |
| } | |
| d += (a[i][j].X / scale) + ", " + (a[i][j].Y / scale); | |
| } | |
| d += "Z"; | |
| path += d; | |
| if (sub_poly_links_update) { | |
| if (d.trim() == "Z") d = ""; | |
| scaled_paths[fr].push(d); | |
| if (benchmark_running) classi = "subpolylinks_disabled"; | |
| link = '<span class="' + classi + '" onclick="popup_path(' + i + ',' + fr + ')" onmouseover="show_path(' + i + ',' + fr + ')" onmouseout="hide_path()" >' + a_i_length + '</span>, '; | |
| this.sub_poly_links += link; | |
| } | |
| } | |
| if (sub_poly_links_update) this.sub_poly_links = this.sub_poly_links.substring(0, this.sub_poly_links.length - 2); | |
| if (path.trim() == "Z") path = ""; | |
| if (benchmark_running) $(".subpolylinks").removeClass("subpolylinks subpolylinks_disabled").addClass("subpolylinks_disabled"); | |
| else $(".subpolylinks_disabled").removeClass("subpolylinks subpolylinks_disabled").addClass("subpolylinks"); | |
| return path; | |
| } | |
| window.SVG = SVG; | |
| })() | |
| function toint(a) { | |
| a = parseInt(a, 10); | |
| return a; | |
| } | |
| // Englarges mypath ( = black partially transparent path) | |
| // when clicked | |
| function popup_path(i, fr) { | |
| if (benchmark_running) return false; | |
| if (typeof(i) == "undefined") d = scaled_paths[fr].join(" "); | |
| else d = scaled_paths[fr][i]; | |
| var points_string = normalize_clipper_poly(d, true); // quiet | |
| var area; | |
| SolutionPolygonClicked = false; | |
| SolutionPolygonString = ""; | |
| if (points_string !== false) { | |
| if (typeof(i) == "undefined") // which means that "Points" column is clicked | |
| { | |
| if (fr == 3) { | |
| $("#polygon_explorer_string_inp").val(format_output(points_string, "ExPolygons")); | |
| SolutionPolygonClicked = true; | |
| SolutionPolygonString = points_string; | |
| } | |
| else { | |
| $("#polygon_explorer_string_inp").val(format_output(points_string)); | |
| } | |
| var scaled_paths_length = scaled_paths.length; | |
| var points_str; | |
| area = 0; | |
| for (var j = 0; j < scaled_paths_length; j++) { | |
| points_str = normalize_clipper_poly(scaled_paths[fr][j], true); | |
| if (points_str !== false) { | |
| var polygon = JSON.parse(points_str.replace(/^\[\[/, "[").replace(/\]\]$/, "]")); | |
| area += ClipperLib.Clipper.Area(polygon); | |
| } | |
| } | |
| $("#area").html("Area: " + area); | |
| } | |
| else { | |
| $("#polygon_explorer_string_inp").val(format_output(points_string).replace(/^\[\[/, "[").replace(/\]\]$/, "]")); | |
| area = ClipperLib.Clipper.Area(JSON.parse(points_string.replace(/^\[\[/, "[").replace(/\]\]$/, "]"))); | |
| $("#area").html("Area: " + area); | |
| } | |
| } | |
| else { | |
| $("#polygon_explorer_string_inp").val("Some error occurred when parsing polygon points!"); | |
| } | |
| $(mypath.node).removeAttr('fill stroke').attr('class', 'svg_mypath'); | |
| $(mypath.node).attr('fill-rule', $('#p' + fr).attr('fill-rule')); | |
| $(mypath.node).attr('vector-effect', 'non-scaling-stroke'); // This is not supported in IE 9 and IE10 pre! | |
| var bb = mypath.node.getBBox(); | |
| var svg_w = toint($('#p').attr('width')); | |
| var svg_h = toint($('#p').attr('height')); | |
| var x_scale = (svg_w - 20) / bb.width; | |
| var y_scale = (svg_h - 20) / bb.height; | |
| var scal = Math.min(x_scale, y_scale); | |
| var x_trans = -(bb.x + bb.width / 2) + svg_w / 2; | |
| var y_trans = -(bb.y + bb.height / 2) + svg_h / 2; | |
| $('#StartMarker')[0].setAttribute('markerWidth', 10 / scal * 2); | |
| $('#StartMarker')[0].setAttribute('markerHeight', 10 / scal * 2); | |
| $('#MidMarker')[0].setAttribute('markerWidth', 4 / scal * 2); | |
| $('#MidMarker')[0].setAttribute('markerHeight', 4 / scal * 2); | |
| $('#EndMarker')[0].setAttribute('markerWidth', 4 / scal * 2); | |
| $('#EndMarker')[0].setAttribute('markerHeight', 4 / scal * 2); | |
| mypath.animate({ | |
| 'transform': 't' + x_trans + ' ' + y_trans + 's' + scal + ' ' + scal | |
| }, 500, function () { | |
| $(mypath.node).attr({ | |
| 'marker-start': 'url(#StartMarker)', | |
| 'marker-mid': 'url(#MidMarker)', | |
| 'marker-end': 'url(#EndMarker)' | |
| }); | |
| }); | |
| } | |
| // Shows mypath ( = black partially transparent path) | |
| // when hovered | |
| function show_path(i, fr) { | |
| if (benchmark_running) return false; | |
| var d; | |
| if (typeof(i) == "undefined") | |
| d = scaled_paths[fr].join(" "); | |
| else | |
| d = scaled_paths[fr][i]; | |
| mypath = p.path(d); | |
| $(mypath.node).removeAttr('fill stroke').attr('class', 'svg_mypath'); | |
| $(mypath.node).attr('fill-rule', $('#p' + fr).attr('fill-rule')); | |
| $(mypath.node).attr('vector-effect', 'non-scaling-stroke'); | |
| } | |
| // Hides mypath ( = black partially transparent path) | |
| function hide_path() { | |
| if (mypath == null) return; | |
| $(mypath.node).attr({ | |
| 'marker-start': 'url(#StartMarker2)', | |
| 'marker-mid': 'url(#MidMarker2)', | |
| 'marker-end': 'url(#EndMarker2)' | |
| }); | |
| if ($(mypath.node).attr('transform')) mypath.animate({ | |
| 'transform': 's1 1' | |
| }, 500, function () { | |
| this.animate({ | |
| 'opacity': '0' | |
| }, 500, function () { | |
| this.remove(); | |
| }); | |
| }); | |
| else mypath.animate({ | |
| 'opacity': '0' | |
| }, 300, function () { | |
| this.remove(); | |
| }); | |
| } | |
| function set_default_custom_polygon() { | |
| var subj = default_custom_subject_polygon; | |
| var clip = default_custom_clip_polygon; | |
| var def_obj = { | |
| "subj": subj, | |
| "clip": clip | |
| }; | |
| var arr = $.totalStorage('custom_polygons'); | |
| if (typeof (arr) == "undefined" || arr === null || !isArray(arr) || arr.length == 0) arr = []; | |
| arr[0] = def_obj; | |
| $.totalStorage('custom_polygons', arr); | |
| } | |
| function update_custom_polygons_select() { | |
| var arr = $.totalStorage('custom_polygons'); | |
| var selected_value = $("#custom_polygons_select").val(); | |
| if (!selected_value && selected_value + "" !== "0") selected_value = 0; | |
| $("#custom_polygons_select option").remove(); | |
| var arr_length = 0; | |
| if (isArray(arr)) arr_length = arr.length; | |
| var selected_txt, i; | |
| if (arr_length > 0) | |
| for (i = 0; i < arr_length; i++) { | |
| selected_txt = ""; | |
| if (i == selected_value) selected_txt = "selected"; | |
| if (arr[i] !== null) $("#custom_polygons_select").append('<option ' + selected_txt + ' value="' + i + '">Poly ' + i + '</option>'); | |
| } | |
| else set_default_custom_polygon(); | |
| if ($("#custom_polygons_select option").length === 0) set_default_custom_polygon(); | |
| // If previously selected value is removed, select the next one | |
| if (arr_length > 0) | |
| if (arr[selected_value] === null) { | |
| for (i = parseInt(selected_value, 10); i < arr_length; i++) { | |
| if (arr[i] !== null) { | |
| $('#custom_polygons_select').val(i); | |
| break; | |
| } | |
| } | |
| } | |
| $('#custom_polygons_select').change(); | |
| } | |
| function show_alert(e, obj, txt) { | |
| var x = e.pageX - obj.offsetLeft + $(obj).width() / 2; | |
| var y = e.pageY - obj.offsetTop; | |
| $("#custom_poly_message_box_green") | |
| .stop() | |
| .css({ | |
| height: '0px', | |
| opacity: '0', | |
| left: x + 1, | |
| top: y - 1, | |
| width: '0px', | |
| display: 'block' | |
| }) | |
| .html("") | |
| .animate({ | |
| left: (x + 200), | |
| top: (y - 40), | |
| height: '40px', | |
| width: '200px', | |
| opacity: '0.9' | |
| }, 300, function () { | |
| $(this).html(txt).animate({ | |
| opacity: '0.9' | |
| }, 1200, function () { | |
| $(this).animate({ | |
| opacity: 0 | |
| }, 600, function () { | |
| $(this).css("display", "none"); | |
| }) | |
| }) | |
| }); | |
| } | |
| function update_fieldset_heights() { | |
| $("#misc_fieldset").css("min-height", "").css("height", ""); | |
| $("#polygon_explorer_fieldset").css("min-height", "").css("height", ""); | |
| $("#benchmark_fieldset_f").css("min-height", "").css("height", ""); | |
| var td_heights = []; | |
| td_heights.push($("#td0").innerHeight()); | |
| td_heights.push($("#td1").innerHeight()); | |
| td_heights.push($("#td2").innerHeight()); | |
| td_heights.push($("#td3").innerHeight()); | |
| var max_td_height = Array_max(td_heights); | |
| var max_index; | |
| var children_heights; | |
| var children_heights_arr = []; | |
| for (var i = 0; i < td_heights.length; i++) { | |
| children_heights = 0; | |
| if (td_heights != max_td_height || 1 == 1) { | |
| $("#td" + i).children().each(function () { | |
| if ($(this).css("display") != "none") { | |
| children_heights += $(this).outerHeight(true); | |
| //console.log(this.type + ":" + $(this).attr("id") + ":" + $(this).outerHeight(true) + ":" + $(this).outerWidth(true)); | |
| } | |
| else { | |
| //console.log("----- NOT COUNTED:"+this.type + ":" + $(this).attr("id") + ":" + $(this).outerHeight(true) + ":" + $(this).outerWidth(true)); | |
| } | |
| }); | |
| } | |
| children_heights_arr.push(children_heights); | |
| //console.log("-------------"); | |
| } | |
| var misc_fieldset_height = $("#misc_fieldset").height(); | |
| var polygon_explorer_fieldset_height = $("#polygon_explorer_fieldset").height(); | |
| var benchmark_fieldset_f_height = $("#benchmark_fieldset_f").height(); | |
| misc_fieldset_height += (max_td_height - children_heights_arr[0]); | |
| polygon_explorer_fieldset_height += (max_td_height - children_heights_arr[1]); | |
| benchmark_fieldset_f_height += (max_td_height - children_heights_arr[2]); | |
| $("#misc_fieldset").css("min-height", misc_fieldset_height + "px"); | |
| $("#polygon_explorer_fieldset").css("min-height", polygon_explorer_fieldset_height + "px"); | |
| $("#benchmark_fieldset_f").css("min-height", benchmark_fieldset_f_height + "px"); | |
| } | |
| function resize() { | |
| myresize(); | |
| } | |
| function myresize() { | |
| var freeheight = $(window).height(); | |
| var polygon_explorer_div_max_height = freeheight - 360; | |
| if (polygon_explorer_div_max_height < 170) polygon_explorer_div_max_height = 170; | |
| // This causes problems when resizing polygon explorer textarea, so have to comment: | |
| //$("#polygon_explorer_div").css("max-height", polygon_explorer_div_max_height +"px"); | |
| } | |
| // ADDITIONAL SVG WINDOW STARTS | |
| window.update_enlarged_SVG = false; | |
| window.update_enlarged_SVG_source = false; | |
| window.svg_source_place = "svg_source_container"; | |
| function svg_source_enlarge() { | |
| var source = $("#svg_source_textarea").val().replace(/ id=\"/g, ' id="_'); | |
| $("#enlarged_svg").html(source); | |
| var original_height = $("#_p").attr("height"); | |
| var original_width = $("#_p").attr("width"); | |
| // get bbox of all children of svg | |
| $("body").append('<div id="dummy" style="display:block;visibility:hidden"><svg><g id="g123"></g></svg></div>'); | |
| $("#g123").append($("#_p").children().clone()); | |
| $("#dummy").html($("#dummy").html()); | |
| var bb = $("#g123")[0].getBBox(); | |
| var g_width = bb.width + 10; | |
| var g_height = bb.height + 10; | |
| var g_x = bb.x - 5; | |
| var g_y = bb.y - 5; | |
| $("#dummy").remove(); | |
| /* | |
| $("body").append('<img id="width_img" width="100%" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7">'); | |
| var window_width = parseInt(window.getComputedStyle($("#width_img")[0],null).getPropertyValue("width")); | |
| $("#width_img").remove(); | |
| */ | |
| $("#_p").attr("viewBox", g_x + " " + g_y + " " + g_width + " " + g_height); | |
| $("#enlarged_svg").html($("#enlarged_svg").html()); | |
| $("#enlarged_svg").css("display", "block"); | |
| if (svg_source_place == "svg_source_container") // at the bottom | |
| { | |
| $("#_p").attr("width", window_width); | |
| $("#_p").attr("height", parseInt((window_width / original_width) * original_height)); | |
| $("#_p1,#_p2,#_p3").css("stroke-width", 0.8 * (original_width / window_width)); | |
| } | |
| else // at the right | |
| { | |
| $("#_p").attr("height", window_height); | |
| $("#_p").attr("width", parseInt((window_height / original_height) * original_width)); | |
| $("#_p1,#_p2,#_p3").css("stroke-width", 0.8 * (original_height / window_height)); | |
| } | |
| $("#svg_source_textarea").css("display", "none"); | |
| if (benchmark_running) var disabled = "disabled"; | |
| else disabled = ""; | |
| $("#svg_source_enlarge_button").html('<button ' + disabled + ' class="textarea_hide_buttons" onClick="show_svg_source_f()" title="Show SVG source">Show SVG source</button>'); | |
| update_enlarged_SVG_source = true; | |
| update_enlarged_SVG = true; | |
| } | |
| function show_svg_source_f() { | |
| $("#svg_source_textarea").css("display", "block"); | |
| show_svg_source_click("non_click"); | |
| $("#enlarged_svg").html(""); | |
| if (benchmark_running) var disabled = "disabled"; | |
| else disabled = ""; | |
| $("#svg_source_enlarge_button").html('<button ' + disabled + ' class="textarea_hide_buttons" onClick="svg_source_enlarge()" title="Show SVG">Show SVG</button>'); | |
| update_enlarged_SVG_source = true; | |
| update_enlarged_SVG = false; | |
| } | |
| window_height = $(document).height() * 0.9; | |
| window_width = $(document).width() * 0.9; | |
| function show_svg_source_click(non_click) { | |
| update_enlarged_SVG_source = true; | |
| if (!update_enlarged_SVG) update_enlarged_SVG = false; | |
| if (non_click !== "non_click") { | |
| if ($("#show_svg_source").html() == "Show SVG source B") { | |
| $("#show_svg_source").html("Show SVG source R"); | |
| $("#show_svg_source").attr("title", "Show current SVG source in textarea at the right side of the page"); | |
| svg_source_place = "svg_source_container"; | |
| $("#svg_source_container2").html(""); | |
| } | |
| else { | |
| $("#show_svg_source").html("Show SVG source B"); | |
| $("#show_svg_source").attr("title", "Show current SVG source in textarea at the bottom of the page"); | |
| svg_source_place = "svg_source_container2"; | |
| $("#svg_source_container").html(""); | |
| } | |
| var textarea_str = '<div id="svg_source_textarea_div">'; | |
| textarea_str += '<button class="textarea_hide_buttons" onClick="$(\'#svg_source_textarea_div\').remove();update_enlarged_SVG_source=false;update_enlarged_SVG=false" title="Hide SVG source">Hide</button>'; | |
| if (benchmark_running) var disabled = "disabled"; | |
| else disabled = ""; | |
| textarea_str += '<span id="svg_source_enlarge_button"><button ' + disabled + ' class="textarea_hide_buttons" onClick="svg_source_enlarge()" title="Show SVG">Show SVG</button></span><br>'; | |
| textarea_str += '<div style="display:none" id="enlarged_svg"></div>'; | |
| textarea_str += '<textarea id="svg_source_textarea"></textarea>'; | |
| textarea_str += '</div>'; | |
| $("#" + svg_source_place).append(textarea_str); | |
| } | |
| var svg_source = $("#svgcontainer").html().replace(/\>/g, ">\n"); | |
| $("#svg_source_textarea").val(svg_source); | |
| } | |
| // ADDITIONAL SVG WINDOW ENDS | |
| // BENCHMARKING STARTS | |
| function benchmark2(i) { | |
| var start_time = new Date().getTime(); | |
| var obj = bench_glob[i]; | |
| $("#clean").removeAttr('checked'); | |
| clean = false; | |
| $("#lighten").removeAttr('checked'); | |
| lighten = false; | |
| joinType = obj.joinType; | |
| $('input[type="radio"][name="joinType"][value="' + joinType + '"]').attr('checked', 'checked'); | |
| offsettable_poly = obj.offsettable_poly; | |
| $('input[type="radio"][name="offsettable_poly"][value="' + offsettable_poly + '"]').attr('checked', 'checked'); | |
| delta = obj.delta; | |
| $('#delta').val(delta); | |
| miterLimit = obj.miterLimit; | |
| $('#miterLimit').val(miterLimit); | |
| AutoFix = obj.AutoFix; | |
| if (AutoFix) $('#AutoFix').attr('checked', 'checked'); | |
| else $('#AutoFix').removeAttr('checked'); | |
| ExPolygons = obj.ExPolygons; | |
| if (ExPolygons) $('#ExPolygons').attr('checked', 'checked'); | |
| else $('#ExPolygons').removeAttr('checked'); | |
| Simplify = obj.Simplify; | |
| if (Simplify) $('#Simplify').attr('checked', 'checked'); | |
| else $('#Simplify').removeAttr('Simplify'); | |
| subject_fillType = obj.subject_fillType; | |
| $("input[name='subject_fillType'][value='" + subject_fillType + "']").attr("checked", "checked"); | |
| clip_fillType = obj.clip_fillType; | |
| $("input[name='clip_fillType'][value='" + clip_fillType + "']").attr("checked", "checked"); | |
| clipType = obj.clipType; | |
| $("input[name='clipType'][value='" + clipType + "']").attr("checked", "checked"); | |
| scale = obj.scale; | |
| $('#scale').val(scale); | |
| if (obj.polygon_id == 4 || obj.polygon_id == 5) rnd_sett = obj.rnd_sett; | |
| $('input[type="radio"][name="polygons"][value="' + obj.polygon_id + '"]').attr('checked', 'checked').trigger("change"); | |
| obj = null; | |
| window.last_completed_bench = i; | |
| var end_time = new Date().getTime(); | |
| var time = end_time - start_time; | |
| bench_glob[i].measured_time = time; | |
| bench_elapsed_time += time; | |
| bench_glob[i].elapsed_time = bench_elapsed_time; | |
| // update next timeouts | |
| for (var lsk = i + 1; lsk < bench_glob.length; lsk++) { | |
| clearTimeout(bench_glob[lsk].setTimeout); | |
| bench_glob[lsk].setTimeout = setTimeout("benchmark2(" + (lsk) + ")", | |
| bench_glob[lsk].elapsed_time + lsk * bench_glob[lsk].time); | |
| } | |
| var results = ''; | |
| var elapsed_time = end_time - bench.list[0].start; | |
| results += Math.floor((i + 1) / bench_glob.length * 100) + " % ("; | |
| results += (elapsed_time / 1000).toFixed(1) + " s) of "; | |
| results += "benchmark " + (repeat + 1) + " / " + repeat_times + ". "; | |
| results += "Remaining: "; | |
| results += Math.floor((((elapsed_time / (i + 1)) * (bench_glob.length - i + 1)) / 1000)) + " s."; | |
| $("#benchmark_multiple_status").html(results); | |
| $("#benchmark_multiple_status").css("display", "table-cell"); | |
| if (i == 0) { | |
| var multiple_runs_table = bench.print_multiple_runs(); | |
| if ($("#benchmark_multiple_table").length) $("#benchmark_multiple_table").remove(); | |
| $("#benchmark_multiple_table_cont").append(multiple_runs_table); | |
| } | |
| else if (i == bench_glob.length - 1) { | |
| if (!$("#benchmark_exports_textarea").length) { | |
| var textarea_str = '<div id="benchmark_exports_textarea_div">'; | |
| textarea_str += '<button class="textarea_hide_buttons" onClick="$(\'#benchmark_exports_textarea_div\').remove()" title="Hide Benchmark exports">Hide</button><br>'; | |
| textarea_str += '<textarea id="benchmark_exports_textarea"></textarea>'; | |
| textarea_str += '</div>'; | |
| $("#benchmark_exports_container").append(textarea_str); | |
| } | |
| if (benchmark_exports.indexOf("max_point_x") == -1) { | |
| benchmark_exports += "bench.max_point_x:" + bench.max_point_x + "\n"; | |
| benchmark_exports += "bench.max_point_y:" + bench.max_point_y + "\n"; | |
| benchmark_exports += "bench.min_point_x:" + bench.min_point_x + "\n"; | |
| benchmark_exports += "bench.min_point_y:" + bench.min_point_y + "\n"; | |
| } | |
| benchmark_exports += bench.totals + ";" + browserg.browser + ";" + browserg.version + "\n"; | |
| $("#benchmark_exports_textarea").val(benchmark_exports); | |
| bench.totals_arr_multiple.push(bench.totals_arr[0]); | |
| var multiple_runs_table = bench.print_multiple_runs(); | |
| if ($("#benchmark_multiple_table").length) $("#benchmark_multiple_table").remove(); | |
| $("#benchmark_multiple_table_cont").append(multiple_runs_table); | |
| repeat++; | |
| if (repeat < repeat_times) { | |
| benchmark_automatic_click = 1; | |
| $("#" + clicked_benchmark_button_id).trigger("click"); | |
| } | |
| else { | |
| bench_glob.length = 0; | |
| repeat = 0; | |
| benchmark_running = 0; | |
| ClipperLib.MaxSteps = ClipperLib_MaxSteps_original; | |
| $("#" + clicked_benchmark_button_id).html($("#" + clicked_benchmark_button_id).html().replace("Stop", "Run")); | |
| $("#" + clicked_benchmark_button_id).attr("title", $("#" + clicked_benchmark_button_id).attr("title").replace("Stop", "Execute")); | |
| $("button,input,select").removeAttr('disabled'); | |
| $('#sub_poly_links_update').trigger("change"); | |
| } | |
| } | |
| } | |
| (function (window) { | |
| var benchmark = function (varname) { | |
| if (typeof (varname) == "string") this.varname = varname; | |
| else this.varname = ""; | |
| this.list = []; | |
| this.cats = []; | |
| this.cats.arr = []; | |
| this.type = "benchmark"; | |
| this.includeSVG = true; | |
| this.totals = ""; | |
| this.totals_arr = []; | |
| this.totals_arr_multiple = []; | |
| this.max_point_x = Number.NEGATIVE_INFINITY; | |
| this.min_point_x = Number.POSITIVE_INFINITY; | |
| this.max_point_y = Number.NEGATIVE_INFINITY; | |
| this.min_point_y = Number.POSITIVE_INFINITY; | |
| this.points = []; | |
| }; | |
| // returns index | |
| // cat = category, which name belongs to | |
| // name = code region name or function, which is measured | |
| benchmark.prototype.start = function (cat, name) { | |
| if (cat == "") return; | |
| if (name == "") return; | |
| var b = {}; | |
| b.start = new Date().getTime(); | |
| b.name = name; | |
| b.cat = cat; | |
| this.list.push(b); | |
| return this.list.length - 1; | |
| } | |
| benchmark.prototype.end = function (index) { | |
| this.list[index].end = new Date().getTime(); | |
| this.list[index].time = this.list[index].end - this.list[index].start; | |
| var this_list_cat = this.list[index].cat; | |
| var this_list_cat_counts = this_list_cat + "_counts"; | |
| var this_list_cat_time_sum = this_list_cat + "_time_sum" | |
| if (typeof (this.cats[this_list_cat_counts]) == "undefined") this.cats.arr.push(this_list_cat); | |
| if (typeof (this.cats[this_list_cat_time_sum]) == "undefined") this.cats[this_list_cat_time_sum] = 0; | |
| if (typeof (this.cats[this_list_cat_counts]) == "undefined") this.cats[this_list_cat_counts] = 0; | |
| this.cats[this_list_cat_time_sum] += this.list[index].time; | |
| this.cats[this_list_cat_counts]++; | |
| if (typeof (bench_glob) != "undefined" && bench_glob.length > 0) { | |
| this.list[index].bench_glob_index = window.last_completed_bench; | |
| } | |
| } | |
| benchmark.prototype.clear = function () { | |
| this.list = []; | |
| this.cats = []; | |
| this.cats.arr = []; | |
| this.includeSVG = true; | |
| return true; | |
| } | |
| benchmark.prototype.print = function (all) { | |
| var tbl = '<style>'; | |
| tbl += '.bench {width:317px;border-collapse:collapse;white-space:nowrap;}'; | |
| tbl += '.bench td, .bench th{font-size:12px;text-align:left;border:1px solid #444444; padding:2px}'; | |
| tbl += '.bench th{background-color:#DDDDDD;}'; | |
| tbl += '.bench tfoot td{background-color:#DDDDDD;}'; | |
| tbl += '.bench_foot{font-weight:bold;}'; | |
| tbl += '</style>'; | |
| tbl += '<table class="bench"><thead><tr>'; | |
| tbl += '<th>Num</th>'; | |
| tbl += '<th>Name</th>'; | |
| tbl += '<th>Category</th>'; | |
| tbl += '<th>Time</th>'; | |
| tbl += '</tr></thead>'; | |
| tbl += '<tbody>'; | |
| var time, totaltime = 0, | |
| i, m; | |
| if (this.list && this.list.length) { | |
| m = this.list.length; | |
| // print all | |
| var start_index = 0; | |
| if (!all) { | |
| if (bench_glob.length) start_index = m - 3; | |
| else start_index = m - 16; | |
| if (start_index < 0) start_index = 0; | |
| } | |
| for (i = start_index; i < m; i++) { | |
| time = (this.list[i].time); | |
| tbl += '<tr><td>'; | |
| tbl += (i + 1); | |
| tbl += '</td><td>'; | |
| tbl += this.list[i].name; | |
| tbl += '</td><td>'; | |
| tbl += this.list[i].cat; | |
| tbl += '</td><td>'; | |
| tbl += time; | |
| tbl += '</td></tr>'; | |
| } | |
| } | |
| tbl += '</tbody>'; | |
| tbl += '</table>'; | |
| var tbl2 = ""; | |
| tbl2 += '<table style="margin-top:10px;margin-bottom:10px" class="bench"><thead><tr>'; | |
| tbl2 += '<th>Num</th>'; | |
| tbl2 += '<th>Category</th>'; | |
| tbl2 += '<th>Calls</th>'; | |
| tbl2 += '<th>Sum</th>'; | |
| tbl2 += '<th>Avg</th>'; | |
| tbl2 += '</tr></thead>'; | |
| tbl2 += '<tbody>'; | |
| m = this.cats.arr.length; | |
| var item; | |
| this.totals = ""; | |
| this.totals_arr = []; | |
| var totals_arr_item = []; | |
| var counts_sum = 0, | |
| cat_time_sum = 0, | |
| cat_time_avg = 0, | |
| this_list_i_cat, | |
| this_list_i_cat_time_sum, this_list_i_cat_counts; | |
| if (m > 0) for (i = 0; i < m; i++) { | |
| tbl2 += '<tr><td>'; | |
| item = (i + 1); | |
| tbl2 += item; | |
| tbl2 += '</td><td>'; | |
| this_list_i_cat = this.cats.arr[i]; | |
| item = this_list_i_cat; | |
| tbl2 += item; | |
| tbl2 += '</td><td>'; | |
| this_list_i_cat_counts = this.cats[this_list_i_cat + "_counts"]; | |
| item = this_list_i_cat_counts; | |
| tbl2 += item; | |
| counts_sum += this_list_i_cat_counts; | |
| tbl2 += '</td><td>'; | |
| this_list_i_cat_time_sum = this.cats[this_list_i_cat + "_time_sum"]; | |
| item = this_list_i_cat_time_sum; | |
| tbl2 += item; | |
| cat_time_sum += this_list_i_cat_time_sum; | |
| tbl2 += '</td><td>'; | |
| item = (this_list_i_cat_time_sum / this_list_i_cat_counts).toFixed(4); | |
| tbl2 += item; | |
| tbl2 += '</td></tr>'; | |
| } | |
| tbl2 += '</tbody>'; | |
| if (m > 0) { | |
| tbl2 += '<tfoot><tr><td class="bench_foot" colspan="2">Total</td>'; | |
| item = (counts_sum); | |
| totals_arr_item.push(item); | |
| this.totals += item + ";"; | |
| tbl2 += '<td>' + item + '</td>'; | |
| item = (cat_time_sum); | |
| totals_arr_item.push(item); | |
| this.totals += item + ";"; | |
| tbl2 += '<td>' + item + '</td>'; | |
| item = (cat_time_sum / counts_sum).toFixed(4); | |
| totals_arr_item.push(item); | |
| this.totals += item; | |
| tbl2 += '<td style="padding: 4px; background-color: hsl(125, 73%, 80%); border: 3px solid black; font-weight: bold; font-size: 16px;">' + item + '</td>'; | |
| tbl2 += '</tr>'; | |
| tbl2 += '</tfoot>'; | |
| } | |
| tbl2 += '</table>'; | |
| this.totals_arr.push(totals_arr_item); | |
| tbl += tbl2; | |
| if (benchmark_running) var disabled = "disabled"; | |
| else disabled = ""; | |
| tbl += '<button ' + disabled + ' onClick="try { ' + this.varname + '.clear();$(\'#benchmark_div\').html(' + this.varname + '.print()); return true; } catch (e) {return false}">Clear Benchmarks</button>'; | |
| tbl += '<button ' + disabled + ' onClick="try { $(\'#benchmark_div\').html(' + this.varname + '.print(1)); } catch (e) {return false}">Show all</button>'; | |
| return tbl; | |
| } | |
| benchmark.prototype.print_multiple_runs = function (str) { | |
| var tbl2 = ""; | |
| tbl2 += '<table id="benchmark_multiple_table" style="margin-top:10px;margin-bottom:10px" class="bench"><thead><tr>'; | |
| tbl2 += '<th>Num</th>'; | |
| tbl2 += '<th>Calls</th>'; | |
| tbl2 += '<th>Sum</th>'; | |
| tbl2 += '<th>Avg</th>'; | |
| tbl2 += '</tr></thead>'; | |
| tbl2 += '<tbody>'; | |
| var item, counts_sum = 0, | |
| time_sum = 0; | |
| var times_array = []; | |
| for (var i = 0, m = this.totals_arr_multiple.length; i < m; i++) { | |
| times_array.push(this.totals_arr_multiple[i][1]); | |
| } | |
| var max = Array_max(times_array); | |
| var min = Array_min(times_array); | |
| var range = max - min; | |
| var average = getAverageFromNumArr(times_array, 4); | |
| var stdev = getStandardDeviation(times_array, 4); | |
| var minus_range = min - average; | |
| var plus_range = max - average; | |
| for (var i = 0, m = this.totals_arr_multiple.length; i < m; i++) { | |
| tbl2 += '<tr><td>'; | |
| item = (i + 1); | |
| tbl2 += item; | |
| tbl2 += '</td><td>'; | |
| item = this.totals_arr_multiple[i][0]; | |
| tbl2 += item; | |
| tbl2 += '</td><td>'; | |
| item = this.totals_arr_multiple[i][1]; | |
| tbl2 += item; | |
| time_sum += item; | |
| tbl2 += '</td><td>'; | |
| item = this.totals_arr_multiple[i][2]; | |
| tbl2 += item; | |
| tbl2 += '</td></tr>'; | |
| } | |
| tbl2 += '<tr><td colspan="4" id="benchmark_multiple_status" style="display:none"></td></tr>'; | |
| if (!isNaN(average)) { | |
| tbl2 += '<tr><td colspan="4">'; | |
| tbl2 += '<b>Average:</b> ' + average + " ms<br>"; | |
| tbl2 += '<b>Min:</b> ' + min + " ms<br>"; | |
| tbl2 += '<b>Max:</b> ' + max + " ms<br>"; | |
| tbl2 += '<b>Range:</b> ' + range.toFixed(4) + " ms<br>"; | |
| tbl2 += '<b>Minus-Range:</b> ' + minus_range.toFixed(4) + " ms<br>"; | |
| tbl2 += '<b>Plus-Range:</b> ' + plus_range.toFixed(4) + " ms<br>"; | |
| tbl2 += '<b>Stdev:</b> ' + stdev + " ms<br>"; | |
| tbl2 += '<b>Range/Average %:</b> ' + (range / average * 100).toFixed(4) + "<br>"; | |
| tbl2 += '<b>Minus-Range/Average %:</b> ' + (minus_range / average * 100).toFixed(4) + "<br>"; | |
| tbl2 += '<b>Plus-Range/Average %:</b> ' + (plus_range / average * 100).toFixed(4) + "<br>"; | |
| tbl2 += '<b>Stdev/Average %:</b> ' + (stdev / average * 100).toFixed(4) + "<br>"; | |
| tbl2 += '</td></tr>'; | |
| } | |
| tbl2 += '</tbody>'; | |
| return tbl2; | |
| } | |
| window.benchmark = benchmark; | |
| })(window); | |
| // Programmer: Larry Battle | |
| // Date: Mar 06, 2011 | |
| // Purpose: Calculate standard deviation, variance, and average among an array of numbers. | |
| function isArray(obj) { | |
| return Object.prototype.toString.call(obj) === "[object Array]"; | |
| } | |
| ; | |
| function getNumWithSetDec(num, numOfDec) { | |
| var pow10s = Math.pow(10, numOfDec || 0); | |
| return (numOfDec) ? Math.round(pow10s * num) / pow10s : num; | |
| } | |
| ; | |
| function getAverageFromNumArr(numArr, numOfDec) { | |
| if (!isArray(numArr)) { | |
| return false; | |
| } | |
| var i = numArr.length, | |
| sum = 0; | |
| while (i--) { | |
| sum += numArr[i]; | |
| } | |
| return getNumWithSetDec((sum / numArr.length), numOfDec); | |
| } | |
| ; | |
| function getVariance(numArr, numOfDec) { | |
| if (!isArray(numArr)) { | |
| return false; | |
| } | |
| var avg = getAverageFromNumArr(numArr, numOfDec), | |
| i = numArr.length, | |
| v = 0; | |
| while (i--) { | |
| v += Math.pow((numArr[i] - avg), 2); | |
| } | |
| v /= numArr.length; | |
| return getNumWithSetDec(v, numOfDec); | |
| } | |
| ; | |
| function getStandardDeviation(numArr, numOfDec) { | |
| if (!isArray(numArr)) { | |
| return false; | |
| } | |
| var stdDev = Math.sqrt(getVariance(numArr, numOfDec)); | |
| return getNumWithSetDec(stdDev, numOfDec); | |
| } | |
| ; | |
| function Array_max(array) { | |
| return Math.max.apply(Math, array); | |
| } | |
| ; | |
| function Array_min(array) { | |
| return Math.min.apply(Math, array); | |
| } | |
| ; | |
| // BENCHMARKING ENDS | |
| function colorize_boxes_like_in_svg() { | |
| var bg_color = $("#p").css("background-color"); | |
| bg_color = new RGBColor(bg_color); | |
| var fill_color, stroke_color, fill_opacity, stroke_opacity, box; | |
| for (var i = 1; i <= 3; i++) { | |
| fill_color = $("#p" + i).css("fill"); | |
| fill_color = new RGBColor(fill_color); | |
| fill_opacity = $("#p" + i).css("fill-opacity"); | |
| fill_color = fill_color.flattenRGBA(fill_opacity, bg_color); | |
| stroke_color = $("#p" + i).css("stroke"); | |
| stroke_color = new RGBColor(stroke_color); | |
| stroke_opacity = $("#p" + i).css("stroke-opacity"); | |
| stroke_color = stroke_color.flattenRGBA(stroke_opacity, bg_color); | |
| if (i == 1) box = "#subject_box"; | |
| else if (i == 2) box = "#clip_box"; | |
| else if (i == 3) box = "#solution_box"; | |
| $(box).css("background-color", fill_color); | |
| } | |
| } | |
| // The following class exists only for transferring color values from SVG to html table | |
| // It could be got without this, but wanted to try this. | |
| // May be I remove this and make precalculated values | |
| /** | |
| * A class to parse color values | |
| * @author Stoyan Stefanov <[email protected]> | |
| * @link http://www.phpied.com/rgb-color-parser-in-javascript/ | |
| * @license Use it if you like it | |
| */ | |
| function RGBColor(color_string) { | |
| if (typeof(color_string) == "undefined") color_string = ""; | |
| this.ok = false; | |
| // strip any leading # | |
| if (color_string.charAt(0) == '#') { // remove # if any | |
| color_string = color_string.substr(1, 6); | |
| } | |
| color_string = color_string.replace(/ /g, ''); | |
| color_string = color_string.toLowerCase(); | |
| // before getting into regexps, try simple matches | |
| // and overwrite the input | |
| var simple_colors = { aliceblue: 'f0f8ff', antiquewhite: 'faebd7', aqua: '00ffff', aquamarine: '7fffd4', azure: 'f0ffff', beige: 'f5f5dc', bisque: 'ffe4c4', black: '000000', blanchedalmond: 'ffebcd', blue: '0000ff', blueviolet: '8a2be2', brown: 'a52a2a', burlywood: 'deb887', cadetblue: '5f9ea0', chartreuse: '7fff00', chocolate: 'd2691e', coral: 'ff7f50', cornflowerblue: '6495ed', cornsilk: 'fff8dc', crimson: 'dc143c', cyan: '00ffff', darkblue: '00008b', darkcyan: '008b8b', darkgoldenrod: 'b8860b', darkgray: 'a9a9a9', darkgreen: '006400', darkkhaki: 'bdb76b', darkmagenta: '8b008b', darkolivegreen: '556b2f', darkorange: 'ff8c00', darkorchid: '9932cc', darkred: '8b0000', darksalmon: 'e9967a', darkseagreen: '8fbc8f', darkslateblue: '483d8b', darkslategray: '2f4f4f', darkturquoise: '00ced1', darkviolet: '9400d3', deeppink: 'ff1493', deepskyblue: '00bfff', dimgray: '696969', dodgerblue: '1e90ff', feldspar: 'd19275', firebrick: 'b22222', floralwhite: 'fffaf0', forestgreen: '228b22', fuchsia: 'ff00ff', gainsboro: 'dcdcdc', ghostwhite: 'f8f8ff', gold: 'ffd700', goldenrod: 'daa520', gray: '808080', green: '008000', greenyellow: 'adff2f', honeydew: 'f0fff0', hotpink: 'ff69b4', indianred: 'cd5c5c', indigo: '4b0082', ivory: 'fffff0', khaki: 'f0e68c', lavender: 'e6e6fa', lavenderblush: 'fff0f5', lawngreen: '7cfc00', lemonchiffon: 'fffacd', lightblue: 'add8e6', lightcoral: 'f08080', lightcyan: 'e0ffff', lightgoldenrodyellow: 'fafad2', lightgrey: 'd3d3d3', lightgreen: '90ee90', lightpink: 'ffb6c1', lightsalmon: 'ffa07a', lightseagreen: '20b2aa', lightskyblue: '87cefa', lightslateblue: '8470ff', lightslategray: '778899', lightsteelblue: 'b0c4de', lightyellow: 'ffffe0', lime: '00ff00', limegreen: '32cd32', linen: 'faf0e6', magenta: 'ff00ff', maroon: '800000', mediumaquamarine: '66cdaa', mediumblue: '0000cd', mediumorchid: 'ba55d3', mediumpurple: '9370d8', mediumseagreen: '3cb371', mediumslateblue: '7b68ee', mediumspringgreen: '00fa9a', mediumturquoise: '48d1cc', mediumvioletred: 'c71585', midnightblue: '191970', mintcream: 'f5fffa', mistyrose: 'ffe4e1', moccasin: 'ffe4b5', navajowhite: 'ffdead', navy: '000080', oldlace: 'fdf5e6', olive: '808000', olivedrab: '6b8e23', orange: 'ffa500', orangered: 'ff4500', orchid: 'da70d6', palegoldenrod: 'eee8aa', palegreen: '98fb98', paleturquoise: 'afeeee', palevioletred: 'd87093', papayawhip: 'ffefd5', peachpuff: 'ffdab9', peru: 'cd853f', pink: 'ffc0cb', plum: 'dda0dd', powderblue: 'b0e0e6', purple: '800080', red: 'ff0000', rosybrown: 'bc8f8f', royalblue: '4169e1', saddlebrown: '8b4513', salmon: 'fa8072', sandybrown: 'f4a460', seagreen: '2e8b57', seashell: 'fff5ee', sienna: 'a0522d', silver: 'c0c0c0', skyblue: '87ceeb', slateblue: '6a5acd', slategray: '708090', snow: 'fffafa', springgreen: '00ff7f', steelblue: '4682b4', tan: 'd2b48c', teal: '008080', thistle: 'd8bfd8', tomato: 'ff6347', turquoise: '40e0d0', violet: 'ee82ee', violetred: 'd02090', wheat: 'f5deb3', white: 'ffffff', whitesmoke: 'f5f5f5', yellow: 'ffff00', yellowgreen: '9acd32' }; | |
| for (var key in simple_colors) { | |
| if (color_string == key) { | |
| color_string = simple_colors[key]; | |
| } | |
| } | |
| // emd of simple type-in colors | |
| // array of color definition objects | |
| var color_defs = [ | |
| { | |
| re: /^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/, | |
| example: ['rgb(123, 234, 45)', 'rgb(255,234,245)'], | |
| process: function (bits) { | |
| return [ | |
| parseInt(bits[1], 10), | |
| parseInt(bits[2], 10), | |
| parseInt(bits[3], 10)]; | |
| } | |
| }, | |
| { | |
| re: /^(\w{2})(\w{2})(\w{2})$/, | |
| example: ['#00ff00', '336699'], | |
| process: function (bits) { | |
| return [ | |
| parseInt(bits[1], 16), | |
| parseInt(bits[2], 16), | |
| parseInt(bits[3], 16)]; | |
| } | |
| }, | |
| { | |
| re: /^(\w{1})(\w{1})(\w{1})$/, | |
| example: ['#fb0', 'f0f'], | |
| process: function (bits) { | |
| return [ | |
| parseInt(bits[1] + bits[1], 16), | |
| parseInt(bits[2] + bits[2], 16), | |
| parseInt(bits[3] + bits[3], 16)]; | |
| } | |
| } | |
| ]; | |
| // search through the definitions to find a match | |
| for (var i = 0; i < color_defs.length; i++) { | |
| var re = color_defs[i].re; | |
| var processor = color_defs[i].process; | |
| var bits = re.exec(color_string); | |
| if (bits) { | |
| channels = processor(bits); | |
| this.r = channels[0]; | |
| this.g = channels[1]; | |
| this.b = channels[2]; | |
| this.ok = true; | |
| } | |
| } | |
| // validate/cleanup values | |
| this.r = (this.r < 0 || isNaN(this.r)) ? 0 : ((this.r > 255) ? 255 : this.r); | |
| this.g = (this.g < 0 || isNaN(this.g)) ? 0 : ((this.g > 255) ? 255 : this.g); | |
| this.b = (this.b < 0 || isNaN(this.b)) ? 0 : ((this.b > 255) ? 255 : this.b); | |
| // some getters | |
| this.toRGB = function () { | |
| return 'rgb(' + this.r + ', ' + this.g + ', ' + this.b + ')'; | |
| }; | |
| this.toHex = function () { | |
| var r = this.r.toString(16); | |
| var g = this.g.toString(16); | |
| var b = this.b.toString(16); | |
| if (r.length == 1) r = '0' + r; | |
| if (g.length == 1) g = '0' + g; | |
| if (b.length == 1) b = '0' + b; | |
| return '#' + r + g + b; | |
| }; | |
| this.flattenRGBA = function (a, bg) { | |
| var alpha = 1 - parseFloat(a); | |
| this.r = Math.round((a * (this.r / 255) + (alpha * (bg.r / 255))) * 255); | |
| this.g = Math.round((a * (this.g / 255) + (alpha * (bg.g / 255))) * 255); | |
| this.b = Math.round((a * (this.b / 255) + (alpha * (bg.b / 255))) * 255); | |
| return this.toHex(); | |
| }; | |
| } | |
| // Not used, because using mouse and repeated events is better | |
| function offset_key(e) { | |
| return; | |
| if (e.which == 38) { | |
| make_offset(1); | |
| } | |
| else if (e.which == 40) { | |
| make_offset(-1); | |
| } | |
| e.stopPropagation(); | |
| e.preventDefault(); | |
| } | |
| function round_to(num, dec) { | |
| if (typeof (num) == "undefined" || typeof (dec) == "undefined" || isNaN(dec)) { | |
| alert("Cannot round other than number"); | |
| return false; | |
| } | |
| else return Math.round(num * Math.pow(10, dec)) / Math.pow(10, dec); | |
| } | |
| function update_enlarged_SVG_if_needed() { | |
| if (update_enlarged_SVG) { | |
| show_svg_source_click("non_click"); | |
| svg_source_enlarge(); | |
| } | |
| else if (update_enlarged_SVG_source && !update_enlarged_SVG) { | |
| show_svg_source_click("non_click"); | |
| } | |
| } | |
| function to_printable(a) { | |
| return a.toFixed(1); | |
| } | |
| function get_polys(scale_again) { | |
| var polygon = parseInt($('input[type="radio"][name="polygons"]:checked').val(), 10); | |
| var polys; | |
| if (polygon === 0) { | |
| polys = get_gb_and_arrow(); | |
| ss = polys.ss; | |
| cc = polys.cc; | |
| } | |
| else if (polygon == 1) { | |
| polys = get_text_polys(); | |
| ss = polys.ss; | |
| cc = polys.cc; | |
| } | |
| else if (polygon == 2) { | |
| polys = get_rectangle_polys(); | |
| ss = polys.ss; | |
| cc = polys.cc; | |
| } | |
| else if (polygon == 3) { | |
| polys = get_same_self_intersecting_polys(); | |
| ss = polys.ss; | |
| cc = polys.cc; | |
| } | |
| else if (polygon == 6) { | |
| polys = get_star_and_rect(); | |
| ss = polys.ss; | |
| cc = polys.cc; | |
| } | |
| else if (polygon == 7) { | |
| polys = get_spiral_and_rects(); | |
| ss = polys.ss; | |
| cc = polys.cc; | |
| } | |
| else if (polygon == 8) { | |
| polys = get_rounded_grid_and_star(); | |
| ss = polys.ss; | |
| cc = polys.cc; | |
| } | |
| else if (polygon == 9) { | |
| polys = get_glyph_and_grid(); | |
| ss = polys.ss; | |
| cc = polys.cc; | |
| } | |
| else if (polygon == 10) { | |
| polys = get_custom_poly(); | |
| ss = polys.ss; | |
| cc = polys.cc; | |
| } | |
| else if (polygon == 4 || polygon == 5) { | |
| if (!random_subj) random_subj = get_random_polys("subj"); | |
| if (!random_clip) random_clip = get_random_polys("clip"); | |
| if (scale_again) { | |
| random_subj = scale_again_random_poly(random_subj); | |
| random_clip = scale_again_random_poly(random_clip); | |
| ss = random_subj; | |
| cc = random_clip; | |
| rnd_sett.scale = scale; | |
| } | |
| else { | |
| ss = random_subj; | |
| cc = random_clip; | |
| } | |
| } | |
| sss = [ | |
| [] | |
| ]; | |
| } | |
| // Main function which setups defaults | |
| // and attach events | |
| // and finally draw the default svg image | |
| function main() { | |
| // formats internal representation of polygons to | |
| // specified output format and prints them on input fields | |
| $("#output_format").change(function () { | |
| var selected_val = parseInt($(this).val(), 10); | |
| output_format = selected_val; | |
| if ($("#custom_polygons_fieldset").css("display") != "none") { | |
| var subj = $("#custom_polygon_subj").val(); | |
| var clip = $("#custom_polygon_clip").val(); | |
| subj = normalize_clipper_poly(subj, true); // quiet | |
| clip = normalize_clipper_poly(clip, true); // quiet | |
| if (subj !== false && clip !== false) { | |
| $("#custom_polygon_subj").val(format_output(subj)); | |
| $("#custom_polygon_clip").val(format_output(clip)); | |
| } | |
| } | |
| var polygon_explorer_string = $("#polygon_explorer_string_inp").val(); | |
| polygon_explorer_string = normalize_clipper_poly(polygon_explorer_string, true); // quiet | |
| if (polygon_explorer_string !== false) { | |
| $("#polygon_explorer_string_inp").val(format_output(polygon_explorer_string)); | |
| } | |
| }); | |
| $("#sample_custom_polygon").change(function () { | |
| var polygon = parseInt($(this).val(), 10); | |
| var polys; | |
| global_do_not_round_and_scale = true; | |
| var subj = "", | |
| clip = ""; | |
| if (polygon === "") { | |
| subj = ""; | |
| clip = ""; | |
| } | |
| else if (polygon === 0) { | |
| polys = get_gb_and_arrow(); | |
| subj = polys.ss; | |
| clip = polys.cc; | |
| } | |
| else if (polygon == 1) { | |
| polys = get_text_polys(); | |
| subj = polys.ss; | |
| clip = polys.cc; | |
| } | |
| else if (polygon == 2) { | |
| polys = get_rectangle_polys(); | |
| subj = polys.ss; | |
| clip = polys.cc; | |
| } | |
| else if (polygon == 3) { | |
| polys = get_same_self_intersecting_polys(); | |
| subj = polys.ss; | |
| clip = polys.cc; | |
| } | |
| else if (polygon == 4 || polygon == 5) { | |
| subj = get_random_polys("subj", polygon); | |
| clip = get_random_polys("clip", polygon); | |
| } | |
| else if (polygon == 6) { | |
| polys = get_star_and_rect(); | |
| subj = polys.ss; | |
| clip = polys.cc; | |
| } | |
| else if (polygon == 7) { | |
| polys = get_spiral_and_rects(); | |
| subj = polys.ss; | |
| clip = polys.cc; | |
| } | |
| else if (polygon == 8) { | |
| polys = get_rounded_grid_and_star(); | |
| subj = polys.ss; | |
| clip = polys.cc; | |
| } | |
| else if (polygon == 9) { | |
| polys = get_glyph_and_grid(); | |
| subj = polys.ss; | |
| clip = polys.cc; | |
| } | |
| else if (polygon == 10) { | |
| polys = get_custom_poly(); | |
| subj = polys.ss; | |
| clip = polys.cc; | |
| } | |
| global_do_not_round_and_scale = false; | |
| if (subj != "") subj = JSON.stringify(subj); | |
| if (clip != "") clip = JSON.stringify(clip); | |
| $("#custom_polygon_subj").val(format_output(subj)); | |
| $("#custom_polygon_clip").val(format_output(clip)); | |
| }); | |
| $("#help_custom_polygon").click(function () { | |
| var txt = ""; | |
| txt += 'A) You can add your own custom polygons in several formats:\n\n'; | |
| txt += '1) The program uses as an inner default the following format: JSON-stringified array of arrays of point objects eg. [[{"X":100,"Y":100},{"X":200,"Y":100},{"X":200,"Y":200},{"X":100,"Y":200}],[{"X":110,"Y":110},{"X":210,"Y":110},{"X":210,"Y":210},{"X":110,"Y":210}]]. This format allows to input sub polygons. Each sub polygon is an array of point objects. This format makes it easy to transfer polygons to other programs that use Clipper library and is suitable for storing polygons in database.\n\n'; | |
| txt += '2) JSON-stringified array of point objects eg. [{"X":100,"Y":100},{"X":200,"Y":100},{"X":200,"Y":200},{"X":100,"Y":200}]. This format doesn\'t allow to input sub polygons.\n\n'; | |
| txt += '3) JSON-stringified array of arrays of coordinates without "X" and "Y" eg. [[100,100,200,100,200,200,100,200],[110,110,210,110,210,210,110,210]]. This format allows to input sub polygons. Each sub polygon is an array of coordinates so that each x coordinate is followed by an y coordinate. This format makes it easy to transfer polygons to other programs that use Clipper library and is suitable for storing polygons in database.\n\n'; | |
| txt += '4) JSON-stringified array of x and y coordinates eg. [100,100,200,100,200,200,100,200] or [100 100 200 100 200 200 100 200] or [100 100,200 100,200 200,100 200] or the same without []:s. This format doesn\'t allow to input sub polygons.\n\n'; | |
| txt += '5) SVG path strings with commands MLVHZ or mlvhz eg. M100,100 L200,100 L200,200 L100,200Z M110,110 L210,110 L210,210 L110,210Z. This format allows to input sub polygons. Each subpolygon starts with M (moveto) command.\n\n'; | |
| txt += '\n'; | |
| txt += 'B) Custom polygons are saved in browser\'s Local Storage, so they should be tolerant for page reload and browser crashes.\n'; | |
| txt += '\n'; | |
| alert(txt); | |
| }); | |
| $("#help_builtin_polygon_sets").click(function () { | |
| var txt = ''; | |
| txt += 'Builtin polygon sets\n\n'; | |
| txt += 'You can add builtin polygon sets into Subj and Clip input fields to edit copies of them.\n\n'; | |
| txt += 'Note! Before saving, nothing happens. After saving, the SVG window is also updated.'; | |
| alert(txt); | |
| }); | |
| $("#help_output_format").click(function () { | |
| var txt = ''; | |
| txt += 'Output format\n\n'; | |
| txt += 'To change the polygon coordinate output format please use the above dropdown. The available formats are:\n\n'; | |
| txt += '- Clipper: [[{"X":100,"Y":100},{"X":200,"Y":200}]]\n\n'; | |
| txt += '- Plain: [[100,100,200,200]]\n\n'; | |
| txt += '- SVG: M100,100L200,200Z\n\n'; | |
| txt += 'There are two places where this has effect: 1) the above text box 2) the Subj and Clip text boxes in Custom Polygon fieldset.'; | |
| alert(txt); | |
| }); | |
| $("#remove_custom_polygon").click(function () { | |
| var selected_value = $("#custom_polygons_select").val(); | |
| if (selected_value + "" === "0") { | |
| alert("Cannot remove the default polygon."); | |
| } | |
| else if (selected_value) { | |
| if (confirm("Remove custom polygon " + selected_value + "?")) { | |
| var arr = $.totalStorage('custom_polygons'); | |
| arr[selected_value] = null; | |
| $.totalStorage('custom_polygons', arr); | |
| update_custom_polygons_select(); | |
| } | |
| } | |
| else alert("Nothing removable."); | |
| }); | |
| $("#removeall_custom_polygon").click(function () { | |
| var count = $("#custom_polygons_select option").length; | |
| if (count > 1) { | |
| if (confirm("Remove all " + (count - 1) + " custom polygons?")) { | |
| $.totalStorage('custom_polygons', []); | |
| set_default_custom_polygon(); | |
| update_custom_polygons_select(); | |
| } | |
| } | |
| else alert("Nothing removable."); | |
| }); | |
| $("#save_custom_polygon").click(function (e) { | |
| var subj = $("#custom_polygon_subj").val(); | |
| var clip = $("#custom_polygon_clip").val(); | |
| subj = normalize_clipper_poly(subj); | |
| clip = normalize_clipper_poly(clip); | |
| if (subj === false || clip === false) return false; | |
| if (typeof ($.totalStorage('custom_polygons')) == "undefined" || $.totalStorage('custom_polygons') === null) { | |
| set_default_custom_polygon(); | |
| } | |
| var polygon_set = {}; | |
| polygon_set.subj = subj; | |
| polygon_set.clip = clip; | |
| var arr2 = $.totalStorage('custom_polygons'); | |
| var selected_value = $("#custom_polygons_select").val(); | |
| if (selected_value + "" !== "0" && selected_value) { | |
| arr2[selected_value] = polygon_set; | |
| $.totalStorage('custom_polygons', arr2); | |
| update_custom_polygons_select(); | |
| show_alert(e, this, "Polygon " + (selected_value) + " updated!"); | |
| } | |
| else if (selected_value + "" === "0") { | |
| alert("The default custom polygon cannot be overwrited. If you want to modify it, save it first as a new."); | |
| } | |
| else alert("Polygon update failed!"); | |
| }); | |
| $("#add_as_new_custom_polygon").click(function (e) { | |
| var subj = $("#custom_polygon_subj").val(); | |
| var clip = $("#custom_polygon_clip").val(); | |
| subj = normalize_clipper_poly(subj); | |
| clip = normalize_clipper_poly(clip); | |
| if (subj === false || clip === false) return false; | |
| if (typeof ($.totalStorage('custom_polygons')) == "undefined" || $.totalStorage('custom_polygons') === null) { | |
| set_default_custom_polygon(); | |
| } | |
| var polygon_set = {}; | |
| polygon_set.subj = subj; | |
| polygon_set.clip = clip; | |
| var arr2 = $.totalStorage('custom_polygons'); | |
| arr2.push(polygon_set); | |
| $.totalStorage('custom_polygons', arr2); | |
| update_custom_polygons_select(); | |
| $('#custom_polygons_select').val(arr2.length - 1).change(); | |
| show_alert(e, this, "New polygon " + (arr2.length - 1) + " added!"); | |
| }); | |
| $("#custom_polygons_select").change(function () { | |
| var selected_value = $("#custom_polygons_select").val(); | |
| if (!selected_value && selected_value + "" !== "0") selected_value = 0; | |
| var arr = $.totalStorage('custom_polygons'); | |
| if (isArray(arr) && arr.length && typeof (arr[selected_value]) != "undefined") { | |
| $("#custom_polygon_subj").val(format_output(arr[selected_value].subj)); | |
| $("#custom_polygon_clip").val(format_output(arr[selected_value].clip)); | |
| make_clip(); | |
| } | |
| }); | |
| $("input[type='radio'][name='polygons']").change(function (e) { | |
| var val = parseInt($(this).val(), 10); | |
| if (val == 10) { | |
| $("#random_polygons_fieldset").css("display", "none"); | |
| $("#custom_polygons_fieldset").css("display", "block"); | |
| set_default_custom_polygon(); | |
| update_custom_polygons_select(); | |
| update_fieldset_heights(); | |
| $("#custom_polygons_select").change(); | |
| make_clip(); | |
| return; | |
| } | |
| else $("#custom_polygons_fieldset").css("display", "none"); | |
| if (val == 4 || val == 5) { | |
| $("#random_polygons_fieldset").css("display", "block"); | |
| if (val == 4) rnd_sett_defaults.current = "rect"; | |
| else rnd_sett_defaults.current = "norm"; | |
| // Test for ranges | |
| if (rnd_sett.clip_point_count < rnd_sett_defaults[rnd_sett_defaults.current].min.clip_point_count) rnd_sett.clip_point_count = rnd_sett_defaults[rnd_sett_defaults.current].min.clip_point_count; | |
| if (rnd_sett.clip_point_count > rnd_sett_defaults[rnd_sett_defaults.current].max.clip_point_count) rnd_sett.clip_point_count = rnd_sett_defaults[rnd_sett_defaults.current].max.clip_point_count; | |
| $('#clip_point_count').val(rnd_sett.clip_point_count); | |
| if (rnd_sett.clip_polygon_count < rnd_sett_defaults[rnd_sett_defaults.current].min.clip_polygon_count) rnd_sett.clip_polygon_count = rnd_sett_defaults[rnd_sett_defaults.current].min.clip_polygon_count; | |
| if (rnd_sett.clip_polygon_count > rnd_sett_defaults[rnd_sett_defaults.current].max.clip_polygon_count) rnd_sett.clip_polygon_count = rnd_sett_defaults[rnd_sett_defaults.current].max.clip_polygon_count; | |
| $('#clip_polygon_count').val(rnd_sett.clip_polygon_count); | |
| if (rnd_sett.subj_point_count < rnd_sett_defaults[rnd_sett_defaults.current].min.subj_point_count) rnd_sett.subj_point_count = rnd_sett_defaults[rnd_sett_defaults.current].min.subj_point_count; | |
| if (rnd_sett.subj_point_count > rnd_sett_defaults[rnd_sett_defaults.current].max.subj_point_count) rnd_sett.subj_point_count = rnd_sett_defaults[rnd_sett_defaults.current].max.subj_point_count; | |
| $('#subj_point_count').val(rnd_sett.subj_point_count); | |
| if (rnd_sett.subj_polygon_count < rnd_sett_defaults[rnd_sett_defaults.current].min.subj_polygon_count) rnd_sett.subj_polygon_count = rnd_sett_defaults[rnd_sett_defaults.current].min.subj_polygon_count; | |
| if (rnd_sett.subj_polygon_count > rnd_sett_defaults[rnd_sett_defaults.current].max.subj_polygon_count) rnd_sett.subj_polygon_count = rnd_sett_defaults[rnd_sett_defaults.current].max.subj_polygon_count; | |
| $('#subj_polygon_count').val(rnd_sett.subj_polygon_count); | |
| random_subj = get_random_polys("subj", val); | |
| random_clip = get_random_polys("clip", val); | |
| } | |
| else $("#random_polygons_fieldset").css("display", "none"); | |
| make_clip(); | |
| update_fieldset_heights(); | |
| }); | |
| $('#generate_random_polygons').hold(function (e) { | |
| random_subj = get_random_polys("subj"); | |
| random_clip = get_random_polys("clip"); | |
| make_clip(); | |
| }, { | |
| duration: 300, | |
| speed: 0, | |
| min: 150 | |
| }); | |
| $("input[name='clipType']").change(function () { | |
| if ($('input[type="radio"][name="clipType"][value=""]').is(":checked")) { | |
| $('input[type="radio"][name="offsettable_poly"][value="1"]').attr('checked', 'checked'); | |
| offsettable_poly = 1; | |
| } | |
| else { | |
| $("#offsettable_poly3").attr('checked', 'checked'); | |
| offsettable_poly = 3; | |
| } | |
| clipType = $('input[type="radio"][name="clipType"]:checked').val(); | |
| if (clipType !== "") clipType = parseInt(clipType, 10); | |
| make_clip(); | |
| }); | |
| $("input[name='subject_fillType']").change(function () { | |
| subject_fillType = parseInt(this.value, 10); | |
| make_clip(); | |
| }); | |
| $("input[name='clip_fillType']").change(function () { | |
| clip_fillType = parseInt(this.value, 10); | |
| make_clip(); | |
| }); | |
| $("input[name='offsettable_poly']").change(function () { | |
| offsettable_poly = parseInt(this.value, 10); | |
| // When offsettable poly is set to Subject or Clip, then boolean operations are not done. | |
| // To show this to user, set clipType to "No" | |
| if (offsettable_poly == 1 || offsettable_poly == 2) { | |
| $('input[type="radio"][name="clipType"][value=""]').attr('checked', 'checked'); | |
| clipType = ""; | |
| } | |
| make_clip(); | |
| }); | |
| $('#scale_minus').hold(function () { | |
| var scale_orig = $('#scale').val(); | |
| if (scale_orig && !isNaN(scale_orig) && parseInt(scale_orig, 10).toString() !== "0") scale = parseFloat(scale_orig); | |
| scale = scale - scale_addition; | |
| scale = Math.round(scale / scale_addition) * scale_addition; | |
| if (scale <= 0) scale = 1.0; | |
| $('#scale').val(to_printable(scale)); | |
| $('#scale').trigger('change'); | |
| }, { | |
| duration: 300, | |
| speed: 0, | |
| min: 150 | |
| }); | |
| $('#scale').change(function () { | |
| if (this.value && !isNaN(this.value) && parseInt(this.value, 10).toString() !== "0") { | |
| scale = parseFloat(this.value); | |
| get_polys(true); | |
| sss = cc; | |
| make_clip(); | |
| } | |
| else alert("Scale has to be a number"); | |
| }); | |
| $('#scale_plus').hold(function () { | |
| var scale_orig = $('#scale').val(); | |
| if (scale_orig && !isNaN(scale_orig) && parseInt(scale_orig, 10).toString() !== "0") scale = parseFloat(scale_orig); | |
| scale = scale + scale_addition; | |
| scale = Math.round(scale / scale_addition) * scale_addition; | |
| $('#scale').val(to_printable(scale)); | |
| $('#scale').trigger('change'); | |
| }, { | |
| duration: 300, | |
| speed: 0, | |
| min: 150 | |
| }); | |
| $('#delta_minus').hold(function () { | |
| var delta_orig = $('#delta').val(); | |
| if (!isNaN(delta_orig)) delta = parseFloat(delta_orig); | |
| delta = delta - 1; | |
| make_clip(); | |
| }, { | |
| duration: 300, | |
| speed: 0, | |
| min: 150 | |
| }); | |
| $('#delta').change(function (e) { | |
| var delta_orig = $('#delta').val(); | |
| if (!isNaN(delta_orig)) delta = parseFloat(delta_orig); | |
| make_clip(); | |
| }); | |
| $('#delta_plus').hold(function () { | |
| var delta_orig = $('#delta').val(); | |
| if (!isNaN(delta_orig)) delta = parseFloat(delta_orig); | |
| delta = delta + 1; | |
| make_clip(); | |
| }, { | |
| duration: 300, | |
| speed: 0, | |
| min: 150 | |
| }); | |
| $("input[name='joinType']").change(function () { | |
| joinType = parseInt(this.value, 10); | |
| //make_offset(); | |
| make_clip(); | |
| }); | |
| $('#miterLimit_minus').hold(function () { | |
| var miterLimit_orig = $('#miterLimit').val(); | |
| if (!isNaN(miterLimit_orig)) miterLimit = parseFloat(miterLimit_orig); | |
| miterLimit = miterLimit - 0.1; | |
| if (miterLimit < 1.0) miterLimit = 1.0; | |
| make_clip(); | |
| }, { | |
| duration: 300, | |
| speed: 0, | |
| min: 150 | |
| }); | |
| $("#miterLimit").change(function () { | |
| miterLimit = parseFloat(this.value); | |
| make_clip(); | |
| }); | |
| $("#cleandelta").change(function () { | |
| var cleandelta_orig = $(this).val(); | |
| var this_value = parseFloat(this.value); | |
| if (!isNaN(this_value)) cleandelta = this_value; | |
| //else cleandelta = cleandelta_orig; | |
| make_clip(); | |
| }); | |
| $("#lighten_distance").change(function () { | |
| var lighten_distance_orig = $(this).val(); | |
| var this_value = parseFloat(this.value); | |
| if (!isNaN(this_value)) lighten_distance = this_value; | |
| //else lighten_distance = lighten_distance_orig; | |
| make_clip(); | |
| }); | |
| $('#miterLimit_plus').hold(function () { | |
| var miterLimit_orig = $('#miterLimit').val(); | |
| if (!isNaN(miterLimit_orig)) miterLimit = parseFloat(miterLimit_orig); | |
| miterLimit = miterLimit + 0.1; | |
| if (miterLimit < 1.0) miterLimit = 1.0; | |
| make_clip(); | |
| }, { | |
| duration: 300, | |
| speed: 0, | |
| min: 150 | |
| }); | |
| $("#AutoFix").change(function () { | |
| if ($(this).attr('checked')) AutoFix = true; | |
| else AutoFix = false; | |
| make_clip(); | |
| }); | |
| $("#ExPolygons").change(function () { | |
| if ($(this).attr('checked')) ExPolygons = true; | |
| else ExPolygons = false; | |
| if (SolutionPolygonClicked) $("#polygon_explorer_string_inp").val(format_output(SolutionPolygonString, "ExPolygons")); | |
| //make_clip(); | |
| }); | |
| $("#Simplify").change(function () { | |
| if ($(this).attr('checked')) Simplify = true; | |
| else Simplify = false; | |
| make_clip(); | |
| }); | |
| $("#clean").change(function () { | |
| if ($(this).attr('checked')) { | |
| if ($("#cleandelta").val() + "".trim() == "") { | |
| cleandelta = cleandelta_default; | |
| $("#cleandelta").val(cleandelta); | |
| } | |
| clean = true; | |
| } | |
| else { | |
| clean = false; | |
| } | |
| make_clip(); | |
| }); | |
| $("#lighten").change(function () { | |
| if ($(this).attr('checked')) lighten = true; | |
| else lighten = false; | |
| make_clip(); | |
| }); | |
| $("#lighten").change(function () { | |
| if ($(this).attr('checked')) { | |
| if ($("#lighten_distance").val() + "".trim() == "") { | |
| lighten_distance = lighten_distance_default; | |
| $("#lighten_distance").val(lighten_distance); | |
| } | |
| lighten = true; | |
| } | |
| else { | |
| lighten = false; | |
| } | |
| make_clip(); | |
| }); | |
| $('#subj_polygon_count_minus').hold(function () { | |
| var subj_polygon_count_orig = $('#subj_polygon_count').val(); | |
| if (!isNaN(subj_polygon_count_orig)) rnd_sett.subj_polygon_count = parseFloat(subj_polygon_count_orig); | |
| rnd_sett.subj_polygon_count = rnd_sett.subj_polygon_count - 1; | |
| if (rnd_sett.subj_polygon_count < rnd_sett_defaults[rnd_sett_defaults.current].min.subj_polygon_count) rnd_sett.subj_polygon_count = rnd_sett_defaults[rnd_sett_defaults.current].min.subj_polygon_count; | |
| $('#subj_polygon_count').val(rnd_sett.subj_polygon_count); | |
| random_subj = get_random_polys("subj"); | |
| random_clip = get_random_polys("clip"); | |
| make_clip(); | |
| }, { | |
| duration: 300, | |
| speed: 0, | |
| min: 150 | |
| }); | |
| $('#subj_polygon_count').change(function (e) { | |
| var subj_polygon_count_orig = $('#subj_polygon_count').val(); | |
| if (!isNaN(subj_polygon_count_orig)) rnd_sett.subj_polygon_count = parseFloat(subj_polygon_count_orig); | |
| if (rnd_sett.subj_polygon_count < rnd_sett_defaults[rnd_sett_defaults.current].min.subj_polygon_count) rnd_sett.subj_polygon_count = rnd_sett_defaults[rnd_sett_defaults.current].min.subj_polygon_count; | |
| if (rnd_sett.subj_polygon_count > rnd_sett_defaults[rnd_sett_defaults.current].max.subj_polygon_count) rnd_sett.subj_polygon_count = rnd_sett_defaults[rnd_sett_defaults.current].max.subj_polygon_count; | |
| $('#subj_polygon_count').val(rnd_sett.subj_polygon_count); | |
| random_subj = get_random_polys("subj"); | |
| random_clip = get_random_polys("clip"); | |
| make_clip(); | |
| }); | |
| $('#subj_polygon_count_plus').hold(function () { | |
| var subj_polygon_count_orig = $('#subj_polygon_count').val(); | |
| if (!isNaN(subj_polygon_count_orig)) rnd_sett.subj_polygon_count = parseFloat(subj_polygon_count_orig); | |
| rnd_sett.subj_polygon_count = rnd_sett.subj_polygon_count + 1; | |
| if (rnd_sett.subj_polygon_count > rnd_sett_defaults[rnd_sett_defaults.current].max.subj_polygon_count) rnd_sett.subj_polygon_count = rnd_sett_defaults[rnd_sett_defaults.current].max.subj_polygon_count; | |
| $('#subj_polygon_count').val(rnd_sett.subj_polygon_count); | |
| random_subj = get_random_polys("subj"); | |
| random_clip = get_random_polys("clip"); | |
| make_clip(); | |
| }, { | |
| duration: 300, | |
| speed: 0, | |
| min: 150 | |
| }); | |
| $('#subj_point_count_minus').hold(function () { | |
| var subj_point_count_orig = $('#subj_point_count').val(); | |
| if (!isNaN(subj_point_count_orig)) rnd_sett.subj_point_count = parseFloat(subj_point_count_orig); | |
| rnd_sett.subj_point_count = rnd_sett.subj_point_count - 1; | |
| if (rnd_sett.subj_point_count < rnd_sett_defaults[rnd_sett_defaults.current].min.subj_point_count) rnd_sett.subj_point_count = rnd_sett_defaults[rnd_sett_defaults.current].min.subj_point_count; | |
| $('#subj_point_count').val(rnd_sett.subj_point_count); | |
| random_subj = get_random_polys("subj"); | |
| random_clip = get_random_polys("clip"); | |
| make_clip(); | |
| }, { | |
| duration: 300, | |
| speed: 0, | |
| min: 150 | |
| }); | |
| $('#subj_point_count').change(function (e) { | |
| var subj_point_count_orig = $('#subj_point_count').val(); | |
| if (!isNaN(subj_point_count_orig)) rnd_sett.subj_point_count = parseFloat(subj_point_count_orig); | |
| if (rnd_sett.subj_point_count < rnd_sett_defaults[rnd_sett_defaults.current].min.subj_point_count) rnd_sett.subj_point_count = rnd_sett_defaults[rnd_sett_defaults.current].min.subj_point_count; | |
| if (rnd_sett.subj_point_count > rnd_sett_defaults[rnd_sett_defaults.current].max.subj_point_count) rnd_sett.subj_point_count = rnd_sett_defaults[rnd_sett_defaults.current].max.subj_point_count; | |
| $('#subj_point_count').val(rnd_sett.subj_point_count); | |
| random_subj = get_random_polys("subj"); | |
| random_clip = get_random_polys("clip"); | |
| make_clip(); | |
| }); | |
| $('#subj_point_count_plus').hold(function () { | |
| var subj_point_count_orig = $('#subj_point_count').val(); | |
| if (!isNaN(subj_point_count_orig)) rnd_sett.subj_point_count = parseFloat(subj_point_count_orig); | |
| rnd_sett.subj_point_count = rnd_sett.subj_point_count + 1; | |
| if (rnd_sett.subj_point_count > rnd_sett_defaults[rnd_sett_defaults.current].max.subj_point_count) rnd_sett.subj_point_count = rnd_sett_defaults[rnd_sett_defaults.current].max.subj_point_count; | |
| $('#subj_point_count').val(rnd_sett.subj_point_count); | |
| random_subj = get_random_polys("subj"); | |
| random_clip = get_random_polys("clip"); | |
| make_clip(); | |
| }, { | |
| duration: 300, | |
| speed: 0, | |
| min: 150 | |
| }); | |
| $('#clip_polygon_count_minus').hold(function () { | |
| var clip_polygon_count_orig = $('#clip_polygon_count').val(); | |
| if (!isNaN(clip_polygon_count_orig)) rnd_sett.clip_polygon_count = parseFloat(clip_polygon_count_orig); | |
| rnd_sett.clip_polygon_count = rnd_sett.clip_polygon_count - 1; | |
| if (rnd_sett.clip_polygon_count < rnd_sett_defaults[rnd_sett_defaults.current].min.clip_polygon_count) rnd_sett.clip_polygon_count = rnd_sett_defaults[rnd_sett_defaults.current].min.clip_polygon_count; | |
| $('#clip_polygon_count').val(rnd_sett.clip_polygon_count); | |
| random_subj = get_random_polys("subj"); | |
| random_clip = get_random_polys("clip"); | |
| make_clip(); | |
| }, { | |
| duration: 300, | |
| speed: 0, | |
| min: 150 | |
| }); | |
| $('#clip_polygon_count').change(function (e) { | |
| var clip_polygon_count_orig = $('#clip_polygon_count').val(); | |
| if (!isNaN(clip_polygon_count_orig)) rnd_sett.clip_polygon_count = parseFloat(clip_polygon_count_orig); | |
| if (rnd_sett.clip_polygon_count < rnd_sett_defaults[rnd_sett_defaults.current].min.clip_polygon_count) rnd_sett.clip_polygon_count = rnd_sett_defaults[rnd_sett_defaults.current].min.clip_polygon_count; | |
| if (rnd_sett.clip_polygon_count > rnd_sett_defaults[rnd_sett_defaults.current].max.clip_polygon_count) rnd_sett.clip_polygon_count = rnd_sett_defaults[rnd_sett_defaults.current].max.clip_polygon_count; | |
| $('#clip_polygon_count').val(rnd_sett.clip_polygon_count); | |
| random_subj = get_random_polys("subj"); | |
| random_clip = get_random_polys("clip"); | |
| make_clip(); | |
| }); | |
| $('#clip_polygon_count_plus').hold(function () { | |
| var clip_polygon_count_orig = $('#clip_polygon_count').val(); | |
| if (!isNaN(clip_polygon_count_orig)) rnd_sett.clip_polygon_count = parseFloat(clip_polygon_count_orig); | |
| rnd_sett.clip_polygon_count = rnd_sett.clip_polygon_count + 1; | |
| if (rnd_sett.clip_polygon_count > rnd_sett_defaults[rnd_sett_defaults.current].max.clip_polygon_count) rnd_sett.clip_polygon_count = rnd_sett_defaults[rnd_sett_defaults.current].max.clip_polygon_count; | |
| $('#clip_polygon_count').val(rnd_sett.clip_polygon_count); | |
| random_subj = get_random_polys("subj"); | |
| random_clip = get_random_polys("clip"); | |
| make_clip(); | |
| }, { | |
| duration: 300, | |
| speed: 0, | |
| min: 150 | |
| }); | |
| $('#clip_point_count_minus').hold(function () { | |
| var clip_point_count_orig = $('#clip_point_count').val(); | |
| if (!isNaN(clip_point_count_orig)) rnd_sett.clip_point_count = parseFloat(clip_point_count_orig); | |
| rnd_sett.clip_point_count = rnd_sett.clip_point_count - 1; | |
| if (rnd_sett.clip_point_count < rnd_sett_defaults[rnd_sett_defaults.current].min.clip_point_count) rnd_sett.clip_point_count = rnd_sett_defaults[rnd_sett_defaults.current].min.clip_point_count; | |
| $('#clip_point_count').val(rnd_sett.clip_point_count); | |
| random_subj = get_random_polys("subj"); | |
| random_clip = get_random_polys("clip"); | |
| make_clip(); | |
| }, { | |
| duration: 300, | |
| speed: 0, | |
| min: 150 | |
| }); | |
| $('#clip_point_count').change(function (e) { | |
| var clip_point_count_orig = $('#clip_point_count').val(); | |
| if (!isNaN(clip_point_count_orig)) rnd_sett.clip_point_count = parseFloat(clip_point_count_orig); | |
| if (rnd_sett.clip_point_count < rnd_sett_defaults[rnd_sett_defaults.current].min.clip_point_count) rnd_sett.clip_point_count = rnd_sett_defaults[rnd_sett_defaults.current].min.clip_point_count; | |
| if (rnd_sett.clip_point_count > rnd_sett_defaults[rnd_sett_defaults.current].max.clip_point_count) rnd_sett.clip_point_count = rnd_sett_defaults[rnd_sett_defaults.current].max.clip_point_count; | |
| $('#clip_point_count').val(rnd_sett.clip_point_count); | |
| random_subj = get_random_polys("subj"); | |
| random_clip = get_random_polys("clip"); | |
| make_clip(); | |
| }); | |
| $('#clip_point_count_plus').hold(function () { | |
| var clip_point_count_orig = $('#clip_point_count').val(); | |
| if (!isNaN(clip_point_count_orig)) rnd_sett.clip_point_count = parseFloat(clip_point_count_orig); | |
| rnd_sett.clip_point_count = rnd_sett.clip_point_count + 1; | |
| if (rnd_sett.clip_point_count > rnd_sett_defaults[rnd_sett_defaults.current].max.clip_point_count) rnd_sett.clip_point_count = rnd_sett_defaults[rnd_sett_defaults.current].max.clip_point_count; | |
| $('#clip_point_count').val(rnd_sett.clip_point_count); | |
| random_subj = get_random_polys("subj"); | |
| random_clip = get_random_polys("clip"); | |
| make_clip(); | |
| }, { | |
| duration: 300, | |
| speed: 0, | |
| min: 150 | |
| }); | |
| $("#bevel").change(function () { | |
| if ($(this).attr('checked')) { | |
| bevel = true; | |
| $("#p3").attr("filter", "url(#innerbewel)"); | |
| } | |
| else { | |
| bevel = false; | |
| $("#p3").removeAttr("filter"); | |
| } | |
| update_enlarged_SVG_if_needed(); | |
| }); | |
| $("#show_svg_source").click(show_svg_source_click); | |
| $('#sub_poly_links_update').change(function () { | |
| if ($(this).is(':checked')) { | |
| sub_poly_links_update = 1; | |
| if (!p) p = SVG.create(); | |
| if (p1) p1.remove(); | |
| if (p2) p2.remove(); | |
| if (p3) p3.remove(); | |
| SVG.addpaths(ss, cc, off_result, subject_fillType, clip_fillType); | |
| } | |
| else { | |
| sub_poly_links_update = 0; | |
| $("#subj_subpolygons, #subj_points_in_subpolygons, #subj_points_total, #clip_subpolygons, #clip_points_in_subpolygons, #clip_points_total, #solution_subpolygons, #solution_points_in_subpolygons, #solution_points_total, #points_total, #all_subpolygons").html(""); | |
| } | |
| }); | |
| $("#output_format").val(output_format); | |
| myresize(); | |
| $("document").mousedown(function () { | |
| ismousedown = true; | |
| }); | |
| $("document").mouseup(function () { | |
| ismousedown = false; | |
| }); | |
| $("#benchmark1,#benchmark2,#benchmark1b,#benchmark2b").click(function () { | |
| clicked_benchmark_button_id = $(this).attr("id"); | |
| if (benchmark_running && !benchmark_automatic_click) { | |
| for (var lsk = 0; lsk < bench_glob.length; lsk++) { | |
| clearTimeout(bench_glob[lsk].setTimeout); | |
| } | |
| bench_glob.length = 0; | |
| repeat = 0; | |
| benchmark_running = 0; | |
| ClipperLib.MaxSteps = ClipperLib_MaxSteps_original; | |
| $("#" + clicked_benchmark_button_id).html($("#" + clicked_benchmark_button_id).html().replace("Stop", "Run")); | |
| $("#" + clicked_benchmark_button_id).attr("title", $("#" + clicked_benchmark_button_id).attr("title").replace("Stop", "Execute")); | |
| $("button,input,select").removeAttr('disabled'); | |
| $('#sub_poly_links_update').trigger("change"); | |
| return; | |
| } | |
| else { | |
| // disable buttons that are not allowed to click when running | |
| $("button,input,select").attr('disabled', 'disabled'); | |
| $("#benchmark1,#benchmark2,#benchmark1b,#benchmark2b").each(function () { | |
| var $this = $(this); | |
| if ($this.attr("id") != clicked_benchmark_button_id) $this.attr('disabled', 'disabled'); | |
| else $this.removeAttr('disabled'); | |
| }); | |
| } | |
| benchmark_automatic_click = 0; | |
| benchmark_running = 1; | |
| ClipperLib.MaxSteps = 10; | |
| $("#" + clicked_benchmark_button_id).html($("#" + clicked_benchmark_button_id).html().replace("Run", "Stop")); | |
| $("#" + clicked_benchmark_button_id).attr("title", $("#" + clicked_benchmark_button_id).attr("title").replace("Execute", "Stop")); | |
| bench.clear(); | |
| bench.includeSVG = false; | |
| var scaleLocal; | |
| var polygon_id; | |
| var deltaLocal, clipTypeLocal, offsettable_polyLocal_start, offsettable_polyLocal_end; | |
| var offsettable_polyLocal, joinTypeLocal, miterLimitLocal, miterLimitLocal_start, miterLimitLocal_end; | |
| var fillTypeLocal, fillTypeLocal_start, fillTypeLocal_end; | |
| var timeout_time = 0; | |
| var timeout_time_addition = 100; | |
| window.last_completed_bench = ""; | |
| bench_glob.length = 0; | |
| timeout_time = 0; | |
| timeout_time_addition = 0; | |
| latest_time = 10; | |
| window.last_completed_bench = ""; | |
| var count; | |
| var deltaLocals = [-5, 0, 10, 30]; | |
| var deltaLocal_i, m; | |
| var this_id = $(this).attr("id"); | |
| if (this_id == "benchmark1b" || this_id == "benchmark2b") repeat_times = 5; | |
| else repeat_times = 1; | |
| for (count = 0; count < 2; count++) { | |
| if (this_id == "benchmark1" || this_id == "benchmark1b") { | |
| scaleLocal = 100; | |
| if (count === 1) continue; | |
| } | |
| else if (this_id == "benchmark2" || this_id == "benchmark2b") { | |
| scaleLocal = 100000000; | |
| if (count === 1) continue; | |
| } | |
| for (polygon_id = 0; polygon_id < 10; polygon_id++) { | |
| if (!(polygon_id == 0 || polygon_id == 1 || polygon_id == 7 || polygon_id == 8 || polygon_id == 9)) continue; | |
| if (polygon_id == 4 || polygon_id == 5) { | |
| fillTypeLocal_start = 0; | |
| fillTypeLocal_end = 1; | |
| } | |
| else { | |
| fillTypeLocal_start = 1; | |
| fillTypeLocal_end = 1 | |
| } | |
| for (fillTypeLocal = fillTypeLocal_start; fillTypeLocal < fillTypeLocal_end + 1; fillTypeLocal++) { | |
| for (clipTypeLocal = 0; clipTypeLocal < 5; clipTypeLocal++) { | |
| if (clipTypeLocal === 0) { | |
| offsettable_polyLocal_start = 1; | |
| offsettable_polyLocal_end = 2; | |
| } | |
| else { | |
| offsettable_polyLocal_start = 3; | |
| offsettable_polyLocal_end = 3; | |
| } | |
| for (offsettable_polyLocal = offsettable_polyLocal_start; offsettable_polyLocal < offsettable_polyLocal_end + 1; offsettable_polyLocal++) { | |
| if (polygon_id === 0 && offsettable_polyLocal == 2) continue; | |
| for (joinTypeLocal = 0; joinTypeLocal < 3; joinTypeLocal++) { | |
| for (deltaLocal_i = 0, m = deltaLocals.length; deltaLocal_i < m; deltaLocal_i++) { | |
| deltaLocal = deltaLocals[deltaLocal_i]; | |
| if (joinTypeLocal == 2 && deltaLocal !== 0) { | |
| miterLimitLocal_start = 1; | |
| miterLimitLocal_end = 6; | |
| } | |
| else { | |
| miterLimitLocal_start = 1; | |
| miterLimitLocal_end = 1 | |
| } | |
| for (miterLimitLocal = miterLimitLocal_start; miterLimitLocal < miterLimitLocal_end + 1; miterLimitLocal += 2) { | |
| bench_glob[bench_glob.length] = { | |
| polygon_id: polygon_id, | |
| joinType: joinTypeLocal, // 0,1 | |
| offsettable_poly: offsettable_polyLocal, // 1,2,3 | |
| delta: deltaLocal, // -10 - 10 | |
| miterLimit: miterLimitLocal, // 1 - 5 | |
| AutoFix: true, // false, true | |
| ExPolygons: false, // false, true | |
| Simplify: false, // false, true | |
| subject_fillType: fillTypeLocal, // 0,1 | |
| clip_fillType: fillTypeLocal, //0, 1 | |
| clipType: (clipTypeLocal === 0) ? "" : clipTypeLocal - 1, // "",0,1,2,3 | |
| scale: scaleLocal, // 100, 100 000, 1000 000 000 | |
| rnd_sett: { | |
| clip_polygon_count: 4, | |
| clip_point_count: 4, | |
| subj_polygon_count: 4, | |
| subj_point_count: 4 | |
| } | |
| }; | |
| timeout_time += timeout_time_addition; | |
| bench_glob[bench_glob.length - 1].setTimeout = setTimeout("benchmark2(" + (bench_glob.length - 1) + ")", timeout_time); | |
| bench_glob[bench_glob.length - 1].timeout_time = timeout_time; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| }); | |
| $('#scale').val(to_printable(scale)); | |
| $('#delta').val(to_printable(delta)); | |
| $('#miterLimit').val(to_printable(miterLimit)); | |
| if (AutoFix) $('#AutoFix').attr('checked', 'checked'); | |
| else $('#AutoFix').removeAttr('checked'); | |
| if (ExPolygons) $('#ExPolygons').attr('checked', 'checked'); | |
| else $('#ExPolygons').removeAttr('checked'); | |
| if (Simplify) $('#Simplify').attr('checked', 'checked'); | |
| else $('#Simplify').removeAttr('checked'); | |
| if (clean) $('#clean').attr('checked', 'checked'); | |
| else $('#clean').removeAttr('checked'); | |
| $("#cleandelta").val(cleandelta_default); | |
| if (lighten) $('#lighten').attr('checked', 'checked'); | |
| else $('#lighten').removeAttr('checked'); | |
| $('#lighten_distance').val(lighten_distance_default); | |
| $('input[type="radio"][name="subject_fillType"][value="' + subject_fillType + '"]').attr('checked', 'checked'); | |
| $('input[type="radio"][name="clip_fillType"][value="' + clip_fillType + '"]').attr('checked', 'checked'); | |
| $('input[type="radio"][name="polygons"][value="' + polygons_default + '"]').attr('checked', 'checked').change(); | |
| var polygon = parseInt(polygons_default, 10); | |
| if (polygon == 4) rnd_sett = rnd_sett_defaults.rects.default; | |
| else rnd_sett = rnd_sett_defaults.norm.default; | |
| $('input[type="radio"][name="offsettable_poly"][value="' + offsettable_poly + '"]').attr('checked', 'checked'); | |
| $('#subj_polygon_count').val(rnd_sett.subj_polygon_count); | |
| $('#subj_point_count').val(rnd_sett.subj_point_count); | |
| $('#clip_polygon_count').val(rnd_sett.clip_polygon_count); | |
| $('#clip_point_count').val(rnd_sett.clip_point_count); | |
| if (bevel) $('#bevel').attr('checked', 'checked'); | |
| else $('#bevel').removeAttr('checked'); | |
| if (sub_poly_links_update) $('#sub_poly_links_update').attr('checked', 'checked'); | |
| else $('#sub_poly_links_update').removeAttr('checked'); | |
| make_clip(); | |
| colorize_boxes_like_in_svg(); | |
| } // main() | |
| function make_offset() { | |
| // -------------------------------- | |
| // SELECT OFSETTABLE POLYGON STARTS | |
| // -------------------------------- | |
| var off_poly, off_poly1, off_poly2, off_poly3; | |
| offsettable_poly = parseInt($('input[type="radio"][name="offsettable_poly"]:checked').val(), 10); | |
| if (offsettable_poly == 1) { | |
| off_poly = ClipperLib.Clone(ss); | |
| } | |
| else if (offsettable_poly == 2) { | |
| off_poly = ClipperLib.Clone(cc); | |
| } | |
| else if (offsettable_poly == 3) { | |
| off_poly = sss; | |
| } | |
| if (typeof(off_poly) == "undefined" || !(sss instanceof Array)) off_poly = [ | |
| [] | |
| ]; | |
| // ------------------------------ | |
| // SELECT OFSETTABLE POLYGON ENDS | |
| // ------------------------------ | |
| if (ClipperLib.biginteger_used === null) ClipperLib.biginteger_used = 0; | |
| // ------------- | |
| // CLEAN STARTS | |
| // ------------- | |
| if (clean) { | |
| off_poly = ClipperLib.Clean(off_poly, cleandelta * scale); | |
| } | |
| //if (isArray(off_poly) && off_poly.length) | |
| //console.log(JSON.stringify(off_poly[0])); | |
| // ------------- | |
| // CLEAN ENDS | |
| // ------------- | |
| // --------------- | |
| // SIMPLIFY STARTS | |
| // --------------- | |
| // Must simplify before offsetting, to get offsetting right in certain cases. | |
| // Other operations (boolean ones) doesn't need this. | |
| // This is needed when offsetting polygons that has selfintersecting parts ( eg. 5-point star needs this ) | |
| if (Simplify) { | |
| // Simplifying is only needed when offsetting original polys, because | |
| // results of boolean operations are already simplified. | |
| // Note! if clip polygon is the same as subject polygon | |
| // then it seems that simplifying is needed also for result of boolean operation (ie. solution). | |
| if (offsettable_poly == 1) // subj | |
| { | |
| off_poly = cpr.SimplifyPolygons(off_poly, subject_fillType); | |
| } | |
| if (offsettable_poly == 2) // clip | |
| { | |
| off_poly = cpr.SimplifyPolygons(off_poly, clip_fillType); | |
| } | |
| if (offsettable_poly == 3) // solution | |
| { | |
| off_poly = cpr.SimplifyPolygons(off_poly, clip_fillType); | |
| if (subject_fillType !== clip_fillType) { | |
| console.log("Subject filltype and Clip filltype are different. We used Clip filltype in SimplifyPolygons()."); | |
| } | |
| } | |
| } | |
| // ------------- | |
| // SIMPLIFY ENDS | |
| // ------------- | |
| // ------------------------------ | |
| // ACTUAL OFFSET OPERATION STARTS | |
| // ------------------------------ | |
| if (delta) { | |
| for (var i = 0; i < 10; i++) { | |
| cpr.Clear(); | |
| cpr.alternateAlgo = false; | |
| var param_delta = round_to(delta * scale, 3); | |
| var param_miterLimit = round_to(miterLimit, 3); | |
| var B0 = bench.start("Offset", "Offset(" + param_delta + ", " + joinType + ", " + param_miterLimit + ", " + AutoFix + ")"); | |
| off_result = cpr.OffsetPolygons(off_poly, param_delta, joinType, param_miterLimit, AutoFix); | |
| bench.end(B0); | |
| cpr.Clear(); | |
| cpr.alternateAlgo = true; | |
| var param_delta = round_to(delta * scale, 3); | |
| var param_miterLimit = round_to(miterLimit, 3); | |
| var B0 = bench.start("Offset bulk insert", "Offset(" + param_delta + ", " + joinType + ", " + param_miterLimit + ", " + AutoFix + ")"); | |
| off_result = cpr.OffsetPolygons(off_poly, param_delta, joinType, param_miterLimit, AutoFix); | |
| bench.end(B0); | |
| } | |
| } | |
| else { | |
| off_result = off_poly; | |
| } | |
| // ---------------------------- | |
| // ACTUAL OFFSET OPERATION ENDS | |
| // ------------------------------ | |
| // ---------------------------- | |
| // LIGHTENING STARTS | |
| // ------------------------------ | |
| if (lighten) { | |
| off_result = ClipperLib.Lighten(off_result, lighten_distance * scale); | |
| // Because lighten may produce self-intersections, | |
| // must Simplify to be sure that result is free of them, | |
| // but only if user wants | |
| if (Simplify) { | |
| off_result = cpr.SimplifyPolygons(off_result, subject_fillType); | |
| } | |
| } | |
| // ---------------------------- | |
| // LIGHTENING ENDS | |
| // ------------------------------ | |
| // -------------------------------- | |
| // SVG UPDATE STARTS | |
| // -------------------------------- | |
| if (!p) p = SVG.create(); | |
| if (p1) p1.remove(); | |
| if (p2) p2.remove(); | |
| if (p3) p3.remove(); | |
| if (bench.includeSVG) var B3 = bench.start("SVG", "addpaths(ss, cc, off_result, " + subject_fillType + ", " + clip_fillType + ")"); | |
| SVG.addpaths(ss, cc, off_result, subject_fillType, clip_fillType); | |
| if (bench.includeSVG) bench.end(B3); | |
| // -------------------------------- | |
| // SVG UPDATE ENDS | |
| // -------------------------------- | |
| // -------------------------------- | |
| // UPDATE BIGINTEGERS TOGGLE STARTS | |
| // -------------------------------- | |
| if (ClipperLib.biginteger_used !== null) { | |
| if (ClipperLib.biginteger_used) $("#biginteger_used").html("true"); | |
| else $("#biginteger_used").html("false"); | |
| } | |
| else $("#biginteger_used").html("unknown"); | |
| ClipperLib.biginteger_used = null; | |
| // ------------------------------ | |
| // UPDATE BIGINTEGERS TOGGLE ENDS | |
| // ------------------------------ | |
| // UPDATE DELTA TO FORM | |
| $('#delta').val(to_printable(delta)); | |
| // UPDATE MITERLIMIT TO FORM | |
| $('#miterLimit').val(to_printable(miterLimit)); | |
| // PRINT BENCHMARKS | |
| $("#benchmark_div").html(bench.print()); | |
| update_enlarged_SVG_if_needed(); | |
| update_fieldset_heights(); | |
| } | |
| function make_clip() { | |
| ClipperLib.biginteger_used = null; | |
| if (!cpr) { | |
| cpr = new ClipperLib.Clipper(); | |
| } | |
| else cpr.Clear(); | |
| if (!cpr2) { | |
| cpr2 = new ClipperLib.Clipper(); | |
| } | |
| else cpr2.Clear(); | |
| get_polys(); | |
| if (clipType !== "" && offsettable_poly == 3) { | |
| for (var i = 0; i < 10; i++) { | |
| cpr.Clear() | |
| cpr.AddPolygons(ss, ClipperLib.PolyType.ptSubject); | |
| cpr.AddPolygons(cc, ClipperLib.PolyType.ptClip); | |
| sss = new ClipperLib.Polygons(); | |
| cpr.alternateAlgo = false; | |
| var B1 = bench.start("Boolean", "Execute(" + clipType + ", sss, " + subject_fillType + ", " + clip_fillType + ")"); | |
| cpr.Execute(clipType, sss, subject_fillType, clip_fillType); | |
| bench.end(B1); | |
| cpr2.Clear(); | |
| cpr2.AddPolygons(ss, ClipperLib.PolyType.ptSubject); | |
| cpr2.AddPolygons(cc, ClipperLib.PolyType.ptClip); | |
| sss2 = new ClipperLib.Polygons(); | |
| cpr2.alternateAlgo = true; | |
| var B2 = bench.start("Boolean bulk insert", "Execute(" + clipType + ", sss2, " + subject_fillType + ", " + clip_fillType + ")"); | |
| cpr2.Execute(clipType, sss2, subject_fillType, clip_fillType); | |
| bench.end(B2); | |
| } | |
| } | |
| make_offset(); | |
| } | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment