<html>
<head>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
<meta charset="utf-8">
<title>Directions service</title>
<link href="https://google-developers.appspot.com/maps/documentation/javascript/examples/default.css" rel="stylesheet">
<script src="https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false"></script>
<script>
var directionsDisplay;
var directionsService = new google.maps.DirectionsService();
var map;
var bermudaTriangle;
var tacke;
var primer;
var rezultat;
function initialize() {
directionsDisplay = new google.maps.DirectionsRenderer();
var chicago = new google.maps.LatLng(41.850033, -87.6500523);
var mapOptions = {
zoom:7,
mapTypeId: google.maps.MapTypeId.ROADMAP,
center: chicago
}
map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions);
directionsDisplay.setMap(map);
}
function calcRoute() {
var start = document.getElementById('start').value;
var end = document.getElementById('end').value;
var request = {
origin:start,
destination:end,
travelMode: google.maps.DirectionsTravelMode.DRIVING
};
directionsService.route(request, function(response, status) {
if (status == google.maps.DirectionsStatus.OK) {
directionsDisplay.setDirections(response);
tacke = response.routes[0].overview_path;
primer = [[{"X":72,"Y":59.45},{"X":136,"Y":66},{"X":170,"Y":99},{"X":171,"Y":114},{"X":183,"Y":125},{"X":218,"Y":144},{"X":218,"Y":165},{"X":226,"Y":193},{"X":254,"Y":195},{"X":283,"Y":195},{"X":292,"Y":202},{"X":325,"Y":213},{"X":341,"Y":234},{"X":397,"Y":245},{"X":417,"Y":248}]];
function draw() {
var polygons = response.routes[0].overview_path;
var scale = 100;
reverse_copy(polygons);
polygons = scaleup(polygons, scale);
var cpr = new ClipperLib.Clipper();
var delta = 25;
var joinType = ClipperLib.JoinType.jtRound;
var miterLimit = 2;
var AutoFix = true;
var svg, offsetted_polygon,
cont = document.getElementById('map-canvas');
offsetted_polygon = cpr.OffsetPolygons(polygons, delta * scale, joinType, miterLimit, AutoFix);
console.log(JSON.stringify(offsetted_polygon));
// Draw red offset polygon
//svg = '<svg style="margin-top:10px;margin-right:10px;margin-bottom:10px;background-color:#dddddd" width="540" height="340">';
svg = polys2path(offsetted_polygon, scale);
rezultat = polys2path(offsetted_polygon, scale);
//Draw blue polyline
//svg += '<path stroke="blue" stroke-width="3" d="' + polys2path(polygons, scale) + '"/>';
//svg += '</svg>';
//cont.innerHTML += svg;
bermudaTriangle = new google.maps.Polygon({
paths: svg,
strokeColor: '#FF0000',
strokeOpacity: 0.8,
strokeWeight: 2,
fillColor: '#FF0000',
fillOpacity: 0.35
});
bermudaTriangle.setMap(map);
}
// helper function to scale up polygon coordinates
function scaleup(poly, scale) {
var i, j;
if (!scale) scale = 1;
for(i = 0; i < poly.length; i++) {
for(j = 0; j < poly[i].length; j++) {
poly[i][j].lb *= scale;
poly[i][j].mb *= scale;
}
}
return poly;
}
// converts polygons to SVG path string
function polys2path (poly, scale) {
var path = "", i, j;
if (!scale) scale = 1;
for(i = 0; i < poly.length; i++) {
for(j = 0; j < poly[i].length; j++){
if (!j) path += "M";
else path += "L";
path += (poly[i][j].lb / scale) + ", " + (poly[i][j].mb / scale);
}
path += "Z";
}
return path;
}
function reverse_copy(poly) {
// Make reverse copy of polygons = convert polyline to a 'flat' polygon ...
var k, klen = poly.length, len, j;
for (k = 0; k < klen; k++) {
len = poly[k].length;
poly[k].length = len * 2 - 2;
for (j = 1; j <= len - 2; j++) {
poly[k][len - 1 + j] = {
lb: poly[k][len - 1 - j].lb,
mb: poly[k][len - 1 - j].mb
}
}
}
}
}
});
}
google.maps.event.addDomListener(window, 'load', initialize);
</script>
</head>
<body>
<div id="panel">
<b>Start: </b>
<select id="start" onchange="calcRoute();draw();">
<option value="chicago, il">Chicago</option>
<option value="st louis, mo">St Louis</option>
<option value="joplin, mo">Joplin, MO</option>
<option value="oklahoma city, ok">Oklahoma City</option>
<option value="amarillo, tx">Amarillo</option>
<option value="gallup, nm">Gallup, NM</option>
<option value="flagstaff, az">Flagstaff, AZ</option>
<option value="winona, az">Winona</option>
<option value="kingman, az">Kingman</option>
<option value="barstow, ca">Barstow</option>
<option value="san bernardino, ca">San Bernardino</option>
<option value="los angeles, ca">Los Angeles</option>
</select>
<b>End: </b>
<select id="end" onchange="calcRoute();">
<option value="chicago, il">Chicago</option>
<option value="st louis, mo">St Louis</option>
<option value="joplin, mo">Joplin, MO</option>
<option value="oklahoma city, ok">Oklahoma City</option>
<option value="amarillo, tx">Amarillo</option>
<option value="gallup, nm">Gallup, NM</option>
<option value="flagstaff, az">Flagstaff, AZ</option>
<option value="winona, az">Winona</option>
<option value="kingman, az">Kingman</option>
<option value="barstow, ca">Barstow</option>
<option value="san bernardino, ca">San Bernardino</option>
<option value="los angeles, ca">Los Angeles</option>
</select>
</div>
<div id="map-canvas"></div> <div id="svgcontainer"></div>
</body>
</html>
/*******************************************************************************
* *
* 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 = 64; // How many steps at maximum in arc in BuildArc() function
ClipperLib.PI = 3.141592653589793;
ClipperLib.PI2 = 2 * 3.141592653589793;
ClipperLib.IntPoint = function ()
{
var a = arguments;
if (a.length == 1)
{
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
{
this.BuildIntersectList(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.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 > 64) steps = ClipperLib.MaxSteps; // to avoid overload
// if (steps > 1048576) steps = 1048576; // 0x100000
// if (steps > ClipperLib.MaxSteps) steps = ClipperLib.MaxSteps; // 0x100000
// Had to change 1048576 to lower value, because when coordinates are near or above lorange, program starts hanging
// Adjust this value to meet your needs, maybe 10 is enough for most purposes
var n = ClipperLib.Cast_Int32(steps);
var result = new ClipperLib.Polygon();
var da = (a2 - a1) / (n - 1);
var a = a1;
for (var i = 0; i < n; ++i)
{
result.push(new ClipperLib.IntPoint(pt.X + ClipperLib.Clipper.Round(Math.cos(a) * r), pt.Y + ClipperLib.Clipper.Round(Math.sin(a) * r)));
a += da;
}
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;
})();
Output
You can jump to the latest bin by adding /latest
to your URL
Keyboard Shortcuts
Shortcut | Action |
---|---|
ctrl + [num] | Toggle nth panel |
ctrl + 0 | Close focused panel |
ctrl + enter | Re-render output. If console visible: run JS in console |
Ctrl + l | Clear the console |
ctrl + / | Toggle comment on selected lines |
ctrl + ] | Indents selected lines |
ctrl + [ | Unindents selected lines |
tab | Code complete & Emmet expand |
ctrl + shift + L | Beautify code in active panel |
ctrl + s | Save & lock current Bin from further changes |
ctrl + shift + s | Open the share options |
ctrl + y | Archive Bin |
Complete list of JS Bin shortcuts |
JS Bin URLs
URL | Action |
---|---|
/ | Show the full rendered output. This content will update in real time as it's updated from the /edit url. |
/edit | Edit the current bin |
/watch | Follow a Code Casting session |
/embed | Create an embeddable version of the bin |
/latest | Load the very latest bin (/latest goes in place of the revision) |
/[username]/last | View the last edited bin for this user |
/[username]/last/edit | Edit the last edited bin for this user |
/[username]/last/watch | Follow the Code Casting session for the latest bin for this user |
/quiet | Remove analytics and edit button from rendered output |
.js | Load only the JavaScript for a bin |
.css | Load only the CSS for a bin |
Except for username prefixed urls, the url may start with http://jsbin.com/abc and the url fragments can be added to the url to view it differently. |