Polygon(raw).each(function(p, c, n, i) { var isect = segseg(c, c.point, n, n.point); if (isect && isect !== true) { // This means that the offset is self-intersecting // find where and use that as the current vec instead var isect2 = segseg( p, c, n, this.point(i+2) ); if (isect2 && isect2 !== false) { isect = isect2; } this.remove(c); c.set(isect[0], isect[1]); } ret.push(c) });
orig.each(function(prev, current, next, idx) { var normal = Vec2(delta, 0); var pdiff = current.subtract(prev, true); var ndiff = next.subtract(current, true); var pnormal = normal.clone().rotate(normal.angleTo(pdiff)).skew(); var nnormal = normal.clone().rotate(normal.angleTo(ndiff)).skew(); if (delta < 0) { pnormal.negate(); nnormal.negate(); } var a = pnormal.add(prev, true) var b = pnormal.add(current, true); var c = nnormal.add(current, true); var d = nnormal.add(next, true); var cornerAngle = toTAU(current.subtract(prev, true).angleTo(next.subtract(current, true))); if ((delta < 0 && cornerAngle - TAU/2 < 0) || (delta > 0 && cornerAngle - TAU/2 > 0)) { !skip && ret.push(b); ret = ret.concat(cornerFn.call(orig, current, b, c, delta, cornerAngle) || []); skip = false; } else { var i = segseg(a, b, c, d); if (i && i!==true) { console.log(i); ret.push(Vec2.fromArray(i)); skip = true; } } });
containsPolygon : function(subject) { if (isArray(subject)) { subject = new Polygon(subject); } for (var i=0; i<subject.points.length; i++) { if (!this.containsPoint(subject.points[i])) { return false; } } for (var i=0; i<this.points.length; i++) { var outer = this.line(i); for (var j=0; j<subject.points.length; j++) { var inner = subject.line(j); var isect = segseg(outer[0], outer[1], inner[0], inner[1]); if (isect && isect !== true) { return false; } } } return true; },
this.lines(function(s2, e2, i2) { if (!s2.equal(e) && !s2.equal(s) && !e2.equal(s) && !e2.equal(e) && i+1 < i2) { var isect = segseg(s, e, s2, e2); // self-intersection if (isect && isect !== true) { var vec = Vec2.fromArray(isect); // TODO: wow, this is inneficient but is crucial for creating the // tree later on. vec.s = i + (s.subtract(vec, true).length() / s.subtract(e, true).length()) vec.b = i2 + (s2.subtract(vec, true).length() / s2.subtract(e2, true).length()) ret.push(vec); } } });
O.each(function(p, c, n, i) { var n2 = (i<O.length-2) ? O.point(i+2) : O.point((i+2) % O.length); var i2 = segseg(p, c, n, n2); if (i2 && i2 !== true) { var i2v = v.fromArray(i2); i++; var c1 = closest(p.point, c.point, i2v, i2v); var d1 = i2v.distance(c1[0]); ctx.strokeStyle = '#fff'; ctx.beginPath(); ctx.moveTo(i2v.x, i2v.y); ctx.lineTo(c1[0].x, c1[0].y); ctx.closePath(); ctx.stroke(); var c2 = closest(n.point, n2.point, i2v, i2v); var d2 = i2v.distance(c2[0]); ctx.strokeStyle = '#fff'; ctx.beginPath(); ctx.moveTo(i2v.x, i2v.y); ctx.lineTo(c2[0].x, c2[0].y); ctx.closePath(); ctx.stroke(); var c3 = closest(c.point, n.point, i2v, i2v); var d3 = i2v.distance(c3[0]); ctx.strokeStyle = '#fff'; ctx.beginPath(); ctx.moveTo(i2v.x, i2v.y); ctx.lineTo(c3[0].x, c3[0].y); ctx.closePath(); ctx.stroke(); var skipIsect = 0; if (skipIsect < 2) { out.push(i2v); circle(i2v, r, '#333'); } circle(v(i2[0], i2[1]), r, '#333'); } else if (!i2 && polygon.closestPointTo(c).distance(c) >= (r - .0001)) { out.push(c); } });
poly.each(function(ip, ic) { var i = segseg(prev, current, ip, ic); if (i && i!==true) { i = Vec2.fromArray(i); if (ip.equal(i) || ic.equal(i)) { return; } var key = i.x + ':'+ i.y; if (!seen[key]) { f.push(i); seen[key] = true; // Exit early when we've found a new intersection return false; } } });
lines.forEach(function(line, idx) { var prev = (idx === 0) ? lines[lines.length-1] : lines[idx-1]; var i = segseg(line[0], line[1], prev[0], prev[1]); i && i!==true && ret.push(Vec2.fromArray(i)); });
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){if(typeof require!=="undefined"){var Vec2=require("vec2");var segseg=require("segseg")}var isArray=function(a){return Object.prototype.toString.call(a)==="[object Array]"};var defined=function(a){return typeof a!=="undefined"};var definedOr=function(a,defaultValue){return defined(a)?a:defaultValue};var finite=function(a){return a!==Infinity&&a!==-Infinity};var det=function(x1,y1,x2,y2){return x1*y2-y1*x2};function Line2(slope,yintercept,x2,y2){this._listeners=[];if(!(this instanceof Line2)){return new Line2(slope,yintercept,x2,y2)}if(defined(x2)&&defined(y2)){return Line2.fromPoints(slope,yintercept,x2,y2)}else{if(defined(slope)){this.slope(slope)}if(defined(yintercept)){this.yintercept(yintercept)}}}Line2.prototype._yintercept=null;Line2.prototype._xintercept=null;Line2.prototype._slope=null;Line2.prototype.change=function(fn){if(typeof fn==="function"){this._listeners.push(fn);return fn}};Line2.prototype.ignore=function(fn){if(!fn){this._listeners=[]}else{this._listeners=this._listeners.filter(function(a){return a!==fn})}};Line2.prototype.notify=function(fn){var fns=this._listeners,l=fns.length;for(var i=0;i<l;i++){fns[i](this)}};Line2.prototype.yintercept=function(val){if(defined(val)){if(finite(val)){val=Vec2.clean(val)}if(this._yintercept!==val){this._yintercept=val;if(!this.isHorizontal()){this._xintercept=this.solveForX(0)}this.notify()}}return definedOr(this._yintercept,null)};Line2.prototype.xintercept=function(val){if(defined(val)){if(finite(val)){val=Vec2.clean(val)}if(this._xintercept!==val){if(!this.isVertical()){var diff=this._xintercept-val;this._yintercept-=diff*-this._slope}this._xintercept=val;this.notify()}}return definedOr(this._xintercept,null)};Line2.prototype.slope=function(val){if(defined(val)){if(finite(val)){val=Vec2.clean(val)}if(this._slope!==val){var old=this._slope;this._slope=val;if(old!==null){var x=this.solveForX(0);if(!finite(x)){x=null}this._xintercept=x}this.notify()}}return definedOr(this._slope,null)};Line2.prototype.intersectSegment=function(x1,y1,x2,y2){var dx=x2-x1;var dy=y2-y1;var lx1,ly1,lx2,ly2;var horizontal=this.isHorizontal();var vertical=this.isVertical();if(dx===0){if(vertical){return x1===this.xintercept()}}else if(dy===0){if(horizontal){return y1===this.yintercept()}}else{if(dy/dx===this.slope()){return y1===this.solveForY(x1)}}if(x1>x2){lx1=x2-10;lx2=x1+10}else{lx1=x1-10;lx2=x2+10}var isect;if(this.isHorizontal()){y=this.yintercept();isect=segseg(lx1,y,lx2,y,x1,y1,x2,y2)}else if(this.isVertical()){if(y1>y2){ly1=y2-10;ly2=y1+10}else{ly1=y1-10;ly2=y2+10}var x=this.xintercept();isect=segseg(x,ly1,x,ly2,x1,y1,x2,y2)}else{ly1=this.solveForY(lx1);ly2=this.solveForY(lx2);isect=segseg(lx1,ly1,lx2,ly2,x1,y1,x2,y2)}if(isect&&isect!==true){return Vec2.fromArray(isect)}return isect};Line2.prototype.createPerpendicular=function(vec){if(this.isVertical()){return new Line2(0,vec.y)}else if(this.isHorizontal()){var l=new Line2;l.xintercept(vec.x);l.slope(Infinity);return l}else{var perpSlope=-1/this.slope();return new Line2(perpSlope,vec.y-perpSlope*vec.x)}};Line2.prototype.intersectCircle=function(vec,radius){var r2=radius*radius,slope=this.slope(),yintercept=this.yintercept(),f,g,v1,v2;if(this.isHorizontal()){f=1;g=0}else if(this.isVertical()){slope=radius;yintercept=r2;f=0;g=slope}else{f=1/slope;g=1}var x0=this.isVertical()?this.xintercept():1;var y0=yintercept+slope;var f2=f*f;var g2=g*g;var tmp=f*(vec.y-y0)-g*(vec.x-x0);tmp*=tmp;var den=f2+g2;var discriminant=Math.sqrt(r2*(f2+g2)-tmp);if(isNaN(discriminant)){return[]}discriminant/=den;var num=f*(vec.x-x0)+g*(vec.y-y0);var t1=num/den+discriminant;var t2=num/den-discriminant;v1=new Vec2(x0+t1*f,y0+t1*g);v2=new Vec2(x0+t2*f,y0+t2*g);var ret=[v1];if(!v1.equal(v2)){ret.push(v2)}return ret};Line2.prototype.solveForX=function(y){if(this.isVertical()){return this.xintercept()}else{return(y-this.yintercept())/this.slope()}};Line2.prototype.solveForY=function(x){if(this.isHorizontal()){return this.yintercept()}else{return this.slope()*x+this.yintercept()}};Line2.prototype.intersect=function(line,y1,x2,y2){if(defined(y1)&&defined(y2)||defined(line.end)){return this.intersectSegment(line,y1,x2,y2)}var s1=this.slope();var s2=line.slope();if(s1===s2){return this.yintercept()===line.yintercept()&&this.xintercept()===line.xintercept()}if(finite(s1)&&finite(s2)){if(this.isHorizontal()){return new Vec2(line.solveForX(this.yintercept()),this.yintercept())}if(line.isHorizontal()){return new Vec2(this.solveForX(line.yintercept()),line.yintercept())}var x1=line.solveForX(-1);y1=line.solveForY(x1);x2=line.solveForX(1);y2=line.solveForY(x2);var x3=this.solveForX(-1);var y3=this.solveForY(x3);var x4=this.solveForX(1);var y4=this.solveForY(x4);var a=det(x1,y1,x2,y2);var b=det(x3,y3,x4,y4);var xnum=det(a,x1-x2,b,x3-x4);var ynum=det(a,y1-y2,b,y3-y4);var den=det(x1-x2,y1-y2,x3-x4,y3-y4);return Vec2(xnum/den,ynum/den)}else{var slope,yi,x=this.xintercept()||line.xintercept();if(!finite(s1)){slope=s2;yi=line.yintercept()}else{slope=s1;yi=this.yintercept()}if(slope!==0){return Vec2(x,x*slope+yi)}else{return Vec2(x,yi)}}};Line2.fromPoints=function(x1,y1,x2,y2){if(isArray(y1)){y2=y1[1];x2=y1[0]}else if(defined(y1)&&defined(y1.x)&&defined(y1.y)){y2=y1.y;x2=y1.x}if(isArray(x1)){y1=x1[1];x1=x1[0]}else if(defined(x1)&&defined(x1.x)&&defined(x1.y)){y1=x1.y;x1=x1.x}var line=new Line2;var slope=(y2-y1)/(x2-x1);line.slope(slope);if(line.isHorizontal()){line.yintercept(y1)}else if(line.isVertical()){line.xintercept(x2)}else{line.yintercept(y1-slope*x1)}return line};Line2.prototype.isHorizontal=function(){return!this.slope()};Line2.prototype.isVertical=function(){return!finite(this.slope())};Line2.prototype.closestPointTo=function(vec){var yi=this.yintercept();var xi=this.xintercept();var s=this.slope();if(this.isHorizontal()){return Vec2(vec.x,yi)}else if(this.isVertical()){return Vec2(xi,vec.y)}else{return this.intersect(this.createPerpendicular(vec))}};Line2.prototype.containsPoint=function(vec,y){var x=vec;if(!defined(y)){y=vec.y;x=vec.x}if(this.isHorizontal()){return y===this.yintercept()}else if(this.isVertical()){return x===this.xintercept()}else{return y===this.solveForY(x)}};if(typeof module!=="undefined"&&typeof module.exports=="object"){module.exports=Line2}if(typeof window!=="undefined"){window.Line2=window.Line2||Line2}},{segseg:2,vec2:3}],2:[function(require,module,exports){function segseg(x1,y1,x2,y2,x3,y3,x4,y4){if(arguments.length===4){var p1=x1;var p2=y1;var p3=x2;var p4=y2;if(p1.length&&p1.length===2){x1=p1[0];y1=p1[1];x2=p2[0];y2=p2[1];x3=p3[0];y3=p3[1];x4=p4[0];y4=p4[1]}else{x1=p1.x;y1=p1.y;x2=p2.x;y2=p2.y;x3=p3.x;y3=p3.y;x4=p4.x;y4=p4.y}}var a1,a2,b1,b2,c1,c2;var r1,r2,r3,r4;var denom,offset;var x,y;a1=y2-y1;b1=x1-x2;c1=x2*y1-x1*y2;r3=a1*x3+b1*y3+c1;r4=a1*x4+b1*y4+c1;if(r3!==0&&r4!==0&&(r3>=0&&r4>=0||r3<0&&r4<0)){return}a2=y4-y3;b2=x3-x4;c2=x4*y3-x3*y4;r1=a2*x1+b2*y1+c2;r2=a2*x2+b2*y2+c2;if(r1!==0&&r2!==0&&(r1>=0&&r2>=0||r1<0&&r2<0)){return}denom=a1*b2-a2*b1;if(denom===0){return true}offset=denom<0?-denom/2:denom/2;x=b1*c2-b2*c1;y=a2*c1-a1*c2;return[(x<0?x:x)/denom,(y<0?y:y)/denom]}if(typeof module!=="undefined"&&module.exports){module.exports=segseg}if(typeof window!=="undefined"){window.segseg=window.segseg||segseg}},{}],3:[function(require,module,exports){(function inject(clean,precision,undef){function Vec2(x,y){if(!(this instanceof Vec2)){return new Vec2(x,y)}if("object"===typeof x&&x){this.y=x.y||0;this.x=x.x||0;return}this.x=Vec2.clean(x||0);this.y=Vec2.clean(y||0)}Vec2.prototype={change:function(fn){if(fn){if(this.observers){this.observers.push(fn)}else{this.observers=[fn]}}else if(this.observers){for(var i=this.observers.length-1;i>=0;i--){this.observers[i](this)}}return this},ignore:function(fn){if(this.observers){var o=this.observers,l=o.length;while(l--){o[l]===fn&&o.splice(l,1)}}return this},set:function(x,y,silent){if("number"!=typeof x){silent=y;y=x.y;x=x.x}if(this.x===x&&this.y===y)return this;this.x=Vec2.clean(x);this.y=Vec2.clean(y);if(silent!==false)return this.change()},zero:function(){return this.set(0,0)},clone:function(){return new Vec2(this.x,this.y)},negate:function(returnNew){if(returnNew){return new Vec2(-this.x,-this.y)}else{return this.set(-this.x,-this.y)}},add:function(vec2,returnNew){if(!returnNew){this.x+=vec2.x;this.y+=vec2.y;return this.change()}else{return new Vec2(this.x+vec2.x,this.y+vec2.y)}},subtract:function(vec2,returnNew){if(!returnNew){this.x-=vec2.x;this.y-=vec2.y;return this.change()}else{return new Vec2(this.x-vec2.x,this.y-vec2.y)}},multiply:function(vec2,returnNew){var x,y;if("number"!==typeof vec2){x=vec2.x;y=vec2.y}else{x=y=vec2}if(!returnNew){return this.set(this.x*x,this.y*y)}else{return new Vec2(this.x*x,this.y*y)}},rotate:function(r,inverse,returnNew){var x=this.x,y=this.y,cos=Math.cos(r),sin=Math.sin(r),rx,ry;inverse=inverse?-1:1;rx=cos*x-inverse*sin*y;ry=inverse*sin*x+cos*y;if(returnNew){return new Vec2(rx,ry)}else{return this.set(rx,ry)}},length:function(){var x=this.x,y=this.y;return Math.sqrt(x*x+y*y)},lengthSquared:function(){var x=this.x,y=this.y;return x*x+y*y},distance:function(vec2){var x=this.x-vec2.x;var y=this.y-vec2.y;return Math.sqrt(x*x+y*y)},normalize:function(returnNew){var length=this.length();var invertedLength=length<Number.MIN_VALUE?0:1/length;if(!returnNew){return this.set(this.x*invertedLength,this.y*invertedLength)}else{return new Vec2(this.x*invertedLength,this.y*invertedLength)}},equal:function(v,w){if(w===undef){w=v.y;v=v.x}return Vec2.clean(v)===this.x&&Vec2.clean(w)===this.y},abs:function(returnNew){var x=Math.abs(this.x),y=Math.abs(this.y);if(returnNew){return new Vec2(x,y)}else{return this.set(x,y)}},min:function(v,returnNew){var tx=this.x,ty=this.y,vx=v.x,vy=v.y,x=tx<vx?tx:vx,y=ty<vy?ty:vy;if(returnNew){return new Vec2(x,y)}else{return this.set(x,y)}},max:function(v,returnNew){var tx=this.x,ty=this.y,vx=v.x,vy=v.y,x=tx>vx?tx:vx,y=ty>vy?ty:vy;if(returnNew){return new Vec2(x,y)}else{return this.set(x,y)}},clamp:function(low,high,returnNew){var ret=this.min(high,true).max(low);if(returnNew){return ret}else{return this.set(ret.x,ret.y)}},lerp:function(vec,amount){return this.add(vec.subtract(this,true).multiply(amount),true)},skew:function(){return new Vec2(-this.y,this.x)},dot:function(b){return Vec2.clean(this.x*b.x+b.y*this.y)},perpDot:function(b){return Vec2.clean(this.x*b.y-this.y*b.x)},angleTo:function(vec){return Math.atan2(this.perpDot(vec),this.dot(vec))},divide:function(vec2,returnNew){var x,y;if("number"!==typeof vec2){x=vec2.x;y=vec2.y}else{x=y=vec2}if(x===0||y===0){throw new Error("division by zero")}if(isNaN(x)||isNaN(y)){throw new Error("NaN detected")}if(returnNew){return new Vec2(this.x/x,this.y/y)}return this.set(this.x/x,this.y/y)},isPointOnLine:function(start,end){return(start.y-this.y)*(start.x-end.x)===(start.y-end.y)*(start.x-this.x)},toArray:function(){return[this.x,this.y]},fromArray:function(array){return this.set(array[0],array[1])},toJSON:function(){return{x:this.x,y:this.y}},toString:function(){return"("+this.x+", "+this.y+")"}};Vec2.fromArray=function(array){return new Vec2(array[0],array[1])};Vec2.precision=precision||8;var p=Math.pow(10,Vec2.precision);Vec2.clean=clean||function(val){if(isNaN(val)){throw new Error("NaN detected")}if(!isFinite(val)){throw new Error("Infinity detected")}if(Math.round(val)===val){return val}return Math.round(val*p)/p};Vec2.inject=inject;if(!clean){Vec2.fast=inject(function(k){return k});if(typeof module!=="undefined"&&typeof module.exports=="object"){module.exports=Vec2}else{window.Vec2=window.Vec2||Vec2}}return Vec2})()},{}]},{},[1]);
Line2.prototype.intersectSegment = function(x1, y1, x2, y2) { var dx = x2 - x1; var dy = y2 - y1; var lx1, ly1, lx2, ly2; var horizontal = this.isHorizontal(); var vertical = this.isVertical(); // vertical if (dx === 0) { if (vertical) { return x1 === this.xintercept(); } // horizontal } else if (dy === 0) { // parallel if (horizontal) { return y1 === this.yintercept(); } // diagonal } else { if (dy/dx === this.slope()) { return y1 === this.solveForY(x1); } } if (x1 > x2) { lx1 = x2-10; lx2 = x1+10; } else { lx1 = x1-10; lx2 = x2+10; } var isect; if (this.isHorizontal()) { y = this.yintercept(); isect = segseg(lx1, y, lx2, y, x1, y1, x2, y2); } else if (this.isVertical()) { if (y1 > y2) { ly1 = y2-10; ly2 = y1+10; } else { ly1 = y1-10; ly2 = y2+10; } var x = this.xintercept(); isect = segseg(x, ly1, x, ly2, x1, y1, x2, y2); } else { ly1 = this.solveForY(lx1); ly2 = this.solveForY(lx2); isect = segseg(lx1, ly1, lx2, ly2, x1, y1, x2, y2); } if (isect && isect !== true) { return Vec2.fromArray(isect); } return isect; };