UNPKG

voronator

Version:

Compute the Voronoi diagram of a set of two-dimensional points.

4 lines (3 loc) 11.6 kB
// https://github.com/observablehq/voronator Version 1.1.0. Copyright 2018 Observable, Inc. // https://github.com/mapbox/delaunator Version 1.0.5. Copyright 2017, Mapbox, Inc. !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e(t.voronator={})}(this,function(t){"use strict";class e{constructor(t){this.voronoi=t,this.triangles=[],this.v0=null,this.vn=null}_connect(t,e){const{triangles:i}=this;if(e<0)0===i.length&&i.push([t]);else{for(let n=i.length,s=0;s<n;++s){let h=i[s];if(h[0]===e){for(let e=s+1;e<n;++e){let n=i[e];if(n[n.length-1]===t)return i.splice(e,1),void(i[s]=h=n.concat(h))}return void h.unshift(t)}if(h[h.length-1]===t){for(let t=s+1;t<n;++t){let n=i[t];if(n[0]===e)return i.splice(t,1),void(i[s]=h=h.concat(n))}return void h.push(e)}}i.push([t,e])}}_points(){const{triangles:t,voronoi:{circumcenters:e}}=this;if(null===t)return null;const i=new Float64Array(2*t.length);for(let n=0,s=t.length;n<s;++n){const s=2*n,h=2*t[n];i[s]=e[h],i[s+1]=e[h+1]}return i}render(t){const{v0:e,vn:i}=this;let n;if(null!==(n=this._points())&&null!==(n=this.voronoi._clip(n,e,i))){t.moveTo(n[0],n[1]);for(let e=2,i=n.length;e<i;e+=2)t.lineTo(n[e],n[e+1]);t.closePath()}}contains(t,e){const s=this._points();return null!==s&&(null===this.v0?i(s,t,e):n(s,this.v0,this.vn,t,e))}}function i(t,e,i){const n=t.length;let s,h,r=t[n-2],l=t[n-1];for(let o=0;o<n;o+=2)if(s=r,h=l,((r=t[o])-s)*(i-h)<((l=t[o+1])-h)*(e-s))return!1;return!0}function n(t,[e,i],[n,s],h,r){const l=t.length;let o,a,c=t[0],u=t[1];if((o+e-h)*(u-r)<(a+i-r)*(c-h))return!1;for(let e=2;e<l;e+=2)if(o=c,a=u,c=t[e],(o-h)*((u=t[e+1])-r)<(a-r)*(c-h))return!1;return!((o-h)*(u+s-r)<(a-r)*(c+n-h))}var s=r,h=r;function r(t,e,i){e||(e=g),i||(i=m);for(var n=1/0,s=1/0,h=-1/0,r=-1/0,d=this.coords=[],v=this.ids=new Uint32Array(t.length),y=0;y<t.length;y++){var _=t[y],p=e(_),w=i(_);v[y]=y,d[2*y]=p,d[2*y+1]=w,p<n&&(n=p),w<s&&(s=w),p>h&&(h=p),w>r&&(r=w)}var T,b,k,M=(n+h)/2,z=(s+r)/2,E=1/0;for(y=0;y<t.length;y++){var A=l(M,z,d[2*y],d[2*y+1]);A<E&&(T=y,E=A)}for(E=1/0,y=0;y<t.length;y++)y!==T&&(A=l(d[2*T],d[2*T+1],d[2*y],d[2*y+1]))<E&&A>0&&(b=y,E=A);var S=1/0;for(y=0;y<t.length;y++)if(y!==T&&y!==b){var j=a(d[2*T],d[2*T+1],d[2*b],d[2*b+1],d[2*y],d[2*y+1]);j<S&&(k=y,S=j)}if(S===1/0)throw new Error("No Delaunay triangulation exists for this input.");if(o(d[2*T],d[2*T+1],d[2*b],d[2*b+1],d[2*k],d[2*k+1])<0){var F=b;b=k,k=F}var I=d[2*T],L=d[2*T+1],K=d[2*b],P=d[2*b+1],D=d[2*k],H=d[2*k+1],U=function(t,e,i,n,s,h){var r=(i-=t)*i+(n-=e)*n,l=(s-=t)*s+(h-=e)*h,o=i*h-n*s;return{x:t+.5*(h*r-n*l)/o,y:e+.5*(i*l-s*r)/o}}(I,L,K,P,D,H);for(this._cx=U.x,this._cy=U.y,function t(e,i,n,s,h,r){var l,o,a;if(s-n<=20)for(l=n+1;l<=s;l++){for(a=e[l],o=l-1;o>=n&&f(i,e[o],a,h,r)>0;)e[o+1]=e[o--];e[o+1]=a}else{var c=n+s>>1;for(o=s,x(e,c,l=n+1),f(i,e[n],e[s],h,r)>0&&x(e,n,s),f(i,e[l],e[s],h,r)>0&&x(e,l,s),f(i,e[n],e[l],h,r)>0&&x(e,n,l),a=e[l];;){do{l++}while(f(i,e[l],a,h,r)<0);do{o--}while(f(i,e[o],a,h,r)>0);if(o<l)break;x(e,l,o)}e[n+1]=e[o],e[o]=a,s-l+1>=o-n?(t(e,i,l,s,h,r),t(e,i,n,o-1,h,r)):(t(e,i,n,o-1,h,r),t(e,i,l,s,h,r))}}(v,d,0,v.length-1,U.x,U.y),this._hashSize=Math.ceil(Math.sqrt(t.length)),this._hash=[],y=0;y<this._hashSize;y++)this._hash[y]=null;var q=this.hull=c(d,T);this._hashEdge(q),q.t=0,q=c(d,b,q),this._hashEdge(q),q.t=1,q=c(d,k,q),this._hashEdge(q),q.t=2;var B,C,N=2*t.length-5,O=this.triangles=new Uint32Array(3*N),V=this.halfedges=new Int32Array(3*N);this.trianglesLen=0,this._addTriangle(T,b,k,-1,-1,-1);for(var G=0;G<v.length;G++)if(p=d[2*(y=v[G])],w=d[2*y+1],!(p===B&&w===C||(B=p,C=w,p===I&&w===L||p===K&&w===P||p===D&&w===H))){var J,Q=this._hashKey(p,w),R=Q;do{J=this._hash[R],R=(R+1)%this._hashSize}while((!J||J.removed)&&R!==Q);for(q=J;o(p,w,q.x,q.y,q.next.x,q.next.y)>=0;)if((q=q.next)===J)throw new Error("Something is wrong with the input points.");var W=q===J,X=this._addTriangle(q.i,y,q.next.i,-1,-1,q.t);q.t=X,(q=c(d,y,q)).t=this._legalize(X+2),q.prev.prev.t===V[X+1]&&(q.prev.prev.t=X+2);for(var Y=q.next;o(p,w,Y.x,Y.y,Y.next.x,Y.next.y)<0;)X=this._addTriangle(Y.i,y,Y.next.i,Y.prev.t,-1,Y.t),Y.prev.t=this._legalize(X+2),this.hull=u(Y),Y=Y.next;if(W)for(Y=q.prev;o(p,w,Y.prev.x,Y.prev.y,Y.x,Y.y)<0;)X=this._addTriangle(Y.prev.i,y,Y.i,-1,Y.t,Y.prev.t),this._legalize(X+2),Y.prev.t=X,this.hull=u(Y),Y=Y.prev;this._hashEdge(q),this._hashEdge(q.prev)}this.triangles=O.subarray(0,this.trianglesLen),this.halfedges=V.subarray(0,this.trianglesLen)}function l(t,e,i,n){var s=t-i,h=e-n;return s*s+h*h}function o(t,e,i,n,s,h){return(n-e)*(s-i)-(i-t)*(h-n)}function a(t,e,i,n,s,h){var r=(i-=t)*i+(n-=e)*n,l=(s-=t)*s+(h-=e)*h;if(0===r||0===l)return 1/0;var o=i*h-n*s;if(0===o)return 1/0;var a=.5*(h*r-n*l)/o,c=.5*(i*l-s*r)/o;return a*a+c*c}function c(t,e,i){var n={i:e,x:t[2*e],y:t[2*e+1],t:0,prev:null,next:null,removed:!1};return i?(n.next=i.next,n.prev=i,i.next.prev=n,i.next=n):(n.prev=n,n.next=n),n}function u(t){return t.prev.next=t.next,t.next.prev=t.prev,t.removed=!0,t.prev}function f(t,e,i,n,s){return l(t[2*e],t[2*e+1],n,s)-l(t[2*i],t[2*i+1],n,s)||t[2*e]-t[2*i]||t[2*e+1]-t[2*i+1]}function x(t,e,i){var n=t[e];t[e]=t[i],t[i]=n}function g(t){return t[0]}function m(t){return t[1]}r.prototype={_hashEdge:function(t){this._hash[this._hashKey(t.x,t.y)]=t},_hashKey:function(t,e){var i=t-this._cx,n=e-this._cy,s=1-i/(Math.abs(i)+Math.abs(n));return Math.floor((2+(n<0?-s:s))/4*this._hashSize)},_legalize:function(t){var e,i,n,s,h,r,l,o,a,c,u=this.triangles,f=this.coords,x=this.halfedges,g=x[t],m=t-t%3,d=g-g%3,v=m+(t+1)%3,y=m+(t+2)%3,_=d+(g+2)%3,p=u[y],w=u[t],T=u[v],b=u[_];if(e=f[2*p],i=f[2*p+1],n=f[2*w],s=f[2*w+1],h=f[2*T],r=f[2*T+1],l=f[2*b],o=f[2*b+1],a=(n-=l)*n+(s-=o)*s,c=(h-=l)*h+(r-=o)*r,(e-=l)*(s*c-a*r)-(i-=o)*(n*c-a*h)+(e*e+i*i)*(n*r-s*h)<0){u[t]=b,u[g]=p,this._link(t,x[_]),this._link(g,x[y]),this._link(y,_);var k=d+(g+1)%3;return this._legalize(t),this._legalize(k)}return y},_link:function(t,e){this.halfedges[t]=e,-1!==e&&(this.halfedges[e]=t)},_addTriangle:function(t,e,i,n,s,h){var r=this.trianglesLen;return this.triangles[r]=t,this.triangles[r+1]=e,this.triangles[r+2]=i,this._link(r,n),this._link(r+1,s),this._link(r+2,h),this.trianglesLen+=3,r}},s.default=h;class d{constructor(t,e,i,n,s,h,r){if(!((h=+h)>=(n=+n)&&(r=+r)>=(s=+s)))throw new Error("invalid bounds");this.cells=t,this.circumcenters=e,this.delaunay=i,this.xmax=h,this.xmin=n,this.ymax=r,this.ymin=s}find(t,e){return this.cells[this.findIndex(t,e)]}findIndex(t,e){const{cells:i,delaunay:{halfedges:n,points:s,triangles:h}}=this;if(0===i.length||(t=+t)!=t||(e=+e)!=e)return-1;let r=0,l=(t-s[0])**2+(e-s[1])**2;for(;;){let n=r,o=l;for(let l=i[r].triangles,a=0,c=l.length;a<c;++a){let i=3*l[a];switch(r){case h[i]:i=h[i+1];break;case h[i+1]:i=h[i+2];break;case h[i+2]:i=h[i]}let c=(t-s[2*i])**2+(e-s[2*i+1])**2;c<o&&(o=c,n=i)}if(n===r)return n;r=n,l=o}}render(t){const{cells:e,circumcenters:i,delaunay:{halfedges:n,hull:s}}=this;for(let e=0,s=n.length;e<s;++e){const s=n[e];if(s<e)continue;const h=2*Math.floor(e/3),r=2*Math.floor(s/3);t.moveTo(i[h],i[h+1]),t.lineTo(i[r],i[r+1])}let h=s;do{const n=2*Math.floor(h.t/3),s=i[n],r=i[n+1],l=this._project(s,r,e[h.i].vn);l&&(t.moveTo(s,r),t.lineTo(l[0],l[1]))}while((h=h.next)!==s)}renderBounds(t){t.rect(this.xmin,this.ymin,this.xmax-this.xmin,this.ymax-this.ymin)}_clip(t,e,i){return e?this._clipInfinite(t,e,i):this._clipFinite(t)}_clipFinite(t){const e=t.length;let n,s,h,r,l,o,a=null,c=t[e-2],u=t[e-1],f=this._regioncode(c,u);for(let i=0;i<e;i+=2)if(s=c,h=u,c=t[i],u=t[i+1],r=f,f=this._regioncode(c,u),0===r&&0===f)l=o,o=0,a?a.push(c,u):a=[c,u];else if(n=this._clipSegment(s,h,c,u,r,f)){const[e,i,s,h]=n;r&&(l=o,o=this._edgecode(e,i),l&&o&&this._edge(t,l,o,a),a?a.push(e,i):a=[e,i]),l=o,o=this._edgecode(s,h),l&&o&&this._edge(t,l,o,a),a?a.push(s,h):a=[s,h]}if(a)l=o,o=this._edgecode(a[0],a[1]),l&&o&&this._edge(t,l,o,a);else if(i(t,(this.xmin+this.xmax)/2,(this.ymin+this.ymax)/2))return[this.xmax,this.ymin,this.xmax,this.ymax,this.xmin,this.ymax,this.xmin,this.ymin];return a}_clipSegment(t,e,i,n,s,h){for(;;){if(0===s&&0===h)return[t,e,i,n];if(s&h)return;let r,l,o=s||h;8&o?(r=t+(i-t)*(this.ymax-e)/(n-e),l=this.ymax,o^=8):4&o?(r=t+(i-t)*(this.ymin-e)/(n-e),l=this.ymin,o^=4):2&o?(l=e+(n-e)*(this.xmax-t)/(i-t),r=this.xmax,o^=2):(l=e+(n-e)*(this.xmin-t)/(i-t),r=this.xmin,o^=1),s?(t=r,e=l,s=o):(i=r,n=l,h=o)}}_clipInfinite(t,e,i){let s,h=Array.from(t);if((s=this._project(h[0],h[1],e))&&h.unshift(s[0],s[1]),(s=this._project(h[h.length-2],h[h.length-1],i))&&h.unshift(s[0],s[1]),h=this._clipFinite(h)){for(let s,r=0,l=h.length,o=this._edgecode(h[l-2],h[l-1]);r<l;r+=2)if(s=o,o=this._edgecode(h[r],h[r+1]),s&&o)for(;s!==o;){let o,a;switch(s){case 5:s=4;continue;case 4:s=6,o=this.xmax,a=this.ymin;break;case 6:s=2;continue;case 2:s=10,o=this.xmax,a=this.ymax;break;case 10:s=8;continue;case 8:s=9,o=this.xmin,a=this.ymax;break;case 9:s=1;continue;case 1:s=5,o=this.xmin,a=this.ymin}n(t,e,i,o,a)&&(h.splice(r,0,o,a),l+=2,r+=2)}}else n(t,e,i,(this.xmin+this.xmax)/2,(this.ymin+this.ymax)/2)&&h.push(this.xmin,this.ymin,this.xmax,this.ymin,this.xmax,this.ymax,this.xmin,this.ymax);return h}_edge(t,e,n,s){for(;e!==n;){let n,h;switch(e){case 5:e=4;continue;case 4:e=6,n=this.xmax,h=this.ymin;break;case 6:e=2;continue;case 2:e=10,n=this.xmax,h=this.ymax;break;case 10:e=8;continue;case 8:e=9,n=this.xmin,h=this.ymax;break;case 9:e=1;continue;case 1:e=5,n=this.xmin,h=this.ymin}i(t,n,h)&&s.push(n,h)}}_project(t,e,[i,n]){let s,h,r,l=1/0;if(n<0){if(e<=this.ymin)return;(s=(this.ymin-e)/n)<l&&(r=this.ymin,h=t+(l=s)*i)}else if(n>0){if(e>=this.ymax)return;(s=(this.ymax-e)/n)<l&&(r=this.ymax,h=t+(l=s)*i)}if(i>0){if(t>=this.xmax)return;(s=(this.xmax-t)/i)<l&&(h=this.xmax,r=e+(l=s)*n)}else if(i<0){if(t<=this.xmin)return;(s=(this.xmin-t)/i)<l&&(h=this.xmin,r=e+(l=s)*n)}return[h,r]}_edgecode(t,e){return(t===this.xmin?1:t===this.xmax?2:0)|(e===this.ymin?4:e===this.ymax?8:0)}_regioncode(t,e){return(t<this.xmin?1:t>this.xmax?2:0)|(e<this.ymin?4:e>this.ymax?8:0)}}class v{constructor(t,e,i,n){this.points=t,this.halfedges=e,this.hull=i,this.triangles=n}voronoi([t,i,n,s]=[0,0,960,500]){const{points:h,halfedges:r,hull:l,triangles:o}=this,a=new Array(h.length/2),c=new Float64Array(o.length/3*2),u=new d(a,c,this,t,i,n,s);for(let t=0,i=a.length;t<i;++t)a[t]=new e(u);for(let t=0,e=r.length;t<e;++t)a[o[t]]._connect(Math.floor(t/3),Math.floor(r[t]/3));for(let t=0,e=a.length;t<e;++t){const e=a[t];e.triangles=1===e.triangles.length?e.triangles[0]:null}for(let t=0,e=0,i=o.length;t<i;t+=3,e+=2){const i=2*o[t],n=2*o[t+1],s=2*o[t+2],r=h[i],l=h[i+1],a=h[n],u=h[n+1],f=h[s],x=h[s+1],g=r-a,m=r-f,d=l-u,v=l-x,y=r*r+l*l,_=y-a*a-u*u,p=y-f*f-x*x,w=2*(m*d-g*v);c[e]=(d*p-v*_)/w,c[e+1]=(m*_-g*p)/w}{let t=l;do{const{x:e,y:i,t:n,next:{x:s,y:h,t:r}}=t,l=2*Math.floor(n/3),u=c[l],f=c[l+1],x=(e+s)/2-u,g=(i+h)/2-f,m=(s-e)*(f-i)>(h-i)*(u-e)?-1:1;a[o[n]].vn=a[o[r]].v0=[m*x,m*g]}while((t=t.next)!==l)}return u}render(t){const{points:e,halfedges:i,triangles:n}=this;for(let s=0,h=i.length;s<h;++s){const h=i[s];if(h<s)continue;const r=2*n[s],l=2*n[h];t.moveTo(e[r],e[r+1]),t.lineTo(e[l],e[l+1])}this.renderHull(t)}renderHull(t){const{hull:e}=this;let i=e;do{t.moveTo(i.x,i.y),t.lineTo(i.next.x,i.next.y)}while((i=i.next)!==e)}renderTriangle(t,e){const{points:i,triangles:n}=this,s=2*n[t*=3],h=2*n[t+1],r=2*n[t+2];e.moveTo(i[s],i[s+1]),e.lineTo(i[h],i[h+1]),e.lineTo(i[r],i[r+1]),e.closePath()}}v.from=function(t,e,i){const{coords:n,halfedges:h,hull:r,triangles:l}=new s(t,e,i);return new v(n,h,r,l)},t.Cell=e,t.Delaunay=v,t.Voronoi=d,Object.defineProperty(t,"__esModule",{value:!0})});