Skip to content

Instantly share code, notes, and snippets.

@Fil
Last active February 1, 2018 16:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save Fil/1c2f954201523af16280db018ddd90cc to your computer and use it in GitHub Desktop.
Save Fil/1c2f954201523af16280db018ddd90cc to your computer and use it in GitHub Desktop.
Missions to Mars Urquhart Graph
license: mit
!function(r,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("d3-array"),require("d3-collection"),require("d3-geo"),require("d3-voronoi")):"function"==typeof define&&define.amd?define(["exports","d3-array","d3-collection","d3-geo","d3-voronoi"],e):e(r.d3=r.d3||{},r.d3,r.d3,r.d3,r.d3)}(this,function(r,e,n,o,t){"use strict";function s(r,e){for(var n=0,o=0;o<3;o++)n+=r[o]*e[o];return n}function i(r,e){for(var n=new Array(3),o=0;o<3;o++){var t=o+1;t>=3&&(t-=3);var s=o+2;s>=3&&(s-=3),n[o]=r[t]*e[s]-r[s]*e[t]}return n}function a(r,e,n){return s(i(r,e),n)}function v(r,e){for(var n=0,o=0,t=0;t<3;t++){var s=e[t]-r[t];n+=s*s;var i=e[t]+r[t];o+=i*i}return Math.sqrt(n)-Math.sqrt(o)}function u(r){for(var e,n=new Array(3),o=0,t=0;t<3;t++){var s=r[t];o+=s*s}e=o>0?1/Math.sqrt(o):0;for(var t=0;t<3;t++)n[t]=e*r[t];return n}function f(r,e,n){return i(r[e],r[n])}function l(r,e,n,o){return a(r[e],r[n],r[o])}function p(r,e,n){return v(r[e],r[n])}function d(){for(var r=new Array(3),e=0;e<3;e++)r[e]=0;return r}function g(r){for(var e=new Array(3),n=0;n<3;n++)e[n]=r[n];return e}function h(r,e){for(var n=0;n<3;n++)r[n]+=e[n]}function c(r,e){for(var n=0;n<3;n++)r[n]*=e}function y(r,e){for(var n=d(),o=0;o<3;o++)n[o]=r[o]-e[o];return n}function _(r){return"undefined"==typeof r}function b(r,e){if(_(r))return!1;if(_(e))return!1;for(var n=0;n<3;n++)if(r.verts[n]!=e.verts[n])return!1;return!0}function I(r,e){if(_(r))return!1;if(_(e))return!1;for(var n=0;n<2;n++)if(r.verts[n]!=e.verts[n])return!1;return!0}function x(r,e){return e>r?e:r}function w(r,e){this.verts=e,this.edges=new Array(3),this.dirs=new Array(3);for(var n=0;n<3;n++){var o=n+1;o>=3&&(o-=3);var t=n+2;t>=3&&(t-=3),this.dirs[n]=f(r,e[o],e[t])}this.vol=l(r,e[0],e[1],e[2]);for(var n=0;n<3;n++)c(this.dirs[n],1/this.vol);for(var s=d(),n=0;n<3;n++)h(s,this.dirs[n]);this.ccdir=u(s);for(var i=0,n=0;n<3;n++)i+=v(this.ccdir,r[e[n]]);i/=3,this.ccdsq=i}function k(r){this.verts=r,this.polys=new Array(2)}function m(r,e){this.verts=e,this.pdst=p(r,e[0],e[1]),this.direc=u(f(r,e[0],e[1]));var n=d();h(n,r[e[0]]),h(n,r[e[1]]),this.midpnt=u(n)}function V(r,e){for(var n=0;n<r.length;n++)if(e==r[n])return;r.push(e)}function A(r,e){for(var n=0;n<r.length;n++)if(I(r[n],e))return;r.push(e)}function P(r,e){for(var n=[],o=0;o<r.length;o++)for(var t=r[o],s=0;s<e.length;s++){var i=e[s];if(t==i){n.push(t);break}}return n}function q(r,e){for(var n=[],o=0;o<r.length;o++)for(var t=r[o],s=0;s<e.length;s++){var i=e[s];if(I(t,i)){n.push(t);break}}return n}function C(r,e){if(0!=e.length){for(var n=[],o=0;o<r.length;o++){for(var t=r[o],s=!0,i=0;i<e.length;i++){var a=e[i];if(a==t){s=!1;break}}s&&n.push(t)}r.splice(0,r.length);for(var v=0;v<n.length;v++)r.push(n[v])}}function O(r,e){if(0!=e.length){for(var n=[],o=0;o<r.length;o++){for(var t=r[o],s=!0,i=0;i<e.length;i++){var a=e[i];if(I(t,a)){s=!1;break}}s&&n.push(t)}r.splice(0,r.length);for(var v=0;v<n.length;v++)r.push(n[v])}}function M(r,e){for(var n=r.positions,o=n[e],t=r.triangles.length,s=0;s<t;s++){var i=r.triangles[s];if(i.IsPointInside(o)){for(var a=i.edges,v=[],u=0;u<3;u++)v.push(a[u].PolyIndexIn(i));for(var f=Array(3),l=Array(3),u=0;u<3;u++){var p=u+1;p>=3&&(p-=3),f[u]=new w(n,[i.verts[u],i.verts[p],e]),l[u]=new k([i.verts[u],e])}for(var u=0;u<3;u++){var p=u+1;p>=3&&(p-=3),f[u].edges[0]=l[p],f[u].edges[1]=l[u],l[u].polys[0]=f[u],l[p].polys[1]=f[u]}for(var u=0;u<3;u++)for(var d=a[u],g=v[u],h=0;h<3;h++)for(var c=f[h],y=0,_=0;_<2;_++)if(c.IsVertex(d.verts[_])&&y++,2==y){d.polys[g]=c,c.edges[2]=d;break}r.triangles[s]=f[0];for(var u=1;u<3;u++)r.triangles.push(f[u]);for(var u=0;u<3;u++)r.edges.push(l[u]);return!0}}return!1}function j(r){for(var e=r.positions,n=new Array(4),o=0;o<100;o++){for(var t=0,s=0;s<r.edges.length;s++){var i=r.edges[s],a=i.polys;if(!_(a[0])&&!_(a[1])){for(var v=0;v<3;v++){var u=a[0].verts[v];if(!i.IsVertex(u))break}var f=v+1;f>=3&&(f-=3);var l=v+2;l>=3&&(l-=3),n[0]=u,n[1]=a[0].verts[f],n[3]=a[0].verts[l];for(var v=0;v<3;v++){var u=a[1].verts[v];if(!i.IsVertex(u))break}n[2]=u;var p=a[0].IsPointInCircumcircle(e[n[2]]),d=a[1].IsPointInCircumcircle(e[n[0]]);if(p||d){var g=new w(e,[n[0],n[1],n[2]]);if(g.IsVertexOrderCorrect()){var h=new w(e,[n[0],n[2],n[3]]);if(h.IsVertexOrderCorrect()){t++;for(var v=0;v<3;v++){var c=a[0].edges[v];if(!I(c,i)&&c.IsVertex(n[3])){var y=c,b=v;break}}for(var v=0;v<3;v++){var c=a[1].edges[v];if(!I(c,i)&&c.IsVertex(n[1])){var x=c,k=v;break}}var m=y.PolyIndexIn(a[0]),V=x.PolyIndexIn(a[1]);y.polys[m]=a[1],x.polys[V]=a[0],a[0].edges[b]=x,a[1].edges[k]=y,a[0].copy_vert_info(g),a[1].copy_vert_info(h),i.verts=[n[0],n[2]]}}}}}if(0==t)break}}function L(r){for(var e=new Object,n=-1,o=0;o<r.edges.length;o++){var t=r.edges[o];if(_(t.polys[0])){if(_(t.polys[1]))continue;var s=t.polys[1]}else{if(!_(t.polys[1]))continue;var s=t.polys[0]}var i=t.verts[0],a=t.verts[1],v=s.VertexIndexIn(a)-s.VertexIndexIn(i);if(v<0&&(v+=3),1!=v){var u=i;i=a,a=u}e[i]=a,n=i}if(n>=0){for(var f=n,l=[f];;){var p=e[f];if(p==n)break;l.push(p),f=p}r.hull=l}}function S(r){if(1!=r.triangles.length){if(0!=r.triangles.length){for(var e=0;e<r.triangles.length;e++){var n=r.triangles[e];n.index=e,r.vor_positions.push(n.ccdir)}for(var e=0;e<r.edges.length;e++){var o=r.edges[e];if(!_(o.polys[0])&&!_(o.polys[1])){var t=[o.polys[0].index,o.polys[1].index];r.vor_edges.push(t)}}for(var e=0;e<r.indices.length;e++){var s=r.indices[e];r.vor_polygons[s]=new Object;var a=r.vor_polygons[s];a.edges=[],a.triangles=[],a.boundary=[]}for(var e=0;e<r.edges.length;e++)for(var o=r.edges[e],f=0;f<2;f++)r.vor_polygons[o.verts[f]].edges.push(o);for(var e=0;e<r.triangles.length;e++)for(var n=r.triangles[e],f=0;f<3;f++)r.vor_polygons[n.verts[f]].triangles.push(n);for(var e=0;e<r.indices.length;e++){var s=r.indices[e],a=r.vor_polygons[s],l=a.triangles[0],n=l;a.boundary.push(n.index);for(var f=0;f<3;f++){var o=n.edges[f];if(o.IsVertex(s))break}for(var p=o,x=!1;;){var k=o.PolyIndexIn(n);if(n=o.polys[1-k],_(n))break;if(b(n,l)){x=!0;break}a.boundary.push(n.index);for(var f=0;f<3;f++){var m=n.edges[f];if(!I(m,o)&&m.IsVertex(s)){o=m;break}}}if(!x){a.boundary.reverse(),n=l;for(var f=0;f<3&&(o=n.edges[f],I(o,p)||!o.IsVertex(s));f++);for(;;){var k=o.PolyIndexIn(n);if(n=o.polys[1-k],_(n))break;a.boundary.push(n.index);for(var f=0;f<3;f++){var m=n.edges[f];if(!I(m,o)&&m.IsVertex(s)){o=m;break}}}}x||(a.boundary.reverse(),a.boundary.push(-1),a.boundary.reverse(),a.boundary.push(-1))}if(r.hull.length>=3){for(var V=new Array,A=r.positions,C=r.hull.length,f=0;f<C;f++){var s=r.hull[f],O=f+1;O>=C&&(O=0);var M=r.hull[O],j=r.vor_polygons[s].edges,L=r.vor_polygons[M].edges,S=q(j,L),o=S[0],F=o.polys[0].index,E=A[s],z=A[M],D=y(z,E),T=[F,r.vor_positions[F],s,E,M,z,D];V.push(T)}for(;V.length>3;){for(var B=V.length,G=new Array,H=new Array,J=0;J<B;J++)H.push(new Array);for(var J=0;J<B;J++){var K=J+1;K>=B&&(K=0);var N=u(i(V[J][6],V[K][6]));c(N,-1),H[J].push(G.length),H[K].push(G.length),G.push(N)}for(var J=0;J<B;J++){var Q=H[J];if(Q.length>=2){for(var R=new Array,U=0;U<Q.length;U++)R.push(v(G[Q[U]],V[J][1]));for(var W=0,X=R[W],Y=0;Y<R.length;Y++){var Z=R[Y];Z<X&&(W=Y,X=Z)}var $=Q[W]}else if(1==Q.length)var $=Q[0];else var $=-1;H[J]=$}for(var rr=new Array,J=0;J<B;J++){var K=J+1;K>=B&&(K=0);var er=J-1;er<0&&(er=B-1);var nr,or=0,Q=H[J];if(Q!=-1){var tr=H[er];Q==tr?or=2:(nr=H[K],Q==nr&&(or=1))}if(0==or)rr.push(V[J]);else if(1==or){var sr=V[J],ir=V[K],N=G[J],ar=sr[0],vr=ir[0],ur=vr!=ar;if(ur){for(var fr=r.vor_positions.length,lr=sr[2],E=sr[3],pr=ir[4],z=ir[5],dr=void 0,gr=void 0,hr=0;hr<B;hr++){for(var cr=v(V[hr][3],N),yr=hr-J;yr<0;)yr+=B;for(;yr>=B;)yr-=B;yr<=2?void 0==dr?dr=cr:cr<dr&&(dr=cr):void 0==gr?gr=cr:cr<gr&&(gr=cr)}ur=dr<gr}if(ur){var D=y(z,E),T=[fr,N,lr,E,pr,z,D];rr.push(T),r.vor_positions.push(N);var _r=sr[4];r.vor_edges.push([ar,fr]),r.vor_edges.push([vr,fr]),r.vor_polygons[_r].boundary.shift();var br=r.vor_polygons[_r].boundary.length;r.vor_polygons[_r].boundary[br-1]=fr,r.vor_polygons[lr].boundary[1]==ar?(r.vor_polygons[lr].boundary.unshift(-1),r.vor_polygons[lr].boundary[1]=fr):(br=r.vor_polygons[lr].boundary.length,r.vor_polygons[lr].boundary[br-2]==ar&&(r.vor_polygons[lr].boundary.push(-1),br=r.vor_polygons[lr].boundary.length,r.vor_polygons[lr].boundary[br-2]=fr)),r.vor_polygons[pr].boundary[1]==vr?(r.vor_polygons[pr].boundary.unshift(-1),r.vor_polygons[pr].boundary[1]=fr):(br=r.vor_polygons[pr].boundary.length,r.vor_polygons[pr].boundary[br-2]==vr&&(r.vor_polygons[pr].boundary.push(-1),br=r.vor_polygons[pr].boundary.length,r.vor_polygons[pr].boundary[br-2]=fr))}else rr.push(sr),rr.push(ir)}}if(rr.length==V.length)break;V=rr}if(2==V.length){if(V[0][0]!=V[1][0]){for(var Ir=[],J=0;J<2;J++){var K=V[J][0];Ir.push(K),K=V[J][2],r.vor_polygons[K].boundary.shift(),r.vor_polygons[K].boundary.pop()}r.vor_edges.push(Ir)}}else if(3==V.length){var xr=V[0][0],wr=V[1][0],kr=V[2][0];if(xr!=wr&&xr!=kr&&wr!=kr){var fr=r.vor_positions.length,mr=V[0][3],Vr=V[1][3],Ar=V[2][3],N=d();h(N,i(mr,Vr)),h(N,i(Vr,Ar)),h(N,i(Ar,mr)),N=u(N),c(N,-1),r.vor_positions.push(N);for(var J=0;J<3;J++){var T=V[J];r.vor_edges.push([T[0],fr]);var s=T[2];r.vor_polygons[s].boundary.shift();var br=r.vor_polygons[s].boundary.length;r.vor_polygons[s].boundary[br-1]=fr}}}}for(var J=0;J<r.vor_polygons.length;J++)a=r.vor_polygons[J],a.boundary.length>=3&&a.boundary[0]>=0&&(n=new w(r.vor_positions,a.boundary.slice(0,3)),n.IsVertexOrderCorrect()||a.boundary.reverse())}else if(2==r.hull.length){var mr=r.positions[r.hull[0]],Vr=r.positions[r.hull[1]],E=d();h(E,mr),h(E,Vr),E=u(E),r.vor_positions.push(E);var z=u(i(mr,Vr));r.vor_positions.push(z);var Pr=g(E);c(Pr,-1),r.vor_positions.push(Pr);var qr=g(z);c(qr,-1),r.vor_positions.push(qr),r.vor_edges.push([0,1,2,3,0]),o=r.edges[0];for(var J=0;J<2;J++){var s=r.hull[J];r.vor_polygons[s]=new Object;var a=r.vor_polygons[s];a.edges=[o],a.triangles=[0],0==J?a.boundary=[0,1,2,3]:1==J&&(a.boundary=[0,3,2,1])}}}else if(3==r.hull.length){var n=r.triangles[0];r.vor_positions.push(n.ccdir);for(var J=0;J<3;J++){var K=J+1;K>=3&&(K=0);var er=J-1;er<0&&(er=2);var Vr=r.positions[r.hull[J]],Ar=r.positions[r.hull[K]],Cr=y(Ar,Vr);r.vor_positions.push(u(i(Cr,n.ccdir))),r.vor_edges.push([0,J+1,4]);var s=r.hull[J];r.vor_polygons[s]=new Object;for(var a=r.vor_polygons[s],Or=r.hull[er],Mr=0;Mr<3;Mr++){var o=r.edges[Mr],jr=P([Or,s],o.verts);if(2==jr.length)break}a.edges=[o],a.triangles=[n],a.boundary=[0,er+1,4,J+1]}var Lr=g(n.ccdir);c(Lr,-1),r.vor_positions.push(Lr)}}function F(r,e){var n=new Object;if(n.positions=r,n.indices=e,n.triangles=[],n.edges=[],n.hull=[],n.vor_positions=[],n.vor_edges=[],n.vor_polygons=new Object,e.length<3)return 2==e.length&&(n.edges.push(new k(e)),n.hull=e),S(n),n;var o=new w(r,e.slice(0,3));o.IsVertexOrderCorrect()||(o=new w(r,[e[0],e[2],e[1]])),n.triangles.push(o);for(var t=new Array(3),s=0;s<3;s++){var i=s+1;i>=3&&(i-=3);var a=e[s],v=e[i],u=[a,v],f=new k(u),l=new m(r,u);l.edge=f,t[s]=l,o.edges[s]=f,f.polys[0]=o,n.edges.push(f)}for(var p=e.slice(0,3),d=t,g=Object,s=0;s<3;s++){var i=s+2;i>=3&&(i-=3);var a=e[s];g[a]=[t[s],t[s+1]]}for(var h=3;h<e.length;h++){var a=e[h];if(!M(n,a)){g[a]=[];for(var c=[],y=[],b=[],I=0;I<p.length;I++){for(var v=p[I],x=new m(r,[a,v]),P=!1,F=0;F<d.length;F++){var E=d[F];if(P=x.intersects(r,E))break}if(!P){var f=new k(x.verts);x.edge=f,A(c,x),A(g[a],x),V(y,a),A(g[v],x),V(y,v)}}V(p,a);for(var I=0;I<d.length;I++){for(var x=d[I],z=[],D=0;D<2;D++){var T=q(g[a],g[x.verts[D]]);0!=T.length&&z.push(T[0])}if(!(z.length<2)){for(var B=-1,D=0;D<2;D++)if(_(x.edge.polys[D])){B=D;break}if(!(B<0)){var G=x.edge.polys[1-B],H=x.verts[0],J=G.VertexIndexIn(H),K=x.verts[1],N=G.VertexIndexIn(K),Q=N-J;if(Q<0&&(Q+=3),1==Q)var R=[a,K,H];else if(2==Q)var R=[a,H,K];var o=new w(r,R);if(o.IsVertexOrderCorrect()){n.triangles.push(o),x.edge.polys[B]=o,o.edges[0]=x.edge,o.edges[1]=z[0].edge,o.edges[2]=z[1].edge,A(b,x);for(var D=0;D<2;D++){var U=z[D];_(U.edge.polys[0])?(U.edge.polys[0]=o,n.edges.push(U.edge)):(U.edge.polys[1]=o,A(b,U))}}}}}for(var I=0;I<c.length;I++)A(d,c[I]);O(d,b);for(var W=[],I=0;I<y.length;I++){var X=y[I];O(g[X],b),0==g[X].length&&W.push(X)}C(p,W)}}return j(n),L(n),S(n),n}function E(r){for(var e=new Array(r.length),n=0;n<e.length;n++)e[n]=n;return F(r,e)}function z(){var r=Math.PI/180,s=function(e){var n=e[0]*r,o=e[1]*r,t=Math.cos(o);return[t*Math.cos(n),t*Math.sin(n),Math.sin(o)]},i=function(e){var n=Math.sqrt(e[0]*e[0]+e[1]*e[1]),o=Math.atan2(e[2],n),t=Math.atan2(e[1],e[0]);return[t/r,o/r]},a=function(r,e){return e.map(function(e){return i(r[e])})},v=t.voronoi()([]),u=v.DT=null,f=v.sites=[],l=v.pos=[],p=function(r){return"object"==typeof r&&"type"in r?d3.geoCentroid(r)[0]:0 in r?r[0]:void 0},d=function(r){return"object"==typeof r&&"type"in r?d3.geoCentroid(r)[1]:0 in r?r[1]:void 0},g=function(r){return v._hull=v._polygons=v._links=v._triangles=null,"object"==typeof r&&"FeatureCollection"==r.type&&(r=r.features),f=r.map(function(r,e){return r.index=e,r}),l=r.map(function(r){return[p(r),d(r)]}),u=E(l.map(s)),v};return v.links=g.links=function(r){if(r&&g(r),v._links)return v._links;var t=n.map(),s=u.edges.map(function(r,n){t.set(e.extent(r.verts),n);var s={source:f[r.verts[0]],target:f[r.verts[1]],urquhart:!0,length:o.geoLength({type:"LineString",coordinates:[l[r.verts[0]],l[r.verts[1]]]})};return{type:"LineString",coordinates:[i(u.positions[r.verts[0]]),i(u.positions[r.verts[1]])],properties:s}});return u.triangles.forEach(function(r){for(var n,o,i=0,a=0,v=0;v<3;v++){o=e.extent([r.verts[v],r.verts[(v+1)%3]]);var u=t.get(o);a=s[u].properties.length,a>i&&(i=a,n=u)}s[n].properties.urquhart=!1}),v._links={type:"FeatureCollection",features:s}},v.triangles=g.triangles=function(r){if(r&&g(r),v._triangles)return v._triangles;var e=u.triangles.map(function(r){return r.spherical=r.verts.map(function(r){return u.positions[r]}).map(i),r.ccdsq<0&&(r.spherical=r.spherical.reverse(),r.ccdsq*=-1),r}).map(function(r){return{type:"Polygon",coordinates:[r.spherical.concat([r.spherical[0]])],properties:{sites:r.verts.map(function(r){return f[r]}),area:r.vol,circumcenter:i(r.ccdir),circumradius:r.ccdsq}}});return v._triangles={type:"FeatureCollection",features:e}},v.polygons=g.polygons=function(r){if(r&&g(r),v._polygons)return v._polygons;var e=u.indices.map(function(r,e){var n={},t=u.vor_polygons[u.indices[r]];if(void 0==t)n.type="Sphere";else{var s=a(u.vor_positions,t.boundary.concat([t.boundary[0]])),i={type:"Polygon",coordinates:[[l[r],s[0],s[1],l[r]]]};o.geoArea(i)>2*Math.PI+1e-10&&(s=s.reverse()),n.type="Polygon",n.coordinates=[s]}return n.properties={site:f[r],sitecoordinates:l[r],neighbours:t.edges.map(function(e){return e.verts.filter(function(e){return e!==r})[0]})},n});return v._polygons={type:"FeatureCollection",features:e}},v.hull=g.hull=function(r){if(r&&g(r),v._hull)return v._hull;if(!u.hull.length)return null;var e=u.hull.reverse();return v._hull={type:"Polygon",coordinates:[e.concat([e[0]]).map(function(r){return l[r]})],properties:{sites:e.map(function(r){return f[r]})}}},v.find=function(r,e,n){var o,t=v.polygons().features,s=v.find.found||0,i=t[s]||t[s=0],a=d3.geoLength({type:"LineString",coordinates:[[r,e],i.properties.sitecoordinates]});do i=t[o=s],s=null,i.properties.neighbours.forEach(function(n){var o=d3.geoLength({type:"LineString",coordinates:[[r,e],t[n].properties.sitecoordinates]});if(o<a)return a=o,void(s=n)});while(null!==s);if(v.find.found=o,!n||a<n*n)return i.properties.site},g.x=function(r){return r?(p=r,g):p},g.y=function(r){return r?(d=r,g):d},g.extent=function(r){return r?g:null},g.size=function(r){return r?g:null},g}w.prototype.copy_vert_info=function(r){this.verts=r.verts,this.dirs=r.dirs,this.vol=r.vol,this.ccdir=r.ccdir,this.ccdsq=r.ccdsq},w.prototype.IsVertexOrderCorrect=function(){return this.vol>=0},w.prototype.IsPointInside=function(r){for(var e=0;e<3;e++)if(s(r,this.dirs[e])<0)return!1;return!0},w.prototype.IsPointInCircumcircle=function(r){return v(this.ccdir,r)<this.ccdsq},w.prototype.IsVertex=function(r){for(var e=0;e<3;e++)if(r==this.verts[e])return!0;return!1},w.prototype.VertexIndexIn=function(r){for(var e=0;e<3;e++)if(r==this.verts[e])return e;return-1},w.prototype.EdgeIndexIn=function(r){for(var e=0;e<3;e++)if(I(this.edges[e],r))return e;return-1},k.prototype.IsVertex=function(r){for(var e=0;e<2;e++)if(r==this.verts[e])return!0;return!1},k.prototype.VertexIndexIn=function(r){for(var e=0;e<2;e++)if(r==this.verts[e])return e;return-1},k.prototype.PolyIndexIn=function(r){for(var e=0;e<2;e++)if(b(this.polys[e],r))return e;return-1},m.prototype.intersects=function(r,e){for(var n=0;n<2;n++)for(var o=0;o<2;o++)if(this.verts[n]==e.verts[o])return!1;var t=u(i(this.direc,e.direc)),a=s(t,this.midpnt)>0,f=s(t,e.midpnt)>0;if(f!=a)return!1;for(var l=[],n=0;n<2;n++){var p=v(t,r[this.verts[n]]);l.push(p)}for(var d=[],n=0;n<2;n++){var p=v(t,r[e.verts[n]]);d.push(p)}var g=x(l[0],l[1]),h=x(d[0],d[1]);if(g<=this.pdst&&h<=e.pdst&&a)return!0;c(t,-1),a=!a;for(var n=0;n<2;n++)l[n]=-l[n],d[n]=-d[n];return g=x(l[0],l[1]),h=x(d[0],d[1]),!!(g<=this.pdst&&h<=e.pdst&&a)},r.geoVoronoi=z,Object.defineProperty(r,"__esModule",{value:!0})});
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
<!DOCTYPE html>
<meta charset="utf-8">
<script src="../d3-geo-voronoi/dev/d3.v4.js"></script>
<script src="//d3js.org/d3.v4.min.js"></script>
<script src="d3-geo-voronoi.min.js"></script>
<!--
*Note to self: do not edit this block on blockbuilder*
-->
<style>
body {
margin: 0;
overflow: hidden;
background: black;
font-family: "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", Geneva, Verdana, sans-serif;
}
canvas {
cursor: move;
}
svg, canvas, #legend {
position:absolute;
top:0;
}
.missions path { fill: white }
.polygons path {
fill: rgba(0,0,0,0.001); /* to receive mouseover events */
stroke: rgba(200,255,220,0.1);
stroke-width: 1.5;
}
.links path {
fill: none;
stroke: rgba(180,90,90,0.9);
stroke-width: 3;
}
path.glow {
fill: none;
stroke: rgba(255,255,255,0.7);
stroke-width: 1;
}
#legend {
color: white;
top: 1em;
left: 1em;
padding: 0.5em 1em;
border-left: solid white 4px;
background: rgba(255,255,255,0.1);
width: 19em;
line-height: 1.4em;
}
#legend a {
color: white;
}
</style>
<canvas></canvas>
<svg>
<defs>
<radialGradient id="grad1" cx="50%" cy="50%" r="50%" fx="50%" fy="50%">
<stop offset="0%" style="stop-color:black;stop-opacity:0" />
<stop offset="94%" style="stop-color:black;stop-opacity:0.01" />
<stop offset="97%" style="stop-color:black;stop-opacity:0.2" />
<stop offset="110%" style="stop-color:black;stop-opacity:0.6" />
</radialGradient>
</defs>
</svg>
<div id="legend">
<h1>Missions to Mars</h1>
<p>A d3-geo-voronoi demo<br>by <a href="http://bl.ocks.org/Fil/" target="_blank">Philippe Rivière</a>.</p>
</div>
<script id="vertex-shader" type="x-shader/x-vertex">
attribute vec2 a_position;
void main(void) {
gl_Position = vec4(a_position, 0.0, 1.0);
}
</script>
<script id="fragment-shader" type="x-shader/x-fragment">
precision mediump float;
uniform sampler2D u_image;
uniform vec2 u_translate; /* width/2, height/2 */
uniform float u_scale; /* in pixels ! */
uniform vec3 u_rotate; /* rotation in degrees ! */
const float c_pi = 3.14159265358979323846264;
const float c_halfPi = c_pi * 0.5;
const float c_twoPi = c_pi * 2.0;
// Inclination of the equator on Mars = 25.19° (earth= 23.44)
const float declination = 25.19 / 90.0 * c_halfPi;
float phi0 = -u_rotate.y / 90.0 * c_halfPi;
float cosphi0 = cos(phi0);
float sinphi0 = sin(phi0);
void main(void) {
float x = (gl_FragCoord.x - u_translate.x) / u_scale;
float y = (u_translate.y - gl_FragCoord.y) / u_scale;
// inverse orthographic projection
float rho = sqrt(x * x + y * y);
// color if the point (px, py) does not exist in the texture
if (rho >= 1.0) {
gl_FragColor = texture2D(u_image, vec2(0.0, 0.0));
gl_FragColor[0] = 0.1*(rho-1.0+0.1);
gl_FragColor[1] = 0.06*(rho-1.0+0.1);
gl_FragColor[2] = 0.2*(rho-1.0+0.1);
}
else {
float c = asin(rho);
float sinc = sin(c);
float cosc = cos(c);
float lambda = atan(x * sinc, rho * cosc);
float phi = asin(y * sinc / rho);
// inverse rotation
float cosphi = cos(phi);
float x0 = cos(lambda) * cosphi;
float y0 = sin(lambda) * cosphi;
float cosgamma = cos(u_rotate.z / 90.0 * c_halfPi);
float singamma = sin(u_rotate.z / 90.0 * c_halfPi);
float x1 = x0 * cosgamma - y0 * singamma;
float y1 = y0 * cosgamma + x0 * singamma;
float z1 = y * sinc / rho;
lambda = atan(y1, x1 * cosphi0 + z1 * sinphi0) - u_rotate.x / 90.0 * c_halfPi;
phi = asin(z1 * cosphi0 - x1 * sinphi0);
// pixels
float px = (lambda + c_pi) / c_twoPi;
float py = (phi + c_halfPi) / c_pi;
gl_FragColor = texture2D(u_image, vec2(px, py));
// terminator ?? see https://github.com/joergdietrich/Leaflet.Terminator/blob/master/L.Terminator.js
// float sinh = sin(lambda)*sin(declination) + cos(lambda)*cos(declination)*cos(1.0);
// float intensity = (sinh > 0.0) ? 1.0 + 0.1*sinh : 0.2 + 0.8 * exp(6.0*sinh);
float intensity = 1.1; // boost the pixel by some factor
gl_FragColor[0] = intensity * gl_FragColor[0] * (1.3 - 0.3 * sqrt(gl_FragColor[0]));
gl_FragColor[1] = intensity * gl_FragColor[1];
gl_FragColor[2] = intensity * gl_FragColor[2];
}
}
</script>
<script>
// Select the canvas from the document.
var canvas = document.querySelector("canvas");
var width = +canvas.getAttribute('width') || 600,
height = +canvas.getAttribute('height') || 400;
width = Math.max(width, self.innerWidth);
height = Math.max(height, self.innerHeight);
// save the legend
var legend = d3.select('#legend').html();
// Create the WebGL context, with fallback for experimental support.
var context = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
// Compile the vertex shader.
var vertexShader = context.createShader(context.VERTEX_SHADER);
context.shaderSource(vertexShader, document.querySelector("#vertex-shader").textContent);
context.compileShader(vertexShader);
if (!context.getShaderParameter(vertexShader, context.COMPILE_STATUS)) throw new Error(context.getShaderInfoLog(vertexShader));
// Compile the fragment shader.
var fragmentShader = context.createShader(context.FRAGMENT_SHADER);
context.shaderSource(fragmentShader, document.querySelector("#fragment-shader").textContent);
context.compileShader(fragmentShader);
if (!context.getShaderParameter(fragmentShader, context.COMPILE_STATUS)) throw new Error(context.getShaderInfoLog(fragmentShader));
// Link and use the program.
var program = context.createProgram();
context.attachShader(program, vertexShader);
context.attachShader(program, fragmentShader);
context.linkProgram(program);
if (!context.getProgramParameter(program, context.LINK_STATUS)) throw new Error(context.getProgramInfoLog(program));
context.useProgram(program);
// Define the positions (as vec2) of the square that covers the canvas.
var positionBuffer = context.createBuffer();
context.bindBuffer(context.ARRAY_BUFFER, positionBuffer);
context.bufferData(context.ARRAY_BUFFER, new Float32Array([
-1.0, -1.0,
+1.0, -1.0,
+1.0, +1.0,
-1.0, +1.0
]), context.STATIC_DRAW);
// Bind the position buffer to the position attribute.
var positionAttribute = context.getAttribLocation(program, "a_position");
context.enableVertexAttribArray(positionAttribute);
context.vertexAttribPointer(positionAttribute, 2, context.FLOAT, false, 0, 0);
// Extract the projection parameters.
var translateUniform = context.getUniformLocation(program, "u_translate"),
scaleUniform = context.getUniformLocation(program, "u_scale"),
rotateUniform = context.getUniformLocation(program, "u_rotate");
// Load the reference image.
var image = new Image;
image.src = "Mars_Viking_MDIM21_ClrMosaic_global_1024.jpg";
image.onload = readySoon;
var projection = d3.geoOrthographic()
.translate([width / 2, height / 2])
.scale(0.95 * height / 2);
var path = d3.geoPath()
.projection(projection);
var svg = d3.select('svg')
.attr('width', width)
.attr('height', height);
svg._defs = svg.append("defs");
svg._clip = svg._defs.append("path")
.datum({
type: "Sphere"
})
.attr("id", "sphere");
svg._defs.append("clipPath")
.attr("id", "clip")
.append("use")
.attr("xlink:href", "#sphere");
svg._earth = svg.append('g')
.attr("clip-path", "url(#clip)")
.style('cursor', '-webkit-grab');
svg._polygons = svg._earth.append('g').attr('class', 'polygons');
svg._links = svg._earth.append('g').attr('class', 'links');
svg._missions = svg._earth.append('g').attr('class', 'missions');
svg._shade = svg.append("use")
.attr("class", "stroke")
.attr("xlink:href", "#sphere")
.attr("stroke", "black")
.attr("stroke-width", 3)
.attr("fill", "url(#grad1)")
.attr('pointer-events', 'none');
// Hack to ensure correct inference of window dimensions.
function readySoon() {
// https://raw.githubusercontent.com/rhodges/hodgimoto/master/app/layers/mars_landings.geojson
d3.json('mars_landings.geojson', function (err, missions) {
missions.features.push({"type":"Feature","properties":{"OBJECTID":-1,"ID":-1,"NAME":"Schiaparelli","X_COORD":0.2,"Y_COORD":357.5,"FULL_NAME":"ExoMars Schiaparelli EDM lander","NSSDC_ID":"2016-017A","WEB_LINK":"https://en.wikipedia.org/wiki/Schiaparelli_EDM_lander","COUNTRY":"Europe","YEAR":2016},"geometry":{"type":"MultiPoint","coordinates":[[0.2, 357.5]]}})
var v = svg._voronoi = d3.geoVoronoi()(missions),
polygons = v.polygons(),
urquhart = v.links().features.filter(function(l) {
return l.properties.urquhart;
});
svg._missions = svg._missions
.selectAll('path')
.data(missions.features);
var enter = svg._missions.enter().append('path');
svg._missions = svg._missions.merge(enter);
svg._polygons = svg._polygons
.selectAll('path')
.data(polygons.features);
var enter = svg._polygons.enter().append('path');
svg._polygons = svg._polygons.merge(enter);
svg._links = svg._links
.selectAll('path')
.data(urquhart);
var enter = svg._links.enter().append('path');
svg._links = svg._links.merge(enter);
setTimeout(function () {
resize();
ready();
}, 10);
});
}
// retina display
var devicePixelRatio = window.devicePixelRatio || 1;
function resize() {
canvas.setAttribute('width', width * devicePixelRatio);
canvas.setAttribute('height', height * devicePixelRatio);
canvas.style.width = width + 'px';
canvas.style.height = height + 'px';
context.uniform2f(translateUniform, width / 2 * devicePixelRatio, height / 2 * devicePixelRatio);
context.viewport(0, 0, width * devicePixelRatio, height * devicePixelRatio);
}
function ready() {
// Create a texture and a mipmap for accurate minification.
var texture = context.createTexture();
context.bindTexture(context.TEXTURE_2D, texture);
context.texParameteri(context.TEXTURE_2D, context.TEXTURE_MAG_FILTER, context.LINEAR);
context.texParameteri(context.TEXTURE_2D, context.TEXTURE_MIN_FILTER, context.LINEAR_MIPMAP_LINEAR);
context.texImage2D(context.TEXTURE_2D, 0, context.RGBA, context.RGBA, context.UNSIGNED_BYTE, image);
context.generateMipmap(context.TEXTURE_2D);
// The current rotation
var scale = scale0 = projection.scale(),
rotate = [0, 0, 0];
// Rotate and redraw!
function redraw() {
projection.scale(scale).rotate(rotate);
svg._missions.attr('d', path);
svg._polygons.attr('d', path);
svg._links.attr('d', path);
svg._clip.attr('d', path);
context.uniform1f(scaleUniform, scale * devicePixelRatio);
context.uniform3fv(rotateUniform, rotate);
context.bindTexture(context.TEXTURE_2D, texture); // XXX Safari
context.drawArrays(context.TRIANGLE_FAN, 0, 4);
}
svg
.on('mousemove', function () {
var p = d3.mouse(this),
c = projection.invert(p),
found;
// if we're on the Earth
if (c[0] !== 90 &&
(found = svg._voronoi.find(c[0], c[1], 0.8 /* radian */ ))) {
var center = d3.geoCentroid(svg._missions.data()[found.index]);
var circle = d3.geoCircle().center(center),
r = d3.geoLength({
type: "LineString",
coordinates: [c, center]
}) * 180 / Math.PI;
r = Math.max(1.1 * r, 5);
svg._earth.append('path')
.attr('class', 'glow')
.transition()
.attrTween('d', function () {
return function (t) {
return path(circle.radius(2 + r * d3.easePolyIn(t, 4))()) || ''; // empty path when the signal comes from the hidden side of the planet
};
})
.remove();
var p = svg._missions.data()[found.index].properties;
d3.select('#legend h1').text(p.NAME);
d3.select('#legend p').html(
'<a href="' + p.WEB_LINK + '">' + p.FULL_NAME + '</a>' +
'<br>' +
p.COUNTRY + ', ' + p.YEAR + '.'
/* +
'<br>' +
p.X_COORD + "&times" + p.Y_COORD */
);
path.pointRadius(function (d, j) {
return j == found.index ? 8 : 4.5 /* 4.5 = default value */ ;
});
redraw();
} else {
d3.select('#legend').html(legend);
}
});
var lambda = d3.scaleLinear()
.domain([-width / 2, width / 2])
.range([-180, 180]);
var phi = d3.scaleLinear()
.domain([0, height])
.range([90, -90]);
var q, r, transform, d;
zoom = d3.zoom()
.scaleExtent([.8, 1.5])
.on("start", function () {
q = rotate, d = [0, 0, 0]; // accumulate change in d
r = d3.mouse(this);
svg._earth.style('cursor', '-webkit-grabbing');
})
.on("zoom.redraw", function () {
scale = scale0 * d3.event.transform.k;
var p = d3.mouse(this);
var dr = [lambda(p[0]) - lambda(r[0]), phi(p[1]) - phi(r[1])];
r = p;
// inverse dr[0] if the mouse is beyond one of the poles
var a = (phi(p[1]) - rotate[1]) * Math.PI / 180,
ca = Math.cos(a),
sa = Math.sin(a);
d = [d[0] + dr[0] * (ca < 0 ? -1 : 1),
d[1] + dr[1], d[2] + dr[0] * -sa];
rotate = [q[0] + d[0], q[1] + d[1], q[2] + 0 * d[2]];
redraw();
})
.on('end', function() {
svg._earth.style('cursor', '-webkit-grab');
});
d3.select("svg")
.call(zoom);
redraw();
var elapsed = null;
function animate(t) {
elapsed = t;
//d3.select("canvas").transition().call(zoom.transform, d3.zoomIdentity);
requestAnimationFrame(animate);
}
//animate();
}
// A polyfill for requestAnimationFrame.
if (!self.requestAnimationFrame) requestAnimationFrame =
self.webkitRequestAnimationFrame || self.mozRequestAnimationFrame || self.msRequestAnimationFrame || self.oRequestAnimationFrame || function (f) {
setTimeout(f, 17);
};
</script>
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment