| 1234 |
- // Geometry library. Licensed under the MPL v2 license.
- // (c) 2011-2014 client IO (http://client.io)
- (function(root,factory){if(typeof define==="function"&&define.amd){define([],factory)}else if(typeof exports==="object"){module.exports=factory()}else{root.g=factory()}})(this,function(){var math=Math;var abs=math.abs;var cos=math.cos;var sin=math.sin;var sqrt=math.sqrt;var mmin=math.min;var mmax=math.max;var atan=math.atan;var atan2=math.atan2;var acos=math.acos;var round=math.round;var floor=math.floor;var PI=math.PI;var random=math.random;var toDeg=function(rad){return 180*rad/PI%360};var toRad=function(deg){return deg%360*PI/180};var snapToGrid=function(val,gridSize){return gridSize*Math.round(val/gridSize)};var normalizeAngle=function(angle){return angle%360+(angle<0?360:0)};function point(x,y){if(!(this instanceof point))return new point(x,y);var xy;if(y===undefined&&Object(x)!==x){xy=x.split(x.indexOf("@")===-1?" ":"@");this.x=parseInt(xy[0],10);this.y=parseInt(xy[1],10)}else if(Object(x)===x){this.x=x.x;this.y=x.y}else{this.x=x;this.y=y}}point.prototype={toString:function(){return this.x+"@"+this.y},adhereToRect:function(r){if(r.containsPoint(this)){return this}this.x=mmin(mmax(this.x,r.x),r.x+r.width);this.y=mmin(mmax(this.y,r.y),r.y+r.height);return this},theta:function(p){p=point(p);var y=-(p.y-this.y);var x=p.x-this.x;var PRECISION=10;var rad=y.toFixed(PRECISION)==0&&x.toFixed(PRECISION)==0?0:atan2(y,x);if(rad<0){rad=2*PI+rad}return 180*rad/PI},distance:function(p){return line(this,p).length()},manhattanDistance:function(p){return abs(p.x-this.x)+abs(p.y-this.y)},offset:function(dx,dy){this.x+=dx||0;this.y+=dy||0;return this},magnitude:function(){return sqrt(this.x*this.x+this.y*this.y)||.01},update:function(x,y){this.x=x||0;this.y=y||0;return this},round:function(decimals){this.x=decimals?this.x.toFixed(decimals):round(this.x);this.y=decimals?this.y.toFixed(decimals):round(this.y);return this},normalize:function(len){var s=(len||1)/this.magnitude();this.x=s*this.x;this.y=s*this.y;return this},difference:function(p){return point(this.x-p.x,this.y-p.y)},bearing:function(p){return line(this,p).bearing()},toPolar:function(o){o=o&&point(o)||point(0,0);var x=this.x;var y=this.y;this.x=sqrt((x-o.x)*(x-o.x)+(y-o.y)*(y-o.y));this.y=toRad(o.theta(point(x,y)));return this},rotate:function(o,angle){angle=(angle+360)%360;this.toPolar(o);this.y+=toRad(angle);var p=point.fromPolar(this.x,this.y,o);this.x=p.x;this.y=p.y;return this},move:function(ref,distance){var theta=toRad(point(ref).theta(this));return this.offset(cos(theta)*distance,-sin(theta)*distance)},changeInAngle:function(dx,dy,ref){return point(this).offset(-dx,-dy).theta(ref)-this.theta(ref)},equals:function(p){return this.x===p.x&&this.y===p.y},snapToGrid:function(gx,gy){this.x=snapToGrid(this.x,gx);this.y=snapToGrid(this.y,gy||gx);return this}};point.fromPolar=function(r,angle,o){o=o&&point(o)||point(0,0);var x=abs(r*cos(angle));var y=abs(r*sin(angle));var deg=normalizeAngle(toDeg(angle));if(deg<90)y=-y;else if(deg<180){x=-x;y=-y}else if(deg<270)x=-x;return point(o.x+x,o.y+y)};point.random=function(x1,x2,y1,y2){return point(floor(random()*(x2-x1+1)+x1),floor(random()*(y2-y1+1)+y1))};function line(p1,p2){if(!(this instanceof line))return new line(p1,p2);this.start=point(p1);this.end=point(p2)}line.prototype={toString:function(){return this.start.toString()+" "+this.end.toString()},length:function(){return sqrt(this.squaredLength())},squaredLength:function(){var x0=this.start.x;var y0=this.start.y;var x1=this.end.x;var y1=this.end.y;return(x0-=x1)*x0+(y0-=y1)*y0},midpoint:function(){return point((this.start.x+this.end.x)/2,(this.start.y+this.end.y)/2)},intersection:function(l){var pt1Dir=point(this.end.x-this.start.x,this.end.y-this.start.y);var pt2Dir=point(l.end.x-l.start.x,l.end.y-l.start.y);var det=pt1Dir.x*pt2Dir.y-pt1Dir.y*pt2Dir.x;var deltaPt=point(l.start.x-this.start.x,l.start.y-this.start.y);var alpha=deltaPt.x*pt2Dir.y-deltaPt.y*pt2Dir.x;var beta=deltaPt.x*pt1Dir.y-deltaPt.y*pt1Dir.x;if(det===0||alpha*det<0||beta*det<0){return null}if(det>0){if(alpha>det||beta>det){return null}}else{if(alpha<det||beta<det){return null}}return point(this.start.x+alpha*pt1Dir.x/det,this.start.y+alpha*pt1Dir.y/det)},bearing:function(){var lat1=toRad(this.start.y);var lat2=toRad(this.end.y);var lon1=this.start.x;var lon2=this.end.x;var dLon=toRad(lon2-lon1);var y=sin(dLon)*cos(lat2);var x=cos(lat1)*sin(lat2)-sin(lat1)*cos(lat2)*cos(dLon);var brng=toDeg(atan2(y,x));var bearings=["NE","E","SE","S","SW","W","NW","N"];var index=brng-22.5;if(index<0)index+=360;index=parseInt(index/45);return bearings[index]}};function rect(x,y,w,h){if(!(this instanceof rect))return new rect(x,y,w,h);if(y===undefined){y=x.y;w=x.width;h=x.height;x=x.x}this.x=x;this.y=y;this.width=w;this.height=h}rect.prototype={toString:function(){return this.origin().toString()+" "+this.corner().toString()},origin:function(){return point(this.x,this.y)},corner:function(){return point(this.x+this.width,this.y+this.height)},topRight:function(){return point(this.x+this.width,this.y)},bottomLeft:function(){return point(this.x,this.y+this.height)},center:function(){return point(this.x+this.width/2,this.y+this.height/2)},intersect:function(r){var myOrigin=this.origin();var myCorner=this.corner();var rOrigin=r.origin();var rCorner=r.corner();if(rCorner.x<=myOrigin.x||rCorner.y<=myOrigin.y||rOrigin.x>=myCorner.x||rOrigin.y>=myCorner.y)return false;return true},sideNearestToPoint:function(p){p=point(p);var distToLeft=p.x-this.x;var distToRight=this.x+this.width-p.x;var distToTop=p.y-this.y;var distToBottom=this.y+this.height-p.y;var closest=distToLeft;var side="left";if(distToRight<closest){closest=distToRight;side="right"}if(distToTop<closest){closest=distToTop;side="top"}if(distToBottom<closest){closest=distToBottom;side="bottom"}return side},containsPoint:function(p){p=point(p);if(p.x>=this.x&&p.x<=this.x+this.width&&p.y>=this.y&&p.y<=this.y+this.height){return true}return false},containsRect:function(r){var nr=rect(r).normalize();var W=nr.width;var H=nr.height;var X=nr.x;var Y=nr.y;var w=this.width;var h=this.height;if((w|h|W|H)<0){return false}var x=this.x;var y=this.y;if(X<x||Y<y){return false}w+=x;W+=X;if(W<=X){if(w>=x||W>w)return false}else{if(w>=x&&W>w)return false}h+=y;H+=Y;if(H<=Y){if(h>=y||H>h)return false}else{if(h>=y&&H>h)return false}return true},pointNearestToPoint:function(p){p=point(p);if(this.containsPoint(p)){var side=this.sideNearestToPoint(p);switch(side){case"right":return point(this.x+this.width,p.y);case"left":return point(this.x,p.y);case"bottom":return point(p.x,this.y+this.height);case"top":return point(p.x,this.y)}}return p.adhereToRect(this)},intersectionWithLineFromCenterToPoint:function(p,angle){p=point(p);var center=point(this.x+this.width/2,this.y+this.height/2);var result;if(angle)p.rotate(center,angle);var sides=[line(this.origin(),this.topRight()),line(this.topRight(),this.corner()),line(this.corner(),this.bottomLeft()),line(this.bottomLeft(),this.origin())];var connector=line(center,p);for(var i=sides.length-1;i>=0;--i){var intersection=sides[i].intersection(connector);if(intersection!==null){result=intersection;break}}if(result&&angle)result.rotate(center,-angle);return result},moveAndExpand:function(r){this.x+=r.x;this.y+=r.y;this.width+=r.width;this.height+=r.height;return this},round:function(decimals){this.x=decimals?this.x.toFixed(decimals):round(this.x);this.y=decimals?this.y.toFixed(decimals):round(this.y);this.width=decimals?this.width.toFixed(decimals):round(this.width);this.height=decimals?this.height.toFixed(decimals):round(this.height);return this},normalize:function(){var newx=this.x;var newy=this.y;var newwidth=this.width;var newheight=this.height;if(this.width<0){newx=this.x+this.width;newwidth=-this.width}if(this.height<0){newy=this.y+this.height;newheight=-this.height}this.x=newx;this.y=newy;this.width=newwidth;this.height=newheight;return this}};function ellipse(c,a,b){if(!(this instanceof ellipse))return new ellipse(c,a,b);c=point(c);this.x=c.x;this.y=c.y;this.a=a;this.b=b}ellipse.prototype={toString:function(){return point(this.x,this.y).toString()+" "+this.a+" "+this.b},bbox:function(){return rect(this.x-this.a,this.y-this.b,2*this.a,2*this.b)},intersectionWithLineFromCenterToPoint:function(p,angle){p=point(p);if(angle)p.rotate(point(this.x,this.y),angle);var dx=p.x-this.x;var dy=p.y-this.y;var result;if(dx===0){result=this.bbox().pointNearestToPoint(p);if(angle)return result.rotate(point(this.x,this.y),-angle);return result}var m=dy/dx;var mSquared=m*m;var aSquared=this.a*this.a;var bSquared=this.b*this.b;var x=sqrt(1/(1/aSquared+mSquared/bSquared));x=dx<0?-x:x;var y=m*x;result=point(this.x+x,this.y+y);if(angle)return result.rotate(point(this.x,this.y),-angle);return result}};var bezier={curveThroughPoints:function(points){var controlPoints=this.getCurveControlPoints(points);var path=["M",points[0].x,points[0].y];for(var i=0;i<controlPoints[0].length;i++){path.push("C",controlPoints[0][i].x,controlPoints[0][i].y,controlPoints[1][i].x,controlPoints[1][i].y,points[i+1].x,points[i+1].y)}return path},getCurveControlPoints:function(knots){var firstControlPoints=[];var secondControlPoints=[];var n=knots.length-1;var i;if(n==1){firstControlPoints[0]=point((2*knots[0].x+knots[1].x)/3,(2*knots[0].y+knots[1].y)/3);secondControlPoints[0]=point(2*firstControlPoints[0].x-knots[0].x,2*firstControlPoints[0].y-knots[0].y);return[firstControlPoints,secondControlPoints]}var rhs=[];for(i=1;i<n-1;i++){rhs[i]=4*knots[i].x+2*knots[i+1].x}rhs[0]=knots[0].x+2*knots[1].x;rhs[n-1]=(8*knots[n-1].x+knots[n].x)/2;var x=this.getFirstControlPoints(rhs);for(i=1;i<n-1;++i){rhs[i]=4*knots[i].y+2*knots[i+1].y}rhs[0]=knots[0].y+2*knots[1].y;rhs[n-1]=(8*knots[n-1].y+knots[n].y)/2;var y=this.getFirstControlPoints(rhs);for(i=0;i<n;i++){firstControlPoints.push(point(x[i],y[i]));if(i<n-1){secondControlPoints.push(point(2*knots[i+1].x-x[i+1],2*knots[i+1].y-y[i+1]))}else{secondControlPoints.push(point((knots[n].x+x[n-1])/2,(knots[n].y+y[n-1])/2))}}return[firstControlPoints,secondControlPoints]},getFirstControlPoints:function(rhs){var n=rhs.length;var x=[];var tmp=[];var b=2;x[0]=rhs[0]/b;for(var i=1;i<n;i++){tmp[i]=1/b;b=(i<n-1?4:3.5)-tmp[i];x[i]=(rhs[i]-x[i-1])/b}for(i=1;i<n;i++){x[n-i-1]-=tmp[n-i]*x[n-i]}return x}};var scale={linear:function(domain,range,value){var domainSpan=domain[1]-domain[0];var rangeSpan=range[1]-range[0];return(value-domain[0])/domainSpan*rangeSpan+range[0]||0}};return{toDeg:toDeg,toRad:toRad,snapToGrid:snapToGrid,normalizeAngle:normalizeAngle,point:point,line:line,rect:rect,ellipse:ellipse,bezier:bezier,scale:scale}});
|