Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@benningfield-group/amc-graphics

Package Overview
Dependencies
Maintainers
6
Versions
8
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@benningfield-group/amc-graphics - npm Package Compare versions

Comparing version 1.1.0 to 1.2.0

grunt/karma.js

2

build/AMC.Graphics.min.js

@@ -1,1 +0,1 @@

"use strict";angular.module("AMC.Graphics",[]),angular.module("AMC.Graphics").directive("amcMapGraphic",["$parse",function(a){return{restrict:"A",link:function(b,c,d){function e(){var a=g();a&&f.setAttributeNS("http://www.w3.org/1999/xlink","xlink:href",a)}var f=c[0],g=a(d.amcMapGraphic).bind(this,b);b.$watch(g,e),e()}}}]),angular.module("AMC.Graphics").directive("amcSvgSize",["$parse",function(a){return{restrict:"A",link:function(b,c,d){function e(){var a=i();a&&(h.setAttributeNS(null,"width",a.getWidth()),h.setAttributeNS(null,"height",a.getHeight()))}function f(){var a=i();return a?a.getWidth():null}function g(){var a=i();return a?a.getHeight():null}var h=c[0],i=a(d.amcSvgSize).bind(this,b);b.$watch(f,e),b.$watch(g,e),e()}}}]),angular.module("AMC.Graphics").directive("amcTransform",["$parse","matrixHelper",function(a,b){return{restrict:"A",link:function(c,d,e){function f(){var a=h();a&&g.setAttributeNS(null,"transform",b.matrixToString(a))}var g=d[0],h=a(e.amcTransform).bind(this,c);c.$watch(h,f),f()}}}]),angular.module("AMC.Graphics").factory("linearMath",["vectorMath",function(a){function b(){}return b.prototype.getIntersections=function(b,c,d,e){var f=a.subtract(c,b),g=a.subtract(e,d),h=a.cross(a.subtract(d,b),f),i=a.cross(f,g);if(0===h&&0===i)return d.x>=b.x&&d.x<=c.x&&e.x>=b.x&&e.x<=c.x&&d.y>=b.y&&d.y<=c.y&&e.y>=b.y&&e.y<=c.y?[d,e]:b.x>=d.x&&b.x<=e.x&&c.x>=d.x&&c.x<=e.x&&b.y>=d.y&&b.y<=e.y&&c.y>=d.y&&c.y<=e.y?[b,c]:d.x>=b.x&&d.x<=c.x&&d.y>=b.y&&d.y<=c.y?[d]:e.x>=b.x&&e.x<=c.x&&e.y>=b.y&&e.y<=c.y?[e]:[];if(0===i)return[];var j=h/i,k=a.cross(a.subtract(d,b),g)/i;return k>=0&&k<=1&&j>=0&&j<=1?[a.add(b,a.multiplyScalar(f,k))]:[]},new b}]),angular.module("AMC.Graphics").factory("matrixHelper",["$window",function(a){function b(){this._svg=a.document.createElementNS("http://www.w3.org/2000/svg","svg")}return b.prototype.verticesToString=function(a){return a.map(function(a){return a.x+","+a.y}).join(" ")},b.prototype.matrixToArray=function(a){return[a.a,a.b,a.c,a.d,a.e,a.f]},b.prototype.matrixToString=function(a,b){var c=this.matrixToArray(a).join(" ");return b?c:"matrix("+c+")"},b.prototype.createSVGMatrix=function(a,b,c,d,e,f){var g=this._svg.createSVGMatrix();return void 0!==a&&(g.a=a),void 0!==b&&(g.b=b),void 0!==c&&(g.c=c),void 0!==d&&(g.d=d),void 0!==e&&(g.e=e),void 0!==f&&(g.f=f),g},b.prototype.createSVGPoint=function(a,b){var c=this._svg.createSVGPoint();return void 0!==a&&(c.x=a),void 0!==b&&(c.y=b),c},new b}]),angular.module("AMC.Graphics").factory("pointMath",[function(){function a(){}return a.prototype.distance=function(a,b){return Math.sqrt(Math.pow(a.x-b.x,2)+Math.pow(a.y-b.y,2))},a.prototype.pointsEqual=function(a,b){return a.x===b.x&&a.y===b.y},new a}]),angular.module("AMC.Graphics").factory("polygonMath",["vectorMath","pointMath","linearMath",function(a,b,c){function d(){}return d.prototype.calculateArea=function(a){var b,c,d=0;for(b=0;b<a.length;++b)c=(b+1)%a.length,d+=a[b].x*a[c].y-a[c].x*a[b].y;return d/=2,d<0&&(d*=-1),d},d.prototype.calculateCentroid=function(a){var b,c,d=0,e=0,f=this.calculateArea(a),g={};for(b=0;b<a.length;++b)c=(b+1)%a.length,d+=(a[b].x+a[c].x)*(a[b].x*a[c].y-a[c].x*a[b].y),e+=(a[b].y+a[c].y)*(a[b].x*a[c].y-a[c].x*a[b].y);return d*=1/(6*f),e*=1/(6*f),g.x=d,g.y=e,g},d.prototype.makeVerticesClockwise=function(a){var b,c,d=0;for(b=0;b<a.length;++b)c=(b+1)%a.length,d+=a[b].x*a[c].y-a[c].x*a[b].y;d<0&&a.reverse()},d.prototype.makeVerticesStartWithMin=function(a){var b,c,d=[],e=a.length;for(c=0,b=1;b<e;++b)a[b].x<a[c].x?c=b:a[b].x===a[c].x&&a[b].y<a[c].y&&(c=b);for(b=0;b<e;++b)d.push(a[(b+c)%a.length]);for(a.length=0,b=0;b<e;++b)a.push(d[b])},d.prototype.arePolygonsAdjacent=function(b,c){function d(b,c){var d=a.subtract(b,c),e=a.transpose(d);return e.y*=-1,e}function e(b,c){var d={min:null,max:null};return b.forEach(function(b){var e=a.dot(b,c);(null===d.min||e<d.min)&&(d.min=e),(null===d.max||e>d.max)&&(d.max=e)}),d}function f(a,b){return b.max>=a.min&&a.max>=b.min}return[b,c].every(function(a){var g,h,i,j;for(g=0;g<a.length;++g)if(h=d(a[g],a[(g+1)%a.length]),i=e(b,h),j=e(c,h),!f(i,j))return!1;return!0})},d.prototype.combinePolygons=function(a,d){var e,f,g,h,i,j,k,l,m,n,o,p=0,q=0,r=null,s=[],t=0;f=a.slice(),g=d.slice(),[f,g].forEach(function(a){this.makeVerticesUnique(a),this.makeVerticesClockwise(a),this.makeVerticesStartWithMin(a)}.bind(this)),(f[0].x>g[0].x||f[0].x===g[0].x&&f[0].y>g[0].y)&&(o=f,f=g,g=o),h=f[0];do{if(n=!1,r=f[p],-1!==this.indexOf(r,s))throw new Error("Vertex already present in the combine result.");for(s.push(r),m=null,q=0;q<g.length&&!n;++q)for(i=c.getIntersections(f[p],f[(p+1)%f.length],g[q],g[(q+1)%g.length]),e=0;e<i.length;++e)-1===this.indexOf(i[e],s)&&(k=b.distance(r,i[e]),(null===m||k<j)&&(j=k,l=q,m=i[e]));null!==m&&(s.push(m),p=(l+1)%g.length,b.pointsEqual(m,g[p])&&(p=(p+1)%g.length),o=f,f=g,g=o,n=!0),n?++t:p=(p+1)%f.length}while(!b.pointsEqual(f[p],h));if(0===t)throw new Error("Polygons could not be combined because they are not adjacent.");return s},d.prototype.indexOf=function(a,c){for(var d=0;d<c.length;++d)if(b.pointsEqual(a,c[d]))return d;return-1},d.prototype.makeVerticesUnique=function(a){var b=a.filter(function(a,b,c){return this.indexOf(a,c)===b}.bind(this));a.length=0,b.forEach(function(b){a.push(b)})},new d}]),angular.module("AMC.Graphics").factory("vectorMath",[function(){function a(){}return a.prototype.subtract=function(a,b){return{x:a.x-b.x,y:a.y-b.y}},a.prototype.add=function(a,b){return{x:a.x+b.x,y:a.y+b.y}},a.prototype.multiplyScalar=function(a,b){return{x:a.x*b,y:a.y*b}},a.prototype.transpose=function(a){return{x:a.y,y:a.x}},a.prototype.dot=function(a,b){return a.x*b.x+a.y*b.y},a.prototype.cross=function(a,b){return a.x*b.y-a.y*b.x},new a}]),angular.module("AMC.Graphics").factory("ViewingTransformer",["matrixHelper",function(a){function b(b,c){this._vtm=a.createSVGMatrix(),this._wndWidth=b,this._wndHeight=c,this._aspectRatio=b/c,this._vWndWidth=b,this._vWndHeight=c}return b.prototype.getVTM=function(){return this._vtm},b.prototype.getInverseVTM=function(){return this.getVTM().inverse()},b.prototype.zoom=function(a,b,c){null!==b&&void 0!==b||(b=1/this._aspectRatio*a);var d=(this._wndWidth+a)/this._wndWidth,e=(this._wndHeight+b)/this._wndHeight;return this.scale(d,e,c)},b.prototype.scale=function(b,c,d){return d=d||{x:this._wndWidth/2,y:this._wndHeight/2},this._vtm=a.createSVGMatrix().translate(d.x,d.y).scaleNonUniform(b,c).translate(-d.x,-d.y).multiply(this._vtm),this._vWndWidth+=this._vWndWidth*(1-b),this._vWndHeight+=this._vWndHeight*(1-c),this.getVTM()},b.prototype.pan=function(b,c){var d=a.createSVGPoint(b,c);return d=d.matrixTransform(this.getInverseVTM()),this._vtm=a.createSVGMatrix().translate(-b,-c).multiply(this._vtm),this.getVTM()},b.prototype.panUp=function(a){return this.pan(0,-a)},b.prototype.panDown=function(a){return this.pan(0,a)},b.prototype.panLeft=function(a){return this.pan(-a,0)},b.prototype.panRight=function(a){return this.pan(a,0)},b.prototype.worldToWindowUnits=function(b){var c=1/this.getScaleFactor(),d=a.createSVGMatrix();return d=d.scale(c,c),b=b.matrixTransform(d)},b.prototype.getScaleFactor=function(){return this._wndWidth/this._vWndWidth},b.prototype.clone=function(){var b,c=JSON.parse(JSON.stringify(this));c._vtm=a.createSVGMatrix();for(b in this._vtm)c._vtm[b]=this._vtm[b];return c},b.prototype.restore=function(a){var b;for(b in a)this[b]=a[b];for(b in a._vtm)this._vtm[b]=a._vtm[b]},b}]);
"use strict";function _toConsumableArray(a){if(Array.isArray(a)){for(var b=0,c=Array(a.length);b<a.length;b++)c[b]=a[b];return c}return Array.from(a)}var _typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(a){return typeof a}:function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?"symbol":typeof a};angular.module("AMC.Graphics",[]),angular.module("AMC.Graphics").directive("amcMapGraphic",["$parse",function(a){return{restrict:"A",link:function(b,c,d){function e(){var a=g();a&&f.setAttributeNS("http://www.w3.org/1999/xlink","xlink:href",a)}var f=c[0],g=a(d.amcMapGraphic).bind(this,b);b.$watch(g,e),e()}}}]),angular.module("AMC.Graphics").directive("amcSvgSize",["$parse",function(a){return{restrict:"A",link:function(b,c,d){function e(){var a=i();a&&(h.setAttributeNS(null,"width",a.getWidth()),h.setAttributeNS(null,"height",a.getHeight()))}function f(){var a=i();return a?a.getWidth():null}function g(){var a=i();return a?a.getHeight():null}var h=c[0],i=a(d.amcSvgSize).bind(this,b);b.$watch(f,e),b.$watch(g,e),e()}}}]),angular.module("AMC.Graphics").directive("amcTransform",["$parse","matrixHelper",function(a,b){return{restrict:"A",link:function(c,d,e){function f(){var a=h();a&&g.setAttributeNS(null,"transform",b.matrixToString(a))}var g=d[0],h=a(e.amcTransform).bind(this,c);c.$watch(h,f),f()}}}]),angular.module("AMC.Graphics").factory("linearMath",["vectorMath","pointMath",function(a,b){function c(){}return c.prototype.getIntersections=function(c,d,e,f){if(b.lessThan(d,c)){var g=d;d=c,c=g}if(b.lessThan(f,e)){var h=f;f=e,e=h}var i=a.subtract(d,c),j=a.subtract(f,e),k=a.cross(a.subtract(e,c),i),l=a.cross(i,j);if(0===k&&0===l)return e.x>=c.x&&e.x<=d.x&&f.x>=c.x&&f.x<=d.x&&e.y>=c.y&&e.y<=d.y&&f.y>=c.y&&f.y<=d.y?[e,f]:c.x>=e.x&&c.x<=f.x&&d.x>=e.x&&d.x<=f.x&&c.y>=e.y&&c.y<=f.y&&d.y>=e.y&&d.y<=f.y?[c,d]:e.x>=c.x&&e.x<=d.x&&e.y>=c.y&&e.y<=d.y?[e]:f.x>=c.x&&f.x<=d.x&&f.y>=c.y&&f.y<=d.y?[f]:[];if(0===l)return[];var m=k/l,n=a.cross(a.subtract(e,c),j)/l;return n>=0&&n<=1&&m>=0&&m<=1?[a.add(c,a.multiplyScalar(i,n))]:[]},c.prototype.pointOnLineSegment=function(a,c,d){var e=b.distance(a,c),f=b.distance(a,d),g=b.distance(c,d);return Math.abs(e+f-g)<1e-5},new c}]),angular.module("AMC.Graphics").factory("matrixHelper",["$window",function(a){function b(){this._svg=a.document.createElementNS("http://www.w3.org/2000/svg","svg")}return b.prototype.verticesToString=function(a){return a.map(function(a){return a.x+","+a.y}).join(" ")},b.prototype.matrixToArray=function(a){return[a.a,a.b,a.c,a.d,a.e,a.f]},b.prototype.matrixToString=function(a,b){var c=this.matrixToArray(a).join(" ");return b?c:"matrix("+c+")"},b.prototype.createSVGMatrix=function(a,b,c,d,e,f){var g=this._svg.createSVGMatrix();return void 0!==a&&(g.a=a),void 0!==b&&(g.b=b),void 0!==c&&(g.c=c),void 0!==d&&(g.d=d),void 0!==e&&(g.e=e),void 0!==f&&(g.f=f),g},b.prototype.createSVGPoint=function(a,b){var c=this._svg.createSVGPoint();return void 0!==a&&(c.x=a),void 0!==b&&(c.y=b),c},b.prototype.cloneMatrix=function(a){return this.createSVGMatrix(a.a,a.b,a.c,a.d,a.e,a.f)},b.prototype.getScale=function(a){var b=this.createSVGMatrix();return b.a=a.a,b.d=a.d,b},b.prototype.getTranslation=function(a){var b=this.createSVGMatrix();return b.e=a.e,b.f=a.f,b},new b}]),angular.module("AMC.Graphics").factory("pointMath",[function(){function a(){}return a.prototype.distance=function(a,b){return Math.sqrt(Math.pow(a.x-b.x,2)+Math.pow(a.y-b.y,2))},a.prototype.pointsEqual=function(a,b){return a.x===b.x&&a.y===b.y},a.prototype.lessThan=function(a,b){return a.x===b.x?a.y<b.y:a.x<b.x},new a}]),angular.module("AMC.Graphics").factory("polygonMath",["vectorMath","pointMath","linearMath",function(a,b,c){function d(){}return d.prototype.calculateArea=function(a){var b,c,d=0;for(b=0;b<a.length;++b)c=(b+1)%a.length,d+=a[b].x*a[c].y-a[c].x*a[b].y;return d/=2,d<0&&(d*=-1),d},d.prototype.calculateCentroid=function(a){var b,c,d=0,e=0,f=this.calculateArea(a),g={};for(b=0;b<a.length;++b)c=(b+1)%a.length,d+=(a[b].x+a[c].x)*(a[b].x*a[c].y-a[c].x*a[b].y),e+=(a[b].y+a[c].y)*(a[b].x*a[c].y-a[c].x*a[b].y);return d*=1/(6*f),e*=1/(6*f),g.x=d,g.y=e,g},d.prototype.makeVerticesClockwise=function(a){var b,c,d=0;for(b=0;b<a.length;++b)c=(b+1)%a.length,d+=a[b].x*a[c].y-a[c].x*a[b].y;d<0&&a.reverse()},d.prototype.makeVerticesStartWithMin=function(a){var b,c,d=[],e=a.length;for(c=0,b=1;b<e;++b)a[b].x<a[c].x?c=b:a[b].x===a[c].x&&a[b].y<a[c].y&&(c=b);for(b=0;b<e;++b)d.push(a[(b+c)%a.length]);for(a.length=0,b=0;b<e;++b)a.push(d[b])},d.prototype.arePolygonsAdjacent=function(){function b(a,b){return[a,b].some(function(f){for(var g=0;g<f.length-1;++g){var h=c(f[g],f[(g+1)%f.length]);if(!e(d(a,h),d(b,h)))return!0}return!1})}function c(b,c){var d=a.subtract(b,c),e=a.transpose(d);return e.y*=-1,a.normalize(e)}function d(b,c){var d={min:null,max:null};return b.forEach(function(b){var e=a.dot(b,c);(null===d.min||e<d.min)&&(d.min=e),(null===d.max||e>d.max)&&(d.max=e)}),d}function e(a,b){return b.max>=a.min&&a.max>=b.min}function f(a){return g(a,0).size===a.length}function g(a,b){var c=arguments.length>2&&void 0!==arguments[2]?arguments[2]:new Set;if(!c.has(b)){var d=a[b];c.add(b);for(var e=0;e<d.length;++e)g(a,d[e],c)}return c}for(var h=this,i=arguments.length,j=Array(i),k=0;k<i;k++)j[k]=arguments[k];return 1===j.length||(this.sortPolygons(j),j=j.map(function(a){return h.triangulate(a)}).reduce(function(a,b){return a.concat(b)},[]),f(j.map(function(a){return j.reduce(function(c,d,e){return a===d||b(a,d)?c:c.concat(e)},[])})))},d.prototype.combinePolygons=function(){for(var d=this,e=[],f=0,g=0,h=0,i=void 0,j=arguments.length,k=Array(j),l=0;l<j;l++)k[l]=arguments[l];if(k.forEach(function(a){d.makeVerticesUnique(a),d.makeVerticesClockwise(a),d.makeVerticesStartWithMin(a)}),this.sortPolygons(k),i=k[0],!this.arePolygonsAdjacent.apply(this,k))throw new Error("Cannot combine polygons because they are not adjacent.");do{if(-1!==this.indexOf(i[g],e))throw new Error("Cater-cornered polygons cannot be combined.");e.push(i[g]);for(var m={dist:null,angle:null,intPoint:null,polyInd:null,vertInd:null},n=0;n<k.length;++n)if(n!==f)for(var o=k[n],p=0;p<o.length;++p)for(var q=[i[g],i[(g+1)%i.length]],r=[o[p],o[(p+1)%o.length]],s=c.getIntersections.apply(c,q.concat(r)),t=0;t<s.length;++t)if(-1===this.indexOf(s[t],e)){var u=b.distance(i[g],s[t]),v=a.normalize(a.subtract(r[1],q[0])),w=Math.atan2(-v.y,v.x);(null===m.dist||u<m.dist||u===m.dist&&w>m.angle)&&(m.dist=u,m.angle=w,m.intPoint=s[t],m.polyInd=n,m.vertInd=p)}if(null!==m.dist){f=m.polyInd,i=k[f],g=m.vertInd;var x=b.pointsEqual(m.intPoint,i[g]),y=b.pointsEqual(m.intPoint,i[(g+1)%i.length]);x||(g=(g+1)%i.length),x||y||e.push(m.intPoint),++h}else g=(g+1)%i.length}while(!b.pointsEqual(i[g],k[0][0]));if(0===h)throw new Error("Polygons could not be combined because no intersections were found.");return this.removeExtraneousVertices(e),e},d.prototype.indexOf=function(a,c){for(var d=0;d<c.length;++d)if(b.pointsEqual(a,c[d]))return d;return-1},d.prototype.makeVerticesUnique=function(a){var b=a.filter(function(a,b,c){return this.indexOf(a,c)===b}.bind(this));a.length=0,b.forEach(function(b){a.push(b)})},d.prototype.removeExtraneousVertices=function(a){if(a.length<3)return a;for(var b=0;b<a.length;++b){var d=a[b],e=a[(b+1)%a.length],f=a[(b+2)%a.length];if(c.pointOnLineSegment(e,d,f))return a.splice((b+1)%a.length,1),this.removeExtraneousVertices(a)}return a},d.prototype.sortPolygons=function(a){return a.sort(function(a,b){var c=Math.min.apply(Math,_toConsumableArray(a.map(function(a){return a.x}))),d=Math.min.apply(Math,_toConsumableArray(b.map(function(a){return a.x})));return c!==d?c-d:Math.min.apply(Math,_toConsumableArray(a.map(function(a){return a.y})))-Math.min.apply(Math,_toConsumableArray(b.map(function(a){return a.y})))})},d.prototype.getCircumference=function(a){return a.reduce(function(a,c,d,e){return a+b.distance(c,e[(d+1)%e.length])},0)},d.prototype.triangulate=function(b){function c(b){if(3===b.length){var c=[b[0],b[1],b[2]];return d.makeVerticesStartWithMin(c),b.splice(0,1),c}for(var e=function(c){var e=[b[0===c?b.length-1:c-1],b[c],b[(c+1)%b.length]],f=a.subtract(e[0],e[1]),g=a.subtract(e[2],e[1]);if(a.cross(f,g)<0&&!b.some(function(a){return-1===d.indexOf(a,e)&&d.polygonContainsPoint(e,a)}))return d.makeVerticesStartWithMin(e),b.splice(c,1),{v:e}},f=0;f<b.length;++f){var g=e(f);if("object"===(void 0===g?"undefined":_typeof(g)))return g.v}throw new Error("Failed to clip ear.")}var d=this,e=[];if(b.length<3)throw new Error("Polygon cannot be clipped because it has fewer than three vertices.");b=b.slice();do{e.push(c(b))}while(b.length>=3);return e},d.prototype.polygonContainsPoint=function(a,b){var d=Math.max.apply(Math,_toConsumableArray(a.map(function(a){return a.x}))),e=Math.min.apply(Math,_toConsumableArray(a.map(function(a){return a.x}))),f=Math.max.apply(Math,_toConsumableArray(a.map(function(a){return a.y}))),g=Math.min.apply(Math,_toConsumableArray(a.map(function(a){return a.y})));if(b.x<e)return!1;if(b.x>d)return!1;if(b.y<g)return!1;if(b.y>f)return!1;for(var h=[b,{x:d,y:b.y}],i=[],j=0;j<a.length;++j){var k=[a[j],a[(j+1)%a.length]];if(c.pointOnLineSegment(b,k[0],k[1]))return!0;var l=c.getIntersections(h[0],h[1],k[0],k[1]);l.length&&-1===this.indexOf(l[0],i)&&i.push(l[0])}return i.length%2!=0},new d}]),angular.module("AMC.Graphics").factory("vectorMath",[function(){function a(){}return a.prototype.subtract=function(a,b){return{x:a.x-b.x,y:a.y-b.y}},a.prototype.add=function(a,b){return{x:a.x+b.x,y:a.y+b.y}},a.prototype.multiplyScalar=function(a,b){return{x:a.x*b,y:a.y*b}},a.prototype.transpose=function(a){return{x:a.y,y:a.x}},a.prototype.dot=function(a,b){return a.x*b.x+a.y*b.y},a.prototype.cross=function(a,b){return a.x*b.y-a.y*b.x},a.prototype.length=function(a){return Math.sqrt(Math.pow(a.x,2)+Math.pow(a.y,2))},a.prototype.normalize=function(a){var b=this.length(a);if(0===b)throw new Error("Cannot normalize a vector of length 0.");return this.multiplyScalar(a,1/b)},new a}]),angular.module("AMC.Graphics").factory("ViewingTransformer",["matrixHelper",function(a){function b(b,c){this._vtm=a.createSVGMatrix(),this._wndWidth=b,this._wndHeight=c,this._aspectRatio=b/c}return b.prototype.getScale=function(){return a.getScale(this._vtm)},b.prototype.getTranslate=function(){return a.getTranslation(this._vtm)},b.prototype.getVTM=function(){return this._vtm},b.prototype.getInverseVTM=function(){return this.getVTM().inverse()},b.prototype.zoom=function(a,b,c){null!==b&&void 0!==b||(b=1/this._aspectRatio*a);var d=(this._wndWidth+a)/this._wndWidth,e=(this._wndHeight+b)/this._wndHeight;return this.scale(d,e,c)},b.prototype.scale=function(b,c,d){return d=d||{x:this._wndWidth/2,y:this._wndHeight/2},this._vtm=a.createSVGMatrix().translate(d.x,d.y).scaleNonUniform(b,c).translate(-d.x,-d.y).multiply(this._vtm),this.getVTM()},b.prototype.pan=function(b,c){return this._vtm=a.createSVGMatrix().translate(-b,-c).multiply(this._vtm),this.getVTM()},b.prototype.panUp=function(a){return this.pan(0,-a)},b.prototype.panDown=function(a){return this.pan(0,a)},b.prototype.panLeft=function(a){return this.pan(-a,0)},b.prototype.panRight=function(a){return this.pan(a,0)},b.prototype.worldToWindowUnits=function(a){return a.matrixTransform(this.getInverseVTM())},b.prototype.clone=function(){var b=JSON.parse(JSON.stringify(this));return b._vtm=a.cloneMatrix(this._vtm),b},b.prototype.restore=function(b){for(var c in b)this[c]=b[c];this._vtm=a.cloneMatrix(b._vtm)},b}]);

@@ -17,3 +17,4 @@ /**

!script.match(/build/) &&
!script.match(/grunt/i);
!script.match(/grunt/i) &&
!script.match(/Spec.js/);
}));

@@ -20,0 +21,0 @@

@@ -1,14 +0,13 @@

module.exports = function(grunt)
{
module.exports = function(grunt) {
'use strict';
var scripts = require('./grunt/scriptGarner')();
var buildFile = __dirname + '/build/AMC.Graphics.min.js';
const scripts = require('./grunt/scriptGarner')();
const buildFile = `${__dirname}/build/AMC.Graphics.min.js`;
grunt.initConfig
({
grunt.initConfig({
jshint: require('./grunt/jshint')(grunt, scripts),
uglify: require('./grunt/uglify')(grunt, buildFile, buildFile),
concat: require('./grunt/concat')(grunt, scripts, buildFile),
babel: require('./grunt/babel')(grunt, buildFile)
babel: require('./grunt/babel')(grunt, buildFile),
karma: require('./grunt/karma')(grunt)
});

@@ -15,0 +14,0 @@

{
"name": "@benningfield-group/amc-graphics",
"version": "1.1.0",
"version": "1.2.0",
"description": "Graphics math for AMC.",

@@ -13,6 +13,8 @@ "main": "AMC.Graphics.js",

"devDependencies": {
"babel-preset-es2015": "^6.24.1",
"glob": "~5.0.6",
"angular": "~1.6.9",
"angular-mocks": "~1.6.9",
"babel-preset-es2015": "~6.24.1",
"glob": "~7.1.2",
"grunt": "~0.4.5",
"grunt-babel": "^6.0.0",
"grunt-babel": "~6.0.0",
"grunt-cli": "~0.1.13",

@@ -22,5 +24,9 @@ "grunt-contrib-concat": "~0.5.1",

"grunt-contrib-uglify": "~0.9.1",
"grunt-filerev": "~2.3.1"
"grunt-filerev": "~2.3.1",
"grunt-karma": "~2.0.0",
"jasmine-core": "~3.1.0",
"karma": "~2.0.0",
"karma-jasmine": "~1.1.1"
},
"repository": "git@bitbucket.org:benningfieldsuperitteam/graphics.git"
}

@@ -6,4 +6,4 @@ /**

.factory('linearMath',
['vectorMath',
function(vectorMath)
['vectorMath', 'pointMath',
function(vectorMath, pointMath)
{

@@ -30,7 +30,22 @@ 'use strict';

{
// Make sure the points are ordered left to right, top to bottom.
if (pointMath.lessThan(p2, p1))
{
let hold = p2;
p2 = p1;
p1 = hold;
}
if (pointMath.lessThan(q2, q1))
{
let hold = q2;
q2 = q1;
q1 = hold;
}
// Line p runs from p1 to p1 + r.
var r = vectorMath.subtract(p2, p1);
const r = vectorMath.subtract(p2, p1);
// Line q runs from p2 to p2 + s.
var s = vectorMath.subtract(q2, q1);
const s = vectorMath.subtract(q2, q1);

@@ -44,4 +59,4 @@ // The two lines intersect if we can find t and u such that

// u = (q1 - p1) x r / (r x s)
var uNum = vectorMath.cross(vectorMath.subtract(q1, p1), r);
var uDenom = vectorMath.cross(r, s);
const uNum = vectorMath.cross(vectorMath.subtract(q1, p1), r);
const uDenom = vectorMath.cross(r, s);

@@ -90,7 +105,7 @@ // If both denominators are -0 then the segments are collinear.

// Devision is safe - uDenom cannot be 0.
var u = uNum / uDenom;
const u = uNum / uDenom;
// t is solved for in the same manner as u.
// t = (q1 - p1) x s / (r x s)
var t = vectorMath.cross(vectorMath.subtract(q1, p1), s) / uDenom;
const t = vectorMath.cross(vectorMath.subtract(q1, p1), s) / uDenom;

@@ -111,4 +126,22 @@ // If u and t are both between 0 and 1, the lines intersect (case 3),

/**
* Check if the point p is on the line segment created by points {q1, q2}.
* @param p The point.
* @param q1 The first point of the line segment.
* @param q2 The second point of the line segment.
*/
LinearMath.prototype.pointOnLineSegment = function(p, q1, q2)
{
const distP_Q1 = pointMath.distance(p, q1);
const distP_Q2 = pointMath.distance(p, q2);
const distQ1_Q1 = pointMath.distance(q1, q2);
const EPSILON = 1e-5;
// If dist(p, q1) + dist(p, q2) === dist(q1, q2) the p falls on the segment,
// otherwise the three points form a triangle.
return Math.abs(distP_Q1 + distP_Q2 - distQ1_Q1) < EPSILON;
};
return new LinearMath();
}]);

@@ -96,2 +96,39 @@ /**

/**
* Clone an SVGMatrix.
*/
MatrixHelper.prototype.cloneMatrix = function(matrix)
{
return this.createSVGMatrix(
matrix.a, matrix.b, matrix.c, matrix.d, matrix.e, matrix.f);
};
/**
* Given a matrix that is scaled and/or translated, extract the scale portion.
* Note that this will not work if the matrix is rotated.
*/
MatrixHelper.prototype.getScale = function(matrix)
{
var scale = this.createSVGMatrix();
scale.a = matrix.a;
scale.d = matrix.d;
return scale;
};
/**
* Given a matrix that is scaled and/or translated, extract the translation
* part.
*/
MatrixHelper.prototype.getTranslation = function(matrix)
{
var translation = this.createSVGMatrix();
translation.e = matrix.e;
translation.f = matrix.f;
return translation;
};
// Single instance.

@@ -98,0 +135,0 @@ return new MatrixHelper();

@@ -33,4 +33,17 @@ /**

/**
* Check if p1 is less than p2.
* @param p1 The first point to compare.
* @param p2 The second point to compare.
*/
PointMath.prototype.lessThan = function(p1, p2)
{
if (p1.x === p2.x)
return p1.y < p2.y;
return p1.x < p2.x;
};
return new PointMath();
}]);

@@ -18,3 +18,4 @@ /**

* Calculate the area of the polygon made up of the passed-in vertices.
* @param vertices The vertices of a polygon, for which the centroid will be calculated.
* @param vertices The vertices of a polygon, for which the area will be
* calculated.
*/

@@ -48,3 +49,4 @@ PolygonMath.prototype.calculateArea = function(vertices)

* Calculate the centroid of the polygon made up of the passed-in vertices.
* @param vertices The vertices of a polygon, for which the centroid will be calculated.
* @param vertices The vertices of a polygon, for which the centroid will be
* calculated.
*/

@@ -81,3 +83,3 @@ PolygonMath.prototype.calculateCentroid = function(vertices)

* @param vertices An array of vertices. If they are CCW, they are
* rearranged such that they are clockwise.
* rearranged such that they are clockwise.
*/

@@ -147,15 +149,60 @@ PolygonMath.prototype.makeVerticesClockwise = function(vertices)

/**
* Check if the polygons made up by verts1 and verts2 are adjacent (touching or overlapping).
* This is an implementation of the Separating Axis Theorem, described on
* wikipedia: https://en.wikipedia.org/wiki/Hyperplane_separation_theorem
* @param verts1 The first array of polygons to check for adjacenty.
* @param verts2 The second array of polygons, to compare to verts1.
* Check if the polygons are adjacent (touching or overlapping).
* This is an implementation of the Hyperplane Separation Theorem, described
* on wikipedia: https://en.wikipedia.org/wiki/Hyperplane_separation_theorem
* @param polygons An arbitrary number of polygons, each of which should be
* an array of vertices.
*/
PolygonMath.prototype.arePolygonsAdjacent = function(verts1, verts2)
PolygonMath.prototype.arePolygonsAdjacent = function(...polygons)
{
// Create a an axis that is parallel to the vector pointing from v_2 to v_1.
if (polygons.length === 1)
return true;
// The check is performed left to right, top to bottom.
this.sortPolygons(polygons);
// The Hyperplan Separation Theorem only works on convex polygons, so each
// polygon is triangulated (converted into triangles), then the triangles
// are checked for adjacency.
polygons = polygons
.map(polygon => this.triangulate(polygon))
.reduce((polygons, polygon) => polygons.concat(polygon), []);
// Create an adjacency map for each polygon.
const adjacencyMatrix = polygons
.map(curPoly => polygons
.reduce((adjacencies, otherPoly, i) => {
if (curPoly === otherPoly || hasLineBetween(curPoly, otherPoly))
return adjacencies;
return adjacencies.concat(i);
}, []));
// Now check if the polygons are fully connected using a graph search.
return isFullyConnected(adjacencyMatrix);
// Check if there exists a line between two convex polygons.
function hasLineBetween(poly1, poly2)
{
return [poly1, poly2].some(poly => {
for (let i = 0; i < poly.length - 1; ++i)
{
const axis = createAxis(poly[i], poly[(i + 1) % poly.length]);
const extremum_b1 = project(poly1, axis);
const extremum_b2 = project(poly2, axis);
if (!overlap(extremum_b1, extremum_b2))
return true;
}
return false;
});
}
// Create a normalized axis that is perpendicular to the vector pointing
// from v_2 to v_1.
function createAxis(v_1, v_2)
{
// This is a vector pointing in the direction from v_2 to v_1.
var v_21 = vectorMath.subtract(v_1, v_2);
const v_21 = vectorMath.subtract(v_1, v_2);

@@ -166,6 +213,6 @@ // This is a perpendicular line to v_21, which is the axis upon which each

// |1 0| |y| |1 0| | x|
var axis = vectorMath.transpose(v_21);
const axis = vectorMath.transpose(v_21);
axis.y *= -1;
return axis;
return vectorMath.normalize(axis);
}

@@ -177,3 +224,3 @@

{
var extremum =
const extremum =
{

@@ -186,12 +233,9 @@ min: null,

{
var projected = vectorMath.dot(vert, axis);
const projected = vectorMath.dot(vert, axis);
if (extremum.min === null || projected < extremum.min)
{
extremum.min = projected;
}
if (extremum.max === null || projected > extremum.max)
{
extremum.max = projected;
}
});

@@ -208,121 +252,118 @@

return [verts1, verts2].every(function(verts)
// Check if a graph (adjacency matrix) is fully connected.
function isFullyConnected(adjacencyMatrix)
{
var i, axis, extremum_b1, extremum_b2;
const connections = traverse(adjacencyMatrix, 0);
for (i = 0; i < verts.length; ++i)
return connections.size === adjacencyMatrix.length;
}
// Recursively traverse a graph and return a unique set of connections.
function traverse(adjacencyMatrix, startNodeInd, connections = new Set())
{
if (!connections.has(startNodeInd))
{
axis = createAxis(verts[i], verts[(i + 1) % verts.length]);
extremum_b1 = project(verts1, axis);
extremum_b2 = project(verts2, axis);
const node = adjacencyMatrix[startNodeInd];
// If the projected lines of the polygons do not overlap there exists a line between
// them and the polygons are not colliding. These booths can't be combined.
if (!overlap(extremum_b1, extremum_b2))
{
return false;
}
connections.add(startNodeInd);
for (let i = 0; i < node.length; ++i)
traverse(adjacencyMatrix, node[i], connections);
}
return true;
});
return connections;
}
};
/**
* Combine the two polygons into a single polygon. If the polygons are not
* colliding then an exception is raised.
* @param verts1 The array of vertices for the first polygon.
* @param verts2 The array of vertices for the second polygon.
* Combine (union) multiple polygons into a single polygon.
* @param polygons Multiple polygons, each of which should be an array of
* vertices.
*/
PolygonMath.prototype.combinePolygons = function(verts1, verts2)
PolygonMath.prototype.combinePolygons = function(...polygons)
{
var i;
let result = [];
let curPolyInd = 0;
let curVertInd = 0;
let switchCount = 0;
let curPoly;
// This is the polygon that is being traversed, and the other polygon.
var curVerts;
var otherVerts;
// This is the vertex that iterations starts with. It's used to indicate
// when the polygon has been completely traversed.
var startVert;
// This is the index of the current vertex in curVerts.
var curVertInd = 0;
var otherVertInd = 0;
// The current vertex being iterated over.
var curVert = null;
// These are the resulting vertices (the combination of verts1 and verts2).
var result = [];
// The total number of times the polygons switched due to intersections.
var switchCount = 0;
// This holds the intersection of two edges of the two polygons. Only the
// closest one is of interest.
var intersections, intersectionDist, holdDist,
nearestIntersectionInd, nearestIntersection;
// Whether or not the current and other verts need to be switched due to an intersection.
var switched;
// For swapping curVerts and otherVerts..
var swapHold;
// Make sure verts1 and verts2 are clockwise. This also ensures that the
// first vertex is the top-left one.
curVerts = verts1.slice();
otherVerts = verts2.slice();
[curVerts, otherVerts].forEach(function(verts)
// The polygons must be "normalized." E.g., no duplicates, with points in
// clockwise orientation, and staring with the minimum (top-left) point.
polygons.forEach(polygon =>
{
this.makeVerticesUnique(verts);
this.makeVerticesClockwise(verts);
this.makeVerticesStartWithMin(verts);
}.bind(this));
this.makeVerticesUnique(polygon);
this.makeVerticesClockwise(polygon);
this.makeVerticesStartWithMin(polygon);
});
// Iteration must start with the polygon that is on the left (or if left is the same, on the top).
if (curVerts[0].x > otherVerts[0].x || curVerts[0].x === otherVerts[0].x && curVerts[0].y > otherVerts[0].y)
{
swapHold = curVerts; curVerts = otherVerts; otherVerts = swapHold;
}
// Combinations are done left to right, top to bottom.
this.sortPolygons(polygons);
curPoly = polygons[0];
startVert = curVerts[0];
// All the polygons must be adjacent (touching or overlapping).
if (!this.arePolygonsAdjacent(...polygons))
throw new Error('Cannot combine polygons because they are not adjacent.');
// Loop, making an outer perimeter around all the polygons (similar to a
// convex hull), until reaching the first vertex again.
do
{
switched = false;
// The resulting polygon must be a unique set of vertices. If the
// current vertex has already been added to the result set, then the
// polygons are cater-cornered which is considered to be an error.
if (this.indexOf(curPoly[curVertInd], result) !== -1)
throw new Error('Cater-cornered polygons cannot be combined.');
// Add the current vert to the resulting polygon.
curVert = curVerts[curVertInd];
result.push(curPoly[curVertInd]);
// This should never fire. If it does there is a bug in this combine routine.
if (this.indexOf(curVert, result) !== -1)
throw new Error('Vertex already present in the combine result.');
// Find the nearest intersection between the current edge and all other
// polygons.
const nearestIntersection = {
dist : null,
angle : null,
intPoint : null,
polyInd : null,
vertInd : null
};
result.push(curVert);
for (let p = 0; p < polygons.length; ++p)
{
if (p === curPolyInd)
continue;
// Iterate over the other polygon and check if the current edge intersects
// with any edges of the other polygon.
nearestIntersection = null;
const otherPoly = polygons[p];
for (otherVertInd = 0; otherVertInd < otherVerts.length && !switched; ++otherVertInd)
{
intersections = linearMath.getIntersections(
curVerts[curVertInd], curVerts[(curVertInd + 1) % curVerts.length],
otherVerts[otherVertInd], otherVerts[(otherVertInd + 1) % otherVerts.length]);
for (let otherVertInd = 0; otherVertInd < otherPoly.length; ++otherVertInd)
{
// Intersections between the current edge of the current polygon and
// the current edge of the other polygon.
const curEdge = [curPoly[curVertInd], curPoly[(curVertInd + 1) % curPoly.length]];
const otherEdge = [otherPoly[otherVertInd], otherPoly[(otherVertInd + 1) % otherPoly.length]];
const intersections = linearMath.getIntersections(...curEdge, ...otherEdge);
// There could be 0, 1, or 2 intersections. Keep track of the index of
// the nearest intersection that is a new point (e.g. not in result).
for (i = 0; i < intersections.length; ++i)
{
if (this.indexOf(intersections[i], result) === -1)
// There can be 0, 1, or 2 intersection points, but only the nearest
// unvisited one is part of the resulting polygon.
for (let i = 0; i < intersections.length; ++i)
{
holdDist = pointMath.distance(curVert, intersections[i]);
if (this.indexOf(intersections[i], result) === -1)
{
const dist = pointMath.distance(curPoly[curVertInd], intersections[i]);
if (nearestIntersection === null || holdDist < intersectionDist)
{
intersectionDist = holdDist;
nearestIntersectionInd = otherVertInd;
nearestIntersection = intersections[i];
// When two intersections have the same point, the one with the
// largest angle is used (going from the tip of beginning of the
// first edge to the end of the other). Keep in mind that in the
// browser, y grows down (hence the y negation).
const vec = vectorMath.normalize(vectorMath.subtract(otherEdge[1], curEdge[0]));
const angle = Math.atan2(-vec.y, vec.x);
if ((nearestIntersection.dist === null || dist < nearestIntersection.dist) ||
dist === nearestIntersection.dist && angle > nearestIntersection.angle)
{
nearestIntersection.dist = dist;
nearestIntersection.angle = angle;
nearestIntersection.intPoint = intersections[i];
nearestIntersection.polyInd = p;
nearestIntersection.vertInd = otherVertInd;
}
}

@@ -333,44 +374,47 @@ }

if (nearestIntersection !== null)
// If there was an intersection between the current edge and another
// polygon edge.
if (nearestIntersection.dist !== null)
{
// New intersection. The intersection point needs to be added to
// the resulting polygon.
result.push(nearestIntersection);
// Move to the intersecting polygon, which will now be traversed
// clockwise.
curPolyInd = nearestIntersection.polyInd;
curPoly = polygons[curPolyInd];
curVertInd = nearestIntersection.vertInd;
// Note that traversal is always clockwise around the polygons. When
// there is an intersection, always turn left of the current heading.
// That is, if heading east, move north. If heading south, head
// east. "Left" is always to the second point along otherVerts
// current edge.
curVertInd = (nearestIntersectionInd + 1) % otherVerts.length;
// The intersection may occur at the beginning of the intersected
// polygon's edge, in the middle, or at the end. If it's at the
// beginning, traversal starts at that vertex, otherwise it starts at
// the proceeding one.
const isStart = pointMath.pointsEqual(nearestIntersection.intPoint, curPoly[curVertInd]);
const isEnd = pointMath.pointsEqual(nearestIntersection.intPoint, curPoly[(curVertInd + 1) % curPoly.length]);
if (pointMath.pointsEqual(nearestIntersection, otherVerts[curVertInd]))
{
// The intersection was at the end of the edge. Move to the next point.
curVertInd = (curVertInd + 1) % otherVerts.length;
}
if (!isStart)
curVertInd = (curVertInd + 1) % curPoly.length;
// Swap the curVerts and otherVerts;
swapHold = curVerts; curVerts = otherVerts; otherVerts = swapHold;
switched = true;
}
// If the intersection point is not one of the endpoints of the
// interseted edge (i.e. it's in the middle somewhere), the
// intersection point is part of the resulting polygon.
if (!isStart && !isEnd)
result.push(nearestIntersection.intPoint);
// The polygons were not switched, so move to the next vertex in the current polygon.
if (!switched)
{
curVertInd = (curVertInd + 1) % curVerts.length;
++switchCount;
}
else
{
++switchCount;
// No intersections with the current edge. Move to the next edge on the
// current polygon.
curVertInd = (curVertInd + 1) % curPoly.length;
}
}
// Loop until the starting vertex is reached again.
while (!pointMath.pointsEqual(curVerts[curVertInd], startVert));
while (!pointMath.pointsEqual(curPoly[curVertInd], polygons[0][0]));
// This should not fire, but if it does then there is an error in the
// algorithm. No intersections between any polygons were found.
if (switchCount === 0)
{
throw new Error('Polygons could not be combined because they are not adjacent.');
}
throw new Error('Polygons could not be combined because no intersections were found.');
// Remove an extraneous vertices from the polygon.
this.removeExtraneousVertices(result);
return result;

@@ -416,4 +460,211 @@ };

/**
* Remove any extraneous points from a polygon. For example, two duplicate
* points in succession, identical first and last points, or multiple points
* that fall on a straight line.
* @param verts An array of vertices that make up a polygon.
*/
PolygonMath.prototype.removeExtraneousVertices = function(verts)
{
// Fewer than three points is not a polygon.
if (verts.length < 3)
return verts;
for (let i = 0; i < verts.length; ++i)
{
// If p is on the line segment formed by {q1, q2}, then it's an
// uneccessary point and should be removed.
let q1 = verts[i];
let p = verts[(i + 1) % verts.length];
let q2 = verts[(i + 2) % verts.length];
if (linearMath.pointOnLineSegment(p, q1, q2))
{
verts.splice((i + 1) % verts.length, 1);
return this.removeExtraneousVertices(verts);
}
}
return verts;
};
/**
* Sort a series of polygons, left to right, top to bottom.
* @param polygons One or more polygons, each of which is an array of
* vertices.
*/
PolygonMath.prototype.sortPolygons = function(polygons)
{
return polygons
.sort((l, r) =>
{
const lMinX = Math.min(...l.map(vert => vert.x));
const rMinX = Math.min(...r.map(vert => vert.x));
if (lMinX !== rMinX)
return lMinX - rMinX;
const lMinY = Math.min(...l.map(vert => vert.y));
const rMinY = Math.min(...r.map(vert => vert.y));
return lMinY - rMinY;
});
};
/**
* Get the circumference of a polygon.
* @param polygon A polygon, for which the circumference will be calculated.
*/
PolygonMath.prototype.getCircumference = function(polygon)
{
return polygon
.reduce((circ, vert, i, polygon) =>
{
return circ + pointMath.distance(vert, polygon[(i + 1) % polygon.length]);
}, 0);
};
/**
* Decompose a polygon into a series of triangles by ear clipping. The
* polygon's vertices must be normalized (clockwise with no extraneous
* verts).
* https://en.wikipedia.org/wiki/Polygon_triangulation
* @param polygon The polygon, which is an array of vertices, to decompose.
*/
PolygonMath.prototype.triangulate = function(polygon)
{
const self = this;
const triangles = [];
if (polygon.length < 3)
throw new Error('Polygon cannot be clipped because it has fewer than three vertices.');
polygon = polygon.slice();
do
{
triangles.push(clipEar(polygon));
}
while (polygon.length >= 3);
return triangles;
// Clip one ear off of the polygon, removing the starting vertex.
function clipEar(polygon)
{
if (polygon.length === 3) {
const ear = [
polygon[0],
polygon[1],
polygon[2]
];
self.makeVerticesStartWithMin(ear);
polygon.splice(0, 1);
return ear;
}
for (let i = 0; i < polygon.length; ++i)
{
// These three points form a candidate ear.
const ear =
[
polygon[i === 0 ? polygon.length - 1 : i - 1],
polygon[i],
polygon[(i + 1) % polygon.length]
];
// The mid point, ear[1], is either a convex point or a reflexive one.
// The determinate of a matrix formed by the two edges of the ear that
// are on the polygon, p1->p0 and p1->p2, will be positive if the mid
// point is reflexive, meaning that the line {p0, p1} is not fully
// contained by the polygon. (I.e. p0, p1, p2 is counter clockwise.)
const v_p10 = vectorMath.subtract(ear[0], ear[1]);
const v_p12 = vectorMath.subtract(ear[2], ear[1]);
const det = vectorMath.cross(v_p10, v_p12);
if (det < 0)
{
// If any point on the polygon other than those making up the ear is
// contained by the ear, then the ear cannot be removed.
const hasPointInside = polygon
.some(point =>
self.indexOf(point, ear) === -1 &&
self.polygonContainsPoint(ear, point));
if (!hasPointInside)
{
// Good ear. Normalize it.
self.makeVerticesStartWithMin(ear);
// Remove the point from the polygon.
polygon.splice(i, 1);
return ear;
}
}
}
throw new Error('Failed to clip ear.');
}
};
/**
* Check if a point is inside a polygon. A point on an edge or at a vertex
* is considered to be contained.
* @param polygon The polygon.
* @param point The point.
*/
PolygonMath.prototype.polygonContainsPoint = function(polygon, point)
{
const maxX = Math.max(...polygon.map(p => p.x));
const minX = Math.min(...polygon.map(p => p.x));
const maxY = Math.max(...polygon.map(p => p.y));
const minY = Math.min(...polygon.map(p => p.y));
if (point.x < minX) return false; // Left of polygon.
if (point.x > maxX) return false; // Right of polygon.
if (point.y < minY) return false; // Above polygon (window coords).
if (point.y > maxY) return false; // Below polygon.
// A horizontal ray from the point to the right-most side of the
// polygon.
const ray =
[
point,
{x: maxX, y: point.y}
];
const uniqueInts = [];
for (let i = 0; i < polygon.length; ++i)
{
const segment = [
polygon[i],
polygon[(i + 1) % polygon.length]
];
// If the point lies directly on one of the sides of the polygon,
// the point is contained.
if (linearMath.pointOnLineSegment(point, segment[0], segment[1]))
return true;
// Otherwise the ray intercepts the side 0 or 1 times.
const segInts = linearMath
.getIntersections(ray[0], ray[1], segment[0], segment[1]);
// The intercept must be unqiue, because the point may intercept the
// triangle at a vertex (a vertex is shared by two segments).
if (segInts.length && this.indexOf(segInts[0], uniqueInts) === -1)
uniqueInts.push(segInts[0]);
}
// If there is an even number of intercepts, the point is outside,
// otherwise it's contained.
return (uniqueInts.length % 2) !== 0;
};
return new PolygonMath();
}]);

@@ -14,3 +14,4 @@ /**

/**
* Subtract vec2 from vec1 (vec1 - vec2)
* Subtract vec2 from vec1 (vec1 - vec2) and return a vector from
* vec2 to vec1.
* @param vec1 The first vector, with x and y properties.

@@ -66,3 +67,7 @@ * @param vec2 The second vector, with x and y properties.

* 2D cross product of two vectors, as defined by Wolfram Math World (it's an
* analog to 3D cross product but, unlike 3D, this returns a scalar).
* analog to 3D cross product but, unlike 3D, this returns a scalar). The
* result is the determinant of the 2x2 matrix formed by [vec1 vec2], and
* it's also the signed area of the parallelogram for which vec1 and vec2 are
* the sides. If the area is negative, then the rotation from vec1 to vec2
* is clockwise (in window coords, where +y is down and +x is right).
* http://mathworld.wolfram.com/CrossProduct.html

@@ -77,4 +82,27 @@ * @param vec1 The first vector, with x and y properties.

/**
* Get the length of a vector.
* @param vec The vector to get the length of.
*/
VectorMath.prototype.length = function(vec)
{
return Math.sqrt(Math.pow(vec.x, 2) + Math.pow(vec.y, 2));
};
/**
* Normalize the vector (make it unit length).
* @param vec The vector to normalize.
*/
VectorMath.prototype.normalize = function(vec)
{
const len = this.length(vec);
if (len === 0)
throw new Error('Cannot normalize a vector of length 0.');
return this.multiplyScalar(vec, 1 / len);
};
return new VectorMath();
}]);

@@ -22,14 +22,26 @@ /**

// World window variables.
this._wndWidth = wndWidth;
this._wndHeight = wndHeight;
// Viewport (virtual).
this._wndWidth = wndWidth;
this._wndHeight = wndHeight;
this._aspectRatio = wndWidth / wndHeight;
this._vWndWidth = wndWidth;
this._vWndHeight = wndHeight;
}
/**
* Get the current viewing transformation matrix. It's by reference!
* Get the scale portion of the VTM.
*/
ViewingTransformer.prototype.getScale = function()
{
return matrixHelper.getScale(this._vtm);
};
/**
* Get the translation portion of the VTM.
*/
ViewingTransformer.prototype.getTranslate = function()
{
return matrixHelper.getTranslation(this._vtm);
};
/**
* Get the current viewing transformation matrix by reference.
*/
ViewingTransformer.prototype.getVTM = function()

@@ -86,2 +98,3 @@ {

// Scale in at the origin.
this._vtm = matrixHelper

@@ -94,5 +107,2 @@ .createSVGMatrix()

this._vWndWidth += this._vWndWidth * (1 - xFactor);
this._vWndHeight += this._vWndHeight * (1 - yFactor);
return this.getVTM();

@@ -117,9 +127,2 @@ };

{
// This is a point (x, y) units from (0, 0) in view space.
var topLeft = matrixHelper.createSVGPoint(x, y);
// Transform the point from view space to windows space, which is the
// inverse of the VTM. This gives the new viewport x and y.
topLeft = topLeft.matrixTransform(this.getInverseVTM());
this._vtm = matrixHelper

@@ -176,25 +179,6 @@ .createSVGMatrix()

{
// Figure out how to transform the window units to world units. For
// example, if the floor plan is zoomed by a factor of 1.10 (110% of it's
// native size), then the movement amount should by 90% of the window units
// (1 / scaleFactor).
var invScaleFactor = 1 / this.getScaleFactor();
var scale = matrixHelper.createSVGMatrix();
// Scale the x and y units
scale = scale.scale(invScaleFactor, invScaleFactor);
pt = pt.matrixTransform(scale);
return pt;
return pt.matrixTransform(this.getInverseVTM());
};
/**
* Get the scale factor (the amount the booth is scaled).
*/
ViewingTransformer.prototype.getScaleFactor = function()
{
return this._wndWidth / this._vWndWidth;
};
/**
* Clone this object.

@@ -206,11 +190,5 @@ * @return {Object} A clone that can be restore()'d.

var vtMomento = JSON.parse(JSON.stringify(this));
var prop;
vtMomento._vtm = matrixHelper.createSVGMatrix();
vtMomento._vtm = matrixHelper.cloneMatrix(this._vtm);
for (prop in this._vtm)
{
vtMomento._vtm[prop] = this._vtm[prop];
}
return vtMomento;

@@ -226,13 +204,6 @@ };

{
var prop;
for (prop in momento)
{
for (var prop in momento)
this[prop] = momento[prop];
}
for (prop in momento._vtm)
{
this._vtm[prop] = momento._vtm[prop];
}
this._vtm = matrixHelper.cloneMatrix(momento._vtm);
};

@@ -239,0 +210,0 @@

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc