From c76085f6f92a26fbdcb5dfd0a0a6dd30f22e2ecf Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Fri, 25 Jul 2025 18:38:40 -0700 Subject: [PATCH] add jspoly and checkpoint thinwall v3 --- src/ext/jspoly.js | 7295 +++++++++++++++++++++++++++++++++++++ src/geo/polygon.js | 37 +- src/kiri/mode/fdm/post.js | 128 +- 3 files changed, 7427 insertions(+), 33 deletions(-) create mode 100644 src/ext/jspoly.js diff --git a/src/ext/jspoly.js b/src/ext/jspoly.js new file mode 100644 index 00000000..61bfdb1f --- /dev/null +++ b/src/ext/jspoly.js @@ -0,0 +1,7295 @@ +/* +JSPoly +This is a Javascript translation of the Boost Polygon Library written in C++. +For 128-bit big integer support this library uses JSBN library from Tom Wu. + +Author : Ruwan J Egodagamage (japzi) +Version : 1.0.1 +Date : 3/22/2018 +License : Boost Software License - Version 1.0 - August 17th, 2003 + +Contributors : rodovich, ahinz, gensym, nevernormal1 + +*/ + +/* +THE BOOST.POLYGON LIBRARY +Version : 1_64_0 + +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +*/ + +/* +The jsbn library is a fast, portable implementation of large-number math in pure JavaScript + +Copyright (c) 2003-2005 Tom Wu +All Rights Reserved. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, +EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY +WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +IN NO EVENT SHALL TOM WU BE LIABLE FOR ANY SPECIAL, INCIDENTAL, +INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER +RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF +THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +In addition, the following condition applies: + +All redistributions must retain an intact copy of this copyright notice +and disclaimer. +*/ + +var isNode = false; +if (typeof module !== 'undefined' && module.exports) +{ + isNode = true; +} + +var navigatorAppName; +if (!isNode) +{ + var nav = navigator.userAgent.toString().toLowerCase(); + navigatorAppName = navigator.appName; +} +else +{ + var nav = "chrome"; // Node.js uses Chrome's V8 engine + navigatorAppName = "Netscape"; // Firefox, Chrome and Safari returns "Netscape", so Node.js should also +} + +// Basic JavaScript BN library - subset useful for RSA encryption. + +// Bits per digit +var dbits; + +// JavaScript engine analysis +var canary = 0xdeadbeefcafe; +var j_lm = ((canary&0xffffff)==0xefcafe); + +// (public) Constructor +function BigInteger(a,b,c) { + if(a != null) + if("number" == typeof a) this.fromNumber(a,b,c); + else if(b == null && "string" != typeof a) this.fromString(a,256); + else this.fromString(a,b); +} + +// return new, unset BigInteger +function nbi() { return new BigInteger(null); } + +// am: Compute w_j += (x*this_i), propagate carries, +// c is initial carry, returns final carry. +// c < 3*dvalue, x < 2*dvalue, this_i < dvalue +// We need to select the fastest one that works in this environment. + +// am1: use a single mult and divide to get the high bits, +// max digit bits should be 26 because +// max internal value = 2*dvalue^2-2*dvalue (< 2^53) +function am1(i,x,w,j,c,n) { + while(--n >= 0) { + var v = x*this[i++]+w[j]+c; + c = Math.floor(v/0x4000000); + w[j++] = v&0x3ffffff; + } + return c; +} +// am2 avoids a big mult-and-extract completely. +// Max digit bits should be <= 30 because we do bitwise ops +// on values up to 2*hdvalue^2-hdvalue-1 (< 2^31) +function am2(i,x,w,j,c,n) { + var xl = x&0x7fff, xh = x>>15; + while(--n >= 0) { + var l = this[i]&0x7fff; + var h = this[i++]>>15; + var m = xh*l+h*xl; + l = xl*l+((m&0x7fff)<<15)+w[j]+(c&0x3fffffff); + c = (l>>>30)+(m>>>15)+xh*h+(c>>>30); + w[j++] = l&0x3fffffff; + } + return c; +} +// Alternately, set max digit bits to 28 since some +// browsers slow down when dealing with 32-bit numbers. +function am3(i,x,w,j,c,n) { + var xl = x&0x3fff, xh = x>>14; + while(--n >= 0) { + var l = this[i]&0x3fff; + var h = this[i++]>>14; + var m = xh*l+h*xl; + l = xl*l+((m&0x3fff)<<14)+w[j]+c; + c = (l>>28)+(m>>14)+xh*h; + w[j++] = l&0xfffffff; + } + return c; +} +if(j_lm && (navigatorAppName == "Microsoft Internet Explorer")) { + BigInteger.prototype.am = am2; + dbits = 30; +} +else if(j_lm && (navigatorAppName != "Netscape")) { + BigInteger.prototype.am = am1; + dbits = 26; +} +else { // Mozilla/Netscape seems to prefer am3 + BigInteger.prototype.am = am3; + dbits = 28; +} + +BigInteger.prototype.DB = dbits; +BigInteger.prototype.DM = ((1<= 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))<>(this.DB-sh)); + } + else + this[this.t-1] |= x<= 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)< 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< 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+=this.DB-k); + } + else { + d = (this[i]>>(p-=k))&km; + if(p <= 0) { p += this.DB; --i; } + } + if(d > 0) m = true; + if(m) r += int2char(d); + } + } + return m?r:"0"; +} + +// (public) -this +function bnNegate() { var r = nbi(); BigInteger.ZERO.subTo(this,r); return r; } + +// (public) |this| +function bnAbs() { return (this.s<0)?this.negate():this; } + +// (public) return + if this > a, - if this < a, 0 if equal +function bnCompareTo(a) { + var r = this.s-a.s; + if(r != 0) return r; + var i = this.t; + r = i-a.t; + if(r != 0) return (this.s<0)?-r:r; + while(--i >= 0) if((r=this[i]-a[i]) != 0) return r; + return 0; +} + +// returns bit length of the integer x +function nbits(x) { + var r = 1, t; + if((t=x>>>16) != 0) { x = t; r += 16; } + if((t=x>>8) != 0) { x = t; r += 8; } + if((t=x>>4) != 0) { x = t; r += 4; } + if((t=x>>2) != 0) { x = t; r += 2; } + if((t=x>>1) != 0) { x = t; r += 1; } + return r; +} + +// (public) return the number of bits in "this" +function bnBitLength() { + if(this.t <= 0) return 0; + return this.DB*(this.t-1)+nbits(this[this.t-1]^(this.s&this.DM)); +} + +// (protected) r = this << n*DB +function bnpDLShiftTo(n,r) { + var i; + for(i = this.t-1; i >= 0; --i) r[i+n] = this[i]; + for(i = n-1; i >= 0; --i) r[i] = 0; + r.t = this.t+n; + r.s = this.s; +} + +// (protected) r = this >> n*DB +function bnpDRShiftTo(n,r) { + for(var i = n; i < this.t; ++i) r[i-n] = this[i]; + r.t = Math.max(this.t-n,0); + r.s = this.s; +} + +// (protected) r = this << n +function bnpLShiftTo(n,r) { + var bs = n%this.DB; + var cbs = this.DB-bs; + var bm = (1<= 0; --i) { + r[i+ds+1] = (this[i]>>cbs)|c; + c = (this[i]&bm)<= 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; + for(var i = ds+1; i < this.t; ++i) { + r[i-ds-1] |= (this[i]&bm)<>bs; + } + if(bs > 0) r[this.t-ds-1] |= (this.s&bm)<>= this.DB; + } + if(a.t < this.t) { + c -= a.s; + while(i < this.t) { + c += this[i]; + r[i++] = c&this.DM; + c >>= this.DB; + } + c += this.s; + } + else { + c += this.s; + while(i < a.t) { + c -= a[i]; + r[i++] = c&this.DM; + c >>= this.DB; + } + c -= a.s; + } + r.s = (c<0)?-1:0; + if(c < -1) r[i++] = this.DV+c; + else if(c > 0) r[i++] = c; + r.t = i; + r.clamp(); +} + +// (protected) r = this * a, r != this,a (HAC 14.12) +// "this" should be the larger one if appropriate. +function bnpMultiplyTo(a,r) { + var x = this.abs(), y = a.abs(); + var i = x.t; + r.t = i+y.t; + while(--i >= 0) r[i] = 0; + for(i = 0; i < y.t; ++i) r[i+x.t] = x.am(0,y[i],r,i,0,x.t); + r.s = 0; + r.clamp(); + if(this.s != a.s) BigInteger.ZERO.subTo(r,r); +} + +// (protected) r = this^2, r != this (HAC 14.16) +function bnpSquareTo(r) { + var x = this.abs(); + var i = r.t = 2*x.t; + while(--i >= 0) r[i] = 0; + for(i = 0; i < x.t-1; ++i) { + var c = x.am(i,x[i],r,2*i,0,1); + if((r[i+x.t]+=x.am(i+1,2*x[i],r,2*i+1,c,x.t-i-1)) >= x.DV) { + r[i+x.t] -= x.DV; + r[i+x.t+1] = 1; + } + } + if(r.t > 0) r[r.t-1] += x.am(i,x[i],r,2*i,0,1); + r.s = 0; + r.clamp(); +} + +// (protected) divide this by m, quotient and remainder to q, r (HAC 14.20) +// r != q, this != m. q or r may be null. +function bnpDivRemTo(m,q,r) { + var pm = m.abs(); + if(pm.t <= 0) return; + var pt = this.abs(); + if(pt.t < pm.t) { + if(q != null) q.fromInt(0); + if(r != null) this.copyTo(r); + return; + } + if(r == null) r = nbi(); + var y = nbi(), ts = this.s, ms = m.s; + var nsh = this.DB-nbits(pm[pm.t-1]); // normalize modulus + if(nsh > 0) { pm.lShiftTo(nsh,y); pt.lShiftTo(nsh,r); } + else { pm.copyTo(y); pt.copyTo(r); } + var ys = y.t; + var y0 = y[ys-1]; + if(y0 == 0) return; + var yt = y0*(1<1)?y[ys-2]>>this.F2:0); + var d1 = this.FV/yt, d2 = (1<= 0) { + r[r.t++] = 1; + r.subTo(t,r); + } + BigInteger.ONE.dlShiftTo(ys,t); + t.subTo(y,y); // "negative" y so we can replace sub with am later + while(y.t < ys) y[y.t++] = 0; + while(--j >= 0) { + // Estimate quotient digit + var qd = (r[--i]==y0)?this.DM:Math.floor(r[i]*d1+(r[i-1]+e)*d2); + if((r[i]+=y.am(0,qd,r,j,0,ys)) < qd) { // Try it out + y.dlShiftTo(j,t); + r.subTo(t,r); + while(r[i] < --qd) r.subTo(t,r); + } + } + if(q != null) { + r.drShiftTo(ys,q); + if(ts != ms) BigInteger.ZERO.subTo(q,q); + } + r.t = ys; + r.clamp(); + if(nsh > 0) r.rShiftTo(nsh,r); // Denormalize remainder + if(ts < 0) BigInteger.ZERO.subTo(r,r); +} + +// (public) this mod a +function bnMod(a) { + var r = nbi(); + this.abs().divRemTo(a,null,r); + if(this.s < 0 && r.compareTo(BigInteger.ZERO) > 0) a.subTo(r,r); + return r; +} + +// Modular reduction using "classic" algorithm +function Classic(m) { this.m = m; } +function cConvert(x) { + if(x.s < 0 || x.compareTo(this.m) >= 0) return x.mod(this.m); + else return x; +} +function cRevert(x) { return x; } +function cReduce(x) { x.divRemTo(this.m,null,x); } +function cMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); } +function cSqrTo(x,r) { x.squareTo(r); this.reduce(r); } + +Classic.prototype.convert = cConvert; +Classic.prototype.revert = cRevert; +Classic.prototype.reduce = cReduce; +Classic.prototype.mulTo = cMulTo; +Classic.prototype.sqrTo = cSqrTo; + +// (protected) return "-1/this % 2^DB"; useful for Mont. reduction +// justification: +// xy == 1 (mod m) +// xy = 1+km +// xy(2-xy) = (1+km)(1-km) +// x[y(2-xy)] = 1-k^2m^2 +// x[y(2-xy)] == 1 (mod m^2) +// if y is 1/x mod m, then y(2-xy) is 1/x mod m^2 +// should reduce x and y(2-xy) by m^2 at each step to keep size bounded. +// JS multiply "overflows" differently from C/C++, so care is needed here. +function bnpInvDigit() { + if(this.t < 1) return 0; + var x = this[0]; + if((x&1) == 0) return 0; + var y = x&3; // y == 1/x mod 2^2 + y = (y*(2-(x&0xf)*y))&0xf; // y == 1/x mod 2^4 + y = (y*(2-(x&0xff)*y))&0xff; // y == 1/x mod 2^8 + y = (y*(2-(((x&0xffff)*y)&0xffff)))&0xffff; // y == 1/x mod 2^16 + // last step - calculate inverse mod DV directly; + // assumes 16 < DB <= 32 and assumes ability to handle 48-bit ints + y = (y*(2-x*y%this.DV))%this.DV; // y == 1/x mod 2^dbits + // we really want the negative inverse, and -DV < y < DV + return (y>0)?this.DV-y:-y; +} + +// Montgomery reduction +function Montgomery(m) { + this.m = m; + this.mp = m.invDigit(); + this.mpl = this.mp&0x7fff; + this.mph = this.mp>>15; + this.um = (1<<(m.DB-15))-1; + this.mt2 = 2*m.t; +} + +// xR mod m +function montConvert(x) { + var r = nbi(); + x.abs().dlShiftTo(this.m.t,r); + r.divRemTo(this.m,null,r); + if(x.s < 0 && r.compareTo(BigInteger.ZERO) > 0) this.m.subTo(r,r); + return r; +} + +// x/R mod m +function montRevert(x) { + var r = nbi(); + x.copyTo(r); + this.reduce(r); + return r; +} + +// x = x/R mod m (HAC 14.32) +function montReduce(x) { + while(x.t <= this.mt2) // pad x so am has enough room later + x[x.t++] = 0; + for(var i = 0; i < this.m.t; ++i) { + // faster way of calculating u0 = x[i]*mp mod DV + var j = x[i]&0x7fff; + var u0 = (j*this.mpl+(((j*this.mph+(x[i]>>15)*this.mpl)&this.um)<<15))&x.DM; + // use am to combine the multiply-shift-add into one call + j = i+this.m.t; + x[j] += this.m.am(0,u0,x,i,0,this.m.t); + // propagate carry + while(x[j] >= x.DV) { x[j] -= x.DV; x[++j]++; } + } + x.clamp(); + x.drShiftTo(this.m.t,x); + if(x.compareTo(this.m) >= 0) x.subTo(this.m,x); +} + +// r = "x^2/R mod m"; x != r +function montSqrTo(x,r) { x.squareTo(r); this.reduce(r); } + +// r = "xy/R mod m"; x,y != r +function montMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); } + +Montgomery.prototype.convert = montConvert; +Montgomery.prototype.revert = montRevert; +Montgomery.prototype.reduce = montReduce; +Montgomery.prototype.mulTo = montMulTo; +Montgomery.prototype.sqrTo = montSqrTo; + +// (protected) true iff this is even +function bnpIsEven() { return ((this.t>0)?(this[0]&1):this.s) == 0; } + +// (protected) this^e, e < 2^32, doing sqr and mul with "r" (HAC 14.79) +function bnpExp(e,z) { + if(e > 0xffffffff || e < 1) return BigInteger.ONE; + var r = nbi(), r2 = nbi(), g = z.convert(this), i = nbits(e)-1; + g.copyTo(r); + while(--i >= 0) { + z.sqrTo(r,r2); + if((e&(1< 0) z.mulTo(r2,g,r); + else { var t = r; r = r2; r2 = t; } + } + return z.revert(r); +} + +// (public) this^e % m, 0 <= e < 2^32 +function bnModPowInt(e,m) { + var z; + if(e < 256 || m.isEven()) z = new Classic(m); else z = new Montgomery(m); + return this.exp(e,z); +} + +// protected +BigInteger.prototype.copyTo = bnpCopyTo; +BigInteger.prototype.fromInt = bnpFromInt; +BigInteger.prototype.fromString = bnpFromString; +BigInteger.prototype.clamp = bnpClamp; +BigInteger.prototype.dlShiftTo = bnpDLShiftTo; +BigInteger.prototype.drShiftTo = bnpDRShiftTo; +BigInteger.prototype.lShiftTo = bnpLShiftTo; +BigInteger.prototype.rShiftTo = bnpRShiftTo; +BigInteger.prototype.subTo = bnpSubTo; +BigInteger.prototype.multiplyTo = bnpMultiplyTo; +BigInteger.prototype.squareTo = bnpSquareTo; +BigInteger.prototype.divRemTo = bnpDivRemTo; +BigInteger.prototype.invDigit = bnpInvDigit; +BigInteger.prototype.isEven = bnpIsEven; +BigInteger.prototype.exp = bnpExp; + +// public +BigInteger.prototype.toString = bnToString; +BigInteger.prototype.negate = bnNegate; +BigInteger.prototype.abs = bnAbs; +BigInteger.prototype.compareTo = bnCompareTo; +BigInteger.prototype.bitLength = bnBitLength; +BigInteger.prototype.mod = bnMod; +BigInteger.prototype.modPowInt = bnModPowInt; + +// "constants" +BigInteger.ZERO = nbv(0); +BigInteger.ONE = nbv(1); + + +// Extended JavaScript BN functions, required for RSA private ops. + +// Version 1.1: new BigInteger("0", 10) returns "proper" zero +// Version 1.2: square() API, isProbablePrime fix + +// (public) +function bnClone() { var r = nbi(); this.copyTo(r); return r; } + +// (public) return value as integer +function bnIntValue() { + if(this.s < 0) { + if(this.t == 1) return this[0]-this.DV; + else if(this.t == 0) return -1; + } + else if(this.t == 1) return this[0]; + else if(this.t == 0) return 0; + // assumes 16 < DB < 32 + return ((this[1]&((1<<(32-this.DB))-1))<>24; } + +// (public) return value as short (assumes DB>=16) +function bnShortValue() { return (this.t==0)?this.s:(this[0]<<16)>>16; } + +// (protected) return x s.t. r^x < DV +function bnpChunkSize(r) { return Math.floor(Math.LN2*this.DB/Math.log(r)); } + +// (public) 0 if this == 0, 1 if this > 0 +function bnSigNum() { + if(this.s < 0) return -1; + else if(this.t <= 0 || (this.t == 1 && this[0] <= 0)) return 0; + else return 1; +} + +// (protected) convert to radix string +function bnpToRadix(b) { + if(b == null) b = 10; + if(this.signum() == 0 || b < 2 || b > 36) return "0"; + var cs = this.chunkSize(b); + var a = Math.pow(b,cs); + var d = nbv(a), y = nbi(), z = nbi(), r = ""; + this.divRemTo(d,y,z); + while(y.signum() > 0) { + r = (a+z.intValue()).toString(b).substr(1) + r; + y.divRemTo(d,y,z); + } + return z.intValue().toString(b) + r; +} + +// (protected) convert from radix string +function bnpFromRadix(s,b) { + this.fromInt(0); + if(b == null) b = 10; + var cs = this.chunkSize(b); + var d = Math.pow(b,cs), mi = false, j = 0, w = 0; + for(var i = 0; i < s.length; ++i) { + var x = intAt(s,i); + if(x < 0) { + if(s.charAt(i) == "-" && this.signum() == 0) mi = true; + continue; + } + w = b*w+x; + if(++j >= cs) { + this.dMultiply(d); + this.dAddOffset(w,0); + j = 0; + w = 0; + } + } + if(j > 0) { + this.dMultiply(Math.pow(b,j)); + this.dAddOffset(w,0); + } + if(mi) BigInteger.ZERO.subTo(this,this); +} + +// (protected) alternate constructor +function bnpFromNumber(a,b,c) { + if("number" == typeof b) { + // new BigInteger(int,int,RNG) + if(a < 2) this.fromInt(1); + else { + this.fromNumber(a,c); + if(!this.testBit(a-1)) // force MSB set + this.bitwiseTo(BigInteger.ONE.shiftLeft(a-1),op_or,this); + if(this.isEven()) this.dAddOffset(1,0); // force odd + while(!this.isProbablePrime(b)) { + this.dAddOffset(2,0); + if(this.bitLength() > a) this.subTo(BigInteger.ONE.shiftLeft(a-1),this); + } + } + } + else { + // new BigInteger(int,RNG) + var x = new Array(), t = a&7; + x.length = (a>>3)+1; + b.nextBytes(x); + if(t > 0) x[0] &= ((1< 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+=this.DB-8); + } + else { + d = (this[i]>>(p-=8))&0xff; + if(p <= 0) { p += this.DB; --i; } + } + if((d&0x80) != 0) d |= -256; + if(k == 0 && (this.s&0x80) != (d&0x80)) ++k; + if(k > 0 || d != this.s) r[k++] = d; + } + } + return r; +} + +function bnEquals(a) { return(this.compareTo(a)==0); } +function bnMin(a) { return(this.compareTo(a)<0)?this:a; } +function bnMax(a) { return(this.compareTo(a)>0)?this:a; } + +// (protected) r = this op a (bitwise) +function bnpBitwiseTo(a,op,r) { + var i, f, m = Math.min(a.t,this.t); + for(i = 0; i < m; ++i) r[i] = op(this[i],a[i]); + if(a.t < this.t) { + f = a.s&this.DM; + for(i = m; i < this.t; ++i) r[i] = op(this[i],f); + r.t = this.t; + } + else { + f = this.s&this.DM; + for(i = m; i < a.t; ++i) r[i] = op(f,a[i]); + r.t = a.t; + } + r.s = op(this.s,a.s); + r.clamp(); +} + +// (public) this & a +function op_and(x,y) { return x&y; } +function bnAnd(a) { var r = nbi(); this.bitwiseTo(a,op_and,r); return r; } + +// (public) this | a +function op_or(x,y) { return x|y; } +function bnOr(a) { var r = nbi(); this.bitwiseTo(a,op_or,r); return r; } + +// (public) this ^ a +function op_xor(x,y) { return x^y; } +function bnXor(a) { var r = nbi(); this.bitwiseTo(a,op_xor,r); return r; } + +// (public) this & ~a +function op_andnot(x,y) { return x&~y; } +function bnAndNot(a) { var r = nbi(); this.bitwiseTo(a,op_andnot,r); return r; } + +// (public) ~this +function bnNot() { + var r = nbi(); + for(var i = 0; i < this.t; ++i) r[i] = this.DM&~this[i]; + r.t = this.t; + r.s = ~this.s; + return r; +} + +// (public) this << n +function bnShiftLeft(n) { + var r = nbi(); + if(n < 0) this.rShiftTo(-n,r); else this.lShiftTo(n,r); + return r; +} + +// (public) this >> n +function bnShiftRight(n) { + var r = nbi(); + if(n < 0) this.lShiftTo(-n,r); else this.rShiftTo(n,r); + return r; +} + +// return index of lowest 1-bit in x, x < 2^31 +function lbit(x) { + if(x == 0) return -1; + var r = 0; + if((x&0xffff) == 0) { x >>= 16; r += 16; } + if((x&0xff) == 0) { x >>= 8; r += 8; } + if((x&0xf) == 0) { x >>= 4; r += 4; } + if((x&3) == 0) { x >>= 2; r += 2; } + if((x&1) == 0) ++r; + return r; +} + +// (public) returns index of lowest 1-bit (or -1 if none) +function bnGetLowestSetBit() { + for(var i = 0; i < this.t; ++i) + if(this[i] != 0) return i*this.DB+lbit(this[i]); + if(this.s < 0) return this.t*this.DB; + return -1; +} + +// return number of 1 bits in x +function cbit(x) { + var r = 0; + while(x != 0) { x &= x-1; ++r; } + return r; +} + +// (public) return number of set bits +function bnBitCount() { + var r = 0, x = this.s&this.DM; + for(var i = 0; i < this.t; ++i) r += cbit(this[i]^x); + return r; +} + +// (public) true iff nth bit is set +function bnTestBit(n) { + var j = Math.floor(n/this.DB); + if(j >= this.t) return(this.s!=0); + return((this[j]&(1<<(n%this.DB)))!=0); +} + +// (protected) this op (1<>= this.DB; + } + if(a.t < this.t) { + c += a.s; + while(i < this.t) { + c += this[i]; + r[i++] = c&this.DM; + c >>= this.DB; + } + c += this.s; + } + else { + c += this.s; + while(i < a.t) { + c += a[i]; + r[i++] = c&this.DM; + c >>= this.DB; + } + c += a.s; + } + r.s = (c<0)?-1:0; + if(c > 0) r[i++] = c; + else if(c < -1) r[i++] = this.DV+c; + r.t = i; + r.clamp(); +} + +// (public) this + a +function bnAdd(a) { var r = nbi(); this.addTo(a,r); return r; } + +// (public) this - a +function bnSubtract(a) { var r = nbi(); this.subTo(a,r); return r; } + +// (public) this * a +function bnMultiply(a) { var r = nbi(); this.multiplyTo(a,r); return r; } + +// (public) this^2 +function bnSquare() { var r = nbi(); this.squareTo(r); return r; } + +// (public) this / a +function bnDivide(a) { var r = nbi(); this.divRemTo(a,r,null); return r; } + +// (public) this % a +function bnRemainder(a) { var r = nbi(); this.divRemTo(a,null,r); return r; } + +// (public) [this/a,this%a] +function bnDivideAndRemainder(a) { + var q = nbi(), r = nbi(); + this.divRemTo(a,q,r); + return new Array(q,r); +} + +// (protected) this *= n, this >= 0, 1 < n < DV +function bnpDMultiply(n) { + this[this.t] = this.am(0,n-1,this,0,0,this.t); + ++this.t; + this.clamp(); +} + +// (protected) this += n << w words, this >= 0 +function bnpDAddOffset(n,w) { + if(n == 0) return; + while(this.t <= w) this[this.t++] = 0; + this[w] += n; + while(this[w] >= this.DV) { + this[w] -= this.DV; + if(++w >= this.t) this[this.t++] = 0; + ++this[w]; + } +} + +// A "null" reducer +function NullExp() {} +function nNop(x) { return x; } +function nMulTo(x,y,r) { x.multiplyTo(y,r); } +function nSqrTo(x,r) { x.squareTo(r); } + +NullExp.prototype.convert = nNop; +NullExp.prototype.revert = nNop; +NullExp.prototype.mulTo = nMulTo; +NullExp.prototype.sqrTo = nSqrTo; + +// (public) this^e +function bnPow(e) { return this.exp(e,new NullExp()); } + +// (protected) r = lower n words of "this * a", a.t <= n +// "this" should be the larger one if appropriate. +function bnpMultiplyLowerTo(a,n,r) { + var i = Math.min(this.t+a.t,n); + r.s = 0; // assumes a,this >= 0 + r.t = i; + while(i > 0) r[--i] = 0; + var j; + for(j = r.t-this.t; i < j; ++i) r[i+this.t] = this.am(0,a[i],r,i,0,this.t); + for(j = Math.min(a.t,n); i < j; ++i) this.am(0,a[i],r,i,0,n-i); + r.clamp(); +} + +// (protected) r = "this * a" without lower n words, n > 0 +// "this" should be the larger one if appropriate. +function bnpMultiplyUpperTo(a,n,r) { + --n; + var i = r.t = this.t+a.t-n; + r.s = 0; // assumes a,this >= 0 + while(--i >= 0) r[i] = 0; + for(i = Math.max(n-this.t,0); i < a.t; ++i) + r[this.t+i-n] = this.am(n-i,a[i],r,0,0,this.t+i-n); + r.clamp(); + r.drShiftTo(1,r); +} + +// Barrett modular reduction +function Barrett(m) { + // setup Barrett + this.r2 = nbi(); + this.q3 = nbi(); + BigInteger.ONE.dlShiftTo(2*m.t,this.r2); + this.mu = this.r2.divide(m); + this.m = m; +} + +function barrettConvert(x) { + if(x.s < 0 || x.t > 2*this.m.t) return x.mod(this.m); + else if(x.compareTo(this.m) < 0) return x; + else { var r = nbi(); x.copyTo(r); this.reduce(r); return r; } +} + +function barrettRevert(x) { return x; } + +// x = x mod m (HAC 14.42) +function barrettReduce(x) { + x.drShiftTo(this.m.t-1,this.r2); + if(x.t > this.m.t+1) { x.t = this.m.t+1; x.clamp(); } + this.mu.multiplyUpperTo(this.r2,this.m.t+1,this.q3); + this.m.multiplyLowerTo(this.q3,this.m.t+1,this.r2); + while(x.compareTo(this.r2) < 0) x.dAddOffset(1,this.m.t+1); + x.subTo(this.r2,x); + while(x.compareTo(this.m) >= 0) x.subTo(this.m,x); +} + +// r = x^2 mod m; x != r +function barrettSqrTo(x,r) { x.squareTo(r); this.reduce(r); } + +// r = x*y mod m; x,y != r +function barrettMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); } + +Barrett.prototype.convert = barrettConvert; +Barrett.prototype.revert = barrettRevert; +Barrett.prototype.reduce = barrettReduce; +Barrett.prototype.mulTo = barrettMulTo; +Barrett.prototype.sqrTo = barrettSqrTo; + +// (public) this^e % m (HAC 14.85) +function bnModPow(e,m) { + var i = e.bitLength(), k, r = nbv(1), z; + if(i <= 0) return r; + else if(i < 18) k = 1; + else if(i < 48) k = 3; + else if(i < 144) k = 4; + else if(i < 768) k = 5; + else k = 6; + if(i < 8) + z = new Classic(m); + else if(m.isEven()) + z = new Barrett(m); + else + z = new Montgomery(m); + + // precomputation + var g = new Array(), n = 3, k1 = k-1, km = (1< 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< 0) { + x.rShiftTo(g,x); + y.rShiftTo(g,y); + } + while(x.signum() > 0) { + if((i = x.getLowestSetBit()) > 0) x.rShiftTo(i,x); + if((i = y.getLowestSetBit()) > 0) y.rShiftTo(i,y); + if(x.compareTo(y) >= 0) { + x.subTo(y,x); + x.rShiftTo(1,x); + } + else { + y.subTo(x,y); + y.rShiftTo(1,y); + } + } + if(g > 0) y.lShiftTo(g,y); + return y; +} + +// (protected) this % n, n < 2^26 +function bnpModInt(n) { + if(n <= 0) return 0; + var d = this.DV%n, r = (this.s<0)?n-1:0; + if(this.t > 0) + if(d == 0) r = this[0]%n; + else for(var i = this.t-1; i >= 0; --i) r = (d*r+this[i])%n; + return r; +} + +// (public) 1/this % m (HAC 14.61) +function bnModInverse(m) { + var ac = m.isEven(); + if((this.isEven() && ac) || m.signum() == 0) return BigInteger.ZERO; + var u = m.clone(), v = this.clone(); + var a = nbv(1), b = nbv(0), c = nbv(0), d = nbv(1); + while(u.signum() != 0) { + while(u.isEven()) { + u.rShiftTo(1,u); + if(ac) { + if(!a.isEven() || !b.isEven()) { a.addTo(this,a); b.subTo(m,b); } + a.rShiftTo(1,a); + } + else if(!b.isEven()) b.subTo(m,b); + b.rShiftTo(1,b); + } + while(v.isEven()) { + v.rShiftTo(1,v); + if(ac) { + if(!c.isEven() || !d.isEven()) { c.addTo(this,c); d.subTo(m,d); } + c.rShiftTo(1,c); + } + else if(!d.isEven()) d.subTo(m,d); + d.rShiftTo(1,d); + } + if(u.compareTo(v) >= 0) { + u.subTo(v,u); + if(ac) a.subTo(c,a); + b.subTo(d,b); + } + else { + v.subTo(u,v); + if(ac) c.subTo(a,c); + d.subTo(b,d); + } + } + if(v.compareTo(BigInteger.ONE) != 0) return BigInteger.ZERO; + if(d.compareTo(m) >= 0) return d.subtract(m); + if(d.signum() < 0) d.addTo(m,d); else return d; + if(d.signum() < 0) return d.add(m); else return d; +} + +var lowprimes = [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,541,547,557,563,569,571,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691,701,709,719,727,733,739,743,751,757,761,769,773,787,797,809,811,821,823,827,829,839,853,857,859,863,877,881,883,887,907,911,919,929,937,941,947,953,967,971,977,983,991,997]; +var lplim = (1<<26)/lowprimes[lowprimes.length-1]; + +// (public) test primality with certainty >= 1-.5^t +function bnIsProbablePrime(t) { + var i, x = this.abs(); + if(x.t == 1 && x[0] <= lowprimes[lowprimes.length-1]) { + for(i = 0; i < lowprimes.length; ++i) + if(x[0] == lowprimes[i]) return true; + return false; + } + if(x.isEven()) return false; + i = 1; + while(i < lowprimes.length) { + var m = lowprimes[i], j = i+1; + while(j < lowprimes.length && m < lplim) m *= lowprimes[j++]; + m = x.modInt(m); + while(i < j) if(m%lowprimes[i++] == 0) return false; + } + return x.millerRabin(t); +} + +// (protected) true if probably prime (HAC 4.24, Miller-Rabin) +function bnpMillerRabin(t) { + var n1 = this.subtract(BigInteger.ONE); + var k = n1.getLowestSetBit(); + if(k <= 0) return false; + var r = n1.shiftRight(k); + t = (t+1)>>1; + if(t > lowprimes.length) t = lowprimes.length; + var a = nbi(); + for(var i = 0; i < t; ++i) { + //Pick bases at random, instead of starting at 2 + a.fromInt(lowprimes[Math.floor(Math.random()*lowprimes.length)]); + var y = a.modPow(r,this); + if(y.compareTo(BigInteger.ONE) != 0 && y.compareTo(n1) != 0) { + var j = 1; + while(j++ < k && y.compareTo(n1) != 0) { + y = y.modPowInt(2,this); + if(y.compareTo(BigInteger.ONE) == 0) return false; + } + if(y.compareTo(n1) != 0) return false; + } + } + return true; +} + +// convert the number to a floating point value. +function bnToDouble() { + return parseFloat(this.toString(10)); +} + +// protected +BigInteger.prototype.chunkSize = bnpChunkSize; +BigInteger.prototype.toRadix = bnpToRadix; +BigInteger.prototype.fromRadix = bnpFromRadix; +BigInteger.prototype.fromNumber = bnpFromNumber; +BigInteger.prototype.bitwiseTo = bnpBitwiseTo; +BigInteger.prototype.changeBit = bnpChangeBit; +BigInteger.prototype.addTo = bnpAddTo; +BigInteger.prototype.dMultiply = bnpDMultiply; +BigInteger.prototype.dAddOffset = bnpDAddOffset; +BigInteger.prototype.multiplyLowerTo = bnpMultiplyLowerTo; +BigInteger.prototype.multiplyUpperTo = bnpMultiplyUpperTo; +BigInteger.prototype.modInt = bnpModInt; +BigInteger.prototype.millerRabin = bnpMillerRabin; + +// public +BigInteger.prototype.clone = bnClone; +BigInteger.prototype.intValue = bnIntValue; +BigInteger.prototype.byteValue = bnByteValue; +BigInteger.prototype.shortValue = bnShortValue; +BigInteger.prototype.signum = bnSigNum; +BigInteger.prototype.toByteArray = bnToByteArray; +BigInteger.prototype.equals = bnEquals; +BigInteger.prototype.min = bnMin; +BigInteger.prototype.max = bnMax; +BigInteger.prototype.and = bnAnd; +BigInteger.prototype.or = bnOr; +BigInteger.prototype.xor = bnXor; +BigInteger.prototype.andNot = bnAndNot; +BigInteger.prototype.not = bnNot; +BigInteger.prototype.shiftLeft = bnShiftLeft; +BigInteger.prototype.shiftRight = bnShiftRight; +BigInteger.prototype.getLowestSetBit = bnGetLowestSetBit; +BigInteger.prototype.bitCount = bnBitCount; +BigInteger.prototype.testBit = bnTestBit; +BigInteger.prototype.setBit = bnSetBit; +BigInteger.prototype.clearBit = bnClearBit; +BigInteger.prototype.flipBit = bnFlipBit; +BigInteger.prototype.add = bnAdd; +BigInteger.prototype.subtract = bnSubtract; +BigInteger.prototype.multiply = bnMultiply; +BigInteger.prototype.divide = bnDivide; +BigInteger.prototype.remainder = bnRemainder; +BigInteger.prototype.divideAndRemainder = bnDivideAndRemainder; +BigInteger.prototype.modPow = bnModPow; +BigInteger.prototype.modInverse = bnModInverse; +BigInteger.prototype.pow = bnPow; +BigInteger.prototype.gcd = bnGCD; +BigInteger.prototype.isProbablePrime = bnIsProbablePrime; + +// JSBN-specific extension +BigInteger.prototype.square = bnSquare; + +BigInteger.prototype.toDouble = bnToDouble; + +// BigInteger interfaces not implemented in jsbn: + +// BigInteger(int signum, byte[] magnitude) +// double doubleValue() +// float floatValue() +// int hashCode() +// long longValue() +// static BigInteger valueOf(long val) +function bnBigIntegerFromInt(intValue) { + return new BigInteger(intValue.toString(), 10); +} + +var bnBigInteger = BigInteger; + +if (typeof module !== 'undefined' && module.exports) +{ + module.exports = { + BigIntegerFromInt: bnBigIntegerFromInt, + BigInteger: bnBigInteger + }; +} +// Generated by CoffeeScript 1.12.7 +(function() { + var BITS_MASK, BITS_SHIFT, BIT_IS_LINEAR, BIT_IS_PRIMARY, BigInteger, BigIntegerFromInt, CLOCKWISE, COUNTERCLOCKWISE, DISABLE_REQUIRE, DOWN, EAST, EPSILON, FORWARD, GEOMETRY_CATEGORY_POINT, GEOMETRY_CATEGORY_SEGMENT, HIGH, HORIZONTAL, IS_INVERSE, LEFT, LOW, NEGATIVE, NORTH, POSITIVE, PROXIMAL, REVERSE, RIGHT, SOURCE_CATEGORY_BITMASK, SOURCE_CATEGORY_GEOMETRY_SHIFT, SOURCE_CATEGORY_INITIAL_SEGMENT, SOURCE_CATEGORY_REVERSE_SEGMENT, SOURCE_CATEGORY_SEGMENT_END_POINT, SOURCE_CATEGORY_SEGMENT_START_POINT, SOURCE_CATEGORY_SINGLE_POINT, SOUTH, UP, VERTICAL, WEST, abstract_priority_queue, array_strategy, beach_line_node_data, beach_line_node_key, belongs, binarySearchForIndexReversed, circle_event, circle_existence_predicate, circle_formation_functor, circle_formation_functor_big, circle_formation_predicate, circle_formation_predicate_big, clockwise_winding, construct_voronoi, counterclockwise_winding, cppgen, direction_1d, direction_2d, direction_3d, distance_predicate, event_comparison_predicate, fast_ps, find_distance_to_point_arc, find_distance_to_segment_arc, get_sqrt, insert, is_neg, is_neg_big, is_pos, is_pos_big, is_vertical, is_zero, is_zero_big, kPredicateResult, lazy_circle_formation_functor, list, list_item, node_comparison_predicate, ordered_map, ordered_map_item, ordered_queue, orientation_2d, orientation_3d, ot, point_2d, point_comparison_predicate, point_data, pp, priority_queue, ps, ref, ref1, ref10, ref2, ref3, ref4, ref5, ref6, ref7, ref8, ref9, robust_cross_product, segment_data, site_event, sqrt_expr_, sqrt_expr_big_, sqrt_expr_evaluator_pss3, sqrt_expr_evaluator_pss3_big, sqrt_expr_evaluator_pss4, sqrt_expr_evaluator_pss4_big, sqrt_expr_evaluator_pss4_temp, ss, static_cast_big_int, static_cast_int, unknown_winding, vertex_equality_predicate_, voronoi_builder, voronoi_cell, voronoi_diagnostics, voronoi_diagram, voronoi_edge, voronoi_vertex, voronoi_visual_utils, + extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, + hasProp = {}.hasOwnProperty; + + DISABLE_REQUIRE = 1; + + EPSILON = 1e-5; + + GEOMETRY_CATEGORY_POINT = 0x0; + + GEOMETRY_CATEGORY_SEGMENT = 0x1; + + SOURCE_CATEGORY_SINGLE_POINT = 0x0; + + SOURCE_CATEGORY_SEGMENT_START_POINT = 0x1; + + SOURCE_CATEGORY_SEGMENT_END_POINT = 0x2; + + SOURCE_CATEGORY_INITIAL_SEGMENT = 0x8; + + SOURCE_CATEGORY_REVERSE_SEGMENT = 0x9; + + SOURCE_CATEGORY_GEOMETRY_SHIFT = 0x3; + + SOURCE_CATEGORY_BITMASK = 0x1F; + + belongs = function(source_category, geometry_category) { + return (source_category >> SOURCE_CATEGORY_GEOMETRY_SHIFT) === geometry_category; + }; + + LOW = 0; + + HIGH = 1; + + LEFT = 0; + + RIGHT = 1; + + CLOCKWISE = 0; + + COUNTERCLOCKWISE = 1; + + REVERSE = 0; + + FORWARD = 1; + + NEGATIVE = 0; + + POSITIVE = 1; + + HORIZONTAL = 0; + + VERTICAL = 1; + + WEST = 0; + + EAST = 1; + + SOUTH = 2; + + NORTH = 3; + + PROXIMAL = 2; + + DOWN = 4; + + UP = 5; + + clockwise_winding = 0; + + counterclockwise_winding = 1; + + unknown_winding = 2; + + IS_INVERSE = 0x20; + + BITS_SHIFT = 0x5; + + BITS_MASK = 0x1F; + + BIT_IS_LINEAR = 0x1; + + BIT_IS_PRIMARY = 0x2; + + if (typeof module !== "undefined" && module !== null) { + module.exports = { + EPSILON: EPSILON, + GEOMETRY_CATEGORY_POINT: GEOMETRY_CATEGORY_POINT, + GEOMETRY_CATEGORY_SEGMENT: GEOMETRY_CATEGORY_SEGMENT, + SOURCE_CATEGORY_SINGLE_POINT: SOURCE_CATEGORY_SINGLE_POINT, + SOURCE_CATEGORY_SEGMENT_START_POINT: SOURCE_CATEGORY_SEGMENT_START_POINT, + SOURCE_CATEGORY_SEGMENT_END_POINT: SOURCE_CATEGORY_SEGMENT_END_POINT, + SOURCE_CATEGORY_INITIAL_SEGMENT: SOURCE_CATEGORY_INITIAL_SEGMENT, + SOURCE_CATEGORY_REVERSE_SEGMENT: SOURCE_CATEGORY_REVERSE_SEGMENT, + SOURCE_CATEGORY_GEOMETRY_SHIFT: SOURCE_CATEGORY_GEOMETRY_SHIFT, + SOURCE_CATEGORY_BITMASK: SOURCE_CATEGORY_BITMASK, + belongs: belongs, + LOW: LOW, + HIGH: HIGH, + LEFT: LEFT, + RIGHT: RIGHT, + CLOCKWISE: CLOCKWISE, + COUNTERCLOCKWISE: COUNTERCLOCKWISE, + REVERSE: REVERSE, + FORWARD: FORWARD, + NEGATIVE: NEGATIVE, + POSITIVE: POSITIVE, + HORIZONTAL: HORIZONTAL, + VERTICAL: VERTICAL, + WEST: WEST, + EAST: EAST, + SOUTH: SOUTH, + NORTH: NORTH, + PROXIMAL: PROXIMAL, + DOWN: DOWN, + UP: UP, + clockwise_winding: clockwise_winding, + counterclockwise_winding: counterclockwise_winding, + unknown_winding: unknown_winding, + IS_INVERSE: IS_INVERSE, + BITS_SHIFT: BITS_SHIFT, + BITS_MASK: BITS_MASK, + BIT_IS_LINEAR: BIT_IS_LINEAR, + BIT_IS_PRIMARY: BIT_IS_PRIMARY + }; + } + + if ((DISABLE_REQUIRE == null) && (typeof require !== "undefined" && require !== null)) { + ref = require('./constants'), LOW = ref.LOW, HORIZONTAL = ref.HORIZONTAL, WEST = ref.WEST; + } + + direction_1d = (function() { + direction_1d.prototype.val_ = LOW; + + function direction_1d(param) { + if (param !== void 0) { + if (typeof param === "object") { + if (param instanceof direction_1d) { + this.val_ = param.val_; + } else if (param instanceof direction_2d) { + this.val_ = param.to_int() & 1; + } else if (param instanceof direction_3d) { + this.val_ = param.to_int() & 1; + } + } else if (typeof param === "number") { + this.val_ = param; + } + } else { + this.val_ = LOW; + } + } + + direction_1d.prototype.assign = function(dir_1d) { + this.val_ = dir_1d.val_; + }; + + direction_1d.prototype.equals = function(dir) { + return Math.abs(this.val_ - dir.val_) < EPSILON; + }; + + direction_1d.prototype.to_int = function() { + return this.val_; + }; + + direction_1d.prototype.backward = function() { + if (Math.abs(this.val_ - LOW) < EPSILON) { + this.val_ = HIGH; + } else { + this.val_ = LOW; + } + return this; + }; + + direction_1d.prototype.get_sign = function() { + return this.val_ * 2 - 1; + }; + + return direction_1d; + + })(); + + orientation_2d = (function() { + orientation_2d.prototype.val_ = HORIZONTAL; + + function orientation_2d(param) { + if (param !== void 0) { + if (typeof param === "object") { + if (param instanceof orientation_2d) { + this.val_ = param.val_; + } else if (param instanceof direction_2d) { + this.val_ = param.to_int() >> 1; + } + } else if (typeof param === "number") { + this.val_ = param; + } + } else { + this.val_ = HORIZONTAL; + } + } + + orientation_2d.prototype.assign = function(ori) { + this.val_ = ori.val_; + }; + + orientation_2d.prototype.equals = function(ori) { + return Math.abs(this.val_ - ori.val_) < EPSILON; + }; + + orientation_2d.prototype.to_int = function() { + return this.val_; + }; + + orientation_2d.prototype.turn_90 = function() { + if (Math.abs(this.val_ - HORIZONTAL) < EPSILON) { + this.val_ = VERTICAL; + } else { + this.val_ = HORIZONTAL; + } + }; + + orientation_2d.prototype.get_perpendicular = function() { + var obj; + obj = new orientation_2d(this); + obj.turn_90(); + return obj; + }; + + orientation_2d.prototype.get_direction = function(dir) { + return new direction_2d((val_ << 1) + dir.to_int()); + }; + + return orientation_2d; + + })(); + + direction_2d = (function() { + direction_2d.prototype.val_ = WEST; + + function direction_2d(param) { + if (param !== void 0) { + if (typeof param === "object") { + if (param instanceof direction_2d) { + this.val_ = param.val_; + } else if (param instanceof direction_2d) { + this.val_ = param.to_int() & 1; + } + } else if (typeof param === "number") { + this.val_ = param; + } + } else { + this.val_ = WEST; + } + } + + direction_2d.prototype.assign = function(dir) { + return this.val_ = dir.val_; + }; + + direction_2d.prototype.equals = function(dir) { + return Math.abs(this.val_ - dir.val_) < EPSILON; + }; + + direction_2d.prototype.lessThan = function(dir) { + return this.val_ < dir.val_; + }; + + direction_2d.prototype.lessThanEqual = function(dir) { + return this.val_ <= dir.val_; + }; + + direction_2d.prototype.greaterThan = function(dir) { + return this.val_ > dir.val_; + }; + + direction_2d.prototype.greaterThanEqual = function(dir) { + return this.val_ >= dir.val_; + }; + + + /* // Casting to int */ + + direction_2d.prototype.to_int = function() { + return this.val_; + }; + + + /* // flip the LSB, toggles 0 - 1 and 2 - 3 */ + + direction_2d.prototype.backward = function() { + var dir; + dir = new direction_2d(); + dir.val_ = this.val_ ^ 1; + return dir; + }; + + + /* // Returns a direction 90 degree left (LOW) or right(HIGH) to this one */ + + direction_2d.prototype.turn = function(t) { + var dir; + dir = new direction_2d(); + dir.val_ = this.val_ ^ 3 ^ (this.val_ >> 1) ^ t.to_int(); + return dir; + }; + + + /* // Returns a direction 90 degree left to this one */ + + direction_2d.prototype.left = function() { + return this.turn(HIGH); + }; + + + /* // Returns a direction 90 degree right to this one */ + + direction_2d.prototype.right = function() { + return this.turn(LOW); + }; + + + /* // N, E are positive, S, W are negative */ + + direction_2d.prototype.is_positive = function() { + return this.val_ & 1; + }; + + direction_2d.prototype.is_negative = function() { + return !this.is_positive(); + }; + + direction_2d.prototype.get_sign = function() { + return (this.is_positive() << 1) - 1; + }; + + return direction_2d; + + })(); + + orientation_3d = (function() { + orientation_3d.prototype.val_ = HORIZONTAL; + + function orientation_3d(param) { + if (param !== void 0) { + if (typeof param === "object") { + if (param instanceof orientation_3d) { + this.val_ = param.val_; + } else if (param instanceof orientation_2d) { + this.val_ = param.to_int(); + } else if (param instanceof direction_2d) { + this.val_ = param.to_int() >> 1; + } else if (param instanceof direction_3d) { + this.val_ = param.to_int() >> 1; + } + } else if (typeof param === "number") { + this.val_ = param; + } + } else { + this.val_ = HORIZONTAL; + } + } + + orientation_3d.prototype.assign = function(ori) { + this.val_ = ori.val_; + }; + + orientation_3d.prototype.equals = function(ori) { + return Math.abs(this.val_ - ori.val_) < EPSILON; + }; + + orientation_3d.prototype.to_int = function() { + return this.val_; + }; + + orientation_3d.prototype.get_direction = function() { + throw new Error("orientation.get_direction, not implemented"); + }; + + return orientation_3d; + + })(); + + direction_3d = (function() { + direction_3d.prototype.val_ = WEST; + + function direction_3d(param) { + if (param !== void 0) { + if (typeof param === "object") { + if (param instanceof direction_3d) { + this.val_ = param.val_; + } else if (param instanceof direction_2d) { + this.val_ = param.to_int(); + } + } else if (typeof param === "number") { + this.val_ = param; + } + } else { + this.val_ = WEST; + } + } + + direction_3d.prototype.assign = function(dir) { + this.val_ = dir.val_; + }; + + direction_3d.prototype.equals = function(dir) { + return Math.abs(this.val_ - dir.val_) < EPSILON; + }; + + direction_3d.prototype.lessThan = function(dir) { + return this.val_ < dir.val_; + }; + + direction_3d.prototype.lessThanEqual = function(dir) { + return this.val_ <= dir.val_; + }; + + direction_3d.prototype.greaterThan = function(dir) { + return this.val_ > dir.val_; + }; + + direction_3d.prototype.greaterThanEqual = function(dir) { + return this.val_ >= dir.val_; + }; + + + /* // Casting to int */ + + direction_3d.prototype.to_int = function() { + return this.val_; + }; + + direction_3d.prototype.backward = function() { + return new direction_3d(this.val_ ^ 1); + }; + + direction_3d.prototype.is_positive = function() { + return this.val_ & 1; + }; + + direction_3d.prototype.is_negative = function() { + return !this.is_positive(); + }; + + direction_3d.prototype.get_sign = function() { + return (this.is_positive() << 1) - 1; + }; + + return direction_3d; + + })(); + + list_item = (function() { + list_item.prototype.attr = []; + + list_item.prototype._owningList = null; + + list_item.prototype._prev = null; + + list_item.prototype._next = null; + + list_item.prototype._value = null; + + function list_item(owner, value) { + if (value == null) { + value = null; + } + this._owningList = owner; + this._value = value; + this; + } + + list_item.prototype.next = function(param) { + if (param !== void 0) { + this._next = param; + return this; + } + return this._next; + }; + + list_item.prototype.prev = function(param) { + if (param !== void 0) { + this._prev = param; + return this; + } + return this._prev; + }; + + list_item.prototype.owningList = function(param) { + if (param !== void 0) { + this._owningList = param; + return this; + } + return this._owningList; + }; + + list_item.prototype.isFirst = function() { + return this._prev == null; + }; + + list_item.prototype.value = function(param) { + if (param !== void 0) { + this._value = param; + return this; + } + return this._value; + }; + + list_item.prototype.clearConnections = function() { + this._prev = null; + this._next = null; + return true; + }; + + return list_item; + + })(); + + list = (function() { + list.prototype._first = null; + + list.prototype._last = null; + + list.prototype._length = 0; + + function list(param1, param2, param3) { + var endIndex, i, l, ref1, ref2, startIndex; + if (param1 == null) { + this._last = new list_item(this); + return this; + } else if (param1 instanceof Array) { + this._last = new list_item(this); + startIndex = param2 != null ? param2 : 0; + endIndex = param3 != null ? param3 : param1.length; + for (i = l = ref1 = startIndex, ref2 = endIndex; ref1 <= ref2 ? l < ref2 : l > ref2; i = ref1 <= ref2 ? ++l : --l) { + this.push(param1[i]); + } + return this; + } else { + throw new Error("TODO: Implement create for these parameters"); + } + this; + } + + list.prototype.swap = function(item1, item2) { + var item1Next, item1Prev, item2Next, item2Prev; + if (!this.contains(item1) || !this.contains(item2)) { + throw new Error("items should belong to this list"); + } + if (item1 === this.end() || item2 === this.end()) { + throw new Error("swapping reserved @end item is not allowed"); + } + item1Prev = item1.prev(); + item1Next = item1.next(); + item2Prev = item2.prev(); + item2Next = item2.next(); + if (item1 === item2Prev && item2 === item1Next) { + item1.prev(item2); + item1.next(item2Next); + item2.next(item1); + item2.prev(item1Prev); + if (item1Prev != null) { + item1Prev.next(item2); + } + if (item2Next != null) { + item2Next.prev(item1); + } + } else if (item1 === item2Next && item2 === item1Prev) { + item1.next(item2); + item1.prev(item2Prev); + item2.prev(item1); + item2.next(item1Next); + if (item2Prev != null) { + item2Prev.next(item1); + } + if (item1Next != null) { + item1Next.prev(item2); + } + } else { + item1.prev(item2Prev); + item2.next(item1Next); + item1.next(item2Next); + item2.prev(item1Prev); + if (item1Prev != null) { + item1Prev.next(item2); + } + if (item1Next != null) { + item1Next.prev(item2); + } + if (item2Next != null) { + item2Next.prev(item1); + } + if (item2Prev != null) { + item2Prev.next(item1); + } + } + if (item1.prev() === null) { + this._first = item1; + } + if (item2.prev() === null) { + this._first = item2; + } + }; + + list.prototype.sort = function(comparator) { + var start, stop, stopNext; + start = this.begin(); + if (start == null) { + return; + } + if (start.next() == null) { + return; + } + while (start !== this.end().prev()) { + stop = start.next(); + while (stop !== this.end()) { + if (comparator(start.value(), stop.value())) { + stop = stop.next(); + } else { + stopNext = stop.next(); + this.swap(start, stop); + start = stop; + stop = stopNext; + } + } + start = start.next(); + } + }; + + list.prototype.jsSort = function(comparator) { + var arr, curr, i, l, node, nodeComparator, prev, ref1; + if (this._first == null) { + return; + } + if (this._first.next() == null) { + return; + } + nodeComparator = function(n1, n2) { + if (comparator(n1.value(), n2.value())) { + return -1; + } else { + return 1; + } + }; + arr = []; + node = this.begin(); + while (node !== this.end()) { + arr.push(node); + node = node.next(); + } + arr.sort(nodeComparator); + this._first = arr[0]; + prev = arr[0]; + curr = arr[0]; + for (i = l = 1, ref1 = arr.length; 1 <= ref1 ? l < ref1 : l > ref1; i = 1 <= ref1 ? ++l : --l) { + curr = arr[i]; + prev._next = curr; + curr._prev = prev; + prev = curr; + } + curr._next = this._last; + this._last._prev = curr; + }; + + list.prototype.print = function() { + var a, str; + a = this.begin(); + str = "["; + while (a !== this.end()) { + str += JSON.stringify(a.value()); + a = a.next(); + str += a !== this.end() ? ", \n" : "\n"; + } + str += "]"; + console.log(str); + }; + + list.prototype.to_json = function() { + return { + "TODO": "list" + }; + }; + + + /* assumes the array is sorted!!!! */ + + list.prototype.unique = function(equalsCheck) { + var curr, currValue, newList1, newList2; + if (this._length === 0) { + return null; + } + newList1 = new list(); + newList2 = new list(); + newList1.push(this.end().prev().value()); + currValue = this.end().prev().value(); + curr = this.end().prev().prev(); + while (curr !== null) { + if (!equalsCheck(currValue, curr.value())) { + newList1.push(curr.value()); + currValue = curr.value(); + } + curr = curr.prev(); + } + curr = newList1.end().prev(); + while (curr !== null) { + newList2.push(curr.value()); + curr = curr.prev(); + } + return newList2; + }; + + list.prototype.uniqueWithTieBreaker = function(equalsCheck, tieBreaker) { + var curr, currValue, equalItems, returnList; + if (this._length === 0) { + return null; + } + returnList = new list(); + currValue = this._first.value(); + equalItems = [this._first.value()]; + curr = this._first.next(); + while (curr !== this._last) { + if (equalsCheck(currValue, curr.value())) { + equalItems.push(curr.value()); + } else { + returnList.push(equalItems.sort(tieBreaker)[0]); + currValue = curr.value(); + equalItems = [currValue]; + } + curr = curr.next(); + } + returnList.push(equalItems.sort(tieBreaker)[0]); + return returnList; + }; + + list.prototype.clear = function() { + this._first = null; + return this._length = 0; + }; + + list.prototype.contains = function(item) { + return (item != null) && (item.owningList() === this); + }; + + list.prototype.erase = function(item) { + var nextItem, prevItem; + if ((item != null) && !this.contains(item)) { + throw new Error("item is null or not part of this list"); + } + prevItem = item.prev(); + nextItem = item.next(); + if (nextItem != null) { + nextItem.prev(prevItem); + } + if (prevItem != null) { + prevItem.next(nextItem); + } + if (prevItem == null) { + this._first = nextItem; + } + item.clearConnections(); + item.owningList(null); + this._length--; + return null; + }; + + list.prototype.insert = function(item1, item2) { + var nextItem, prevItem; + if ((item1 != null) && !this.contains(item1)) { + throw new Error("item1 is null or not part of this list"); + } + if (item2 instanceof list_item) { + prevItem = this.end().prev(); + nextItem = this.end(); + if (item1 != null) { + prevItem = item1.prev(); + nextItem = item1; + } + if (item2 != null) { + item2.next(nextItem); + item2.prev(prevItem); + if (nextItem != null) { + nextItem.prev(item2); + } + if (prevItem != null) { + prevItem.next(item2); + } + if (prevItem == null) { + this._first = item2; + } + this._length++; + } + return item2; + } else { + return this.insert(item1, new list_item(this, item2)); + } + return null; + }; + + list.prototype.push = function(item) { + return this.insert(null, item); + }; + + list.prototype.begin = function() { + return this._first; + }; + + list.prototype.beginValue = function() { + if (this._first != null) { + return this._first.value(); + } + throw new Error("List is empty"); + }; + + list.prototype.end = function() { + return this._last; + }; + + list.prototype.size = function() { + return this._length; + }; + + list.prototype.empty = function() { + return this._length <= 0; + }; + + list.prototype.concat = function(object) { + throw new Error("List concat is not implemented"); + }; + + list.prototype.at = function(index) { + var i, item; + item = this.begin(); + i = 0; + while (item !== this.end && i < this._length) { + if (i === index) { + return item; + } + i++; + item = item.next(); + } + throw new Error("Index out of bound"); + }; + + return list; + + })(); + + abstract_priority_queue = (function() { + function abstract_priority_queue(options) { + var ref1; + if ((options != null ? options.strategy : void 0) == null) { + throw 'Must pass options.strategy, a strategy'; + } + if ((options != null ? options.comparator : void 0) == null) { + throw 'Must pass options.comparator, a comparator'; + } + this.priv = new options.strategy(options); + this.length = (options != null ? (ref1 = options.initialValues) != null ? ref1.length : void 0 : void 0) || 0; + } + + abstract_priority_queue.prototype.queue = function(value) { + this.length++; + this.priv.queue(value); + return void 0; + }; + + abstract_priority_queue.prototype.dequeue = function(value) { + if (!this.length) { + throw 'Empty queue'; + } + this.length--; + return this.priv.dequeue(); + }; + + abstract_priority_queue.prototype.peek = function(value) { + if (!this.length) { + throw 'Empty queue'; + } + return this.priv.peek(); + }; + + abstract_priority_queue.prototype.clear = function() { + this.length = 0; + return this.priv.clear(); + }; + + abstract_priority_queue.prototype.empty = function() { + return this.length === 0; + }; + + abstract_priority_queue.prototype.print = function() { + return this.priv.print(); + }; + + abstract_priority_queue.prototype.to_json = function() { + return this.priv.to_json(); + }; + + return abstract_priority_queue; + + })(); + + binarySearchForIndexReversed = function(array, value, comparator) { + var high, low, mid; + low = 0; + high = array.length; + while (low < high) { + mid = (low + high) >>> 1; + if (comparator(array[mid], value)) { + low = mid + 1; + } else { + high = mid; + } + } + return low; + }; + + array_strategy = (function() { + function array_strategy(options1) { + var ref1; + this.options = options1; + this.comparator = this.options.comparator; + this.data = ((ref1 = this.options.initialValues) != null ? ref1.slice(0) : void 0) || []; + this.data.sort(this.comparator).reverse(); + } + + array_strategy.prototype.queue = function(value) { + var pos; + pos = binarySearchForIndexReversed(this.data, value, this.comparator); + this.data.splice(pos, 0, value); + return void 0; + }; + + array_strategy.prototype.dequeue = function() { + return this.data.pop(); + }; + + array_strategy.prototype.peek = function() { + return this.data[this.data.length - 1]; + }; + + array_strategy.prototype.clear = function() { + this.data.length = 0; + return void 0; + }; + + array_strategy.prototype.print = function() { + var i, str; + i = this.data.length - 1; + str = ""; + while (i >= 0) { + if ((this.data[i].value != null) && (this.data[i].value().first != null) && (this.data[i].value().first.print != null)) { + str += this.data[i].value().first.print() + "\n"; + } + i--; + } + return str; + }; + + array_strategy.prototype.to_json = function() { + var i, result; + result = []; + i = this.data.length - 1; + while (i >= 0) { + if ((this.data[i].value != null) && (this.data[i].value().first != null) && (this.data[i].value().first.to_json != null)) { + result.push(this.data[i].value().first.to_json()); + } + i--; + } + return result; + }; + + return array_strategy; + + })(); + + priority_queue = (function(superClass) { + extend(priority_queue, superClass); + + function priority_queue(options) { + options || (options = {}); + options.strategy || (options.strategy = array_strategy); + options.comparator || (options.comparator = function(a, b) { + return (a || 0) - (b || 0); + }); + priority_queue.__super__.constructor.call(this, options); + } + + return priority_queue; + + })(abstract_priority_queue); + + + /* + c++ comment + * // Event queue data structure, holds circle events. + * // During algorithm run, some of the circle events disappear (become + * // inactive). Priority queue data structure doesn't support + * // iterators (there is no direct ability to modify its elements). + * // Instead list is used to store all the circle events and priority queue + * // of the iterators to the list elements is used to keep the correct circle + * // events ordering. + * template + + js comment + ordered queue is a std::priority_queue with iterable elements + + std::priority_queue documentation + O(1) largest, O(logN) insert, extraction + A user-provided Compare can be supplied to change the ordering, e.g. + using std::greater would cause the smallest element to appear as the top(). + */ + + ordered_queue = (function() { + ordered_queue.prototype.c_list_ = null; + + ordered_queue.prototype.c_ = null; + + function ordered_queue(comparator_) { + this.c_list_ = new list(); + this.c_ = new priority_queue({ + comparator: function(ite1, ite2) { + return comparator_(ite1.value(), ite2.value()); + } + }); + if (comparator_ == null) { + throw "comparator is not provided for ordered queue"; + } + } + + ordered_queue.prototype.empty = function() { + return this.c_.empty(); + }; + + ordered_queue.prototype.top = function() { + return this.c_.peek().value(); + }; + + ordered_queue.prototype.pop = function() { + var ite; + ite = this.c_.peek(); + this.c_.dequeue(); + return this.c_list_.erase(ite); + }; + + ordered_queue.prototype.push = function(e) { + this.c_list_.push(e); + this.c_.queue(this.c_list_.end().prev()); + return this.c_list_.end().prev().value(); + }; + + ordered_queue.prototype.clear = function() { + this.c_.clear(); + return this.c_list_.clear(); + }; + + ordered_queue.prototype.size = function() { + return this.c_list_.size(); + }; + + ordered_queue.prototype.print = function() { + return this.c_.print(); + }; + + ordered_queue.prototype.to_json = function() { + return this.c_.to_json(); + }; + + return ordered_queue; + + })(); + + ordered_map_item = (function() { + ordered_map_item.prototype.attr = []; + + ordered_map_item.prototype._owningMap = null; + + ordered_map_item.prototype._prev = null; + + ordered_map_item.prototype._next = null; + + ordered_map_item.prototype._key = null; + + ordered_map_item.prototype._value = null; + + function ordered_map_item(owner, key, value) { + if (key == null) { + key = null; + } + if (value == null) { + value = null; + } + this._owningMap = owner; + this._key = key; + this._value = value; + this; + } + + ordered_map_item.prototype.next = function(param) { + if (param !== void 0) { + this._next = param; + return this; + } + return this._next; + }; + + ordered_map_item.prototype.prev = function(param) { + if (param !== void 0) { + this._prev = param; + return this; + } + return this._prev; + }; + + ordered_map_item.prototype.owningMap = function(param) { + if (param !== void 0) { + this._owningMap = param; + return this; + } + return this._owningMap; + }; + + ordered_map_item.prototype.isFirst = function() { + return this._prev == null; + }; + + ordered_map_item.prototype.key = function(param) { + if (param !== void 0) { + this._key = param; + return this; + } + return this._key; + }; + + ordered_map_item.prototype.value = function(param) { + if (param !== void 0) { + this._value = param; + return this; + } + return this._value; + }; + + ordered_map_item.prototype.clearConnections = function() { + this._prev = null; + this._next = null; + return true; + }; + + return ordered_map_item; + + })(); + + ordered_map = (function() { + ordered_map.prototype._first = null; + + ordered_map.prototype._last = null; + + ordered_map.prototype._length = 0; + + function ordered_map(comparator) { + this._comparator = comparator; + if (comparator == null) { + throw new Error("comparator should be defined for the map"); + } + this._last = new ordered_map_item(this); + this; + } + + ordered_map.prototype.insertBefore = function(pos, map_item) { + var nextItem, prevItem; + prevItem = this._last.prev(); + nextItem = this._last; + if (pos != null) { + prevItem = pos.prev(); + nextItem = pos; + } + map_item.next(nextItem); + map_item.prev(prevItem); + if (nextItem != null) { + nextItem.prev(map_item); + } + if (prevItem != null) { + prevItem.next(map_item); + } + if (prevItem == null) { + this._first = map_item; + } + this._length++; + return map_item; + }; + + ordered_map.prototype.insert = function(param1, param2) { + var curr, item, key, value; + if (param1 instanceof ordered_map_item) { + item = param1; + curr = this.begin(); + while (curr !== this.end() && curr !== null) { + if (item.key() === curr.key()) { + curr.value(item.value()); + return curr; + } else { + if (this._comparator(item.key(), curr.key())) { + return this.insertBefore(curr, item); + } else { + curr = curr.next(); + } + } + } + return this.insertBefore(null, item); + } else { + key = param1; + value = param2; + return this.insert(new ordered_map_item(this, key, value)); + } + }; + + ordered_map.prototype.lowerBound = function(key) { + var curr; + curr = this.begin(); + while (curr !== this.end()) { + + /*if item.key() is curr.key() + curr.value(item.value()) # overwrite existing item, only one item per key!! + return curr + else + */ + if (this._comparator(curr.key(), key)) { + curr = curr.next(); + } else { + return curr; + } + } + return this.end(); + }; + + ordered_map.prototype.swap = function(item1, item2) { + var item1Next, item1Prev, item2Next, item2Prev; + if (!this.contains(item1) || !this.contains(item2)) { + throw new Error("items should belong to this list"); + } + if (item1 === this.end() || item2 === this.end()) { + throw new Error("swapping reserved @end item is not allowed"); + } + item1Prev = item1.prev(); + item1Next = item1.next(); + item2Prev = item2.prev(); + item2Next = item2.next(); + if (item1 === item2Prev && item2 === item1Next) { + item1.prev(item2); + item1.next(item2Next); + item2.next(item1); + item2.prev(item1Prev); + if (item1Prev != null) { + item1Prev.next(item2); + } + if (item2Next != null) { + item2Next.prev(item1); + } + } else if (item1 === item2Next && item2 === item1Prev) { + item1.next(item2); + item1.prev(item2Prev); + item2.prev(item1); + item2.next(item1Next); + if (item2Prev != null) { + item2Prev.next(item1); + } + if (item1Next != null) { + item1Next.prev(item2); + } + } else { + item1.prev(item2Prev); + item2.next(item1Next); + item1.next(item2Next); + item2.prev(item1Prev); + if (item1Prev != null) { + item1Prev.next(item2); + } + if (item1Next != null) { + item1Next.prev(item2); + } + if (item2Next != null) { + item2Next.prev(item1); + } + if (item2Prev != null) { + item2Prev.next(item1); + } + } + if (item1.prev() === null) { + this._first = item1; + } + if (item2.prev() === null) { + this._first = item2; + } + }; + + ordered_map.prototype.print = function() { + var a, str; + a = this.begin(); + str = "as is: ["; + while (a !== this.end()) { + str += JSON.stringify(a.key()); + a = a.next(); + str += a !== this.end() ? ", " : ""; + } + str += "] reverse: ["; + a = this.end().prev(); + while (a != null) { + str += JSON.stringify(a.key()); + a = a.prev(); + str += a != null ? ", " : ""; + } + str += "]"; + console.log(str); + }; + + ordered_map.prototype.clear = function() { + this._first = null; + this._last.prev(null); + return this._length = 0; + }; + + ordered_map.prototype.contains = function(item) { + return (item != null) && (item.owningMap() === this); + }; + + ordered_map.prototype.erase = function(item) { + var nextItem, prevItem; + if ((item != null) && !this.contains(item)) { + throw new Error("item is null or not part of this list"); + } + prevItem = item.prev(); + nextItem = item.next(); + if (nextItem != null) { + nextItem.prev(prevItem); + } + if (prevItem != null) { + prevItem.next(nextItem); + } + if (prevItem == null) { + this._first = nextItem; + } + item.clearConnections(); + item.owningMap(null); + this._length--; + return null; + }; + + ordered_map.prototype.begin = function() { + return this._first; + }; + + ordered_map.prototype.end = function() { + return this._last; + }; + + ordered_map.prototype.size = function() { + return this._length; + }; + + ordered_map.prototype.empty = function() { + return this._length <= 0; + }; + + ordered_map.prototype.at = function(index) { + var i, item; + item = this.begin(); + i = 0; + while (item !== this.end() && i < this._length) { + if (i === index) { + return item; + } + i++; + item = item.next(); + } + throw new Error("Index out of bound"); + }; + + return ordered_map; + + })(); + + if (typeof module !== "undefined" && module !== null) { + module.exports = { + list: list, + ordered_queue: ordered_queue, + ordered_map: ordered_map, + list_item: list_item + }; + } + + is_pos = function(num) { + return num > 0; + }; + + is_neg = function(num) { + return num < 0; + }; + + is_zero = function(num) { + return num === 0; + }; + + if (typeof module !== "undefined" && module !== null) { + module.exports = { + is_neg: is_neg, + is_pos: is_pos, + is_zero: is_zero + }; + } + + if ((DISABLE_REQUIRE == null) && (typeof require !== "undefined" && require !== null)) { + EPSILON = require('./constants').EPSILON; + ref1 = require('./voronoi_structures'), site_event = ref1.site_event, circle_event = ref1.circle_event, point_2d = ref1.point_2d; + ref2 = require('./voronoi_ctypes'), is_zero = ref2.is_zero, is_neg = ref2.is_neg, is_pos = ref2.is_pos; + ref3 = require('../thirdparty/jsbn'), BigIntegerFromInt = ref3.BigIntegerFromInt, BigInteger = ref3.BigInteger; + } else { + BigInteger = bnBigInteger; + BigIntegerFromInt = bnBigIntegerFromInt; + } + + static_cast_int = function(num) { + if (num > 0) { + return Math.floor(num); + } + return Math.ceil(num); + }; + + static_cast_big_int = function(num) { + return static_cast_int(num); + }; + + is_zero_big = function(num) { + return num.compareTo(BigInteger.ZERO) === 0; + }; + + is_pos_big = function(num) { + return num.compareTo(BigInteger.ZERO) > 0; + }; + + is_neg_big = function(num) { + return num.compareTo(BigInteger.ZERO) < 0; + }; + + get_sqrt = Math.sqrt; + + is_vertical = function(param1, param2) { + if (param2 != null) { + return Math.abs(param1.x() - param2.x()) < EPSILON; + } + return is_vertical(param1.point0(), param1.point1()); + }; + + + /* js comment: using not so robust cross product ;-) */ + + robust_cross_product = function(a1_, b1_, a2_, b2_) { + return a1_ * b2_ - b1_ * a2_; + }; + + ot = {}; + + ot.RIGHT = -1; + + ot.COLLINEAR = 0; + + ot.LEFT = 1; + + ot["eval"] = function(param1, param2, param3, param4) { + var dx1, dx2, dy1, dy2; + if (param4 != null) { + return ot["eval"](robust_cross_product(param1, param2, param3, param4)); + } else if (param3 != null) { + dx1 = static_cast_int(param1.x()) - static_cast_int(param2.x()); + dx2 = static_cast_int(param2.x()) - static_cast_int(param3.x()); + dy1 = static_cast_int(param1.y()) - static_cast_int(param2.y()); + dy2 = static_cast_int(param2.y()) - static_cast_int(param3.y()); + return ot["eval"](robust_cross_product(dx1, dy1, dx2, dy2)); + } else { + if (is_zero(param1)) { + return ot.COLLINEAR; + } + if (is_neg(param1)) { + return ot.RIGHT; + } else { + return ot.LEFT; + } + } + }; + + point_comparison_predicate = function(lhs, rhs) { + if (Math.abs(lhs.x() - rhs.x()) < EPSILON) { + return lhs.y() < rhs.y(); + } + return lhs.x() < rhs.x(); + }; + + event_comparison_predicate = function(lhs, rhs) { + if (lhs.eventType() === 'site_event' && rhs.eventType() === 'site_event') { + if (Math.abs(lhs.x0() - rhs.x0()) > EPSILON) { + return lhs.x0() < rhs.x0(); + } + if (!lhs.is_segment()) { + if (!rhs.is_segment()) { + return lhs.y0() < rhs.y0() && Math.abs(lhs.y0() - rhs.y0()) > EPSILON; + } + if (is_vertical(rhs)) { + return lhs.y0() < rhs.y0() || Math.abs(lhs.y0() - rhs.y0()) < EPSILON; + } + return true; + } else { + if (is_vertical(rhs)) { + if (is_vertical(lhs)) { + return lhs.y0() < rhs.y0() && Math.abs(lhs.y0() - rhs.y0()) > EPSILON; + } + return false; + } + if (is_vertical(lhs)) { + return true; + } + if (Math.abs(lhs.y0() - rhs.y0()) > EPSILON) { + return lhs.y0() < rhs.y0(); + } + return ot["eval"](lhs.point1(), lhs.point0(), rhs.point1()) === ot.LEFT; + } + + /* + bool operator()(const site_type& lhs, const circle_type& rhs) const { + typename ulp_cmp_type::Result xCmp = + ulp_cmp(to_fpt(lhs.x0()), to_fpt(rhs.lower_x()), ULPS); + return xCmp == ulp_cmp_type::LESS; + */ + } else if (lhs.eventType() === 'site_event' && rhs.eventType() === 'circle_event') { + return lhs.x0() < rhs.lower_x() && Math.abs(lhs.x0() - rhs.lower_x()) > EPSILON; + + /* + bool operator()(const circle_type& lhs, const site_type& rhs) const { + typename ulp_cmp_type::Result xCmp = + ulp_cmp(to_fpt(lhs.lower_x()), to_fpt(rhs.x0()), ULPS); + return xCmp == ulp_cmp_type::LESS; + */ + } else if (lhs.eventType() === 'circle_event' && rhs.eventType() === 'site_event') { + return lhs.lower_x() < rhs.x0() && Math.abs(lhs.lower_x() - rhs.x0()) > EPSILON; + + /* + bool operator()(const circle_type& lhs, const circle_type& rhs) const { + if (lhs.lower_x() != rhs.lower_x()) { + return lhs.lower_x() < rhs.lower_x(); + } + return lhs.y() < rhs.y(); + */ + } else if (lhs.eventType() === 'circle_event' && rhs.eventType() === 'circle_event') { + if (Math.abs(lhs.lower_x() - rhs.lower_x()) > EPSILON) { + return lhs.lower_x() < rhs.lower_x(); + } + return lhs.y() < rhs.y() && Math.abs(lhs.y() - rhs.y()) > EPSILON; + } else { + throw new Error("event_comparison_predicate should never reach here!"); + } + }; + + + /* // Represents the result of the epsilon robust predicate. If the + * // result is undefined some further processing is usually required. + */ + + kPredicateResult = { + LESS: -1, + UNDEFINED: 0, + MORE: 1 + }; + + distance_predicate = function(left_site, right_site, new_point) { + if (!left_site.is_segment()) { + if (!right_site.is_segment()) { + return pp(left_site, right_site, new_point); + } else { + return ps(left_site, right_site, new_point, false); + } + } else { + if (!right_site.is_segment()) { + return ps(right_site, left_site, new_point, true); + } else { + return ss(left_site, right_site, new_point); + } + } + }; + + + /* // Robust predicate, avoids using high-precision libraries. + * // Returns true if a horizontal line going through the new point site + * // intersects right arc at first, else returns false. If horizontal line + * // goes through intersection point of the given two arcs returns false. + */ + + pp = function(left_site, right_site, new_point) { + var dist1, dist2, left_point, right_point; + left_point = left_site.point0(); + right_point = right_site.point0(); + if (left_point.x() > right_point.x()) { + if (new_point.y() <= left_point.y()) { + return false; + } + } else if (left_point.x() < right_point.x()) { + if (new_point.y() >= right_point.y()) { + return true; + } + } else { + return (left_point.y() + right_point.y()) < (new_point.y() * 2); + } + dist1 = find_distance_to_point_arc(left_site, new_point); + dist2 = find_distance_to_point_arc(right_site, new_point); + + /*// The undefined ulp range is equal to 3EPS + 3EPS <= 6ULP. */ + return dist1 < dist2; + }; + + ps = function(left_site, right_site, new_point, reverse_order) { + var dist1, dist2, fast_res, val; + fast_res = fast_ps(left_site, right_site, new_point, reverse_order); + if (fast_res !== kPredicateResult.UNDEFINED) { + return fast_res === kPredicateResult.LESS; + } + dist1 = find_distance_to_point_arc(left_site, new_point); + dist2 = find_distance_to_segment_arc(right_site, new_point); + + /* // The undefined ulp range is equal to 3EPS + 7EPS <= 10ULP. */ + val = reverse_order ^ (dist1 < dist2); + return val === 1; + }; + + ss = function(left_site, right_site, new_point) { + + /* // Handle temporary segment sites. */ + var dist1, dist2; + if (left_site.sorted_index() === right_site.sorted_index()) { + return ot["eval"](left_site.point0(), left_site.point1(), new_point) === ot.LEFT; + } + dist1 = find_distance_to_segment_arc(left_site, new_point); + dist2 = find_distance_to_segment_arc(right_site, new_point); + + /* // The undefined ulp range is equal to 7EPS + 7EPS <= 14ULP. */ + return dist1 < dist2; + }; + + find_distance_to_point_arc = function(site, point) { + var dx, dy; + dx = site.x() - point.x(); + dy = site.y() - point.y(); + + /* // The relative error is at most 3EPS. */ + return (dx * dx + dy * dy) / (2.0 * dx); + }; + + find_distance_to_segment_arc = function(site, point) { + var a1, b1, k, segment0, segment1; + if (is_vertical(site)) { + return (site.x() - point.x()) / 2; + } else { + segment0 = site.point0(); + segment1 = site.point1(); + a1 = segment1.x() - segment0.x(); + b1 = segment1.y() - segment0.y(); + k = Math.sqrt(a1 * a1 + b1 * b1); + + /* // Avoid subtraction while computing k. */ + if (!is_neg(b1)) { + k = 1.0 / (b1 + k); + } else { + k = (k - b1) / (a1 * a1); + } + + /* // The relative error is at most 7EPS. */ + return k * robust_cross_product(static_cast_int(segment1.x()) - static_cast_int(segment0.x()), static_cast_int(segment1.y()) - static_cast_int(segment0.y()), static_cast_int(point.x()) - static_cast_int(segment0.x()), static_cast_int(point.y()) - static_cast_int(segment0.y())); + } + }; + + fast_ps = function(left_site, right_site, new_point, reverse_order) { + var a, b, dif_x, dif_y, fast_left_expr, fast_right_expr, orientation, segment_end, segment_start, site_point; + site_point = left_site.point0(); + segment_start = right_site.point0(); + segment_end = right_site.point1(); + if (ot["eval"](segment_start, segment_end, new_point) !== ot.RIGHT) { + if (!right_site.is_inverse()) { + return kPredicateResult.LESS; + } else { + return kPredicateResult.MORE; + } + } + dif_x = new_point.x() - site_point.x(); + dif_y = new_point.y() - site_point.y(); + a = segment_end.x() - segment_start.x(); + b = segment_end.y() - segment_start.y(); + if (is_vertical(right_site)) { + if (new_point.y() < site_point.y() && !reverse_order) { + return kPredicateResult.MORE; + } else if (new_point.y() > site_point.y() && reverse_order) { + return kPredicateResult.LESS; + } + return kPredicateResult.UNDEFINED; + } else { + orientation = ot["eval"](static_cast_int(segment_end.x()) - static_cast_int(segment_start.x()), static_cast_int(segment_end.y()) - static_cast_int(segment_start.y()), static_cast_int(new_point.x()) - static_cast_int(site_point.x()), static_cast_int(new_point.y()) - static_cast_int(site_point.y())); + if (orientation === ot.LEFT) { + if (!right_site.is_inverse()) { + if (reverse_order) { + return kPredicateResult.LESS; + } else { + return kPredicateResult.UNDEFINED; + } + } + if (reverse_order) { + return kPredicateResult.UNDEFINED; + } else { + return kPredicateResult.MORE; + } + } + } + fast_left_expr = a * (dif_y + dif_x) * (dif_y - dif_x); + fast_right_expr = (2.0 * b) * dif_x * dif_y; + if (Math.abs(fast_left_expr - fast_right_expr) > EPSILON) { + if ((fast_left_expr > fast_right_expr) ^ reverse_order) { + if (reverse_order) { + return kPredicateResult.LESS; + } else { + return kPredicateResult.MORE; + } + } + return kPredicateResult.UNDEFINED; + } + return kPredicateResult.UNDEFINED; + }; + + node_comparison_predicate = function(node1, node2) { + var get_comparison_point, get_comparison_site, get_comparison_y, point1, point2, site1, site2, y1, y2; + get_comparison_site = function(node) { + if (node.left_site().sorted_index() > node.right_site().sorted_index()) { + return node.left_site(); + } + return node.right_site(); + }; + get_comparison_point = function(site) { + if (point_comparison_predicate(site.point0(), site.point1())) { + return site.point0(); + } else { + return site.point1(); + } + }; + + /* // Get comparison pair: y coordinate and direction of the newer site. */ + get_comparison_y = function(node, is_new_node) { + if (is_new_node == null) { + is_new_node = true; + } + if (node.left_site().sorted_index() === node.right_site().sorted_index()) { + return { + first: node.left_site().y0(), + second: 0 + }; + } + if (node.left_site().sorted_index() > node.right_site().sorted_index()) { + if (!is_new_node && node.left_site().is_segment() && is_vertical(node.left_site())) { + return { + first: node.left_site().y0(), + second: 1 + }; + } + return { + first: node.left_site().y1(), + second: 1 + }; + } + return { + first: node.right_site().y0(), + second: -1 + }; + }; + site1 = get_comparison_site(node1); + site2 = get_comparison_site(node2); + point1 = get_comparison_point(site1); + point2 = get_comparison_point(site2); + if (Math.abs(point1.x() - point2.x()) < EPSILON) { + + /* // This checks were evaluated experimentally. */ + if (site1.sorted_index() === site2.sorted_index()) { + + /* // Both nodes are new (inserted during same site event processing). */ + y1 = get_comparison_y(node1); + y2 = get_comparison_y(node2); + return (y1.first < y2.first) || ((y1.first === y2.first) && y1.second < y2.second); + } else if (site1.sorted_index() < site2.sorted_index()) { + y1 = get_comparison_y(node1, false); + y2 = get_comparison_y(node2, true); + if (y1.first !== y2.first) { + return y1.first < y2.first; + } + if (!site1.is_segment()) { + return y1.second < 0; + } else { + return false; + } + } else { + y1 = get_comparison_y(node1, true); + y2 = get_comparison_y(node2, false); + if (y1.first !== y2.first) { + return y1.first < y2.first; + } + if (!site2.is_segment()) { + return y2.second > 0; + } else { + return true; + } + } + } else if (point1.x() < point2.x()) { + + /* // The second node contains a new site. */ + return distance_predicate(node1.left_site(), node1.right_site(), point2); + } else if (point1.x() > point2.x()) { + + /* // The first node contains a new site. */ + return !distance_predicate(node2.left_site(), node2.right_site(), point1); + } + }; + + circle_existence_predicate = { + ppp: function(site1, site2, site3) { + return ot["eval"](site1.point0(), site2.point0(), site3.point0()) === ot.RIGHT; + }, + pps: function(site1, site2, site3, segment_index) { + var orient1, orient2; + if (segment_index !== 2) { + orient1 = ot["eval"](site1.point0(), site2.point0(), site3.point0()); + orient2 = ot["eval"](site1.point0(), site2.point0(), site3.point1()); + if (segment_index === 1 && site1.x0() >= site2.x0()) { + if (orient1 !== ot.RIGHT) { + return false; + } + } else if (segment_index === 3 && site2.x0() >= site1.x0()) { + if (orient2 !== ot.RIGHT) { + return false; + } + } else if (orient1 !== ot.RIGHT && orient2 !== ot.RIGHT) { + return false; + } + } else { + return (!site3.point0().equals(site1.point0())) || (!site3.point1().equals(site2.point0())); + } + return true; + }, + pss: function(site1, site2, site3, point_index) { + if (site2.sorted_index() === site3.sorted_index()) { + return false; + } + if (point_index === 2) { + if (!site2.is_inverse() && site3.is_inverse()) { + return false; + } + if (site2.is_inverse() === site3.is_inverse() && ot["eval"](site2.point0(), site1.point0(), site3.point1()) !== ot.RIGHT) { + return false; + } + } + return true; + }, + sss: function(site1, site2, site3) { + return (site1.sorted_index() !== site2.sorted_index()) && (site2.sorted_index() !== site3.sorted_index()); + } + }; + + + /* + * eval functions are approximated! revisit!! ruwan + */ + + sqrt_expr_ = { + eval1: function(A, B) { + if (A instanceof Array) { + return A[0] * Math.sqrt(B[0]); + } else { + return A * Math.sqrt(B); + } + }, + eval2: function(A0, B0, A1, B1) { + if (A0 instanceof Array) { + return sqrt_expr_.eval2(A0[0], B0[0], A0[1], B0[1]); + } else { + return sqrt_expr_.eval1(A0, B0) + sqrt_expr_.eval1(A1, B1); + } + }, + eval3: function(A0, B0, A1, B1, A2, B2) { + if (A0 instanceof Array) { + return sqrt_expr_.eval3(A0[0], B0[0], A0[1], B0[1], A0[2], B0[2]); + } else { + return sqrt_expr_.eval2(A0, B0, A1, B1) + sqrt_expr_.eval1(A2, B2); + } + }, + eval4: function(A0, B0, A1, B1, A2, B2, A3, B3) { + if (A0 instanceof Array) { + return sqrt_expr_.eval4(A0[0], B0[0], A0[1], B0[1], A0[2], B0[2], A0[3], B0[3]); + } else { + return sqrt_expr_.eval2(A0, B0, A1, B1) + sqrt_expr_.eval2(A2, B2, A3, B3); + } + } + }; + + sqrt_expr_big_ = { + eval1: function(A, B) { + var a, afpt, b, bfpt; + a = A; + b = B; + if (A instanceof Array) { + a = A[0]; + b = B[0]; + } + afpt = a.toDouble(); + bfpt = b.toDouble(); + return afpt * Math.sqrt(bfpt); + }, + eval2: function(A0, B0, A1, B1) { + var a, b; + if (A0 instanceof Array) { + return sqrt_expr_big_.eval2(A0[0], B0[0], A0[1], B0[1]); + } else { + a = sqrt_expr_big_.eval1(A0, B0); + b = sqrt_expr_big_.eval1(A1, B1); + if ((!is_neg(a) && !is_neg(b)) || (!is_pos(a) && !is_pos(b))) { + return a + b; + } + } + return ((A0.multiply(A0).multiply(B0)).subtract(A1.multiply(A1).multiply(B1))).toDouble() / (a - b); + }, + eval3: function(A0, B0, A1, B1, A2, B2) { + var a, b, tA, tB; + if (A0 instanceof Array) { + return sqrt_expr_big_.eval3(A0[0], B0[0], A0[1], B0[1], A0[2], B0[2]); + } else { + a = sqrt_expr_big_.eval2(A0, B0, A1, B1); + b = sqrt_expr_big_.eval1(A2, B2); + if ((!is_neg(a) && !is_neg(b)) || (!is_pos(a) && !is_pos(b))) { + return a + b; + } + tA = []; + tB = []; + tA[0] = (A0.multiply(A0).multiply(B0)).add(A1.multiply(A1).multiply(B1)).subtract(A2.multiply(A2).multiply(B2)); + tB[0] = BigIntegerFromInt(1); + tA[1] = A0.multiply(A1).multiply(BigIntegerFromInt(2)); + tB[1] = B0.multiply(B1); + return sqrt_expr_big_.eval2(tA, tB) / (a - b); + } + }, + eval4: function(A0, B0, A1, B1, A2, B2, A3, B3) { + var a, b, tA, tB; + if (A0 instanceof Array) { + return sqrt_expr_big_.eval4(A0[0], B0[0], A0[1], B0[1], A0[2], B0[2], A0[3], B0[3]); + } else { + a = sqrt_expr_big_.eval2(A0, B0, A1, B1); + b = sqrt_expr_big_.eval2(A2, B2, A3, B3); + if ((!is_neg(a) && !is_neg(b)) || (!is_pos(a) && !is_pos(b))) { + return a + b; + } + tA = []; + tB = []; + tA[0] = (A0.multiply(A0).multiply(B0)).add(A1.multiply(A1).multiply(B1)).subtract(A2.multiply(A2).multiply(B2)).subtract(A3.multiply(A3).multiply(B3)); + tB[0] = BigIntegerFromInt(1); + tA[1] = A0.multiply(A1).multiply(BigIntegerFromInt(2)); + tB[1] = B0.multiply(B1); + tA[2] = A2.multiply(A3).multiply(BigIntegerFromInt(-2)); + tB[2] = B2.multiply(B3); + return sqrt_expr_big_.eval3(tA, tB) / (a - b); + } + } + }; + + sqrt_expr_evaluator_pss4 = function(A, B) { + throw new Error("sqrt_expr_evaluator_pss4 should never be called"); + }; + + sqrt_expr_evaluator_pss4_temp = function(A, B) { + return A[3] + A[0] * Math.sqrt(B[0]) + A[1] * Math.sqrt(B[1]) + A[2] * Math.sqrt(B[3] * (Math.sqrt(B[0] * B[1]) + B[2])); + }; + + sqrt_expr_evaluator_pss4_big = function(A, B) { + var cA, cB, lh, numer, rh; + cA = []; + cB = []; + if (is_zero_big(A[3])) { + lh = sqrt_expr_big_.eval2(A, B); + cA[0] = BigIntegerFromInt(1); + cB[0] = B[0].multiply(B[1]); + cA[1] = B[2]; + cB[1] = BigIntegerFromInt(1); + rh = sqrt_expr_big_.eval1(A[2], B[3]) * get_sqrt(sqrt_expr_big_.eval2(cA, cB)); + if ((!is_neg(lh) && !is_neg(rh)) || (!is_pos(lh) && !is_pos(rh))) { + return lh + rh; + } + cA[0] = ((A[0].multiply(A[0]).multiply(B[0])).add(A[1].multiply(A[1]).multiply(B[1]))).subtract(A[2].multiply(A[2]).multiply(B[3]).multiply(B[2])); + cB[0] = BigIntegerFromInt(1); + cA[1] = (A[0].multiply(A[1]).multiply(BigIntegerFromInt(2))).subtract(A[2].multiply(A[2]).multiply(B[3])); + cB[1] = B[0].multiply(B[1]); + numer = sqrt_expr_big_.eval2(cA, cB); + return numer / (lh - rh); + } + cA[0] = BigIntegerFromInt(1); + cB[0] = B[0].multiply(B[1]); + cA[1] = B[2]; + cB[1] = BigIntegerFromInt(1); + rh = sqrt_expr_big_.eval1(A[2], B[3]) * get_sqrt(sqrt_expr_big_.eval2(cA, cB)); + cA[0] = A[0]; + cB[0] = B[0]; + cA[1] = A[1]; + cB[1] = B[1]; + cA[2] = A[3]; + cB[2] = BigIntegerFromInt(1); + lh = sqrt_expr_big_.eval3(cA, cB); + if ((!is_neg(lh) && !is_neg(rh)) || (!is_pos(lh) && !is_pos(rh))) { + return lh + rh; + } + cA[0] = A[3].multiply(A[0]).multiply(BigIntegerFromInt(2)); + cA[1] = A[3].multiply(A[1]).multiply(BigIntegerFromInt(2)); + cA[2] = (A[0].multiply(A[0]).multiply(B[0])).add(A[1].multiply(A[1]).multiply(B[1])).add(A[3].multiply(A[3])).subtract(A[2].multiply(A[2]).multiply(B[2]).multiply(B[3])); + cA[3] = (A[0].multiply(A[1]).multiply(BigIntegerFromInt(2))).subtract(A[2].multiply(A[2]).multiply(B[3])); + cB[3] = B[0].multiply(B[1]); + numer = sqrt_expr_evaluator_pss3_big(cA, cB); + return numer / (lh - rh); + }; + + sqrt_expr_evaluator_pss3 = function(A, B) { + throw new Error("sqrt_expr_evaluator_pss3 should never be called"); + }; + + sqrt_expr_evaluator_pss3_big = function(A, B) { + var cA, cB, lh, numer, rh; + cA = []; + cB = []; + lh = sqrt_expr_big_.eval2(A[0], B[0], A[1], B[1]); + rh = sqrt_expr_big_.eval2(A[2], B[2], A[3], B[3]); + if (((!is_neg(lh)) && (!is_neg(rh))) || ((!is_pos(lh)) && (!is_pos(rh)))) { + return lh + rh; + } + cA[0] = ((A[0].multiply(A[0]).multiply(B[0])).add(A[1].multiply(A[1]).multiply(B[1]))).subtract(A[2].multiply(A[2])).subtract(A[3].multiply(A[3]).multiply(B[0]).multiply(B[1])); + cB[0] = BigIntegerFromInt(1); + cA[1] = ((A[0].multiply(A[1])).subtract(A[2].multiply(A[3]))).multiply(BigIntegerFromInt(2)); + cB[1] = B[3]; + numer = sqrt_expr_big_.eval2(cA, cB); + return numer / (lh - rh); + }; + + circle_formation_functor = { + ppp: function(site1, site2, site3, circle, recompute_c_x, recompute_c_y, recompute_lower_x) { + var c_x, c_y, dif_x, dif_y, inv_denom, lower_x, numer, numer1, numer2, r, sqr_r, sum_x, sum_y; + if (recompute_c_x == null) { + recompute_c_x = true; + } + if (recompute_c_y == null) { + recompute_c_y = true; + } + if (recompute_lower_x == null) { + recompute_lower_x = true; + } + dif_x = []; + dif_y = []; + sum_x = []; + sum_y = []; + dif_x[0] = static_cast_int(site1.x()) - static_cast_int(site2.x()); + dif_x[1] = static_cast_int(site2.x()) - static_cast_int(site3.x()); + dif_x[2] = static_cast_int(site1.x()) - static_cast_int(site3.x()); + dif_y[0] = static_cast_int(site1.y()) - static_cast_int(site2.y()); + dif_y[1] = static_cast_int(site2.y()) - static_cast_int(site3.y()); + dif_y[2] = static_cast_int(site1.y()) - static_cast_int(site3.y()); + sum_x[0] = static_cast_int(site1.x()) + static_cast_int(site2.x()); + sum_x[1] = static_cast_int(site2.x()) + static_cast_int(site3.x()); + sum_y[0] = static_cast_int(site1.y()) + static_cast_int(site2.y()); + sum_y[1] = static_cast_int(site2.y()) + static_cast_int(site3.y()); + inv_denom = 0.5 / static_cast_big_int(dif_x[0] * dif_y[1] - dif_x[1] * dif_y[0]); + numer1 = dif_x[0] * sum_x[0] + dif_y[0] * sum_y[0]; + numer2 = dif_x[1] * sum_x[1] + dif_y[1] * sum_y[1]; + if (recompute_c_x || recompute_lower_x) { + c_x = numer1 * dif_y[1] - numer2 * dif_y[0]; + circle.x(c_x * inv_denom); + if (recompute_lower_x) { + + /*// Evaluate radius of the circle. */ + sqr_r = (dif_x[0] * dif_x[0] + dif_y[0] * dif_y[0]) * (dif_x[1] * dif_x[1] + dif_y[1] * dif_y[1]) * (dif_x[2] * dif_x[2] + dif_y[2] * dif_y[2]); + r = get_sqrt(sqr_r); + + /*// If c_x >= 0 then lower_x = c_x + r, + // else lower_x = (c_x * c_x - r * r) / (c_x - r). + // To guarantee epsilon relative error. + */ + if (!is_neg(circle.x())) { + if (!is_neg(inv_denom)) { + circle.lower_x(circle.x() + r * inv_denom); + } else { + circle.lower_x(circle.x() - r * inv_denom); + } + } else { + numer = c_x * c_x - sqr_r; + lower_x = numer * inv_denom / (c_x + r); + circle.lower_x(lower_x); + } + } + } + if (recompute_c_y) { + c_y = numer2 * dif_x[0] - numer1 * dif_x[1]; + circle.y(c_y * inv_denom); + } + }, + + /* // Recompute parameters of the circle event using high-precision library. */ + pps: function(site1, site2, site3, segment_index, c_event, recompute_c_x, recompute_c_y, recompute_lower_x) { + var A, B, cA, cB, denom, det, dif0, dif1, inv_denom, inv_denom_sqr, line_a, line_b, numer, segm_len, sum_AB, sum_x, sum_y, teta, vec_x, vec_y; + if (recompute_c_x == null) { + recompute_c_x = true; + } + if (recompute_c_y == null) { + recompute_c_y = true; + } + if (recompute_lower_x == null) { + recompute_lower_x = true; + } + cA = []; + cB = []; + line_a = static_cast_int(site3.y1()) - static_cast_int(site3.y0()); + line_b = static_cast_int(site3.x0()) - static_cast_int(site3.x1()); + segm_len = line_a * line_a + line_b * line_b; + vec_x = static_cast_int(site2.y()) - static_cast_int(site1.y()); + vec_y = static_cast_int(site1.x()) - static_cast_int(site2.x()); + sum_x = static_cast_int(site1.x()) + static_cast_int(site2.x()); + sum_y = static_cast_int(site1.y()) + static_cast_int(site2.y()); + teta = line_a * vec_x + line_b * vec_y; + denom = vec_x * line_b - vec_y * line_a; + dif0 = static_cast_int(site3.y1()) - static_cast_int(site1.y()); + dif1 = static_cast_int(site1.x()) - static_cast_int(site3.x1()); + A = line_a * dif1 - line_b * dif0; + dif0 = static_cast_int(site3.y1()) - static_cast_int(site2.y()); + dif1 = static_cast_int(site2.x()) - static_cast_int(site3.x1()); + B = line_a * dif1 - line_b * dif0; + sum_AB = A + B; + if (is_zero(denom)) { + numer = teta * teta - sum_AB * sum_AB; + denom = teta * sum_AB; + cA[0] = denom * sum_x * 2 + numer * vec_x; + cB[0] = segm_len; + cA[1] = denom * sum_AB * 2 + numer * teta; + cB[1] = 1; + cA[2] = denom * sum_y * 2 + numer * vec_y; + inv_denom = 1.0 / denom; + if (recompute_c_x) { + c_event.x(0.25 * cA[0] * inv_denom); + } + if (recompute_c_y) { + c_event.y(0.25 * cA[2] * inv_denom); + } + if (recompute_lower_x) { + c_event.lower_x(0.25 * sqrt_expr_.eval2(cA, cB) * inv_denom / get_sqrt(segm_len)); + } + return; + } + det = (teta * teta + denom * denom) * A * B * 4; + inv_denom_sqr = 1.0 / denom; + inv_denom_sqr *= inv_denom_sqr; + if (recompute_c_x || recompute_lower_x) { + cA[0] = sum_x * denom * denom + teta * sum_AB * vec_x; + cB[0] = 1; + cA[1] = segment_index === 2 ? -vec_x : vec_x; + cB[1] = det; + if (recompute_c_x) { + c_event.x(0.5 * sqrt_expr_.eval2(cA, cB) * inv_denom_sqr); + } + } + if (recompute_c_y || recompute_lower_x) { + cA[2] = sum_y * denom * denom + teta * sum_AB * vec_y; + cB[2] = 1; + cA[3] = segment_index === 2 ? -vec_y : vec_y; + cB[3] = det; + if (recompute_c_y) { + c_event.y(0.5 * sqrt_expr_.eval2(cA[2], cB[2], cA[3], cB[3]) * inv_denom_sqr); + } + } + if (recompute_lower_x) { + cB[0] = cB[0] * segm_len; + cB[1] = cB[1] * segm_len; + cA[2] = sum_AB * (denom * denom + teta * teta); + cB[2] = 1; + cA[3] = segment_index === 2 ? -teta : teta; + cB[3] = det; + return c_event.lower_x(0.5 * sqrt_expr_.eval4(cA, cB) * inv_denom_sqr / get_sqrt(segm_len)); + } + }, + + /*// Recompute parameters of the circle event using high-precision library. */ + pss: function(site1, site2, site3, point_index, c_event, recompute_c_x, recompute_c_y, recompute_lower_x) { + var a, b, c, cA, cB, c_x, c_y, cx, cy, denom, dx, dy, ix, iy, lower_x, orientation, segm_end1, segm_end2, segm_start1, segm_start2, sign, temp; + if (recompute_c_x == null) { + recompute_c_x = true; + } + if (recompute_c_y == null) { + recompute_c_y = true; + } + if (recompute_lower_x == null) { + recompute_lower_x = true; + } + a = []; + b = []; + c = []; + cA = []; + cB = []; + segm_start1 = site2.point1(); + segm_end1 = site2.point0(); + segm_start2 = site3.point0(); + segm_end2 = site3.point1(); + a[0] = static_cast_int(segm_end1.x()) - static_cast_int(segm_start1.x()); + b[0] = static_cast_int(segm_end1.y()) - static_cast_int(segm_start1.y()); + a[1] = static_cast_int(segm_end2.x()) - static_cast_int(segm_start2.x()); + b[1] = static_cast_int(segm_end2.y()) - static_cast_int(segm_start2.y()); + orientation = a[1] * b[0] - a[0] * b[1]; + if (is_zero(orientation)) { + denom = 2.0 * static_cast_big_int(a[0] * a[0] + b[0] * b[0]); + c[0] = b[0] * (static_cast_int(segm_start2.x()) - static_cast_int(segm_start1.x())) - a[0] * (static_cast_int(segm_start2.y()) - static_cast_int(segm_start1.y())); + dx = a[0] * (static_cast_int(site1.y()) - static_cast_int(segm_start1.y())) - b[0] * (static_cast_int(site1.x()) - static_cast_int(segm_start1.x())); + dy = b[0] * (static_cast_int(site1.x()) - static_cast_int(segm_start2.x())) - a[0] * (static_cast_int(site1.y()) - static_cast_int(segm_start2.y())); + cB[0] = dx * dy; + cB[1] = 1; + if (recompute_c_y) { + cA[0] = b[0] * (point_index === 2 ? 2 : -2); + cA[1] = a[0] * a[0] * (static_cast_int(segm_start1.y()) + static_cast_int(segm_start2.y())) - a[0] * b[0] * (static_cast_int(segm_start1.x()) + static_cast_int(segm_start2.x()) - static_cast_int(site1.x()) * 2) + b[0] * b[0] * (static_cast_int(site1.y()) * 2); + c_y = sqrt_expr_.eval2(cA, cB); + c_event.y(c_y / denom); + } + if (recompute_c_x || recompute_lower_x) { + cA[0] = a[0] * (point_index === 2 ? 2 : -2); + cA[1] = b[0] * b[0] * (static_cast_int(segm_start1.x()) + static_cast_int(segm_start2.x())) - a[0] * b[0] * (static_cast_int(segm_start1.y()) + static_cast_int(segm_start2.y()) - static_cast_int(site1.y()) * 2) + a[0] * a[0] * (static_cast_int(site1.x()) * 2); + if (recompute_c_x) { + c_x = sqrt_expr_.eval2(cA, cB); + c_event.x(c_x / denom); + } + if (recompute_lower_x) { + cA[2] = is_neg(c[0]) ? -c[0] : c[0]; + cB[2] = a[0] * a[0] + b[0] * b[0]; + lower_x = sqrt_expr_.eval3(cA, cB); + c_event.lower_x(lower_x / denom); + } + } + return; + } + c[0] = b[0] * segm_end1.x() - a[0] * segm_end1.y(); + c[1] = a[1] * segm_end2.y() - b[1] * segm_end2.x(); + ix = a[0] * c[1] + a[1] * c[0]; + iy = b[0] * c[1] + b[1] * c[0]; + dx = ix - orientation * site1.x(); + dy = iy - orientation * site1.y(); + if (is_zero(dx) && is_zero(dy)) { + denom = orientation; + c_x = ix / denom; + c_y = iy / denom; + c_event.x(c_x); + c_event.y(c_y); + c_event.lower_x(c_x); + return; + } + sign = (point_index === 2 ? 1 : -1) * (is_neg(orientation) ? 1 : -1); + cA[0] = a[1] * -dx + b[1] * -dy; + cA[1] = a[0] * -dx + b[0] * -dy; + cA[2] = sign; + cA[3] = 0; + cB[0] = a[0] * a[0] + b[0] * b[0]; + cB[1] = a[1] * a[1] + b[1] * b[1]; + cB[2] = a[0] * a[1] + b[0] * b[1]; + cB[3] = (a[0] * dy - b[0] * dx) * (a[1] * dy - b[1] * dx) * -2; + temp = sqrt_expr_evaluator_pss4_temp(cA, cB); + denom = temp * orientation; + if (recompute_c_y) { + cA[0] = b[1] * (dx * dx + dy * dy) - iy * (dx * a[1] + dy * b[1]); + cA[1] = b[0] * (dx * dx + dy * dy) - iy * (dx * a[0] + dy * b[0]); + cA[2] = iy * sign; + cy = sqrt_expr_evaluator_pss4_temp(cA, cB); + c_event.y(cy / denom); + } + if (recompute_c_x || recompute_lower_x) { + cA[0] = a[1] * (dx * dx + dy * dy) - ix * (dx * a[1] + dy * b[1]); + cA[1] = a[0] * (dx * dx + dy * dy) - ix * (dx * a[0] + dy * b[0]); + cA[2] = ix * sign; + if (recompute_c_x) { + cx = sqrt_expr_evaluator_pss4_temp(cA, cB); + c_event.x(cx / denom); + } + if (recompute_lower_x) { + cA[3] = orientation * (dx * dx + dy * dy) * (is_neg(temp) ? -1 : 1); + lower_x = sqrt_expr_evaluator_pss4_temp(cA, cB); + return c_event.lower_x(lower_x / denom); + } + } + }, + + /*// Recompute parameters of the circle event using high-precision library. */ + sss: function(site1, site2, site3, c_event, recompute_c_x, recompute_c_y, recompute_lower_x) { + var a, b, c, cA, cB, c_x, c_y, denom, i, j, k, l, lower_x, m, n, o; + if (recompute_c_x == null) { + recompute_c_x = true; + } + if (recompute_c_y == null) { + recompute_c_y = true; + } + if (recompute_lower_x == null) { + recompute_lower_x = true; + } + a = []; + b = []; + c = []; + cA = []; + cB = []; + + /* + // cA - corresponds to the cross product. + // cB - corresponds to the squared length. + */ + a[0] = static_cast_int(site1.x1()) - static_cast_int(site1.x0()); + a[1] = static_cast_int(site2.x1()) - static_cast_int(site2.x0()); + a[2] = static_cast_int(site3.x1()) - static_cast_int(site3.x0()); + b[0] = static_cast_int(site1.y1()) - static_cast_int(site1.y0()); + b[1] = static_cast_int(site2.y1()) - static_cast_int(site2.y0()); + b[2] = static_cast_int(site3.y1()) - static_cast_int(site3.y0()); + c[0] = static_cast_int(site1.x0()) * static_cast_int(site1.y1()) - static_cast_int(site1.y0()) * static_cast_int(site1.x1()); + c[1] = static_cast_int(site2.x0()) * static_cast_int(site2.y1()) - static_cast_int(site2.y0()) * static_cast_int(site2.x1()); + c[2] = static_cast_int(site3.x0()) * static_cast_int(site3.y1()) - static_cast_int(site3.y0()) * static_cast_int(site3.x1()); + for (i = l = 0; l < 3; i = ++l) { + cB[i] = a[i] * a[i] + b[i] * b[i]; + } + for (i = m = 0; m < 3; i = ++m) { + j = (i + 1) % 3; + k = (i + 2) % 3; + cA[i] = a[j] * b[k] - a[k] * b[j]; + } + denom = sqrt_expr_.eval3(cA, cB); + if (recompute_c_y) { + for (i = n = 0; n < 3; i = ++n) { + j = (i + 1) % 3; + k = (i + 2) % 3; + cA[i] = b[j] * c[k] - b[k] * c[j]; + } + c_y = sqrt_expr_.eval3(cA, cB); + c_event.y(c_y / denom); + } + if (recompute_c_x || recompute_lower_x) { + cA[3] = 0; + for (i = o = 0; o < 3; i = ++o) { + j = (i + 1) % 3; + k = (i + 2) % 3; + cA[i] = a[j] * c[k] - a[k] * c[j]; + if (recompute_lower_x) { + cA[3] = cA[3] + cA[i] * b[i]; + } + } + if (recompute_c_x) { + c_x = sqrt_expr_.eval3(cA, cB); + c_event.x(c_x / denom); + } + if (recompute_lower_x) { + cB[3] = 1; + lower_x = sqrt_expr_.eval4(cA, cB); + return c_event.lower_x(lower_x / denom); + } + } + } + }; + + circle_formation_functor_big = { + ppp: function(site1, site2, site3, circle, recompute_c_x, recompute_c_y, recompute_lower_x) { + var c_x, c_y, dif_x, dif_y, inv_denom, lower_x, numer, numer1, numer2, r, sqr_r, sum_x, sum_y; + if (recompute_c_x == null) { + recompute_c_x = true; + } + if (recompute_c_y == null) { + recompute_c_y = true; + } + if (recompute_lower_x == null) { + recompute_lower_x = true; + } + dif_x = []; + dif_y = []; + sum_x = []; + sum_y = []; + dif_x[0] = BigIntegerFromInt(static_cast_int(site1.x()) - static_cast_int(site2.x())); + dif_x[1] = BigIntegerFromInt(static_cast_int(site2.x()) - static_cast_int(site3.x())); + dif_x[2] = BigIntegerFromInt(static_cast_int(site1.x()) - static_cast_int(site3.x())); + dif_y[0] = BigIntegerFromInt(static_cast_int(site1.y()) - static_cast_int(site2.y())); + dif_y[1] = BigIntegerFromInt(static_cast_int(site2.y()) - static_cast_int(site3.y())); + dif_y[2] = BigIntegerFromInt(static_cast_int(site1.y()) - static_cast_int(site3.y())); + sum_x[0] = BigIntegerFromInt(static_cast_int(site1.x()) + static_cast_int(site2.x())); + sum_x[1] = BigIntegerFromInt(static_cast_int(site2.x()) + static_cast_int(site3.x())); + sum_y[0] = BigIntegerFromInt(static_cast_int(site1.y()) + static_cast_int(site2.y())); + sum_y[1] = BigIntegerFromInt(static_cast_int(site2.y()) + static_cast_int(site3.y())); + inv_denom = 0.5 / ((dif_x[0].multiply(dif_y[1])).subtract(dif_x[1].multiply(dif_y[0]))).toDouble(); + numer1 = (dif_x[0].multiply(sum_x[0])).add(dif_y[0].multiply(sum_y[0])); + numer2 = (dif_x[1].multiply(sum_x[1])).add(dif_y[1].multiply(sum_y[1])); + if (recompute_c_x || recompute_lower_x) { + c_x = (numer1.multiply(dif_y[1])).subtract(numer2.multiply(dif_y[0])); + circle.x(c_x.toDouble() * inv_denom); + if (recompute_lower_x) { + + /*// Evaluate radius of the circle. */ + sqr_r = ((dif_x[0].multiply(dif_x[0])).add(dif_y[0].multiply(dif_y[0]))).multiply((dif_x[1].multiply(dif_x[1])).add(dif_y[1].multiply(dif_y[1]))).multiply((dif_x[2].multiply(dif_x[2])).add(dif_y[2].multiply(dif_y[2]))); + r = get_sqrt(sqr_r.toDouble()); + + /*// If c_x >= 0 then lower_x = c_x + r, + // else lower_x = (c_x * c_x - r * r) / (c_x - r). + // To guarantee epsilon relative error. + */ + if (!is_neg(circle.x())) { + if (!is_neg(inv_denom)) { + circle.lower_x(circle.x() + r * inv_denom); + } else { + circle.lower_x(circle.x() - r * inv_denom); + } + } else { + numer = (c_x.multiply(c_x)).subtract(sqr_r); + lower_x = numer.toDouble() * inv_denom / (c_x.toDouble() + r); + circle.lower_x(lower_x); + } + } + } + if (recompute_c_y) { + c_y = (numer2.multiply(dif_x[0])).subtract(numer1.multiply(dif_x[1])); + circle.y(c_y.toDouble() * inv_denom); + } + }, + + /* // Recompute parameters of the circle event using high-precision library. */ + pps: function(site1, site2, site3, segment_index, c_event, recompute_c_x, recompute_c_y, recompute_lower_x) { + var A, B, cA, cB, denom, det, dif0, dif1, inv_denom, inv_denom_sqr, line_a, line_b, numer, segm_len, sum_AB, sum_x, sum_y, teta, vec_x, vec_y; + if (recompute_c_x == null) { + recompute_c_x = true; + } + if (recompute_c_y == null) { + recompute_c_y = true; + } + if (recompute_lower_x == null) { + recompute_lower_x = true; + } + cA = []; + cB = []; + line_a = BigIntegerFromInt(static_cast_int(site3.y1()) - static_cast_int(site3.y0())); + line_b = BigIntegerFromInt(static_cast_int(site3.x0()) - static_cast_int(site3.x1())); + segm_len = (line_a.multiply(line_a)).add(line_b.multiply(line_b)); + vec_x = BigIntegerFromInt(static_cast_int(site2.y()) - static_cast_int(site1.y())); + vec_y = BigIntegerFromInt(static_cast_int(site1.x()) - static_cast_int(site2.x())); + sum_x = BigIntegerFromInt(static_cast_int(site1.x()) + static_cast_int(site2.x())); + sum_y = BigIntegerFromInt(static_cast_int(site1.y()) + static_cast_int(site2.y())); + teta = (line_a.multiply(vec_x)).add(line_b.multiply(vec_y)); + denom = (vec_x.multiply(line_b)).subtract(vec_y.multiply(line_a)); + dif0 = BigIntegerFromInt(static_cast_int(site3.y1()) - static_cast_int(site1.y())); + dif1 = BigIntegerFromInt(static_cast_int(site1.x()) - static_cast_int(site3.x1())); + A = (line_a.multiply(dif1)).subtract(line_b.multiply(dif0)); + dif0 = BigIntegerFromInt(static_cast_int(site3.y1()) - static_cast_int(site2.y())); + dif1 = BigIntegerFromInt(static_cast_int(site2.x()) - static_cast_int(site3.x1())); + B = (line_a.multiply(dif1)).subtract(line_b.multiply(dif0)); + sum_AB = A.add(B); + if (is_zero_big(denom)) { + numer = (teta.multiply(teta)).subtract(sum_AB.multiply(sum_AB)); + denom = teta.multiply(sum_AB); + cA[0] = (denom.multiply(sum_x).multiply(BigIntegerFromInt(2))).add(numer.multiply(vec_x)); + cB[0] = segm_len; + cA[1] = (denom.multiply(sum_AB).multiply(BigIntegerFromInt(2))).add(numer.multiply(teta)); + cB[1] = BigIntegerFromInt(1); + cA[2] = (denom.multiply(sum_y).multiply(BigIntegerFromInt(2))).add(numer.multiply(vec_y)); + inv_denom = 1.0 / denom.toDouble(); + if (recompute_c_x) { + c_event.x(0.25 * cA[0].toDouble() * inv_denom); + } + if (recompute_c_y) { + c_event.y(0.25 * cA[2].toDouble() * inv_denom); + } + if (recompute_lower_x) { + c_event.lower_x(0.25 * sqrt_expr_big_.eval2(cA, cB) * inv_denom / get_sqrt(segm_len.toDouble())); + } + return; + } + det = ((teta.multiply(teta)).add(denom.multiply(denom))).multiply(A).multiply(B).multiply(BigIntegerFromInt(4)); + inv_denom_sqr = 1.0 / denom.toDouble(); + inv_denom_sqr *= inv_denom_sqr; + if (recompute_c_x || recompute_lower_x) { + cA[0] = (sum_x.multiply(denom).multiply(denom)).add(teta.multiply(sum_AB).multiply(vec_x)); + cB[0] = BigIntegerFromInt(1); + cA[1] = segment_index === 2 ? vec_x.negate() : vec_x; + cB[1] = det; + if (recompute_c_x) { + c_event.x(0.5 * sqrt_expr_big_.eval2(cA, cB) * inv_denom_sqr); + } + } + if (recompute_c_y || recompute_lower_x) { + cA[2] = (sum_y.multiply(denom).multiply(denom)).add(teta.multiply(sum_AB).multiply(vec_y)); + cB[2] = BigIntegerFromInt(1); + cA[3] = segment_index === 2 ? vec_y.negate() : vec_y; + cB[3] = det; + if (recompute_c_y) { + c_event.y(0.5 * sqrt_expr_big_.eval2(cA[2], cB[2], cA[3], cB[3]) * inv_denom_sqr); + } + } + if (recompute_lower_x) { + cB[0] = cB[0].multiply(segm_len); + cB[1] = cB[1].multiply(segm_len); + cA[2] = sum_AB.multiply((denom.multiply(denom)).add(teta.multiply(teta))); + cB[2] = BigIntegerFromInt(1); + cA[3] = segment_index === 2 ? teta.negate() : teta; + cB[3] = det; + return c_event.lower_x(0.5 * sqrt_expr_big_.eval4(cA, cB) * inv_denom_sqr / get_sqrt(segm_len.toDouble())); + } + }, + + /*// Recompute parameters of the circle event using high-precision library. */ + pss: function(site1, site2, site3, point_index, c_event, recompute_c_x, recompute_c_y, recompute_lower_x) { + var a, b, c, cA, cB, c_x, c_y, cx, cy, denom, dx, dy, ix, iy, lower_x, orientation, segm_end1, segm_end2, segm_start1, segm_start2, sign, temp; + if (recompute_c_x == null) { + recompute_c_x = true; + } + if (recompute_c_y == null) { + recompute_c_y = true; + } + if (recompute_lower_x == null) { + recompute_lower_x = true; + } + a = []; + b = []; + c = []; + cA = []; + cB = []; + segm_start1 = site2.point1(); + segm_end1 = site2.point0(); + segm_start2 = site3.point0(); + segm_end2 = site3.point1(); + a[0] = BigIntegerFromInt(static_cast_int(segm_end1.x()) - static_cast_int(segm_start1.x())); + b[0] = BigIntegerFromInt(static_cast_int(segm_end1.y()) - static_cast_int(segm_start1.y())); + a[1] = BigIntegerFromInt(static_cast_int(segm_end2.x()) - static_cast_int(segm_start2.x())); + b[1] = BigIntegerFromInt(static_cast_int(segm_end2.y()) - static_cast_int(segm_start2.y())); + orientation = (a[1].multiply(b[0])).subtract(a[0].multiply(b[1])); + if (is_zero_big(orientation)) { + denom = 2.0 * ((a[0].multiply(a[0])).add(b[0].multiply(b[0]))).toDouble(); + c[0] = (b[0].multiply(BigIntegerFromInt(static_cast_int(segm_start2.x()) - static_cast_int(segm_start1.x())))).subtract(a[0].multiply(BigIntegerFromInt(static_cast_int(segm_start2.y()) - static_cast_int(segm_start1.y())))); + dx = (a[0].multiply(BigIntegerFromInt(static_cast_int(site1.y()) - static_cast_int(segm_start1.y())))).subtract(b[0].multiply(BigIntegerFromInt(static_cast_int(site1.x()) - static_cast_int(segm_start1.x())))); + dy = (b[0].multiply(BigIntegerFromInt(static_cast_int(site1.x()) - static_cast_int(segm_start2.x())))).subtract(a[0].multiply(BigIntegerFromInt(static_cast_int(site1.y()) - static_cast_int(segm_start2.y())))); + cB[0] = dx.multiply(dy); + cB[1] = BigIntegerFromInt(1); + if (recompute_c_y) { + cA[0] = b[0].multiply(point_index === 2 ? BigIntegerFromInt(2) : BigIntegerFromInt(-2)); + cA[1] = (a[0].multiply(a[0]).multiply(BigIntegerFromInt(static_cast_int(segm_start1.y()) + static_cast_int(segm_start2.y())))).subtract(a[0].multiply(b[0]).multiply(BigIntegerFromInt(static_cast_int(segm_start1.x()) + static_cast_int(segm_start2.x()) - static_cast_int(site1.x()) * 2))).add(b[0].multiply(b[0]).multiply(BigIntegerFromInt(static_cast_int(site1.y()) * 2))); + c_y = sqrt_expr_big_.eval2(cA, cB); + c_event.y(c_y / denom); + } + if (recompute_c_x || recompute_lower_x) { + cA[0] = a[0].multiply(point_index === 2 ? BigIntegerFromInt(2) : BigIntegerFromInt(-2)); + cA[1] = (b[0].multiply(b[0]).multiply(BigIntegerFromInt(static_cast_int(segm_start1.x()) + static_cast_int(segm_start2.x())))).subtract(a[0].multiply(b[0]).multiply(BigIntegerFromInt(static_cast_int(segm_start1.y()) + static_cast_int(segm_start2.y()) - static_cast_int(site1.y()) * 2))).add(a[0].multiply(a[0]).multiply(BigIntegerFromInt(static_cast_int(site1.x()) * 2))); + if (recompute_c_x) { + c_x = sqrt_expr_big_.eval2(cA, cB); + c_event.x(c_x / denom); + } + if (recompute_lower_x) { + cA[2] = is_neg_big(c[0]) ? c[0].negate() : c[0]; + cB[2] = (a[0].multiply(a[0])).add(b[0].multiply(b[0])); + lower_x = sqrt_expr_big_.eval3(cA, cB); + c_event.lower_x(lower_x / denom); + } + } + return; + } + c[0] = (b[0].multiply(BigIntegerFromInt(segm_end1.x()))).subtract(a[0].multiply(BigIntegerFromInt(segm_end1.y()))); + c[1] = (a[1].multiply(BigIntegerFromInt(segm_end2.y()))).subtract(b[1].multiply(BigIntegerFromInt(segm_end2.x()))); + ix = (a[0].multiply(c[1])).add(a[1].multiply(c[0])); + iy = (b[0].multiply(c[1])).add(b[1].multiply(c[0])); + dx = ix.subtract(orientation.multiply(BigIntegerFromInt(site1.x()))); + dy = iy.subtract(orientation.multiply(BigIntegerFromInt(site1.y()))); + if (is_zero_big(dx) && is_zero_big(dy)) { + denom = orientation.toDouble(); + c_x = ix.toDouble() / denom; + c_y = iy.toDouble() / denom; + c_event.x(c_x); + c_event.y(c_y); + c_event.lower_x(c_x); + return; + } + sign = BigIntegerFromInt((point_index === 2 ? 1 : -1) * (is_neg_big(orientation) ? 1 : -1)); + cA[0] = (a[1].multiply(dx.negate())).add(b[1].multiply(dy.negate())); + cA[1] = (a[0].multiply(dx.negate())).add(b[0].multiply(dy.negate())); + cA[2] = sign; + cA[3] = BigIntegerFromInt(0); + cB[0] = (a[0].multiply(a[0])).add(b[0].multiply(b[0])); + cB[1] = (a[1].multiply(a[1])).add(b[1].multiply(b[1])); + cB[2] = (a[0].multiply(a[1])).add(b[0].multiply(b[1])); + cB[3] = ((a[0].multiply(dy)).subtract(b[0].multiply(dx))).multiply((a[1].multiply(dy)).subtract(b[1].multiply(dx))).multiply(BigIntegerFromInt(-2)); + temp = sqrt_expr_evaluator_pss4_big(cA, cB); + denom = temp * orientation.toDouble(); + if (recompute_c_y) { + cA[0] = (b[1].multiply((dx.multiply(dx)).add(dy.multiply(dy)))).subtract(iy.multiply((dx.multiply(a[1])).add(dy.multiply(b[1])))); + cA[1] = (b[0].multiply((dx.multiply(dx)).add(dy.multiply(dy)))).subtract(iy.multiply((dx.multiply(a[0])).add(dy.multiply(b[0])))); + cA[2] = iy.multiply(sign); + cy = sqrt_expr_evaluator_pss4_big(cA, cB); + c_event.y(cy / denom); + } + if (recompute_c_x || recompute_lower_x) { + cA[0] = (a[1].multiply((dx.multiply(dx)).add(dy.multiply(dy)))).subtract(ix.multiply((dx.multiply(a[1])).add(dy.multiply(b[1])))); + cA[1] = (a[0].multiply((dx.multiply(dx)).add(dy.multiply(dy)))).subtract(ix.multiply((dx.multiply(a[0])).add(dy.multiply(b[0])))); + cA[2] = ix.multiply(sign); + if (recompute_c_x) { + cx = sqrt_expr_evaluator_pss4_big(cA, cB); + c_event.x(cx / denom); + } + if (recompute_lower_x) { + cA[3] = orientation.multiply((dx.multiply(dx)).add(dy.multiply(dy))).multiply(is_neg(temp) ? BigIntegerFromInt(-1) : BigIntegerFromInt(1)); + lower_x = sqrt_expr_evaluator_pss4_big(cA, cB); + c_event.lower_x(lower_x / denom); + } + } + }, + + /*// Recompute parameters of the circle event using high-precision library. */ + sss: function(site1, site2, site3, c_event, recompute_c_x, recompute_c_y, recompute_lower_x) { + var a, b, c, cA, cB, c_x, c_y, denom, i, j, k, l, lower_x, m, n, o; + if (recompute_c_x == null) { + recompute_c_x = true; + } + if (recompute_c_y == null) { + recompute_c_y = true; + } + if (recompute_lower_x == null) { + recompute_lower_x = true; + } + a = []; + b = []; + c = []; + cA = []; + cB = []; + + /* + // cA - corresponds to the cross product. + // cB - corresponds to the squared length. + */ + a[0] = BigIntegerFromInt(static_cast_int(site1.x1()) - static_cast_int(site1.x0())); + a[1] = BigIntegerFromInt(static_cast_int(site2.x1()) - static_cast_int(site2.x0())); + a[2] = BigIntegerFromInt(static_cast_int(site3.x1()) - static_cast_int(site3.x0())); + b[0] = BigIntegerFromInt(static_cast_int(site1.y1()) - static_cast_int(site1.y0())); + b[1] = BigIntegerFromInt(static_cast_int(site2.y1()) - static_cast_int(site2.y0())); + b[2] = BigIntegerFromInt(static_cast_int(site3.y1()) - static_cast_int(site3.y0())); + c[0] = BigIntegerFromInt(static_cast_int(site1.x0()) * static_cast_int(site1.y1()) - static_cast_int(site1.y0()) * static_cast_int(site1.x1())); + c[1] = BigIntegerFromInt(static_cast_int(site2.x0()) * static_cast_int(site2.y1()) - static_cast_int(site2.y0()) * static_cast_int(site2.x1())); + c[2] = BigIntegerFromInt(static_cast_int(site3.x0()) * static_cast_int(site3.y1()) - static_cast_int(site3.y0()) * static_cast_int(site3.x1())); + for (i = l = 0; l < 3; i = ++l) { + cB[i] = (a[i].multiply(a[i])).add(b[i].multiply(b[i])); + } + for (i = m = 0; m < 3; i = ++m) { + j = (i + 1) % 3; + k = (i + 2) % 3; + cA[i] = (a[j].multiply(b[k])).subtract(a[k].multiply(b[j])); + } + denom = sqrt_expr_big_.eval3(cA, cB); + if (recompute_c_y) { + for (i = n = 0; n < 3; i = ++n) { + j = (i + 1) % 3; + k = (i + 2) % 3; + cA[i] = (b[j].multiply(c[k])).subtract(b[k].multiply(c[j])); + } + c_y = sqrt_expr_big_.eval3(cA, cB); + c_event.y(c_y / denom); + } + if (recompute_c_x || recompute_lower_x) { + cA[3] = BigIntegerFromInt(0); + for (i = o = 0; o < 3; i = ++o) { + j = (i + 1) % 3; + k = (i + 2) % 3; + cA[i] = (a[j].multiply(c[k])).subtract(a[k].multiply(c[j])); + if (recompute_lower_x) { + cA[3] = cA[3].add(cA[i].multiply(b[i])); + } + } + if (recompute_c_x) { + c_x = sqrt_expr_big_.eval3(cA, cB); + c_event.x(c_x / denom); + } + if (recompute_lower_x) { + cB[3] = BigIntegerFromInt(1); + lower_x = sqrt_expr_big_.eval4(cA, cB); + c_event.lower_x(lower_x / denom); + } + } + } + }; + + lazy_circle_formation_functor = { + ppp: function(site1, site2, site3, c_event) { + throw new Error("lazy_circle_formation_functor should never get called, disabled"); + }, + pps: function(site1, site2, site3, segment_index, c_event) { + throw new Error("lazy_circle_formation_functor should never get called, disabled"); + }, + pss: function(site1, site2, site3, point_index, c_event) { + throw new Error("lazy_circle_formation_functor should never get called, disabled"); + }, + sss: function(site1, site2, site3, c_event) { + throw new Error("lazy_circle_formation_functor should never get called, disabled"); + } + }; + + circle_formation_predicate = function(site1, site2, site3, circle) { + var lies_outside_vertical_segment; + lies_outside_vertical_segment = function(c, s) { + var y0, y1; + if (!s.is_segment() || !is_vertical(s)) { + return false; + } + y0 = s.is_inverse() ? s.y1() : s.y0(); + y1 = s.is_inverse() ? s.y0() : s.y1(); + return (y0 - c.y()) > EPSILON || (c.y() - y1) > EPSILON; + }; + if (!site1.is_segment()) { + if (!site2.is_segment()) { + if (!site3.is_segment()) { + if (!circle_existence_predicate.ppp(site1, site2, site3)) { + return false; + } + circle_formation_functor.ppp(site1, site2, site3, circle); + } else { + if (!circle_existence_predicate.pps(site1, site2, site3, 3)) { + return false; + } + circle_formation_functor.pps(site1, site2, site3, 3, circle); + } + } else { + if (!site3.is_segment()) { + if (!circle_existence_predicate.pps(site1, site3, site2, 2)) { + return false; + } + circle_formation_functor.pps(site1, site3, site2, 2, circle); + } else { + if (!circle_existence_predicate.pss(site1, site2, site3, 1)) { + return false; + } + circle_formation_functor.pss(site1, site2, site3, 1, circle); + } + } + } else { + if (!site2.is_segment()) { + if (!site3.is_segment()) { + if (!circle_existence_predicate.pps(site2, site3, site1, 1)) { + return false; + } + circle_formation_functor.pps(site2, site3, site1, 1, circle); + } else { + if (!circle_existence_predicate.pss(site2, site1, site3, 2)) { + return false; + } + circle_formation_functor.pss(site2, site1, site3, 2, circle); + } + } else { + if (!site3.is_segment()) { + if (!circle_existence_predicate.pss(site3, site1, site2, 3)) { + return false; + } + circle_formation_functor.pss(site3, site1, site2, 3, circle); + } else { + if (!circle_existence_predicate.sss(site1, site2, site3)) { + return false; + } + circle_formation_functor.sss(site1, site2, site3, circle); + } + } + } + if (lies_outside_vertical_segment(circle, site1) || lies_outside_vertical_segment(circle, site2) || lies_outside_vertical_segment(circle, site3)) { + return false; + } + return true; + }; + + circle_formation_predicate_big = function(site1, site2, site3, circle) { + var lies_outside_vertical_segment; + lies_outside_vertical_segment = function(c, s) { + var y0, y1; + if (!s.is_segment() || !is_vertical(s)) { + return false; + } + y0 = s.is_inverse() ? s.y1() : s.y0(); + y1 = s.is_inverse() ? s.y0() : s.y1(); + return (y0 - c.y()) > EPSILON || (c.y() - y1) > EPSILON; + }; + if (!site1.is_segment()) { + if (!site2.is_segment()) { + if (!site3.is_segment()) { + if (!circle_existence_predicate.ppp(site1, site2, site3)) { + return false; + } + circle_formation_functor_big.ppp(site1, site2, site3, circle); + } else { + if (!circle_existence_predicate.pps(site1, site2, site3, 3)) { + return false; + } + circle_formation_functor_big.pps(site1, site2, site3, 3, circle); + } + } else { + if (!site3.is_segment()) { + if (!circle_existence_predicate.pps(site1, site3, site2, 2)) { + return false; + } + circle_formation_functor_big.pps(site1, site3, site2, 2, circle); + } else { + if (!circle_existence_predicate.pss(site1, site2, site3, 1)) { + return false; + } + circle_formation_functor_big.pss(site1, site2, site3, 1, circle); + } + } + } else { + if (!site2.is_segment()) { + if (!site3.is_segment()) { + if (!circle_existence_predicate.pps(site2, site3, site1, 1)) { + return false; + } + circle_formation_functor_big.pps(site2, site3, site1, 1, circle); + } else { + if (!circle_existence_predicate.pss(site2, site1, site3, 2)) { + return false; + } + circle_formation_functor_big.pss(site2, site1, site3, 2, circle); + } + } else { + if (!site3.is_segment()) { + if (!circle_existence_predicate.pss(site3, site1, site2, 3)) { + return false; + } + circle_formation_functor_big.pss(site3, site1, site2, 3, circle); + } else { + if (!circle_existence_predicate.sss(site1, site2, site3)) { + return false; + } + circle_formation_functor_big.sss(site1, site2, site3, circle); + } + } + } + if (lies_outside_vertical_segment(circle, site1) || lies_outside_vertical_segment(circle, site2) || lies_outside_vertical_segment(circle, site3)) { + return false; + } + return true; + }; + + if (typeof module !== "undefined" && module !== null) { + module.exports = { + node_comparison_predicate: node_comparison_predicate, + distance_predicate: distance_predicate, + event_comparison_predicate: event_comparison_predicate, + point_comparison_predicate: point_comparison_predicate, + circle_formation_predicate: circle_formation_predicate, + circle_formation_predicate_big: circle_formation_predicate_big, + is_vertical: is_vertical + }; + } + + if ((DISABLE_REQUIRE == null) && (typeof require !== "undefined" && require !== null)) { + ref4 = require('./constants'), EPSILON = ref4.EPSILON, SOURCE_CATEGORY_BITMASK = ref4.SOURCE_CATEGORY_BITMASK, IS_INVERSE = ref4.IS_INVERSE; + } + + + /* // Cartesian 2D point data structure. */ + + point_2d = (function() { + point_2d.prototype.x_ = 0; + + point_2d.prototype.y_ = 0; + + function point_2d(param1, param2) { + if (param1 != null) { + if (typeof param1 === "object") { + if (param1 instanceof point_2d) { + this.x_ = param1.x_; + this.y_ = param1.y_; + } + } else if (typeof param1 === "number") { + this.x_ = param1; + this.y_ = param2; + } + } else { + this.x_ = 0; + this.y_ = 0; + } + } + + point_2d.prototype.assign = function(pt) { + this.x_ = pt.x_; + this.y_ = pt.y_; + }; + + point_2d.prototype.equals = function(pt) { + return Math.abs(this.x_ - pt.x_) < EPSILON && Math.abs(this.y_ - pt.y_) < EPSILON; + }; + + point_2d.prototype.print = function() { + return "(" + this.x_ + ", " + this.y_ + ")"; + }; + + point_2d.prototype.to_json = function() { + return { + x: this.x_, + y: this.y_ + }; + }; + + point_2d.prototype.x = function() { + return this.x_; + }; + + point_2d.prototype.y = function() { + return this.y_; + }; + + return point_2d; + + })(); + + + /* + // Site event type. + // Occurs when the sweepline sweeps over one of the initial sites: + // 1) point site + // 2) start-point of the segment site + // 3) endpoint of the segment site + // Implicit segment direction is defined: the start-point of + // the segment compares less than its endpoint. + // Each input segment is divided onto two site events: + // 1) One going from the start-point to the endpoint + // (is_inverse() = false) + // 2) Another going from the endpoint to the start-point + // (is_inverse() = true) + // In beach line data structure segment sites of the first + // type precede sites of the second type for the same segment. + // Members: + // point0_ - point site or segment's start-point + // point1_ - segment's endpoint if site is a segment + // sorted_index_ - the last bit encodes information if the site is inverse; + // the other bits encode site event index among the sorted site events + // initial_index_ - site index among the initial input set + // Note: for all sites is_inverse_ flag is equal to false by default. + */ + + site_event = (function() { + site_event.prototype.point0_ = null; + + site_event.prototype.point1_ = null; + + site_event.prototype.sorted_index_ = 0; + + site_event.prototype.initial_index_ = 0; + + site_event.prototype.flags_ = 0; + + function site_event(param1, param2, param3, param4) { + if (param1 != null) { + if (typeof param1 === "object") { + if (param1 instanceof point_2d) { + if (param2 != null) { + this.point0_ = new point_2d(param1); + this.point1_ = new point_2d(param2); + } else { + this.point0_ = new point_2d(param1); + this.point1_ = new point_2d(param1); + } + } else if (param1 instanceof site_event) { + this.point0_ = new point_2d(param1.point0()); + this.point1_ = new point_2d(param1.point1()); + this.sorted_index_ = param1.sorted_index_; + this.flags_ = param1.flags_; + this.initial_index_ = param1.initial_index_; + return; + } + } else if (typeof param1 === "number") { + if (param3 != null) { + this.point0_ = new point_2d(param1, param2); + this.point1_ = new point_2d(param3, param4); + } else { + this.point0_ = new point_2d(param1, param2); + this.point1_ = new point_2d(param1, param2); + } + } + } else { + this.point0_ = new point_2d(0, 0); + this.point1_ = new point_2d(0, 0); + } + this.sorted_index_ = 0; + this.flags_ = 0; + } + + site_event.prototype.eventType = function() { + return 'site_event'; + }; + + site_event.prototype.equals = function(se) { + return this.point0_.equals(se.point0_) && this.point1_.equals(se.point1_); + }; + + site_event.prototype.x = function() { + return this.point0_.x(); + }; + + site_event.prototype.y = function() { + return this.point0_.y(); + }; + + site_event.prototype.x0 = function() { + return this.point0_.x(); + }; + + site_event.prototype.y0 = function() { + return this.point0_.y(); + }; + + site_event.prototype.x1 = function() { + return this.point1_.x(); + }; + + site_event.prototype.y1 = function() { + return this.point1_.y(); + }; + + site_event.prototype.point0 = function() { + return this.point0_; + }; + + site_event.prototype.point1 = function() { + return this.point1_; + }; + + site_event.prototype.sorted_index = function(param) { + if (param !== void 0) { + this.sorted_index_ = param; + return this; + } + return this.sorted_index_; + }; + + site_event.prototype.initial_index = function(param) { + if (param !== void 0) { + this.initial_index_ = param; + return this; + } + return this.initial_index_; + }; + + site_event.prototype.is_inverse = function() { + return (this.flags_ & IS_INVERSE) > 0; + }; + + site_event.prototype.inverse = function() { + var temp; + temp = this.point1_; + this.point1_ = new point_2d(this.point0_); + this.point0_ = new point_2d(temp); + this.flags_ ^= IS_INVERSE; + return this; + }; + + site_event.prototype.source_category = function(param) { + if (param !== void 0) { + this.flags_ |= param; + return this; + } + return this.flags_ & SOURCE_CATEGORY_BITMASK; + }; + + site_event.prototype.is_point = function() { + return this.point0_.equals(this.point1_); + }; + + site_event.prototype.is_segment = function() { + return !this.is_point(); + }; + + site_event.prototype.print = function() { + return "site_event { p0: " + this.point0_.print() + ", p1: " + this.point1_.print() + ", params: s-" + this.sorted_index_ + " i-" + this.initial_index_ + " f-" + this.flags_ + " }"; + }; + + site_event.prototype.to_json = function() { + return { + p0: { + x: this.point0_.x(), + y: this.point0_.y() + }, + p1: { + x: this.point1_.x(), + y: this.point1_.y() + }, + params: "s-" + this.sorted_index_ + " i-" + this.initial_index_ + " f-" + this.flags_ + }; + }; + + return site_event; + + })(); + + + /* + * // Circle event type. + * // Occurs when the sweepline sweeps over the rightmost point of the Voronoi + * // circle (with the center at the intersection point of the bisectors). + * // Circle event is made of the two consecutive nodes in the beach line data + * // structure. In case another node was inserted during algorithm execution + * // between the given two nodes circle event becomes inactive. + * // Variables: + * // center_x_ - center x-coordinate; + * // center_y_ - center y-coordinate; + * // lower_x_ - leftmost x-coordinate; + * // is_active_ - states whether circle event is still active. + * // NOTE: lower_y coordinate is always equal to center_y. + */ + + circle_event = (function() { + circle_event.prototype.center_x_ = 0; + + circle_event.prototype.center_y_ = 0; + + circle_event.prototype.lower_x_ = 0; + + circle_event.prototype.is_active_ = true; + + function circle_event(c_x, c_y, lower_x) { + if (c_x != null) { + this.center_x_ = c_x; + this.center_y_ = c_y; + this.lower_x_ = lower_x; + this.is_active = true; + } + } + + circle_event.prototype.eventType = function() { + return 'circle_event'; + }; + + circle_event.prototype.x = function(param) { + if (param !== void 0) { + this.center_x_ = param; + return this; + } + return this.center_x_; + }; + + circle_event.prototype.y = function(param) { + if (param !== void 0) { + this.center_y_ = param; + return this; + } + return this.center_y_; + }; + + circle_event.prototype.lower_x = function(param) { + if (param !== void 0) { + this.lower_x_ = param; + return this; + } + return this.lower_x_; + }; + + circle_event.prototype.lower_y = function() { + return this.center_y_; + }; + + circle_event.prototype.is_active = function() { + return this.is_active_; + }; + + circle_event.prototype.deactivate = function() { + this.is_active_ = false; + return this; + }; + + circle_event.prototype.print = function() { + return "circle_event { c_x: " + this.center_x_ + " c_y: " + this.center_y_ + " lower_x: " + this.lower_x_ + " is_active: " + (this.is_active_ ? 1 : 0 + " }"); + }; + + circle_event.prototype.to_json = function() { + return { + c_x: parseFloat(this.center_x_.toFixed(6)), + c_y: parseFloat(this.center_y_.toFixed(6)), + lower_x: parseFloat(this.lower_x_.toFixed(6)), + is_active: this.is_active_ ? 1 : 0 + }; + }; + + return circle_event; + + })(); + + + /* // Represents a bisector node made by two arcs that correspond to the left + * // and right sites. Arc is defined as a curve with points equidistant from + * // the site and from the sweepline. If the site is a point then arc is + * // a parabola, otherwise it's a line segment. A segment site event will + * // produce different bisectors based on its direction. + * // In general case two sites will create two opposite bisectors. That's + * // why the order of the sites is important to define the unique bisector. + * // The one site is considered to be newer than the other one if it was + * // processed by the algorithm later (has greater index). + */ + + beach_line_node_key = (function() { + beach_line_node_key.prototype.left_site_ = null; + + beach_line_node_key.prototype.right_site_ = null; + + function beach_line_node_key(param1, param2) { + if (param2 != null) { + this.left_site_ = new site_event(param1); + this.right_site_ = new site_event(param2); + } else { + this.left_site_ = new site_event(param1); + this.right_site_ = new site_event(param1); + } + } + + beach_line_node_key.prototype.left_site = function(param) { + if (param !== void 0) { + this.left_site_ = param; + return this; + } + return this.left_site_; + }; + + beach_line_node_key.prototype.right_site = function(param) { + if (param !== void 0) { + this.right_site_ = param; + return this; + } + return this.right_site_; + }; + + beach_line_node_key.prototype.print = function() { + return "beach_node_key { left_site: " + this.left_site_.print() + " right_site: " + this.right_site_.print() + " }"; + }; + + beach_line_node_key.prototype.to_json = function() { + return { + left_site: this.left_site_.to_json(), + right_site: this.right_site_.to_json() + }; + }; + + return beach_line_node_key; + + })(); + + + /* // Represents edge data structure from the Voronoi output, that is + * // associated as a value with beach line bisector in the beach + * // line. Contains pointer to the circle event in the circle event + * // queue if the edge corresponds to the right bisector of the circle event. + */ + + beach_line_node_data = (function() { + beach_line_node_data.prototype.circle_event_ = null; + + beach_line_node_data.prototype.edge_ = null; + + function beach_line_node_data(new_edge) { + this.circle_event_ = null; + this.edge_ = new_edge; + } + + beach_line_node_data.prototype.circle_event = function(param) { + if (param !== void 0) { + this.circle_event_ = param; + return this; + } + return this.circle_event_; + }; + + beach_line_node_data.prototype.edge = function(param) { + if (param !== void 0) { + this.edge_ = param; + return this; + } + return this.edge_; + }; + + beach_line_node_data.prototype.print = function() { + return "beach_line_node_data { circle_event: " + ((this.circle_event_ !== null) ? this.circle_event_.print() : "NULL ") + " edge: TODO}"; + }; + + beach_line_node_data.prototype.to_json = function() { + return { + circle_event: (this.circle_event_ !== null) ? this.circle_event_.to_json() : null, + edge: (this.edge_ !== null) ? this.edge_.to_json() : null + }; + }; + + return beach_line_node_data; + + })(); + + if (typeof module !== "undefined" && module !== null) { + module.exports = { + beach_line_node_data: beach_line_node_data, + beach_line_node_key: beach_line_node_key, + circle_event: circle_event, + site_event: site_event, + point_2d: point_2d + }; + } + + if ((DISABLE_REQUIRE == null) && (typeof require !== "undefined" && require !== null)) { + ref5 = require('./constants'), BIT_IS_LINEAR = ref5.BIT_IS_LINEAR, BIT_IS_PRIMARY = ref5.BIT_IS_PRIMARY, EPSILON = ref5.EPSILON, BITS_SHIFT = ref5.BITS_SHIFT; + } + + + /* + * // Represents Voronoi cell. + * // Data members: + * // 1) index of the source within the initial input set + * // 2) pointer to the incident edge + * // 3) mutable color member + * // Cell may contain point or segment site inside. + */ + + voronoi_cell = (function() { + voronoi_cell.prototype.source_index_ = 0; + + voronoi_cell.prototype.incident_edge_ = null; + + voronoi_cell.prototype.color_ = 0; + + function voronoi_cell(source_index, sc) { + this.source_index_ = source_index; + this.incident_edge_ = null; + this.color_ = sc; + } + + + /*// Returns true if the cell contains point site, false else. */ + + voronoi_cell.prototype.contains_point = function() { + var sc; + sc = this.source_category(); + return belongs(sc, GEOMETRY_CATEGORY_POINT); + }; + + + /*// Returns true if the cell contains segment site, false else. */ + + voronoi_cell.prototype.contains_segment = function() { + var sc; + sc = this.source_category(); + return belongs(sc, GEOMETRY_CATEGORY_SEGMENT); + }; + + voronoi_cell.prototype.source_index = function() { + return this.source_index_; + }; + + voronoi_cell.prototype.source_category = function() { + return this.color_ & SOURCE_CATEGORY_BITMASK; + }; + + + /* // Degenerate cells don't have any incident edges. */ + + voronoi_cell.prototype.is_generate = function() { + return this.incident_edge_ === null; + }; + + voronoi_cell.prototype.incident_edge = function(param) { + if (param !== void 0) { + this.incident_edge_ = param; + return; + } + return this.incident_edge_; + }; + + voronoi_cell.prototype.color = function(param) { + if (param !== void 0) { + this.color_ &= BITS_MASK; + this.color_ |= param << BITS_SHIFT; + return; + } + return this.color_ >> BITS_SHIFT; + }; + + return voronoi_cell; + + })(); + + + /* + // Represents Voronoi vertex. + // Data members: + // 1) vertex coordinates + // 2) pointer to the incident edge + // 3) mutable color member + */ + + voronoi_vertex = (function() { + voronoi_vertex.prototype.x_ = 0; + + voronoi_vertex.prototype.y_ = 0; + + function voronoi_vertex(x, y) { + this.x_ = x; + this.y_ = y; + this.incident_edge_ = null; + this.color_ = 0; + } + + voronoi_vertex.prototype.x = function() { + return this.x_; + }; + + voronoi_vertex.prototype.y = function() { + return this.y_; + }; + + voronoi_vertex.prototype.is_degenerate = function() { + return this.incident_edge_ === null; + }; + + voronoi_vertex.prototype.incident_edge = function(param) { + if (param !== void 0) { + this.incident_edge_ = param; + return; + } + return this.incident_edge_; + }; + + voronoi_vertex.prototype.color = function(param) { + if (param !== void 0) { + this.color_ &= BITS_MASK; + this.color_ |= param << BITS_SHIFT; + return; + } + return this.color_ >> BITS_SHIFT; + }; + + voronoi_vertex.prototype.to_json = function() { + return { + x: parseFloat(this.x_.toFixed(6)), + y: parseFloat(this.y_.toFixed(6)) + }; + }; + + return voronoi_vertex; + + })(); + + + /* + // Half-edge data structure. Represents Voronoi edge. + // Data members: + // 1) pointer to the corresponding cell + // 2) pointer to the vertex that is the starting + // point of the half-edge + // 3) pointer to the twin edge + // 4) pointer to the CCW next edge + // 5) pointer to the CCW prev edge + // 6) mutable color member + */ + + voronoi_edge = (function() { + voronoi_edge.prototype.cell_ = null; + + voronoi_edge.prototype.vertex_ = null; + + voronoi_edge.prototype.twin_ = null; + + voronoi_edge.prototype.next_ = null; + + voronoi_edge.prototype.prev_ = null; + + voronoi_edge.prototype.color_ = 0; + + function voronoi_edge(is_linear, is_primary) { + if (is_linear) { + this.color_ |= BIT_IS_LINEAR; + } + if (is_primary) { + this.color_ |= BIT_IS_PRIMARY; + } + } + + voronoi_edge.prototype.cell = function(param) { + if (param !== void 0) { + this.cell_ = param; + return; + } + return this.cell_; + }; + + voronoi_edge.prototype.vertex0 = function(param) { + if (param !== void 0) { + this.vertex_ = param; + return; + } + return this.vertex_; + }; + + voronoi_edge.prototype.vertex1 = function() { + return this.twin_.vertex0(); + }; + + voronoi_edge.prototype.twin = function(param) { + if (param !== void 0) { + this.twin_ = param; + return; + } + return this.twin_; + }; + + voronoi_edge.prototype.next = function(param) { + if (param !== void 0) { + this.next_ = param; + return; + } + return this.next_; + }; + + voronoi_edge.prototype.prev = function(param) { + if (param !== void 0) { + this.prev_ = param; + return; + } + return this.prev_; + }; + + + /* // Returns a pointer to the rotation next edge + * // over the starting point of the half-edge. + */ + + voronoi_edge.prototype.rot_next = function() { + return this.prev_.twin(); + }; + + + /* // Returns a pointer to the rotation prev edge + * // over the starting point of the half-edge. + */ + + voronoi_edge.prototype.rot_prev = function() { + return this.twin_.next(); + }; + + + /* // Returns true if the edge is finite (segment, parabolic arc). + * // Returns false if the edge is infinite (ray, line). + */ + + voronoi_edge.prototype.is_finite = function() { + return (this.vertex0() != null) && (this.vertex1() != null); + }; + + + /* // Returns true if the edge is infinite (ray, line). + * // Returns false if the edge is finite (segment, parabolic arc). + */ + + voronoi_edge.prototype.is_infinite = function() { + return (this.vertex0() == null) || (this.vertex1() == null); + }; + + + /* // Returns true if the edge is linear (segment, ray, line). + * // Returns false if the edge is curved (parabolic arc). + */ + + voronoi_edge.prototype.is_linear = function() { + return (this.color_ & BIT_IS_LINEAR) > 0; + }; + + + /* // Returns true if the edge is curved (parabolic arc). + * // Returns false if the edge is linear (segment, ray, line). + */ + + voronoi_edge.prototype.is_curved = function() { + return (this.color_ & BIT_IS_LINEAR) === 0; + }; + + + /* // Returns false if edge goes through the endpoint of the segment. + * // Returns true else. + */ + + voronoi_edge.prototype.is_primary = function() { + return (this.color_ & BIT_IS_PRIMARY) > 0; + }; + + + /* // Returns true if edge goes through the endpoint of the segment. + * // Returns false else. + */ + + voronoi_edge.prototype.is_secondary = function() { + return (this.color_ & BIT_IS_PRIMARY) === 0; + }; + + voronoi_edge.prototype.color = function(param) { + if (param !== void 0) { + this.color_ &= BITS_MASK; + this.color_ |= param << BITS_SHIFT; + return; + } + return this.color_ >> BITS_SHIFT; + }; + + voronoi_edge.prototype.prev_primary = function() { + var edge; + edge = this.prev_; + while (edge != null ? edge.is_secondary() : void 0) { + edge = edge.twin().prev(); + } + if (edge !== this.twin_) { + return edge; + } + }; + + voronoi_edge.prototype.next_primary = function() { + var edge; + edge = this.next_; + while (edge != null ? edge.is_secondary() : void 0) { + edge = edge.twin().next(); + } + if (edge !== this.twin_) { + return edge; + } + }; + + voronoi_edge.prototype.to_json = function() { + return { + is_finite: this.is_finite(), + is_linear: this.is_linear(), + is_primary: this.is_primary(), + color: this.color(), + vertex0: this.vertex0() === null ? null : { + x: parseFloat(this.vertex0().x().toFixed(6)), + y: parseFloat(this.vertex0().y().toFixed(6)) + }, + vertex1: this.vertex1() === null ? null : { + x: parseFloat(this.vertex1().x().toFixed(6)), + y: parseFloat(this.vertex1().y().toFixed(6)) + } + }; + }; + + return voronoi_edge; + + })(); + + vertex_equality_predicate_ = function(v1, v2) { + return Math.abs(v1.x() - v2.x()) < EPSILON && Math.abs(v1.y() - v2.y()) < EPSILON; + }; + + + /* // Voronoi output data structure. + * // CCW ordering is used on the faces perimeter and around the vertices. + */ + + voronoi_diagram = (function() { + voronoi_diagram.prototype.cells_ = []; + + voronoi_diagram.prototype.vertices_ = []; + + voronoi_diagram.prototype.edges_ = []; + + function voronoi_diagram() { + this.cells_ = []; + this.vertices_ = []; + this.edges_ = []; + } + + voronoi_diagram.prototype.clear = function() { + this.cells_ = []; + this.vertices_ = []; + return this.edges_ = []; + }; + + voronoi_diagram.prototype.cells = function() { + return this.cells_; + }; + + voronoi_diagram.prototype.vertices = function() { + return this.vertices_; + }; + + voronoi_diagram.prototype.edges = function() { + return this.edges_; + }; + + voronoi_diagram.prototype.num_cells = function() { + return this.cells_.length; + }; + + voronoi_diagram.prototype.num_edges = function() { + return this.edges_.length; + }; + + voronoi_diagram.prototype.num_vertices = function() { + return this.vertices_.length; + }; + + voronoi_diagram.prototype._reserve = function(num_sites) {}; + + voronoi_diagram.prototype._process_single_site = function(site) { + return this.cells_.push(new voronoi_cell(site.initial_index(), site.source_category())); + }; + + voronoi_diagram.prototype._insert_new_edge = function(param1, param2, param3, param4, param5) { + var circle, data12, data23, edge1, edge12, edge2, edge23, is_linear, is_primary, new_edge1, new_edge2, new_vertex, site1, site2, site3, site_index1, site_index2; + if (param3 == null) { + site1 = param1; + site2 = param2; + + /* // Insert a new half-edge into the output data structure. + * // Takes as input left and right sites that form a new bisector. + * // Returns a pair of pointers to a new half-edges. + */ + + /* // Get sites' indexes. */ + site_index1 = site1.sorted_index(); + site_index2 = site2.sorted_index(); + is_linear = this.is_linear_edge(site1, site2); + is_primary = this.is_primary_edge(site1, site2); + + /*// Create a new half-edge that belongs to the first site. */ + this.edges_.push(new voronoi_edge(is_linear, is_primary)); + edge1 = this.edges_[this.edges_.length - 1]; + + /*// Create a new half-edge that belongs to the second site. */ + this.edges_.push(new voronoi_edge(is_linear, is_primary)); + edge2 = this.edges_[this.edges_.length - 1]; + + /* // Add the initial cell during the first edge insertion. */ + if (this.cells_.length === 0) { + this.cells_.push(new voronoi_cell(site1.initial_index(), site1.source_category())); + } + + /* // The second site represents a new site during site event + * // processing. Add a new cell to the cell records. + */ + this.cells_.push(new voronoi_cell(site2.initial_index(), site2.source_category())); + + /* // Set up pointers to cells. */ + edge1.cell(this.cells_[site_index1]); + edge2.cell(this.cells_[site_index2]); + + /* // Set up twin pointers. */ + edge1.twin(edge2); + edge2.twin(edge1); + + /* // Return a pointer to the new half-edge. */ + return { + first: edge1, + second: edge2 + }; + } else { + site1 = param1; + site3 = param2; + circle = param3; + data12 = param4; + data23 = param5; + + /* // Insert a new half-edge into the output data structure with the + * // start at the point where two previously added half-edges intersect. + * // Takes as input two sites that create a new bisector, circle event + * // that corresponds to the intersection point of the two old half-edges, + * // pointers to those half-edges. Half-edges' direction goes out of the + * // new Voronoi vertex point. Returns a pair of pointers to a new half-edges. + */ + edge12 = data12; + edge23 = data23; + + /* // Add a new Voronoi vertex. */ + this.vertices_.push(new voronoi_vertex(circle.x(), circle.y())); + new_vertex = this.vertices_[this.vertices_.length - 1]; + + /* // Update vertex pointers of the old edges. */ + edge12.vertex0(new_vertex); + edge23.vertex0(new_vertex); + is_linear = this.is_linear_edge(site1, site3); + is_primary = this.is_primary_edge(site1, site3); + + /* // Add a new half-edge. */ + this.edges_.push(new voronoi_edge(is_linear, is_primary)); + new_edge1 = this.edges_[this.edges_.length - 1]; + new_edge1.cell(this.cells_[site1.sorted_index()]); + + /* // Add a new half-edge. */ + this.edges_.push(new voronoi_edge(is_linear, is_primary)); + new_edge2 = this.edges_[this.edges_.length - 1]; + new_edge2.cell(this.cells_[site3.sorted_index()]); + + /* // Update twin pointers. */ + new_edge1.twin(new_edge2); + new_edge2.twin(new_edge1); + + /* // Update vertex pointer. */ + new_edge2.vertex0(new_vertex); + + /* // Update Voronoi prev/next pointers. */ + edge12.prev(new_edge1); + new_edge1.next(edge12); + edge12.twin().next(edge23); + edge23.prev(edge12.twin()); + edge23.twin().next(new_edge2); + new_edge2.prev(edge23.twin()); + + /* // Return a pointer to the new half-edge. */ + return { + first: new_edge1, + second: new_edge2 + }; + } + }; + + voronoi_diagram.prototype._build = function() { + var cell, e, e1, e2, edge, edge1, edge2, edge_it, it, l, last_edge, last_vertex, left_edge, len, len1, m, n, o, ref6, ref7, ref8, ref9, results, right_edge, v, v1, v2; + last_edge = 0; + for (it = l = 0, ref6 = this.edges_.length; l < ref6; it = l += 2) { + v1 = this.edges_[it].vertex0(); + v2 = this.edges_[it].vertex1(); + if ((v1 != null) && (v2 != null) && vertex_equality_predicate_(v1, v2)) { + this.remove_edge(this.edges_[it]); + } else { + if (it !== last_edge) { + this.edges_[last_edge] = this.edges_[it]; + this.edges_[last_edge + 1] = this.edges_[it + 1]; + e1 = this.edges_[last_edge]; + e2 = this.edges_[last_edge + 1]; + e1.twin(e2); + e2.twin(e1); + if (e1.prev() != null) { + e1.prev().next(e1); + e2.next().prev(e2); + } + if (e2.prev() != null) { + e1.next().prev(e1); + e2.prev().next(e2); + } + } + last_edge += 2; + } + } + this.edges_.splice(last_edge, this.edges_.length - last_edge); + + /*// Set up incident edge pointers for cells and vertices. */ + ref7 = this.edges_; + for (m = 0, len = ref7.length; m < len; m++) { + edge = ref7[m]; + edge.cell().incident_edge(edge); + if (edge.vertex0() != null) { + edge.vertex0().incident_edge(edge); + } + } + + /*// Remove degenerate vertices. */ + last_vertex = 0; + if (this.vertices_.length > 0) { + for (it = n = 0, ref8 = this.vertices_.length - 1; 0 <= ref8 ? n < ref8 : n > ref8; it = 0 <= ref8 ? ++n : --n) { + if (this.vertices_[it].incident_edge() != null) { + if (it !== last_vertex) { + this.vertices_[last_vertex] = this.vertices_[it]; + v = this.vertices_[last_vertex]; + e = v.incident_edge(); + while (true) { + e.vertex0(v); + e = e.rot_next(); + if (e === v.incident_edge()) { + break; + } + } + } + ++last_vertex; + } + } + } + this.vertices_.splice(last_vertex, this.vertices_.length - last_vertex); + + /*// Set up next/prev pointers for infinite edges. */ + if (this.vertices_.length === 0) { + if (this.edges_.length !== 0) { + + /*// Update prev/next pointers for the line edges. */ + edge_it = 0; + edge1 = this.edges_[edge_it]; + edge1.next(edge1); + edge1.prev(edge1); + ++edge_it; + edge1 = this.edges_[edge_it]; + ++edge_it; + while (edge_it <= this.edges_.length - 1) { + edge2 = this.edges_[edge_it]; + ++edge_it; + edge1.next(edge2); + edge1.prev(edge2); + edge2.next(edge1); + edge2.prev(edge1); + edge1 = this.edges_[edge_it]; + ++edge_it; + } + edge1.next(edge1); + return edge1.prev(edge1); + } else { + + /*// Update prev/next pointers for the ray edges. */ + ref9 = this.cells_; + results = []; + for (o = 0, len1 = ref9.length; o < len1; o++) { + cell = ref9[o]; + if (cell.is_degenerate()) { + continue; + } + + /*// Move to the previous edge while */ + + /*// it is possible in the CW direction. */ + left_edge = cell.incident_edge(); + while (left_edge.prev() != null) { + left_edge = left_edge.prev(); + + /*// Terminate if this is not a boundary cell. */ + if (left_edge === cell.incident_edge()) { + break; + } + } + if (left_edge.prev() != null) { + continue; + } + right_edge = cell.incident_edge(); + while (right_edge.next() != null) { + right_edge = right_edge.next(); + } + left_edge.prev(right_edge); + results.push(right_edge.next(left_edge)); + } + return results; + } + } + }; + + voronoi_diagram.prototype.is_primary_edge = function(site1, site2) { + var flag1, flag2; + flag1 = site1.is_segment(); + flag2 = site2.is_segment(); + if (flag1 && !flag2) { + return (!site1.point0().equals(site2.point0())) && (!site1.point1().equals(site2.point0())); + } + if (!flag1 && flag2) { + return (!site2.point0().equals(site1.point0())) && (!site2.point1().equals(site1.point0())); + } + return true; + }; + + voronoi_diagram.prototype.is_linear_edge = function(site1, site2) { + if (!this.is_primary_edge(site1, site2)) { + return true; + } + return !(site1.is_segment() ^ site2.is_segment()); + }; + + + /* // Remove degenerate edge. */ + + voronoi_diagram.prototype.remove_edge = function(edge) { + + /* // Update the endpoints of the incident edges to the second vertex. */ + var edge1, edge1_rot_next, edge1_rot_prev, edge2, edge2_rot_next, edge2_rot_prev, updated_edge, vertex; + vertex = edge.vertex0(); + updated_edge = edge.twin().rot_next(); + while (updated_edge !== edge.twin()) { + updated_edge.vertex0(vertex); + updated_edge = updated_edge.rot_next(); + } + edge1 = edge; + edge2 = edge.twin(); + edge1_rot_prev = edge1.rot_prev(); + edge1_rot_next = edge1.rot_next(); + edge2_rot_prev = edge2.rot_prev(); + edge2_rot_next = edge2.rot_next(); + + /* // Update prev/next pointers for the incident edges. */ + edge1_rot_next.twin().next(edge2_rot_prev); + edge2_rot_prev.prev(edge1_rot_next.twin()); + edge1_rot_prev.prev(edge2_rot_next.twin()); + return edge2_rot_next.twin().next(edge1_rot_prev); + }; + + return voronoi_diagram; + + })(); + + if (typeof module !== "undefined" && module !== null) { + module.exports = { + voronoi_diagram: voronoi_diagram + }; + } + + if ((DISABLE_REQUIRE == null) && (typeof require !== "undefined" && require !== null)) { + ref6 = require('./constants'), SOURCE_CATEGORY_SINGLE_POINT = ref6.SOURCE_CATEGORY_SINGLE_POINT, SOURCE_CATEGORY_SEGMENT_START_POINT = ref6.SOURCE_CATEGORY_SEGMENT_START_POINT, SOURCE_CATEGORY_SEGMENT_END_POINT = ref6.SOURCE_CATEGORY_SEGMENT_END_POINT, SOURCE_CATEGORY_REVERSE_SEGMENT = ref6.SOURCE_CATEGORY_REVERSE_SEGMENT, SOURCE_CATEGORY_INITIAL_SEGMENT = ref6.SOURCE_CATEGORY_INITIAL_SEGMENT; + ref7 = require('./collections'), ordered_queue = ref7.ordered_queue, ordered_map = ref7.ordered_map, list = ref7.list; + ref8 = require('./voronoi_predicates'), node_comparison_predicate = ref8.node_comparison_predicate, point_comparison_predicate = ref8.point_comparison_predicate, event_comparison_predicate = ref8.event_comparison_predicate, circle_formation_predicate_big = ref8.circle_formation_predicate_big, circle_formation_predicate = ref8.circle_formation_predicate, is_vertical = ref8.is_vertical; + ref9 = require('./voronoi_structures'), site_event = ref9.site_event, circle_event = ref9.circle_event, point_2d = ref9.point_2d, beach_line_node_key = ref9.beach_line_node_key, beach_line_node_data = ref9.beach_line_node_data; + } + + + /* + // GENERAL INFO: + // The sweepline algorithm implementation to compute Voronoi diagram of + // points and non-intersecting segments (excluding endpoints). + // Complexity - O(N*logN), memory usage - O(N), where N is the total number + // of input geometries. + // + // CONTRACT: + // 1) Input geometries should have integral (e.g. int32, int64) coordinate type. + // 2) Input geometries should not intersect except their endpoints. + // + // IMPLEMENTATION DETAILS: + // Each input point creates one input site. Each input segment creates three + // input sites: two for its endpoints and one for the segment itself (this is + // made to simplify output construction). All the site objects are constructed + // and sorted at the algorithm initialization step. Priority queue is used to + // dynamically hold circle events. At each step of the algorithm execution the + // leftmost event is retrieved by comparing the current site event and the + // topmost element from the circle event queue. STL map (red-black tree) + // container was chosen to hold state of the beach line. The keys of the map + // correspond to the neighboring sites that form a bisector and values map to + // the corresponding Voronoi edges in the output data structure. + */ + + voronoi_builder = (function() { + voronoi_builder.prototype.event_comparison = function(lhs, rhs) { + return event_comparison_predicate(rhs.first, lhs.first); + }; + + voronoi_builder.prototype.end_point_comparison = function(end1, end2) { + return point_comparison_predicate(end2.first, end1.first); + }; + + voronoi_builder.prototype.point_comparison_ = function(end1, end2) { + return point_comparison_predicate(end1, end2); + }; + + voronoi_builder.prototype.site_events_ = null; + + voronoi_builder.prototype.site_event_iterator_ = null; + + voronoi_builder.prototype.end_points_ = null; + + voronoi_builder.prototype.circle_events_ = null; + + voronoi_builder.prototype.beach_line_ = null; + + voronoi_builder.prototype.index_ = 0; + + voronoi_builder.prototype.number_type = { + INTEGERS: 0, + BIG_INTEGERS: 1 + }; + + function voronoi_builder() { + this.index_ = 0; + this.circle_events_ = new ordered_queue(this.event_comparison); + this.end_points_ = new ordered_queue(this.end_point_comparison); + this.beach_line_ = new ordered_map(node_comparison_predicate); + this.site_events_ = new list(); + } + + + /*// Each point creates a single site event. */ + + voronoi_builder.prototype.insert_point = function(x, y) { + var se; + se = new site_event(x, y); + this.site_events_.push(se); + se.initial_index(this.index_); + se.source_category(SOURCE_CATEGORY_SINGLE_POINT); + return this.index_++; + }; + + + /* + // Each segment creates three site events that correspond to: + // 1) the start point of the segment; + // 2) the end point of the segment; + // 3) the segment itself defined by its start point. + */ + + voronoi_builder.prototype.insert_segment = function(x1, y1, x2, y2) { + + /* // Set up start point site. */ + var p1, p2, se, se1, se2; + p1 = new point_2d(x1, y1); + se1 = new site_event(p1); + this.site_events_.push(se1); + se1.initial_index(this.index_); + se1.source_category(SOURCE_CATEGORY_SEGMENT_START_POINT); + p2 = new point_2d(x2, y2); + se2 = new site_event(p2); + this.site_events_.push(se2); + se2.initial_index(this.index_); + se2.source_category(SOURCE_CATEGORY_SEGMENT_END_POINT); + se = null; + if (this.point_comparison_(p1, p2)) { + se = new site_event(p1, p2); + this.site_events_.push(se); + se.source_category(SOURCE_CATEGORY_INITIAL_SEGMENT); + } else { + se = new site_event(p2, p1); + this.site_events_.push(se); + se.source_category(SOURCE_CATEGORY_REVERSE_SEGMENT); + } + se.initial_index(this.index_); + return this.index_++; + }; + + voronoi_builder.prototype.print_beachline = function() { + var ite, str; + str = "beach_line: \n"; + ite = this.beach_line_.begin(); + while (ite !== this.beach_line_.end()) { + str += " "; + str += ite.key().print(); + str += " - "; + if (ite.value() != null) { + str += ite.value().print(); + } + str += "\n"; + ite = ite.next(); + } + str += "\n"; + return str; + }; + + voronoi_builder.prototype.print_site_events = function() { + var ite, str; + str = "site_events:\n"; + ite = this.site_events_.begin(); + while (ite !== this.site_events_.end()) { + if (ite === this.site_event_iterator_) { + str += " >> "; + } else { + str += " "; + } + str += ite.value().print(); + if (ite === this.site_event_iterator_) { + str += " << "; + } + ite = ite.next(); + str += "\n"; + } + str += "\n"; + return str; + }; + + voronoi_builder.prototype.print_current_end_point_top = function() { + var str; + str = "end_points(" + this.end_points_.size() + "):" + "\n"; + if (this.end_points_.size() > 0) { + str += " "; + str += this.end_points_.top().first.print(); + str += " - "; + str += this.end_points_.top().second.key().print(); + str += "\n"; + } + return str; + }; + + voronoi_builder.prototype.print_circle_events = function() { + return "circle_events: \n" + this.circle_events_.print(); + }; + + voronoi_builder.prototype.site_events_to_json = function() { + var event, events, ite; + events = []; + ite = this.site_events_.begin(); + while (ite !== this.site_events_.end()) { + event = ite.value().to_json(); + event["current"] = ite === this.site_event_iterator_; + events.push(event); + ite = ite.next(); + } + return events; + }; + + voronoi_builder.prototype.beachline_to_json = function() { + var event, events, ite; + events = []; + if (this.beach_line_.size() === 0) { + return events; + } + ite = this.beach_line_.begin(); + while (ite !== this.beach_line_.end()) { + event = {}; + event.key = ite.key().to_json(); + event.value = ite.value() != null ? ite.value().to_json() : null; + ite = ite.next(); + events.push(event); + } + return events; + }; + + voronoi_builder.prototype.output_to_json = function(output) { + var e, edges, l, len, len1, m, ref10, ref11, v, vertices; + edges = []; + vertices = []; + ref10 = output.edges_; + for (l = 0, len = ref10.length; l < len; l++) { + e = ref10[l]; + edges.push(e.to_json()); + } + ref11 = output.vertices_; + for (m = 0, len1 = ref11.length; m < len1; m++) { + v = ref11[m]; + vertices.push(v.to_json()); + } + return { + edges: edges, + vertices: vertices, + cells: "TODO" + }; + }; + + voronoi_builder.prototype.generate_iteration_json_node = function(description, output) { + return { + iteration: description, + beachline: this.beachline_to_json(), + site_events: this.site_events_to_json(), + circle_events: this.circle_events_.to_json(), + end_points: this.end_points_.to_json(), + diagram: this.output_to_json(output) + }; + }; + + voronoi_builder.prototype.construct = function(output, number_usage, interim_json_node) { + var iteration; + if (interim_json_node == null) { + interim_json_node = null; + } + + /* // Init structures. */ + output._reserve(this.site_events_.size()); + this.init_sites_queue(); + if (interim_json_node != null) { + interim_json_node.push(this.generate_iteration_json_node("AFTER init_sites_queue", output)); + } + this.init_beach_line(output); + if (interim_json_node != null) { + interim_json_node.push(this.generate_iteration_json_node("AFTER init_beach_line", output)); + } + + /* // The algorithm stops when there are no events to process. */ + iteration = 0; + while ((!this.circle_events_.empty()) || (this.site_event_iterator_ !== this.site_events_.end())) { + iteration++; + if (this.circle_events_.empty()) { + this.process_site_event(output, number_usage); + if (interim_json_node != null) { + interim_json_node.push(this.generate_iteration_json_node("AFTER top process_site_event ITERATION: " + iteration, output)); + } + } else if (this.site_event_iterator_ === this.site_events_.end()) { + this.process_circle_event(output, number_usage); + if (interim_json_node != null) { + interim_json_node.push(this.generate_iteration_json_node("AFTER top process_circle_event ITERATION: " + iteration, output)); + } + } else { + if (event_comparison_predicate(this.site_event_iterator_.value(), this.circle_events_.top().first)) { + this.process_site_event(output, number_usage); + if (interim_json_node != null) { + interim_json_node.push(this.generate_iteration_json_node("AFTER bottom process_site_event ITERATION: " + iteration, output)); + } + } else { + this.process_circle_event(output, number_usage); + if (interim_json_node != null) { + interim_json_node.push(this.generate_iteration_json_node("AFTER bottom process_circle_event ITERATION: " + iteration, output)); + } + } + } + while ((!this.circle_events_.empty()) && (!this.circle_events_.top().first.is_active())) { + this.circle_events_.pop(); + if (interim_json_node != null) { + interim_json_node.push(this.generate_iteration_json_node("AFTER circle_events_.pop() ITERATION: " + iteration, output)); + } + } + } + this.beach_line_.clear(); + + /* // Finish construction. */ + return output._build(); + }; + + voronoi_builder.prototype.clear = function() { + this.index_ = 0; + return this.site_events_.clear(); + }; + + voronoi_builder.prototype.init_sites_queue = function() { + + /* // Sort site events. */ + var cur, l, ref10; + this.site_events_.jsSort(event_comparison_predicate); + + /* // Remove duplicates. */ + + /* TODO: http://stackoverflow.com/questions/9229645/remove-duplicates-from-javascript-array */ + this.site_events_ = this.site_events_.uniqueWithTieBreaker(function(a, b) { + return a.equals(b); + }, function(a, b) { + return a.initial_index_ - b.initial_index_; + }); + for (cur = l = 0, ref10 = this.site_events_.size(); 0 <= ref10 ? l < ref10 : l > ref10; cur = 0 <= ref10 ? ++l : --l) { + this.site_events_.at(cur).value().sorted_index(cur); + } + this.site_event_iterator_ = this.site_events_.begin(); + }; + + voronoi_builder.prototype.init_beach_line = function(output) { + var skip; + if (this.site_events_.empty()) { + return; + } + if (this.site_events_.size() === 1) { + + /* // Handle single site event case. */ + output._process_single_site(this.site_events_.begin().value()); + this.site_event_iterator_ = this.site_event_iterator_.next(); + } else { + skip = 0; + while (this.site_event_iterator_ !== this.site_events_.end() && is_vertical(this.site_event_iterator_.value().point0(), this.site_events_.begin().value().point0()) && is_vertical(this.site_event_iterator_.value())) { + this.site_event_iterator_ = this.site_event_iterator_.next(); + ++skip; + } + if (skip === 1) { + + /* // Init beach line with the first two sites. */ + this.init_beach_line_default(output); + } else { + + /* // Init beach line with collinear vertical sites. */ + this.init_beach_line_collinear_sites(output); + } + } + }; + + + /* // Init beach line with the two first sites. + * // The first site is always a point. + */ + + voronoi_builder.prototype.init_beach_line_default = function(output) { + + /* // Get the first and the second site event. */ + var it_first, it_second; + it_first = this.site_events_.begin(); + it_second = this.site_events_.begin(); + it_second = it_second.next(); + this.insert_new_arc(it_first.value(), it_first.value(), it_second.value(), this.beach_line_.end(), output); + + /* // The second site was already processed. Move the iterator. */ + this.site_event_iterator_ = this.site_event_iterator_.next(); + }; + + + /* // Init beach line with collinear sites. */ + + voronoi_builder.prototype.init_beach_line_collinear_sites = function(output) { + var edge, it_first, it_second, new_node; + it_first = this.site_events_.begin(); + it_second = this.site_events_.begin(); + it_second = it_second.next(); + while (it_second !== this.site_event_iterator_) { + + /* // Create a new beach line node. */ + new_node = new beach_line_node_key(it_first.value(), it_second.value()); + + /* // Update the output. */ + edge = output._insert_new_edge(it_first.value(), it_second.value()).first; + + /* // Insert a new bisector into the beach line. */ + this.beach_line_.insert(new_node, new beach_line_node_data(edge)); + + /* // Update iterators. */ + it_first = it_first.next(); + it_second = it_second.next(); + } + }; + + voronoi_builder.prototype.deactivate_circle_event = function(value) { + if (value.circle_event() != null) { + value.circle_event().deactivate(); + value.circle_event(null); + } + }; + + voronoi_builder.prototype.process_site_event = function(output, number_usage) { + + /* // Get next site event to process. */ + var b_it, last, left_it, new_key, new_node_it, right_it, site1, site3, site_arc, site_arc1, site_arc2, site_event_; + site_event_ = new site_event(this.site_event_iterator_.value()); + + /* // Move site iterator. */ + last = this.site_event_iterator_.next(); + + /* // If a new site is an end point of some segment, + // remove temporary nodes from the beach line data structure. + */ + if (!site_event_.is_segment()) { + while (!this.end_points_.empty() && this.end_points_.top().first.equals(site_event_.point0())) { + b_it = this.end_points_.top().second; + this.end_points_.pop(); + this.beach_line_.erase(b_it); + } + } else { + while (last !== this.site_events_.end() && last.value().is_segment() && last.value().point0().equals(site_event_.point0())) { + last = last.next(); + } + } + + /* // Find the node in the binary search tree with left arc + * // lying above the new site point. + */ + new_key = new beach_line_node_key(this.site_event_iterator_.value()); + right_it = this.beach_line_.lowerBound(new_key); + while (this.site_event_iterator_ !== last) { + site_event_ = new site_event(this.site_event_iterator_.value()); + left_it = right_it; + + /* // Do further processing depending on the above node position. + * // For any two neighboring nodes the second site of the first node + * // is the same as the first site of the second node. + */ + if (right_it === this.beach_line_.end()) { + + /* // The above arc corresponds to the second arc of the last node. + * // Move the iterator to the last node. + */ + left_it = left_it.prev(); + + /* // Get the second site of the last node */ + site_arc = left_it.key().right_site(); + + /* // Insert new nodes into the beach line. Update the output. */ + right_it = this.insert_new_arc(site_arc, site_arc, site_event_, right_it, output); + + /* // Add a candidate circle to the circle event queue. + * // There could be only one new circle event formed by + * // a new bisector and the one on the left. + */ + this.activate_circle_event(left_it.key().left_site(), left_it.key().right_site(), site_event_, right_it, number_usage); + } else if (right_it === this.beach_line_.begin()) { + + /* // The above arc corresponds to the first site of the first node. */ + site_arc = right_it.key().left_site(); + + /* // Insert new nodes into the beach line. Update the output. */ + left_it = this.insert_new_arc(site_arc, site_arc, site_event_, right_it, output); + + /* // If the site event is a segment, update its direction. */ + if (site_event_.is_segment()) { + site_event_.inverse(); + } + + /* // Add a candidate circle to the circle event queue. + * // There could be only one new circle event formed by + * // a new bisector and the one on the right. + */ + this.activate_circle_event(site_event_, right_it.key().left_site(), right_it.key().right_site(), right_it, number_usage); + right_it = left_it; + } else { + + /* // The above arc corresponds neither to the first, + * // nor to the last site in the beach line. + */ + site_arc2 = right_it.key().left_site(); + site3 = right_it.key().right_site(); + + /* // Remove the candidate circle from the event queue. */ + this.deactivate_circle_event(right_it.value()); + left_it = left_it.prev(); + site_arc1 = left_it.key().right_site(); + site1 = left_it.key().left_site(); + + /* // Insert new nodes into the beach line. Update the output. */ + new_node_it = this.insert_new_arc(site_arc1, site_arc2, site_event_, right_it, output); + + /* // Add candidate circles to the circle event queue. + * // There could be up to two circle events formed by + * // a new bisector and the one on the left or right. + */ + this.activate_circle_event(site1, site_arc1, site_event_, new_node_it, number_usage); + + /* // If the site event is a segment, update its direction. */ + if (site_event_.is_segment()) { + site_event_.inverse(); + } + this.activate_circle_event(site_event_, site_arc2, site3, right_it, number_usage); + right_it = new_node_it; + } + this.site_event_iterator_ = this.site_event_iterator_.next(); + } + }; + + + /* + * // In general case circle event is made of the three consecutive sites + * // that form two bisectors in the beach line data structure. + * // Let circle event sites be A, B, C, two bisectors that define + * // circle event are (A, B), (B, C). During circle event processing + * // we remove (A, B), (B, C) and insert (A, C). As beach line comparison + * // works correctly only if one of the nodes is a new one we remove + * // (B, C) bisector and change (A, B) bisector to the (A, C). That's + * // why we use const_cast there and take all the responsibility that + * // map data structure keeps correct ordering. + */ + + voronoi_builder.prototype.process_circle_event = function(output, number_usage) { + + /* // Get the topmost circle event. */ + var bisector1, bisector2, circle_event_, e, it_first, it_last, site1, site3, site_l1, site_r1; + e = this.circle_events_.top(); + circle_event_ = e.first; + it_first = e.second; + it_last = it_first; + + /* // Get the C site. */ + site3 = it_first.key().right_site(); + + /* // Get the half-edge corresponding to the second bisector - (B, C). */ + bisector2 = it_first.value().edge(); + + /* // Get the half-edge corresponding to the first bisector - (A, B). */ + it_first = it_first.prev(); + bisector1 = it_first.value().edge(); + + /* // Get the A site. */ + site1 = it_first.key().left_site(); + if ((!site1.is_segment()) && site3.is_segment() && site3.point1().equals(site1.point0())) { + site3.inverse(); + } + + /* // Change the (A, B) bisector node to the (A, C) bisector node. */ + it_first.key().right_site(site3); + it_first.value().edge(output._insert_new_edge(site1, site3, circle_event_, bisector1, bisector2).first); + + /* // Remove the (B, C) bisector node from the beach line. */ + this.beach_line_.erase(it_last); + it_last = it_first; + + /* // Pop the topmost circle event from the event queue. */ + this.circle_events_.pop(); + + /* // Check new triplets formed by the neighboring arcs + * // to the left for potential circle events. + */ + if (it_first !== this.beach_line_.begin()) { + this.deactivate_circle_event(it_first.value()); + it_first = it_first.prev(); + site_l1 = it_first.key().left_site(); + this.activate_circle_event(site_l1, site1, site3, it_last, number_usage); + } + + /* // Check the new triplet formed by the neighboring arcs + * // to the right for potential circle events. + */ + it_last = it_last.next(); + if (it_last !== this.beach_line_.end()) { + this.deactivate_circle_event(it_last.value()); + site_r1 = it_last.key().right_site(); + this.activate_circle_event(site1, site3, site_r1, it_last, number_usage); + } + }; + + + /* // Insert new nodes into the beach line. Update the output. */ + + voronoi_builder.prototype.insert_new_arc = function(site_arc1, site_arc2, site_event_, position, output) { + + /* // Create two new bisectors with opposite directions. */ + var edges, new_left_node, new_node, new_right_node; + new_left_node = new beach_line_node_key(site_arc1, site_event_); + new_right_node = new beach_line_node_key(site_event_, site_arc2); + + /* // Set correct orientation for the first site of the second node. */ + if (site_event_.is_segment()) { + new_right_node.left_site().inverse(); + } + + /* // Update the output. */ + edges = output._insert_new_edge(site_arc2, site_event_); + + /* TODO */ + position = this.beach_line_.insert(new_right_node, new beach_line_node_data(edges.second)); + if (site_event_.is_segment()) { + + /* + * // Update the beach line with temporary bisector, that will + * // disappear after processing site event corresponding to the + * // second endpoint of the segment site. + */ + new_node = new beach_line_node_key(site_event_, site_event_); + new_node.right_site().inverse(); + + /* TODO */ + position = this.beach_line_.insert(new_node, new beach_line_node_data(null)); + + /* // Update the data structure that holds temporary bisectors. */ + this.end_points_.push({ + first: site_event_.point1(), + second: position + }); + } + + /* TODO */ + position = this.beach_line_.insert(new_left_node, new beach_line_node_data(edges.first)); + return position; + }; + + + /* + * // Add a new circle event to the event queue. + * // bisector_node corresponds to the (site2, site3) bisector. + */ + + voronoi_builder.prototype.activate_circle_event = function(site1, site2, site3, bisector_node, number_usage) { + var c_event, e; + c_event = new circle_event(); + + /* // Check if the three input sites create a circle event. */ + if (number_usage === this.number_type.INTEGERS && circle_formation_predicate(site1, site2, site3, c_event)) { + + /* // Add the new circle event to the circle events queue. + * // Update bisector's circle event iterator to point to the + * // new circle event in the circle event queue. + */ + e = this.circle_events_.push({ + first: c_event, + second: bisector_node + }); + bisector_node.value().circle_event(e.first); + } else if (number_usage === this.number_type.BIG_INTEGERS && circle_formation_predicate_big(site1, site2, site3, c_event)) { + + /* // Add the new circle event to the circle events queue. + * // Update bisector's circle event iterator to point to the + * // new circle event in the circle event queue. + */ + e = this.circle_events_.push({ + first: c_event, + second: bisector_node + }); + bisector_node.value().circle_event(e.first); + } + }; + + voronoi_builder.prototype.test_end_points_datastructure = function() { + var position, pt1, pt2, pt3, results; + position = {}; + pt1 = new point_2d(2, 3); + pt2 = new point_2d(3, 1); + pt3 = new point_2d(2, 2); + this.end_points_.push({ + first: pt1, + second: position + }); + this.end_points_.push({ + first: pt2, + second: position + }); + this.end_points_.push({ + first: pt3, + second: position + }); + console.log("### test_end_points_datastructure ###"); + console.log(" expected: (2, 2), (2, 3), (3, 1) ordering"); + results = []; + while (!this.end_points_.empty()) { + console.log(" " + this.end_points_.top().first.print()); + results.push(this.end_points_.pop()); + } + return results; + }; + + return voronoi_builder; + + })(); + + if (typeof module !== "undefined" && module !== null) { + module.exports = { + voronoi_builder: voronoi_builder + }; + } + + point_data = (function() { + point_data.prototype.x = 0; + + point_data.prototype.y = 0; + + function point_data(x3, y3) { + this.x = x3; + this.y = y3; + } + + point_data.prototype.assign = function(pt) { + this.x = pt.x; + return this.y = pt.y; + }; + + point_data.prototype.equals = function(pt) { + return Math.abs(pt.x - this.x) < EPSILON && Math.abs(pt.y - this.y) < EPSILON; + }; + + point_data.prototype.get = function(orient) { + if (orient === HORIZONTAL) { + return this.x; + } + return this.y; + }; + + point_data.prototype.set = function(orient, val) { + if (orient === HORIZONTAL) { + this.x = val; + } else { + this.y = val; + } + }; + + point_data.prototype.lessThan = function(pt) { + return (this.x < pt.x) || (Math.abs(this.x - pt.x) < EPSILON && this.y < pt.y); + }; + + point_data.prototype.lessThanEqual = function(pt) { + return lessThan(pt); + }; + + point_data.prototype.greaterThan = function(pt) { + return !lessThan(pt); + }; + + point_data.prototype.greaterTHanEqual = function(pt) { + return !lessThan(pt); + }; + + return point_data; + + })(); + + if (typeof module !== "undefined" && module !== null) { + module.exports = { + point_data: point_data + }; + } + + segment_data = (function() { + segment_data.prototype.lowPt = null; + + segment_data.prototype.highPt = null; + + function segment_data(lpt, hpt) { + this.lowPt = lpt; + this.highPt = hpt; + } + + segment_data.prototype.assign = function(segment) { + this.lowPt = point_data(segment.lowPt.x, segment.lowPt.y); + return this.highPt = point_data(segment.highPt.x, segment.highPt.y); + }; + + segment_data.prototype.equals = function(segment) { + return this.lowPt.equals(segment.lowPt) && this.highPt.equals(segment.hightPt); + }; + + segment_data.prototype.get = function(dir) { + if (dir === LOW) { + return this.lowPt; + } + return this.highPt; + }; + + segment_data.prototype.set = function(dir, pt) { + if (dir === LOW) { + return this.lowPt.assign(pt); + } else { + return this.highPt.assign(pt); + } + }; + + segment_data.prototype.low = function() { + return this.lowPt; + }; + + segment_data.prototype.high = function() { + return this.highPt; + }; + + segment_data.prototype.lessThan = function(segment) { + if (!this.lowPt.equals(segment.lowPt)) { + return this.lowPt.lessThan(segment.lowPt); + } + return this.highPt.lessThan(segment.highPt); + }; + + segment_data.prototype.lessThanEqual = function(segment) { + return lessThan(segment); + }; + + segment_data.prototype.greaterThan = function(segment) { + return !lessThan(segment); + }; + + segment_data.prototype.greaterThanEqual = function(segment) { + return !lessThan(segment); + }; + + return segment_data; + + })(); + + if (typeof module !== "undefined" && module !== null) { + module.exports = { + segment_data: segment_data + }; + } + + if ((DISABLE_REQUIRE == null) && (typeof require !== "undefined" && require !== null)) { + voronoi_builder = require('./voronoi_builder').voronoi_builder; + point_data = require('./point_data').point_data; + segment_data = require('./segment_data').segment_data; + } + + + /* + // Public methods to compute Voronoi diagram of a set of points and segments. + // Coordinates of the points and of the endpoints of the segments should belong + // to the 32-bit signed integer range [-2^31, 2^31-1]. To use wider input + // coordinate range voronoi_builder configuration via coordinate type traits + // is required. + // Complexity - O(N*logN), memory usage - O(N), N - number of input objects. + */ + + insert = function(entity, vd) { + var e, l, len, results; + if (entity instanceof point_data) { + return vd.insert_point(entity.x, entity.y); + } else if (entity instanceof segment_data) { + return vd.insert_segment(entity.low().x, entity.low().y, entity.high().x, entity.high().y); + } else if (Array.isArray(entity)) { + results = []; + for (l = 0, len = entity.length; l < len; l++) { + e = entity[l]; + results.push(insert(e, vd)); + } + return results; + } + }; + + construct_voronoi = function(points, segments, vd, number_usage, interim_json_node) { + var builder; + if (interim_json_node == null) { + interim_json_node = null; + } + builder = new voronoi_builder(); + insert(points, builder); + insert(segments, builder); + return builder.construct(vd, number_usage, interim_json_node); + }; + + if (typeof module !== "undefined" && module !== null) { + module.exports = { + construct_voronoi: construct_voronoi + }; + } + + if ((DISABLE_REQUIRE == null) && (typeof require !== "undefined" && require !== null)) { + static_cast_int = require('./voronoi_predicates').static_cast_int; + } + + voronoi_visual_utils = (function() { + function voronoi_visual_utils() {} + + voronoi_visual_utils.prototype.cast = static_cast_int; + + voronoi_visual_utils.prototype.discretize = function(point, segment, max_dist, discretization) { + + /* + // Apply the linear transformation to move start point of the segment to + // the point with coordinates (0, 0) and the direction of the segment to + // coincide the positive direction of the x-axis. + */ + var cur_x, cur_y, dist, err, inter_x, inter_y, last_point, max_dist_transformed, mid_x, mid_y, new_x, new_y, point_stack, point_vec_x, point_vec_y, projection_end, projection_start, rot_x, rot_y, segm_vec_x, segm_vec_y, sqr_segment_length; + segm_vec_x = this.cast(segment.high().x) - this.cast(segment.low().x); + segm_vec_y = this.cast(segment.high().y) - this.cast(segment.low().y); + sqr_segment_length = segm_vec_x * segm_vec_x + segm_vec_y * segm_vec_y; + + /*// Compute x-coordinates of the endpoints of the edge + // in the transformed space. + */ + projection_start = sqr_segment_length * this.get_point_projection(discretization[0], segment); + projection_end = sqr_segment_length * this.get_point_projection(discretization[1], segment); + + /*// Compute parabola parameters in the transformed space. + // Parabola has next representation: + // f(x) = ((x-rot_x)^2 + rot_y^2) / (2.0*rot_y). + */ + point_vec_x = this.cast(point.x) - this.cast(segment.low().x); + point_vec_y = this.cast(point.y) - this.cast(segment.low().y); + rot_x = segm_vec_x * point_vec_x + segm_vec_y * point_vec_y; + rot_y = segm_vec_x * point_vec_y - segm_vec_y * point_vec_x; + if (rot_y === 0) { + err = "Invalid parabola detected: \npoint: " + JSON.stringify(point) + "\nsegment: " + JSON.stringify(segment) + "\ndiscretization: " + JSON.stringify(discretization); + console.error(err); + return; + } + + /* // Save the last point. */ + last_point = discretization[1]; + discretization.pop(); + + /* // Use stack to avoid recursion. */ + point_stack = []; + point_stack.unshift(projection_end); + cur_x = projection_start; + cur_y = this.parabola_y(cur_x, rot_x, rot_y); + + /*// Adjust max_dist parameter in the transformed space. */ + max_dist_transformed = max_dist * max_dist * sqr_segment_length; + while (point_stack.length > 0) { + new_x = point_stack[0]; + new_y = this.parabola_y(new_x, rot_x, rot_y); + + /*// Compute coordinates of the point of the parabola that is + // furthest from the current line segment. + */ + mid_x = (new_y - cur_y) / (new_x - cur_x) * rot_y + rot_x; + mid_y = this.parabola_y(mid_x, rot_x, rot_y); + + /*// Compute maximum distance between the given parabolic arc + // and line segment that discretize it. + */ + dist = (new_y - cur_y) * (mid_x - cur_x) - (new_x - cur_x) * (mid_y - cur_y); + dist = dist * dist / ((new_y - cur_y) * (new_y - cur_y) + (new_x - cur_x) * (new_x - cur_x)); + if (dist <= max_dist_transformed) { + + /*// Distance between parabola and line segment is less than max_dist. */ + point_stack.shift(); + inter_x = (segm_vec_x * new_x - segm_vec_y * new_y) / sqr_segment_length + this.cast(segment.low().x); + inter_y = (segm_vec_x * new_y + segm_vec_y * new_x) / sqr_segment_length + this.cast(segment.low().y); + discretization.push(new point_data(inter_x, inter_y)); + cur_x = new_x; + cur_y = new_y; + } else { + point_stack.unshift(mid_x); + } + } + + /* // Update last point. */ + return discretization[discretization.length - 1] = last_point; + }; + + voronoi_visual_utils.prototype.discretize2 = function(point, segment, delta_radius, discretization) { + + /* + // Apply the linear transformation to move start point of the segment to + // the point with coordinates (0, 0) and the direction of the segment to + // coincide the positive direction of the x-axis. + */ + var cur_x, cur_y, delta_radius_transformed, err, firstRound, inter_x, inter_y, l, last_point, len, new_x, new_y, point_vec_x, point_vec_y, points, projection_end, projection_max, projection_min, projection_start, pt, reverse_points, rot_x, rot_y, segm_vec_x, segm_vec_y, sqr_segment_length; + segm_vec_x = this.cast(segment.high().x) - this.cast(segment.low().x); + segm_vec_y = this.cast(segment.high().y) - this.cast(segment.low().y); + sqr_segment_length = segm_vec_x * segm_vec_x + segm_vec_y * segm_vec_y; + + /*// Compute x-coordinates of the endpoints of the edge + // in the transformed space. + */ + projection_start = sqr_segment_length * this.get_point_projection(discretization[0], segment); + projection_end = sqr_segment_length * this.get_point_projection(discretization[1], segment); + + /*// Compute parabola parameters in the transformed space. + // Parabola has next representation: + // f(x) = ((x-rot_x)^2 + rot_y^2) / (2.0*rot_y). + */ + point_vec_x = this.cast(point.x) - this.cast(segment.low().x); + point_vec_y = this.cast(point.y) - this.cast(segment.low().y); + rot_x = segm_vec_x * point_vec_x + segm_vec_y * point_vec_y; + rot_y = segm_vec_x * point_vec_y - segm_vec_y * point_vec_x; + if (rot_y === 0) { + err = "Invalid parabola detected: \npoint: " + JSON.stringify(point) + "\nsegment: " + JSON.stringify(segment) + "\ndiscretization: " + JSON.stringify(discretization); + console.error(err); + return; + } + reverse_points = projection_start > projection_end; + projection_min = Math.min(projection_start, projection_end); + projection_max = Math.max(projection_start, projection_end); + delta_radius_transformed = delta_radius * Math.sqrt(sqr_segment_length); + points = []; + if ((projection_min <= rot_x && rot_x <= projection_max)) { + cur_x = rot_x; + cur_y = rot_y / 2; + while (projection_min < cur_x) { + points.unshift({ + x: cur_x, + y: cur_y + }); + cur_y += delta_radius_transformed; + cur_x = this.parabola_x_low(cur_y, rot_x, rot_y); + } + cur_x = rot_x; + cur_y = rot_y / 2; + firstRound = true; + while (cur_x < projection_max) { + if (!firstRound) { + points.push({ + x: cur_x, + y: cur_y + }); + } + cur_y += delta_radius_transformed; + cur_x = this.parabola_x_high(cur_y, rot_x, rot_y); + firstRound = false; + } + } else if (rot_x < projection_min) { + cur_x = projection_min; + cur_y = this.parabola_y(cur_x, rot_x, rot_y); + firstRound = true; + while (cur_x < projection_max) { + if (!firstRound) { + points.push({ + x: cur_x, + y: cur_y + }); + } + cur_y += delta_radius_transformed; + cur_x = this.parabola_x_high(cur_y, rot_x, rot_y); + firstRound = false; + } + } else if (projection_max < rot_x) { + cur_x = projection_min; + cur_y = this.parabola_y(cur_x, rot_x, rot_y); + firstRound = true; + while (cur_x < projection_max) { + if (!firstRound) { + points.push({ + x: cur_x, + y: cur_y + }); + } + cur_y -= delta_radius_transformed; + if (cur_y < (rot_y / 2)) { + break; + } + cur_x = this.parabola_x_low(cur_y, rot_x, rot_y); + firstRound = false; + } + } + last_point = discretization[1]; + discretization.pop(); + if (reverse_points) { + points.reverse(); + } + for (l = 0, len = points.length; l < len; l++) { + pt = points[l]; + new_x = pt.x; + new_y = pt.y; + inter_x = (segm_vec_x * new_x - segm_vec_y * new_y) / sqr_segment_length + this.cast(segment.low().x); + inter_y = (segm_vec_x * new_y + segm_vec_y * new_x) / sqr_segment_length + this.cast(segment.low().y); + discretization.push(new point_data(inter_x, inter_y)); + } + return discretization.push(last_point); + }; + + voronoi_visual_utils.prototype.discretize_central_angle = function(point, segment, max_angle, discretization) { + + /* + // Apply the linear transformation to move start point of the segment to + // the point with coordinates (0, 0) and the direction of the segment to + // coincide the positive direction of the x-axis. + */ + var angle, center_x, center_y, cur_x, cur_y, dot, err, inter_x, inter_y, last_point, length, mid_x, mid_y, new_x, new_y, point_stack, point_vec_x, point_vec_y, projection_end, projection_start, rot_x, rot_y, segm_vec_x, segm_vec_y, sqr_segment_length, u_x, u_y, v_x, v_y; + segm_vec_x = this.cast(segment.high().x) - this.cast(segment.low().x); + segm_vec_y = this.cast(segment.high().y) - this.cast(segment.low().y); + sqr_segment_length = segm_vec_x * segm_vec_x + segm_vec_y * segm_vec_y; + + /*// Compute x-coordinates of the endpoints of the edge + // in the transformed space. + */ + projection_start = sqr_segment_length * this.get_point_projection(discretization[0], segment); + projection_end = sqr_segment_length * this.get_point_projection(discretization[1], segment); + + /*// Compute parabola parameters in the transformed space. + // Parabola has next representation: + // f(x) = ((x-rot_x)^2 + rot_y^2) / (2.0*rot_y). + */ + point_vec_x = this.cast(point.x) - this.cast(segment.low().x); + point_vec_y = this.cast(point.y) - this.cast(segment.low().y); + rot_x = segm_vec_x * point_vec_x + segm_vec_y * point_vec_y; + rot_y = segm_vec_x * point_vec_y - segm_vec_y * point_vec_x; + if (rot_y === 0) { + err = "Invalid parabola detected: \npoint: " + JSON.stringify(point) + "\nsegment: " + JSON.stringify(segment) + "\ndiscretization: " + JSON.stringify(discretization); + console.error(err); + return; + } + + /* // Save the last point. */ + last_point = discretization[1]; + discretization.pop(); + + /* // Use stack to avoid recursion. */ + point_stack = []; + point_stack.unshift(projection_end); + cur_x = projection_start; + cur_y = this.parabola_y(cur_x, rot_x, rot_y); + center_x = rot_x; + center_y = rot_y; + + /*// Adjust max_dist parameter in the transformed space. */ + while (point_stack.length > 0) { + new_x = point_stack[0]; + new_y = this.parabola_y(new_x, rot_x, rot_y); + + /*// Compute coordinates of the point of the parabola that is + // furthest from the current line segment. + */ + mid_x = (new_y - cur_y) / (new_x - cur_x) * rot_y + rot_x; + mid_y = this.parabola_y(mid_x, rot_x, rot_y); + + /*// Compute maximum distance between the given parabolic arc + // and line segment that discretize it. + */ + u_x = new_x - center_x; + u_y = new_y - center_y; + v_x = cur_x - center_x; + v_y = cur_y - center_y; + length = Math.sqrt((u_x * u_x + u_y * u_y) * (v_x * v_x + v_y * v_y)); + if (length === 0) { + console.error("Division by zero!"); + angle = 0; + } else { + dot = (u_x * v_x + u_y * v_y) / length; + angle = Math.acos(dot); + } + if (angle <= max_angle) { + + /*// Distance between parabola and line segment is less than max_dist. */ + point_stack.shift(); + inter_x = (segm_vec_x * new_x - segm_vec_y * new_y) / sqr_segment_length + this.cast(segment.low().x); + inter_y = (segm_vec_x * new_y + segm_vec_y * new_x) / sqr_segment_length + this.cast(segment.low().y); + discretization.push(new point_data(inter_x, inter_y)); + cur_x = new_x; + cur_y = new_y; + } else { + point_stack.unshift(mid_x); + } + } + + /* // Update last point. */ + return discretization[discretization.length - 1] = last_point; + }; + + voronoi_visual_utils.prototype.discretize_point_point = function(point, segment, threshold, discretization) { + var cur_x, cur_y, dist1, dist2, dist3, distance, last_point, mid_x, mid_y, new_x, new_y, point_stack; + distance = function(x1, y1, x2, y2) { + var x, y; + x = x1 - x2; + y = y1 - y2; + return Math.sqrt(x * x + y * y); + }; + + /* // Save the last point. */ + last_point = discretization[1]; + discretization.pop(); + + /* // Use stack to avoid recursion. */ + point_stack = []; + point_stack.unshift(last_point); + cur_x = discretization[0].x; + cur_y = discretization[0].y; + while (point_stack.length > 0) { + new_x = point_stack[0].x; + new_y = point_stack[0].y; + mid_x = (cur_x + new_x) / 2; + mid_y = (cur_y + new_y) / 2; + dist1 = distance(cur_x, cur_y, point.x, point.y); + dist2 = distance(mid_x, mid_y, point.x, point.y); + dist3 = distance(new_x, new_y, point.x, point.y); + if (Math.abs(dist1 - dist3) <= threshold && Math.abs(dist1 - dist2) <= threshold) { + point_stack.shift(); + discretization.push(new point_data(new_x, new_y)); + cur_x = new_x; + cur_y = new_y; + } else { + point_stack.unshift({ + x: mid_x, + y: mid_y + }); + } + } + return discretization.push(last_point); + }; + + + /* // Compute y(x) = ((x - a) * (x - a) + b * b) / (2 * b). */ + + voronoi_visual_utils.prototype.parabola_y = function(x, a, b) { + return ((x - a) * (x - a) + b * b) / (b + b); + }; + + voronoi_visual_utils.prototype.parabola_x_low = function(y, a, b) { + var alpha, num; + num = 2 * b * y - b * b; + if (num < 0) { + console.error("can't discretisize! trying to get a square root of a negative number !!"); + return null; + } + alpha = Math.sqrt(num); + return a - alpha; + }; + + voronoi_visual_utils.prototype.parabola_x_high = function(y, a, b) { + var alpha, num; + num = 2 * b * y - b * b; + if (num < 0) { + console.error("can't discretisize! trying to get a square root of a negative number !!"); + return null; + } + alpha = Math.sqrt(num); + return a + alpha; + }; + + + /* + // Get normalized length of the distance between: + // 1) point projection onto the segment + // 2) start point of the segment + // Return this length divided by the segment length. This is made to avoid + // sqrt computation during transformation from the initial space to the + // transformed one and vice versa. The assumption is made that projection of + // the point lies between the start-point and endpoint of the segment. + */ + + voronoi_visual_utils.prototype.get_point_projection = function(point, segment) { + var point_vec_x, point_vec_y, segment_vec_x, segment_vec_y, sqr_segment_length, vec_dot; + segment_vec_x = this.cast(segment.high().x) - this.cast(segment.low().x); + segment_vec_y = this.cast(segment.high().y) - this.cast(segment.low().y); + point_vec_x = point.x - this.cast(segment.low().x); + point_vec_y = point.y - this.cast(segment.low().y); + sqr_segment_length = segment_vec_x * segment_vec_x + segment_vec_y * segment_vec_y; + vec_dot = segment_vec_x * point_vec_x + segment_vec_y * point_vec_y; + return vec_dot / sqr_segment_length; + }; + + return voronoi_visual_utils; + + })(); + + voronoi_diagnostics = (function() { + function voronoi_diagnostics(path1, holes1, filtering_angle1, debug_flags1) { + this.path = path1; + this.holes = holes1; + this.filtering_angle = filtering_angle1; + this.debug_flags = debug_flags1; + } + + voronoi_diagnostics.prototype.showErrorAndDumpData = function(possible_cause) { + var errMsg; + errMsg = "Invalid medial axis detected!\n"; + if (possible_cause != null) { + errMsg += "Possible cause: " + possible_cause + "\n"; + } + errMsg += "Input data: \n"; + errMsg += " path: " + JSON.stringify(this.path) + "\n"; + errMsg += " holes: " + JSON.stringify(this.holes) + "\n"; + errMsg += " filtering_angle: " + JSON.stringify(this.filtering_angle) + "\n"; + errMsg += " debug_flags: " + JSON.stringify(this.debug_flags) + "\n"; + return console.error(errMsg); + }; + + voronoi_diagnostics.prototype.isNaNEdge = function(edge) { + if (isNaN(edge.vertex0().x()) || isNaN(edge.vertex0().y()) || isNaN(edge.vertex1().x()) || isNaN(edge.vertex1().y())) { + this.showErrorAndDumpData("Self intersecting polygon?"); + return true; + } + return false; + }; + + voronoi_diagnostics.prototype.isInvalidPrimaryEdge = function(edge) { + if (!(edge.is_primary() && (edge.vertex0() != null) && (edge.vertex1() != null))) { + this.showErrorAndDumpData("Not a primary edge"); + return true; + } + return false; + }; + + return voronoi_diagnostics; + + })(); + + cppgen = (function() { + function cppgen() {} + + cppgen.prototype.equal_points = function(pt1, pt2) { + return Math.abs(pt1.x - pt2.x) < EPSILON && Math.abs(pt1.y - pt2.y) < EPSILON; + }; + + cppgen.prototype.input_data = function(path) { + var i, l, pt1, pt2, ref10, src; + src = ""; + for (i = l = 0, ref10 = path.length - 1; 0 <= ref10 ? l < ref10 : l > ref10; i = 0 <= ref10 ? ++l : --l) { + pt1 = path[i]; + pt2 = path[i + 1]; + src += "addTwoPoints(" + (Math.round(pt1.x)) + ", " + (Math.round(pt1.y)) + ", " + (Math.round(pt2.x)) + ", " + (Math.round(pt2.y)) + ", points, segments);\n"; + } + if (!this.equal_points(path[0], path[path.length - 1])) { + pt1 = path[path.length - 1]; + pt2 = path[0]; + src += "addTwoPoints(" + (Math.round(pt1.x)) + ", " + (Math.round(pt1.y)) + ", " + (Math.round(pt2.x)) + ", " + (Math.round(pt2.y)) + ", points, segments);\n"; + } + return src; + }; + + cppgen.prototype.generate = function(boundary, holes) { + var l, len, poly, src; + src = ""; + src += this.input_data(boundary); + for (l = 0, len = holes.length; l < len; l++) { + poly = holes[l]; + src += this.input_data(poly); + } + return src; + }; + + return cppgen; + + })(); + + if (typeof module !== "undefined" && module !== null) { + module.exports = { + cppgen: cppgen + }; + } + + + /* + The main medial axis function call. + parameter @boundary contains an array of points [{x, y}, {x, y}, {x, y}....] + parameter @holes contains an array of arrays of points [[{x, y}, ...], [{x, y}, ...]] + @returns an array of segments [{point0: {x, y, radius}, point1: {x, y, radius}}, {point0: {x, y, radius}, point1: {x, y, radius}}] + */ + + if ((DISABLE_REQUIRE == null) && (typeof require !== "undefined" && require !== null)) { + ref10 = require('./constants'), EPSILON = ref10.EPSILON, SOURCE_CATEGORY_SINGLE_POINT = ref10.SOURCE_CATEGORY_SINGLE_POINT; + cppgen = require('./cppgen'); + point_data = require('./point_data').point_data; + segment_data = require('./segment_data').segment_data; + voronoi_diagram = require('./voronoi_diagram').voronoi_diagram; + construct_voronoi = require('./voronoi').construct_voronoi; + } + + self.JSPoly = { + points_: [], + segments_: [], + colors: { + unclassified: 11, + secondary: 1, + outer_primary: 2, + inner_primary: 3, + hole_primary: 4 + }, + parabola_discretization_method: { + ERROR_2D: 0, + RADIUS: 1, + CENTRAL_ANGLE: 2 + }, + number_type: { + INTEGERS: 0, + BIG_INTEGERS: 1 + }, + point_in_poly: function(pt, polygon) { + var i, inside, j, pt1, pt2; + inside = false; + i = 0; + j = polygon.length - 1; + while (i < polygon.length) { + pt1 = polygon[i]; + pt2 = polygon[j]; + if (((pt1.y - pt.y > EPSILON) !== (pt2.y - pt.y > EPSILON)) && (((pt2.x - pt1.x) * (pt.y - pt1.y) / (pt2.y - pt1.y) + pt1.x - pt.x) > EPSILON)) { + inside = !inside; + } + j = i++; + } + return inside; + }, + clear: function() { + this.points_ = []; + return this.segments_ = []; + }, + equals: function(a, b) { + return Math.abs(a - b) < EPSILON; + }, + equal_points: function(pt1, pt2) { + return this.equals(pt1.x, pt2.x) && this.equals(pt1.y, pt2.y); + }, + generate_points_and_segments: function(boundary, is_hole) { + var currentPt, firstPt, i, l, points, pvsPt, ref11, seg, segments; + points = []; + segments = []; + if (boundary.length === 0) { + return { + points: points, + segments: segments + }; + } + pvsPt = new point_data(boundary[0].x, boundary[0].y); + firstPt = pvsPt; + firstPt.is_hole = is_hole; + for (i = l = 1, ref11 = boundary.length; 1 <= ref11 ? l < ref11 : l > ref11; i = 1 <= ref11 ? ++l : --l) { + currentPt = new point_data(boundary[i].x, boundary[i].y); + currentPt.is_hole = is_hole; + seg = new segment_data(pvsPt, currentPt); + seg.is_hole = is_hole; + segments.push(seg); + pvsPt = currentPt; + } + if (!this.equal_points(boundary[0], boundary[boundary.length - 1])) { + seg = new segment_data(pvsPt, firstPt); + seg.is_hole = is_hole; + segments.push(seg); + } + return { + points: points, + segments: segments + }; + }, + construct_voronoi_diagram_for_rounded_data: function(boundary, holes, number_usage, intermediate_debug_data) { + var hole, l, len, poseg, vd; + if (intermediate_debug_data == null) { + intermediate_debug_data = null; + } + this.clear(); + poseg = this.generate_points_and_segments(boundary, false); + this.points_ = this.points_.concat(poseg.points); + this.segments_ = this.segments_.concat(poseg.segments); + for (l = 0, len = holes.length; l < len; l++) { + hole = holes[l]; + poseg = this.generate_points_and_segments(hole, true); + this.points_ = this.points_.concat(poseg.points); + this.segments_ = this.segments_.concat(poseg.segments); + } + vd = new voronoi_diagram(); + construct_voronoi(this.points_, this.segments_, vd, number_usage, intermediate_debug_data); + return vd; + }, + construct_voronoi_diagram: function(boundary, holes, number_usage, intermediate_debug_data) { + var ref11; + if (number_usage == null) { + number_usage = this.number_type.INTEGERS; + } + if (intermediate_debug_data == null) { + intermediate_debug_data = null; + } + ref11 = this.round_input_data(boundary, holes), boundary = ref11.boundary, holes = ref11.holes; + return this.construct_voronoi_diagram_for_rounded_data(boundary, holes, number_usage, intermediate_debug_data); + }, + construct_voronoi_diagram_with_edge_classification: function(boundary, holes, number_usage, intermediate_debug_data) { + var ref11, vd; + if (number_usage == null) { + number_usage = this.number_type.INTEGERS; + } + if (intermediate_debug_data == null) { + intermediate_debug_data = null; + } + ref11 = this.round_input_data(boundary, holes), boundary = ref11.boundary, holes = ref11.holes; + vd = this.construct_voronoi_diagram_for_rounded_data(boundary, holes, number_usage); + this.classify_edges(vd, boundary, holes, intermediate_debug_data); + return vd; + }, + create_edge_json_data_for_testing: function(vd) { + var e, edge, edges, l, len, pt, ref11; + edges = []; + ref11 = vd.edges(); + for (l = 0, len = ref11.length; l < len; l++) { + edge = ref11[l]; + e = {}; + e.is_finite = edge.is_finite(); + e.is_infinite = edge.is_infinite(); + e.is_primary = edge.is_primary(); + e.is_secondary = edge.is_secondary(); + e.is_linear = edge.is_linear(); + e.is_curved = edge.is_curved(); + if (edge.vertex0() === null) { + e.vertex0 = null; + } else { + pt = {}; + pt.x = edge.vertex0().x(); + pt.y = edge.vertex0().y(); + e.vertex0 = pt; + } + if (edge.vertex1() === null) { + e.vertex1 = null; + } else { + pt = {}; + pt.x = edge.vertex1().x(); + pt.y = edge.vertex1().y(); + e.vertex1 = pt; + } + e.color = edge.color(); + edges.push(e); + } + return edges; + }, + retrieve_point: function(cell) { + var category, index; + index = cell.source_index(); + category = cell.source_category(); + if (category === SOURCE_CATEGORY_SINGLE_POINT) { + return this.points_[index]; + } + index -= this.points_.length; + if (category === SOURCE_CATEGORY_SEGMENT_START_POINT) { + return this.segments_[index].low(); + } else { + return this.segments_[index].high(); + } + }, + retrieve_segment: function(cell) { + var index; + index = cell.source_index() - this.points_.length; + return this.segments_[index]; + }, + get_cells_angle: function(edge) { + var angleConsecThreePoints, segment1, segment2; + angleConsecThreePoints = function(pt1, pt2, pt3) { + var dotProduct; + dotProduct = (pt1.x - pt2.x) * (pt3.x - pt2.x) + (pt3.y - pt2.y) * (pt1.y - pt2.y); + return Math.acos(dotProduct / (Math.hypot(pt1.x - pt2.x, pt1.y - pt2.y) * Math.hypot(pt3.x - pt2.x, pt3.y - pt2.y))); + }; + if (edge.cell().contains_point() || edge.twin().cell().contains_point()) { + return null; + } + segment1 = this.retrieve_segment(edge.cell()); + segment2 = this.retrieve_segment(edge.twin().cell()); + if (segment1.low().equals(segment2.low())) { + return angleConsecThreePoints(segment1.high(), segment1.low(), segment2.high()); + } else if (segment1.high().equals(segment2.low())) { + return angleConsecThreePoints(segment1.low(), segment1.high(), segment2.high()); + } else if (segment1.low().equals(segment2.high())) { + return angleConsecThreePoints(segment1.high(), segment1.low(), segment2.low()); + } else if (segment1.high().equals(segment2.high())) { + return angleConsecThreePoints(segment1.low(), segment1.high(), segment2.low()); + } + return null; + }, + distance_to_point: function(pt1, pt2) { + return Math.hypot(pt1.x - pt2.x, pt1.y - pt2.y); + }, + distance_to_segment: function(pt, seg) { + var x1, x2, y1, y2; + x1 = seg.low().x; + y1 = seg.low().y; + x2 = seg.high().x; + y2 = seg.high().y; + return Math.abs((y2 - y1) * pt.x - (x2 - x1) * pt.y + x2 * y1 - y2 * x1) / Math.hypot(y1 - y2, x1 - x2); + }, + create_output_segments: function(edge, vd, output_segments, no_parabola, show_sites, discretize_threshold, discretize_method, pointpoint_segmentation_threshold) { + var i, l, len, len1, m, n, point, point2, pt, pvsPt, ref11, sample, samples, segment, segment2; + if (discretize_threshold == null) { + discretize_threshold = 1e-1; + } + if (discretize_method == null) { + discretize_method = this.parabola_discretization_method.ERROR_2D; + } + if (pointpoint_segmentation_threshold == null) { + pointpoint_segmentation_threshold = -1; + } + samples = []; + samples.push(new point_data(edge.vertex0().x(), edge.vertex0().y())); + samples.push(new point_data(edge.vertex1().x(), edge.vertex1().y())); + if (edge.is_curved()) { + point = edge.cell().contains_point() ? this.retrieve_point(edge.cell()) : this.retrieve_point(edge.twin().cell()); + segment = edge.cell().contains_point() ? this.retrieve_segment(edge.twin().cell()) : this.retrieve_segment(edge.cell()); + if (discretize_method === this.parabola_discretization_method.RADIUS) { + if (!no_parabola) { + new voronoi_visual_utils().discretize2(point, segment, discretize_threshold, samples); + } + } else if (discretize_method === this.parabola_discretization_method.ERROR_2D) { + if (!no_parabola) { + new voronoi_visual_utils().discretize(point, segment, discretize_threshold, samples); + } + } else if (discretize_method === this.parabola_discretization_method.CENTRAL_ANGLE) { + if (!no_parabola) { + new voronoi_visual_utils().discretize_central_angle(point, segment, discretize_threshold, samples); + } + } + for (l = 0, len = samples.length; l < len; l++) { + sample = samples[l]; + sample.radius = this.distance_to_point(point, sample); + if (show_sites) { + sample.sites = [ + { + type: "point", + site: { + x: point.x, + y: point.y + } + }, { + type: "segment", + site: { + x1: segment.low().x, + y1: segment.low().y, + x2: segment.high().x, + y2: segment.high().y + } + } + ]; + } + } + } else { + if (edge.cell().contains_point() && edge.twin().cell().contains_point()) { + point = this.retrieve_point(edge.cell()); + if (pointpoint_segmentation_threshold !== -1) { + new voronoi_visual_utils().discretize_point_point(point, segment, pointpoint_segmentation_threshold, samples); + } + for (m = 0, len1 = samples.length; m < len1; m++) { + sample = samples[m]; + sample.radius = this.distance_to_point(point, sample); + if (show_sites) { + point2 = this.retrieve_point(edge.twin().cell()); + sample.sites = [ + { + type: "point", + site: { + x: point.x, + y: point.y + } + }, { + type: "point", + site: { + x: point2.x, + y: point2.y + } + } + ]; + } + } + } else if (!(edge.cell().contains_point() || edge.twin().cell().contains_point())) { + segment = this.retrieve_segment(edge.cell()); + samples[0].radius = this.distance_to_segment(samples[0], segment); + samples[1].radius = this.distance_to_segment(samples[1], segment); + if (show_sites) { + segment2 = this.retrieve_segment(edge.twin().cell()); + samples[0].sites = [ + { + type: "segment", + site: { + x1: segment.low().x, + y1: segment.low().y, + x2: segment.high().x, + y2: segment.high().y + } + }, { + type: "segment", + site: { + x1: segment2.low().x, + y1: segment2.low().y, + x2: segment2.high().x, + y2: segment2.high().y + } + } + ]; + samples[1].sites = [ + { + type: "segment", + site: { + x1: segment.low().x, + y1: segment.low().y, + x2: segment.high().x, + y2: segment.high().y + } + }, { + type: "segment", + site: { + x1: segment2.low().x, + y1: segment2.low().y, + x2: segment2.high().x, + y2: segment2.high().y + } + } + ]; + } + } else { + return false; + } + } + pvsPt = samples[0]; + for (i = n = 1, ref11 = samples.length; 1 <= ref11 ? n < ref11 : n > ref11; i = 1 <= ref11 ? ++n : --n) { + pt = samples[i]; + output_segments.push({ + point0: { + x: pvsPt.x, + y: pvsPt.y, + radius: pvsPt.radius + }, + point1: { + x: pt.x, + y: pt.y, + radius: pt.radius + }, + sites: show_sites ? pvsPt.sites : void 0 + }); + pvsPt = pt; + } + return true; + }, + cppgen: cppgen, + colorPrimaryEdges: function(startEdge, color, count, debug_callback) { + var currEdge, edge, ite, twinEdges; + twinEdges = [startEdge]; + ite = 0; + if (count != null) { + debug_callback({ + twinEdges: twinEdges.length, + iteration: ite + }); + } + while (twinEdges.length > 0) { + if ((count != null) && ite > count) { + break; + } + edge = twinEdges[0]; + twinEdges.splice(0, 1); + ite++; + if (count != null) { + debug_callback({ + twinEdges: twinEdges.length, + iteration: ite + }); + } + edge.color(color); + currEdge = edge.next_primary(); + while (currEdge !== edge && (currEdge != null) && currEdge.color() === this.colors.unclassified) { + if (currEdge.twin().color() === this.colors.unclassified) { + twinEdges.unshift(currEdge.twin()); + } + if ((count != null) && ite > count) { + break; + } + currEdge.color(color); + currEdge = currEdge.next_primary(); + ite++; + if (count != null) { + debug_callback({ + twinEdges: twinEdges.length, + iteration: ite + }); + } + } + twinEdges = twinEdges.filter(function(e) { + return e.color() === JSPoly.colors.unclassified; + }); + } + if (count != null) { + debug_callback({ + twinEdges: twinEdges.length, + iteration: ite + }); + } + }, + get_non_border_point: function(edge) { + var point, pt0, pt1, segment, vertex_to_pt; + vertex_to_pt = function(vertex) { + return { + x: vertex.x(), + y: vertex.y() + }; + }; + pt0 = vertex_to_pt(edge.vertex0()); + pt1 = vertex_to_pt(edge.vertex1()); + if (edge.cell().contains_point()) { + point = this.retrieve_point(edge.cell()); + if (!this.equal_points(pt0, point)) { + return pt0; + } + } else { + segment = this.retrieve_segment(edge.cell()); + if (!(this.equal_points(pt0, segment.low()) || this.equal_points(pt0, segment.high()))) { + return pt0; + } + } + return pt1; + }, + classify_edges: function(vd, boundary, holes, diagnostics, count, cb) { + var edge, l, len, len1, len2, len3, m, n, o, origin, poly, polygon, polygons, pt, ref11, unclassifiedPrimaryEdges; + if (diagnostics == null) { + diagnostics = new voronoi_diagnostics([], []); + } + if (count == null) { + count = null; + } + if (cb == null) { + cb = function() {}; + } + unclassifiedPrimaryEdges = []; + ref11 = vd.edges(); + for (l = 0, len = ref11.length; l < len; l++) { + edge = ref11[l]; + if (edge.is_primary()) { + if (edge.is_infinite()) { + edge.color(this.colors.outer_primary); + } else { + edge.color(this.colors.unclassified); + unclassifiedPrimaryEdges.push(edge); + } + } else { + edge.color(this.colors.secondary); + } + } + polygons = [].concat([boundary], holes); + origin = { + x: 0, + y: 0 + }; + polygon = [origin]; + for (m = 0, len1 = polygons.length; m < len1; m++) { + poly = polygons[m]; + if (poly.length === 0) { + continue; + } + for (n = 0, len2 = poly.length; n < len2; n++) { + pt = poly[n]; + polygon.push(pt); + } + if (!this.equal_points(poly[0], poly[poly.length - 1])) { + polygon.push(poly[0]); + } + polygon.push(origin); + } + for (o = 0, len3 = unclassifiedPrimaryEdges.length; o < len3; o++) { + edge = unclassifiedPrimaryEdges[o]; + if (edge.color() === this.colors.inner_primary) { + continue; + } + pt = this.get_non_border_point(edge); + if (this.point_in_poly(pt, polygon)) { + edge.color(this.colors.inner_primary); + edge.twin().color(this.colors.inner_primary); + } + } + return vd.edges(); + }, + dump: function(boundary, holes, msg) { + if (msg == null) { + msg = ""; + } + return console.error(msg + JSON.stringify({ + boundary: boundary, + holes: holes + })); + }, + round_input_data: function(boundary, holes) { + return { + boundary: boundary.map(function(pt) { + return { + x: Math.round(pt.x), + y: Math.round(pt.y) + }; + }), + holes: holes.map(function(arr) { + return arr.map(function(pt) { + return { + x: Math.round(pt.x), + y: Math.round(pt.y) + }; + }); + }) + }; + }, + construct_medial_axis: function(boundary, holes, descritize_threshold, descritize_method, filtering_angle, pointpoint_segmentation_threshold, number_usage, debug_flags, intermediate_debug_data) { + var currEdge, diagnostics, edge, edgeSet, edges, entriesIterator, entry, filtered_edges, first_edge, invalid_output, l, len, len1, m, no_parabola, original_e, output_edge, output_edges, output_segments, prev_edge, ref11, show_sites, valid, vd; + if (descritize_threshold == null) { + descritize_threshold = 1e-1; + } + if (descritize_method == null) { + descritize_method = this.parabola_discretization_method.CENTRAL_ANGLE; + } + if (filtering_angle == null) { + filtering_angle = 3 * Math.PI / 4; + } + if (pointpoint_segmentation_threshold == null) { + pointpoint_segmentation_threshold = -1; + } + if (number_usage == null) { + number_usage = this.number_type.INTEGERS; + } + if (debug_flags == null) { + debug_flags = { + no_parabola: false, + show_sites: false + }; + } + if (intermediate_debug_data == null) { + intermediate_debug_data = null; + } + diagnostics = new voronoi_diagnostics(boundary, holes, filtering_angle, debug_flags); + no_parabola = debug_flags.no_parabola, show_sites = debug_flags.show_sites; + ref11 = this.round_input_data(boundary, holes), boundary = ref11.boundary, holes = ref11.holes; + vd = this.construct_voronoi_diagram_for_rounded_data(boundary, holes, number_usage, intermediate_debug_data); + edges = this.classify_edges(vd, boundary, holes, diagnostics).filter(function(edge) { + return edge.color() === JSPoly.colors.inner_primary; + }); + invalid_output = false; + output_edges = []; + edgeSet = new Set(edges); + while (edgeSet.size > 0) { + for (l = 0, len = output_edges.length; l < len; l++) { + output_edge = output_edges[l]; + edgeSet["delete"](output_edge.twin()); + } + if (edgeSet.size === 0) { + break; + } + entriesIterator = edgeSet.values(); + entry = entriesIterator.next(); + edge = entry.value; + while (!entry.done) { + first_edge = entry.value; + prev_edge = first_edge.prev(); + while ((prev_edge != null) && prev_edge.is_secondary() && prev_edge !== first_edge) { + prev_edge = prev_edge.twin().prev(); + } + if (!edgeSet.has(prev_edge)) { + edge = first_edge; + break; + } + entry = entriesIterator.next(); + } + while (edgeSet.has(edge)) { + output_edges.push(edge); + currEdge = edge.next(); + original_e = edge; + while ((currEdge != null) && currEdge.is_secondary() && original_e !== currEdge) { + currEdge = currEdge.twin().next(); + } + edgeSet["delete"](edge); + edge = currEdge; + } + } + filtered_edges = output_edges.filter(function(edge) { + var angle; + angle = JSPoly.get_cells_angle(edge); + if ((angle != null) && angle > filtering_angle) { + return false; + } + return true; + }); + output_segments = []; + for (m = 0, len1 = filtered_edges.length; m < len1; m++) { + edge = filtered_edges[m]; + if (edge.color() === this.colors.inner_primary) { + if (diagnostics.isInvalidPrimaryEdge(edge) || diagnostics.isNaNEdge(edge)) { + this.dump(boundary, holes, "Medial axis generation failed: found an edge either invalid primary or values containing NaN \n"); + return []; + } else if (!this.equals(edge.vertex0().x(), edge.vertex1().x()) || !this.equals(edge.vertex0().y(), edge.vertex1().y())) { + valid = this.create_output_segments(edge, vd, output_segments, no_parabola, show_sites, descritize_threshold, descritize_method, pointpoint_segmentation_threshold); + if (!valid) { + this.dump(boundary, holes, "Medial axis generation failed: invalid edge found, the edge should be either curved or linear \n"); + return []; + } + } + } + } + return output_segments; + } + }; + + if (typeof module !== "undefined" && module !== null) { + module.exports = { + jspoly: this.JSPoly + }; + } + +}).call(this); diff --git a/src/geo/polygon.js b/src/geo/polygon.js index 8cc0f14d..11a5732d 100644 --- a/src/geo/polygon.js +++ b/src/geo/polygon.js @@ -257,32 +257,14 @@ export class Polygon { // including inner polys. return the noodle and the remainder // of the polygon with the noodle removed (for the next pass) noodle(width) { - // inset outer polygon - let inner = this.inner; - let inset = this.clone().offset(width); - let noodot = [ this.clone().setInner(inset) ]; - let noodin = []; - // subtract inners from the inset - if (inner) { - noodot = POLY.subtract(noodot, inner, []); - // for each inner, offset and subtract noodle + itself - let nood = []; - for (let innie of inner) { - let inexp = innie.clone().offset(-width); - let intrm = POLY.trimTo(inexp, [ this ]); - let inood = POLY.subtract(intrm, [ ...noodot, innie ], []); - nood.push(...inood); - } - noodin = POLY.union(nood, 0, true); - } - // unify inner noodles and outer noodle - let noodle = POLY.union([ ...noodot, ...noodin ], 0, true); - // subtract noodle from poly for next noodle calc - let remain = POLY.subtract([ this ], noodle, []); - return { noodle, remain }; + let clone = this.clone(true); + let ins = clone.offset(width); + let nood = POLY.nest(POLY.flatten([ clone, ...ins ], [], true)); + return { noodle: nood, remain: ins }; } // generate center crossing point cloud + // only used for fdm thin-wall type 1 (fdm/post.js) centers(step, z, min, max, opt = {}) { let cloud = [], bounds = this.bounds, @@ -555,6 +537,7 @@ export class Polygon { return this; } + // only used in fdm/slice.js for enclosing supports createConvexHull(points) { function removeMiddle(a, b, c) { let cross = (a.x - b.x) * (c.y - b.y) - (a.y - b.y) * (c.x - b.x); @@ -886,6 +869,7 @@ export class Polygon { /** * hint fill angle hinting from longest segment + * only used in fdm/slice.js for projected infill orientation */ hintFillAngle() { let index = 0, @@ -1217,6 +1201,8 @@ export class Polygon { * 2. ease-down starts, following the polygonal path, decreasing Z at a fixed slope until target Z is hit, * 3. then the rest of the path is completed and repeated at target Z until touchdown point is reached. * 4. this function should probably move to CAM prepare since it's only called from there + * + * possibly no longer used anywhere */ forEachPointEaseDown(fn, fromPoint, degrees = 45) { let index = this.findClosestPointTo(fromPoint).index, @@ -1537,6 +1523,7 @@ export class Polygon { return this; } + // used in geo/slicer.js to free memory freeParentRefs() { if (this.inner && this.inner.length > 0) { for (let inner of this.inner) { @@ -1549,6 +1536,7 @@ export class Polygon { } } + // possibly ununsed newUndeleted() { let poly = newPolygon(); this.forEachPoint(p => { @@ -1888,6 +1876,7 @@ export class Polygon { return out; } + // possibly unused shortestSegmentLength() { let len = Infinity; this.forEachSegment((p1, p2) => { @@ -2362,6 +2351,8 @@ export class Polygon { return this; } + // only used by cam/op-pocket.js + // for any poly points closer than `dist`, replace them with their midpoint midpoints(dist = 0.01) { const newp = []; const points = this.points; diff --git a/src/kiri/mode/fdm/post.js b/src/kiri/mode/fdm/post.js index dd0edbc8..7758a04a 100644 --- a/src/kiri/mode/fdm/post.js +++ b/src/kiri/mode/fdm/post.js @@ -1,9 +1,12 @@ /** Copyright Stewart Allen -- All Rights Reserved */ +import '../../../ext/jspoly.js'; import { base } from '../../../geo/base.js'; +import { Line } from '../../../geo/line.js'; import { polygons as POLY, fillArea } from '../../../geo/polygons.js'; import { getRangeParameters } from './driver.js'; import { slicer } from '../../../geo/slicer.js'; +import { Point } from '../../../geo/point.js'; slicer.slicePost.FDM = slicePost; @@ -164,19 +167,124 @@ function thin_type_3(params) { top.thin_fill = []; top.fill_sparse = []; - let { noodle, remain } = top.poly.noodle(offsetN); + let { noodle, remain } = top.poly.noodle(offsetN * 2); top.shells = noodle; top.gaps = remain; - // top.fill_off = noodle; - // if (remain.length) { - // top.gaps = []; - // for (let poly of remain) { - // let { noodle } = poly.noodle(offsetN); - // console.log({ next_noodle: noodle }); - // top.gaps.push(...noodle); - // } - // } + let thin = top.thin_fill; + let scale = 5000; + let minR = offsetN / 2; + let minSpur = offsetN * 3; + + let pointMap = new Map(); + let lineMap = new Map(); + + function pointToRec(point) { + let key = `${(point.x*100)|0}|${(point.y*100)|0}`; + let rec = pointMap.get(key); + if (!rec) { + rec = { + key, + count: 0, + x: point.x/scale, + y: point.y/scale, + r: point.radius/scale, + }; + pointMap.set(key, rec); + } + return rec; + } + + function pointsToLine(p0, p1) { + p0 = pointToRec(p0); + p1 = pointToRec(p1); + let key = p0.key > p1.key ? `${p1.key}:${p0.key}` : `${p0.key}:${p1.key}`; + let rec = lineMap.get(key); + if (rec) { + rec.count++; + } else { + rec = { + p0, + p1, + key, + count: 1, + }; + p0.count++; + p1.count++; + lineMap.set(key, rec); + } + return rec; + } + + let chains = []; + + for (let poly of noodle) { + poly = poly.clone(true).scale({ x:scale, y:scale, z:scale }); + let out = poly.points.map(p => ({ x: p.x|0, y: p.y|0 })); + let inr = (poly.inner ?? []).map(p => p.points.map(p => ({ x: p.x|0, y: p.y|0 }))); + let ma = JSPoly.construct_medial_axis(out, inr); + for (let { point0, point1 } of ma) { + pointsToLine(point0, point1); + } + for (let seg of lineMap.values()) { + let { p0, p1 } = seg; + let len = seg.len = Math.hypot(p0.x-p1.x, p0.y-p1.y); + if (p0.r < minR || p1.r < minR) { + if (p0.r > minR && len > minSpur) { + // move p1 toward p0 until minR met + let pct = (minR - p1.r) / (p0.r - p1.r); + let dx = p1.x - p0.x; + let dy = p1.y - p0.y; + p1.x -= dx * pct; + p1.y -= dy * pct; + p1.r = minR; + pointMap.delete(p1.key); + } else if (p1.r > minR && len > minSpur) { + // move p0 toward p1 until minR met + let pct = (minR - p0.r) / (p1.r - p0.r); + let dx = p0.x - p1.x; + let dy = p0.y - p1.y; + p0.x -= dx * pct; + p0.y -= dy * pct; + p0.r = minR; + pointMap.delete(p0.key); + } else { + lineMap.delete(seg.key); + continue; + } + } + // thin.push(new Point(p0.x, p0.y)); + // thin.push(new Point(p1.x, p1.y)); + let add; + for (let chain of chains) { + if (p0.count === 2 && p0 === chain[0]) { + chain.splice(0,0,p1); + } else if (p0.count === 2 && p0 === chain.peek()) { + chain.push(p1); + add = true; + } else if (p1.count === 2 && p1 === chain[0]) { + chain.splice(0,0,p0); + add = true; + } else if (p1.count ===2 && p1 === chain.last) { + chain.push(p0); + add = true; + } + if (add) { + break; + } + } + if (!add) { + chains.push([ p0, p1 ]); + } + } + } + + for (let chain of chains) { + for (let i=0; i