Skip to content

Instantly share code, notes, and snippets.

@darosh
Last active October 30, 2015 11:55
Show Gist options
  • Save darosh/7b816a50e66bb62208a7 to your computer and use it in GitHub Desktop.
Save darosh/7b816a50e66bb62208a7 to your computer and use it in GitHub Desktop.
Planetary Grid Browser II
void 0==CanvasRenderingContext2D.prototype.ellipse&&(CanvasRenderingContext2D.prototype.ellipse=function(t,r,n,e,o,a,i,s){this.save(),this.translate(t,r),this.rotate(o),this.scale(n,e),this.arc(0,0,1,a,i,s),this.restore()}),"function"!=typeof Path2D&&!function(){function t(t){if(this.ops_=[],void 0!=t)if("string"==typeof t)try{this.ops_=parser.parse(t)}catch(r){}else{if(!t.hasOwnProperty("ops_"))throw"Error: "+typeof t+"is not a valid argument to Path";this.ops_=t.ops_.slice(0)}}function r(t){return function(){this.ops_.push({type:t,args:Array.prototype.slice.call(arguments,0)})}}parser=function(){function t(t,r){function n(){this.constructor=t}n.prototype=r.prototype,t.prototype=new n}function r(t,r,n,e,o,a){this.message=t,this.expected=r,this.found=n,this.offset=e,this.line=o,this.column=a,this.name="SyntaxError"}function n(t){function n(r){function n(r,n,e){var o,a;for(o=n;e>o;o++)a=t.charAt(o),"\n"===a?(r.seenCR||r.line++,r.column=1,r.seenCR=!1):"\r"===a||"\u2028"===a||"\u2029"===a?(r.line++,r.column=1,r.seenCR=!0):(r.column++,r.seenCR=!1)}return xn!==r&&(xn>r&&(xn=0,mn={line:1,column:1,seenCR:!1}),n(mn,xn,r),xn=r),mn}function e(t){Pn>_n||(_n>Pn&&(Pn=_n,Rn=[]),Rn.push(t))}function o(e,o,a){function i(t){var r=1;for(t.sort(function(t,r){return t.description<r.description?-1:t.description>r.description?1:0});r<t.length;)t[r-1]===t[r]?t.splice(r,1):r++}function s(t,r){function n(t){function r(t){return t.charCodeAt(0).toString(16).toUpperCase()}return t.replace(/\\/g,"\\\\").replace(/"/g,'\\"').replace(/\x08/g,"\\b").replace(/\t/g,"\\t").replace(/\n/g,"\\n").replace(/\f/g,"\\f").replace(/\r/g,"\\r").replace(/[\x00-\x07\x0B\x0E\x0F]/g,function(t){return"\\x0"+r(t)}).replace(/[\x10-\x1F\x80-\xFF]/g,function(t){return"\\x"+r(t)}).replace(/[\u0180-\u0FFF]/g,function(t){return"\\u0"+r(t)}).replace(/[\u1080-\uFFFF]/g,function(t){return"\\u"+r(t)})}var e,o,a,i=new Array(t.length);for(a=0;a<t.length;a++)i[a]=t[a].description;return e=t.length>1?i.slice(0,-1).join(", ")+" or "+i[t.length-1]:i[0],o=r?'"'+n(r)+'"':"end of input","Expected "+e+" but "+o+" found."}var u=n(a),c=a<t.length?t.charAt(a):null;return null!==o&&i(o),new r(null!==e?e:s(o,c),o,c,a,u.line,u.column)}function a(){var t,r,n,e,o;for(t=_n,r=[],n=B();n!==lr;)r.push(n),n=B();if(r!==lr)if(n=i(),n===lr&&(n=gr),n!==lr){for(e=[],o=B();o!==lr;)e.push(o),o=B();e!==lr?(An=t,r=yr(n),t=r):(_n=t,t=vr)}else _n=t,t=vr;else _n=t,t=vr;return t}function i(){var t,r,n,e,o;if(t=_n,r=s(),r!==lr){for(n=_n,e=[],o=B();o!==lr;)e.push(o),o=B();e!==lr?(o=i(),o!==lr?(e=[e,o],n=e):(_n=n,n=vr)):(_n=n,n=vr),n===lr&&(n=gr),n!==lr?(r=[r,n],t=r):(_n=t,t=vr)}else _n=t,t=vr;return t}function s(){var t,r,n,e,o;if(t=_n,r=p(),r!==lr){for(n=_n,e=[],o=B();o!==lr;)e.push(o),o=B();e!==lr?(o=u(),o!==lr?(e=[e,o],n=e):(_n=n,n=vr)):(_n=n,n=vr),n===lr&&(n=gr),n!==lr?(r=[r,n],t=r):(_n=t,t=vr)}else _n=t,t=vr;return t}function u(){var t,r,n,e,o;if(t=_n,r=c(),r!==lr){for(n=_n,e=[],o=B();o!==lr;)e.push(o),o=B();e!==lr?(o=u(),o!==lr?(e=[e,o],n=e):(_n=n,n=vr)):(_n=n,n=vr),n===lr&&(n=gr),n!==lr?(r=[r,n],t=r):(_n=t,t=vr)}else _n=t,t=vr;return t}function c(){var t;return t=f(),t===lr&&(t=h(),t===lr&&(t=g(),t===lr&&(t=d(),t===lr&&(t=C(),t===lr&&(t=x(),t===lr&&(t=R(),t===lr&&(t=D(),t===lr&&(t=b())))))))),t}function p(){var r,n,o,a;if(r=_n,dr.test(t.charAt(_n))?(n=t.charAt(_n),_n++):(n=lr,0===Mn&&e(Cr)),n!==lr){for(o=[],a=B();a!==lr;)o.push(a),a=B();o!==lr?(a=l(),a!==lr?(An=r,n=_r(n,a),r=n):(_n=r,r=vr)):(_n=r,r=vr)}else _n=r,r=vr;return r}function l(){var t,r,n,e,o;return t=_n,r=q(),r!==lr?(n=_n,e=z(),e===lr&&(e=gr),e!==lr?(o=v(),o!==lr?(e=[e,o],n=e):(_n=n,n=vr)):(_n=n,n=vr),n===lr&&(n=gr),n!==lr?(An=t,r=Ar(r,n),t=r):(_n=t,t=vr)):(_n=t,t=vr),t}function f(){var r,n;return r=_n,xr.test(t.charAt(_n))?(n=t.charAt(_n),_n++):(n=lr,0===Mn&&e(mr)),n!==lr&&(An=r,n=Pr()),r=n}function h(){var r,n,o,a;if(r=_n,Rr.test(t.charAt(_n))?(n=t.charAt(_n),_n++):(n=lr,0===Mn&&e(Mr)),n!==lr){for(o=[],a=B();a!==lr;)o.push(a),a=B();o!==lr?(a=v(),a!==lr?(An=r,n=wr(n,a),r=n):(_n=r,r=vr)):(_n=r,r=vr)}else _n=r,r=vr;return r}function v(){var t,r,n,e,o;return t=_n,r=q(),r!==lr?(n=_n,e=z(),e===lr&&(e=gr),e!==lr?(o=v(),o!==lr?(e=[e,o],n=e):(_n=n,n=vr)):(_n=n,n=vr),n===lr&&(n=gr),n!==lr?(An=t,r=Ar(r,n),t=r):(_n=t,t=vr)):(_n=t,t=vr),t}function g(){var r,n,o,a;if(r=_n,Dr.test(t.charAt(_n))?(n=t.charAt(_n),_n++):(n=lr,0===Mn&&e(Tr)),n!==lr){for(o=[],a=B();a!==lr;)o.push(a),a=B();o!==lr?(a=y(),a!==lr?(An=r,n=br(n,a),r=n):(_n=r,r=vr)):(_n=r,r=vr)}else _n=r,r=vr;return r}function y(){var t,r,n,e,o;return t=_n,r=E(),r!==lr?(n=_n,e=z(),e===lr&&(e=gr),e!==lr?(o=y(),o!==lr?(e=[e,o],n=e):(_n=n,n=vr)):(_n=n,n=vr),n===lr&&(n=gr),n!==lr?(An=t,r=Ar(r,n),t=r):(_n=t,t=vr)):(_n=t,t=vr),t}function d(){var r,n,o,a;if(r=_n,Fr.test(t.charAt(_n))?(n=t.charAt(_n),_n++):(n=lr,0===Mn&&e(kr)),n!==lr){for(o=[],a=B();a!==lr;)o.push(a),a=B();o!==lr?(a=y(),a!==lr?(An=r,n=qr(n,a),r=n):(_n=r,r=vr)):(_n=r,r=vr)}else _n=r,r=vr;return r}function C(){var r,n,o,a;if(r=_n,Er.test(t.charAt(_n))?(n=t.charAt(_n),_n++):(n=lr,0===Mn&&e(Sr)),n!==lr){for(o=[],a=B();a!==lr;)o.push(a),a=B();o!==lr?(a=_(),a!==lr?(An=r,n=Ir(n,a),r=n):(_n=r,r=vr)):(_n=r,r=vr)}else _n=r,r=vr;return r}function _(){var t,r,n,e,o;return t=_n,r=A(),r!==lr?(n=_n,e=z(),e===lr&&(e=gr),e!==lr?(o=_(),o!==lr?(e=[e,o],n=e):(_n=n,n=vr)):(_n=n,n=vr),n===lr&&(n=gr),n!==lr?(An=t,r=Ar(r,n),t=r):(_n=t,t=vr)):(_n=t,t=vr),t}function A(){var t,r,n,e,o,a;return t=_n,r=q(),r!==lr?(n=z(),n===lr&&(n=gr),n!==lr?(e=q(),e!==lr?(o=z(),o===lr&&(o=gr),o!==lr?(a=q(),a!==lr?(An=t,r=Or(r,e,a),t=r):(_n=t,t=vr)):(_n=t,t=vr)):(_n=t,t=vr)):(_n=t,t=vr)):(_n=t,t=vr),t}function x(){var r,n,o,a;if(r=_n,zr.test(t.charAt(_n))?(n=t.charAt(_n),_n++):(n=lr,0===Mn&&e(jr)),n!==lr){for(o=[],a=B();a!==lr;)o.push(a),a=B();o!==lr?(a=m(),a!==lr?(An=r,n=Hr(n,a),r=n):(_n=r,r=vr)):(_n=r,r=vr)}else _n=r,r=vr;return r}function m(){var t,r,n,e,o;return t=_n,r=P(),r!==lr?(n=_n,e=z(),e===lr&&(e=gr),e!==lr?(o=m(),o!==lr?(e=[e,o],n=e):(_n=n,n=vr)):(_n=n,n=vr),n===lr&&(n=gr),n!==lr?(An=t,r=Ar(r,n),t=r):(_n=t,t=vr)):(_n=t,t=vr),t}function P(){var t,r,n,e;return t=_n,r=q(),r!==lr?(n=z(),n===lr&&(n=gr),n!==lr?(e=q(),e!==lr?(An=t,r=Qr(r,e),t=r):(_n=t,t=vr)):(_n=t,t=vr)):(_n=t,t=vr),t}function R(){var r,n,o,a;if(r=_n,Vr.test(t.charAt(_n))?(n=t.charAt(_n),_n++):(n=lr,0===Mn&&e(Lr)),n!==lr){for(o=[],a=B();a!==lr;)o.push(a),a=B();o!==lr?(a=M(),a!==lr?(An=r,n=Zr(n,a),r=n):(_n=r,r=vr)):(_n=r,r=vr)}else _n=r,r=vr;return r}function M(){var t,r,n,e,o;return t=_n,r=w(),r!==lr?(n=_n,e=z(),e===lr&&(e=gr),e!==lr?(o=M(),o!==lr?(e=[e,o],n=e):(_n=n,n=vr)):(_n=n,n=vr),n===lr&&(n=gr),n!==lr?(An=t,r=Ar(r,n),t=r):(_n=t,t=vr)):(_n=t,t=vr),t}function w(){var t,r,n,e;return t=_n,r=q(),r!==lr?(n=z(),n===lr&&(n=gr),n!==lr?(e=q(),e!==lr?(An=t,r=Qr(r,e),t=r):(_n=t,t=vr)):(_n=t,t=vr)):(_n=t,t=vr),t}function D(){var r,n,o,a;if(r=_n,Br.test(t.charAt(_n))?(n=t.charAt(_n),_n++):(n=lr,0===Mn&&e(Ur)),n!==lr){for(o=[],a=B();a!==lr;)o.push(a),a=B();o!==lr?(a=T(),a!==lr?(An=r,n=Gr(n,a),r=n):(_n=r,r=vr)):(_n=r,r=vr)}else _n=r,r=vr;return r}function T(){var t,r,n,e,o;return t=_n,r=q(),r!==lr?(n=_n,e=z(),e===lr&&(e=gr),e!==lr?(o=T(),o!==lr?(e=[e,o],n=e):(_n=n,n=vr)):(_n=n,n=vr),n===lr&&(n=gr),n!==lr?(An=t,r=Ar(r,n),t=r):(_n=t,t=vr)):(_n=t,t=vr),t}function b(){var r,n,o,a;if(r=_n,Jr.test(t.charAt(_n))?(n=t.charAt(_n),_n++):(n=lr,0===Mn&&e(Kr)),n!==lr){for(o=[],a=B();a!==lr;)o.push(a),a=B();o!==lr?(a=F(),a!==lr?(An=r,n=Nr(n,a),r=n):(_n=r,r=vr)):(_n=r,r=vr)}else _n=r,r=vr;return r}function F(){var t,r,n,e,o;return t=_n,r=k(),r!==lr?(n=_n,e=z(),e===lr&&(e=gr),e!==lr?(o=F(),o!==lr?(e=[e,o],n=e):(_n=n,n=vr)):(_n=n,n=vr),n===lr&&(n=gr),n!==lr?(An=t,r=Ar(r,n),t=r):(_n=t,t=vr)):(_n=t,t=vr),t}function k(){var t,r,n,e,o,a,i,s,u,c,p,l;return t=_n,r=S(),r!==lr?(n=z(),n===lr&&(n=gr),n!==lr?(e=S(),e!==lr?(o=z(),o===lr&&(o=gr),o!==lr?(a=I(),a!==lr?(i=z(),i!==lr?(s=O(),s!==lr?(u=z(),u===lr&&(u=gr),u!==lr?(c=O(),c!==lr?(p=z(),p===lr&&(p=gr),p!==lr?(l=q(),l!==lr?(An=t,r=Wr(r,e,a,s,c,l),t=r):(_n=t,t=vr)):(_n=t,t=vr)):(_n=t,t=vr)):(_n=t,t=vr)):(_n=t,t=vr)):(_n=t,t=vr)):(_n=t,t=vr)):(_n=t,t=vr)):(_n=t,t=vr)):(_n=t,t=vr)):(_n=t,t=vr),t}function q(){var t,r,n,e;return t=_n,r=E(),r!==lr?(n=z(),n===lr&&(n=gr),n!==lr?(e=E(),e!==lr?(An=t,r=Xr(r,e),t=r):(_n=t,t=vr)):(_n=t,t=vr)):(_n=t,t=vr),t}function E(){var t,r;return t=_n,r=I(),r!==lr&&(An=t,r=Yr(r)),t=r}function S(){var t;return t=H(),t===lr&&(t=Z()),t}function I(){var t,r,n;return t=_n,r=L(),r===lr&&(r=gr),r!==lr?(n=H(),n!==lr?(r=[r,n],t=r):(_n=t,t=vr)):(_n=t,t=vr),t===lr&&(t=_n,r=L(),r===lr&&(r=gr),r!==lr?(n=Z(),n!==lr?(r=[r,n],t=r):(_n=t,t=vr)):(_n=t,t=vr)),t}function O(){var r;return 48===t.charCodeAt(_n)?(r=$r,_n++):(r=lr,0===Mn&&e(tn)),r===lr&&(49===t.charCodeAt(_n)?(r=rn,_n++):(r=lr,0===Mn&&e(nn))),r}function z(){var t,r,n,e,o;if(t=_n,r=[],n=B(),n!==lr)for(;n!==lr;)r.push(n),n=B();else r=vr;if(r!==lr)if(n=j(),n===lr&&(n=gr),n!==lr){for(e=[],o=B();o!==lr;)e.push(o),o=B();e!==lr?(r=[r,n,e],t=r):(_n=t,t=vr)}else _n=t,t=vr;else _n=t,t=vr;if(t===lr)if(t=_n,r=j(),r!==lr){for(n=[],e=B();e!==lr;)n.push(e),e=B();n!==lr?(r=[r,n],t=r):(_n=t,t=vr)}else _n=t,t=vr;return t}function j(){var r;return 44===t.charCodeAt(_n)?(r=en,_n++):(r=lr,0===Mn&&e(on)),r}function H(){var t,r,n;return t=_n,r=Q(),r!==lr?(n=V(),n===lr&&(n=gr),n!==lr?(r=[r,n],t=r):(_n=t,t=vr)):(_n=t,t=vr),t===lr&&(t=_n,r=Z(),r!==lr?(n=V(),n!==lr?(r=[r,n],t=r):(_n=t,t=vr)):(_n=t,t=vr)),t}function Q(){var r,n,o,a;return r=_n,n=Z(),n===lr&&(n=gr),n!==lr?(46===t.charCodeAt(_n)?(o=an,_n++):(o=lr,0===Mn&&e(sn)),o!==lr?(a=Z(),a!==lr?(n=[n,o,a],r=n):(_n=r,r=vr)):(_n=r,r=vr)):(_n=r,r=vr),r===lr&&(r=_n,n=Z(),n!==lr?(46===t.charCodeAt(_n)?(o=an,_n++):(o=lr,0===Mn&&e(sn)),o!==lr?(n=[n,o],r=n):(_n=r,r=vr)):(_n=r,r=vr)),r}function V(){var r,n,o,a;return r=_n,un.test(t.charAt(_n))?(n=t.charAt(_n),_n++):(n=lr,0===Mn&&e(cn)),n!==lr?(o=L(),o===lr&&(o=gr),o!==lr?(a=Z(),a!==lr?(n=[n,o,a],r=n):(_n=r,r=vr)):(_n=r,r=vr)):(_n=r,r=vr),r}function L(){var r;return 43===t.charCodeAt(_n)?(r=pn,_n++):(r=lr,0===Mn&&e(ln)),r===lr&&(45===t.charCodeAt(_n)?(r=fn,_n++):(r=lr,0===Mn&&e(hn))),r}function Z(){var r,n,o;if(r=_n,n=[],vn.test(t.charAt(_n))?(o=t.charAt(_n),_n++):(o=lr,0===Mn&&e(gn)),o!==lr)for(;o!==lr;)n.push(o),vn.test(t.charAt(_n))?(o=t.charAt(_n),_n++):(o=lr,0===Mn&&e(gn));else n=vr;return n!==lr&&(An=r,n=yn(n)),r=n}function B(){var r;return dn.test(t.charAt(_n))?(r=t.charAt(_n),_n++):(r=lr,0===Mn&&e(Cn)),r}function U(t,r){return-1==="mlazhvcsqt".indexOf(t)?wn=r:(wn[0]+=r[0],wn[1]+=r[1]),Fn=t,wn.slice(0)}function G(t,r){for(var n=[],e=wn.slice(0),o=0;o<r.length;o+=2){wn=e.slice(0);var a=U(t,r.slice(o,o+2));n=n.concat(a),o==r.length-4&&(Dn=a.slice(0))}return n}function J(){-1=="CcSsQqTt".indexOf(Fn)&&(Dn=wn.slice(0));var t=[0,0];return t[0]=2*wn[0]-Dn[0],t[1]=2*wn[1]-Dn[1],t}function K(t,r){var n=[r,0];return"H"==t&&(n[1]=wn[1]),U(t,n)}function N(t,r){var n=[0,r];return"V"==t&&(n[0]=wn[0]),U(t,n)}function W(t,r){var n=[t];if(r&&r.length>1)for(var e=r[1],o=0;o<e.length;o++)n.push(e[o]);return n}function X(t){return Math.sqrt(Math.pow(t[0],2)+Math.pow(t[1],2))}function Y(t,r){return t[0]*r[0]+t[1]*r[1]}function $(t,r){return Y(t,r)/(X(t)*X(r))}function tr(t,r){var n=1;return t[0]*r[1]-t[1]*r[0]<0&&(n=-1),n*Math.acos($(t,r))}function rr(t,r){var n=Math.cos(r),e=Math.sin(r);return[n*t[0]+e*t[1],-1*e*t[0]+n*t[1]]}function nr(t,r){var n=Math.cos(r),e=Math.sin(r);return[n*t[0]-e*t[1],e*t[0]+n*t[1]]}function er(t,r){return[(t[0]-r[0])/2,(t[1]-r[1])/2]}function or(t,r){return[(t[0]+r[0])/2,(t[1]+r[1])/2]}function ar(t,r){return[t[0]*r[0],t[1]*r[1]]}function ir(t,r){return[t*r[0],t*r[1]]}function sr(t,r){return[t[0]+r[0],t[1]+r[1]]}function ur(t,r,n,e,o,a,i){if(0==r||0==n)return void Tn.push({type:"lineTo",args:i});var e=e*(Math.PI/180);r=Math.abs(r),n=Math.abs(n);var s=rr(er(t,i),e),u=ar(s,s),c=Math.pow(r,2),p=Math.pow(n,2),l=Math.sqrt(u[0]/c+u[1]/p);l>1&&(r*=l,n*=l,c=Math.pow(r,2),p=Math.pow(n,2));var f=Math.sqrt((c*p-c*u[1]-p*u[0])/(c*u[1]+p*u[0]));o==a&&(f*=-1);var h=ir(f,[r*s[1]/n,-n*s[0]/r]),v=sr(nr(h,e),or(t,i)),g=[(s[0]-h[0])/r,(s[1]-h[1])/n],y=[(-1*s[0]-h[0])/r,(-1*s[1]-h[1])/n],d=tr([1,0],g),C=tr(g,y),_=d,A=d+C;Tn.push({type:"save",args:[]},{type:"translate",args:[v[0],v[1]]},{type:"rotate",args:[e]},{type:"scale",args:[r,n]},{type:"arc",args:[0,0,1,_,A,1-a]},{type:"restore",args:[]})}var cr,pr=arguments.length>1?arguments[1]:{},lr={},fr={svg_path:a},hr=a,vr=lr,gr=null,yr=function(){return Tn},dr=/^[Mm]/,Cr={type:"class",value:"[Mm]",description:"[Mm]"},_r=function(t,r){var n=t;bn&&(n="M",bn=!1),Tn.push({type:"moveTo",args:U(n,r[0])});for(var e=1;e<r.length;e++)Tn.push({type:"lineTo",args:U(t,r[e])})},Ar=function(t,r){return W(t,r)},xr=/^[Zz]/,mr={type:"class",value:"[Zz]",description:"[Zz]"},Pr=function(){Tn.push({type:"closePath",args:[]})},Rr=/^[Ll]/,Mr={type:"class",value:"[Ll]",description:"[Ll]"},wr=function(t,r){for(var n=0;n<r.length;n++)Tn.push({type:"lineTo",args:U(t,r[n])})},Dr=/^[Hh]/,Tr={type:"class",value:"[Hh]",description:"[Hh]"},br=function(t,r){for(var n=0;n<r.length;n++)Tn.push({type:"lineTo",args:K(t,r[n])})},Fr=/^[Vv]/,kr={type:"class",value:"[Vv]",description:"[Vv]"},qr=function(t,r){for(var n=0;n<r.length;n++)Tn.push({type:"lineTo",args:N(t,r[n])})},Er=/^[Cc]/,Sr={type:"class",value:"[Cc]",description:"[Cc]"},Ir=function(t,r){for(var n=0;n<r.length;n++)Tn.push({type:"bezierCurveTo",args:G(t,r[n])})},Or=function(t,r,n){return t.concat(r,n)},zr=/^[Ss]/,jr={type:"class",value:"[Ss]",description:"[Ss]"},Hr=function(t,r){for(var n=0;n<r.length;n++)Tn.push({type:"bezierCurveTo",args:J().concat(G(t,r[n]))})},Qr=function(t,r){return t.concat(r)},Vr=/^[Qq]/,Lr={type:"class",value:"[Qq]",description:"[Qq]"},Zr=function(t,r){for(var n=0;n<r.length;n++)Tn.push({type:"quadraticCurveTo",args:G(t,r[n])})},Br=/^[Tt]/,Ur={type:"class",value:"[Tt]",description:"[Tt]"},Gr=function(t,r){for(var n=0;n<r.length;n++){var e=J();Tn.push({type:"quadraticCurveTo",args:e.concat(G(t,r[n]))}),Dn=e.slice(0)}},Jr=/^[Aa]/,Kr={type:"class",value:"[Aa]",description:"[Aa]"},Nr=function(t,r){for(var n=0;n<r.length;n++){var e=[wn.slice()],o=[U(t,r[n].slice(-2))];absArgs=e.concat(r[n].slice(0,-2),o),ur.apply(this,absArgs)}},Wr=function(t,r,n,e,o,a){return[parseFloat(t),parseFloat(r),parseFloat(n.join("")),parseInt(e),parseInt(o),a[0],a[1]]},Xr=function(t,r){return[t,r]},Yr=function(t){return parseFloat(t.join(""))},$r="0",tn={type:"literal",value:"0",description:'"0"'},rn="1",nn={type:"literal",value:"1",description:'"1"'},en=",",on={type:"literal",value:",",description:'","'},an=".",sn={type:"literal",value:".",description:'"."'},un=/^[eE]/,cn={type:"class",value:"[eE]",description:"[eE]"},pn="+",ln={type:"literal",value:"+",description:'"+"'},fn="-",hn={type:"literal",value:"-",description:'"-"'},vn=/^[0-9]/,gn={type:"class",value:"[0-9]",description:"[0-9]"},yn=function(t){return t.join("")},dn=/^[ \t\n\r]/,Cn={type:"class",value:"[ \\t\\n\\r]",description:"[ \\t\\n\\r]"},_n=0,An=0,xn=0,mn={line:1,column:1,seenCR:!1},Pn=0,Rn=[],Mn=0;if("startRule"in pr){if(!(pr.startRule in fr))throw new Error("Can't start parsing from rule \""+pr.startRule+'".');hr=fr[pr.startRule]}var wn=[0,0],Dn=[0,0],Tn=[],bn=!0,Fn="";if(cr=hr(),cr!==lr&&_n===t.length)return cr;throw cr!==lr&&_n<t.length&&e({type:"end",description:"end of input"}),o(null,Rn,Pn)}return t(r,Error),{SyntaxError:r,parse:n}}();for(var n=["closePath","moveTo","lineTo","quadraticCurveTo","bezierCurveTo","rect","arc","arcTo","ellipse","isPointInPath","isPointInStroke"],e=0;e<n.length;e++){var o=n[e];t.prototype[o]=r(o)}t.prototype.addPath=function(t,r){var n=!1;r&&r.hasOwnProperty("a")&&r.hasOwnProperty("b")&&r.hasOwnProperty("c")&&r.hasOwnProperty("d")&&r.hasOwnProperty("e")&&r.hasOwnProperty("f")&&(n=!0,this.ops_.push({type:"save",args:[]}),this.ops_.push({type:"transform",args:[r.a,r.b,r.c,r.d,r.e,r.f]})),this.ops_=this.ops_.concat(t.ops_),n&&this.ops_.push({type:"restore",args:[]})},original_fill=CanvasRenderingContext2D.prototype.fill,original_stroke=CanvasRenderingContext2D.prototype.stroke,original_clip=CanvasRenderingContext2D.prototype.clip,original_is_point_in_path=CanvasRenderingContext2D.prototype.isPointInPath,original_is_point_in_stroke=CanvasRenderingContext2D.prototype.isPointInStroke,CanvasRenderingContext2D.prototype.fill=function(r){if(r instanceof t){this.beginPath();for(var n=0,e=r.ops_.length;e>n;n++){var o=r.ops_[n];CanvasRenderingContext2D.prototype[o.type].apply(this,o.args)}original_fill.apply(this,Array.prototype.slice.call(arguments,1))}else original_fill.apply(this,arguments)},CanvasRenderingContext2D.prototype.stroke=function(r){if(r instanceof t){this.beginPath();for(var n=0,e=r.ops_.length;e>n;n++){var o=r.ops_[n];CanvasRenderingContext2D.prototype[o.type].apply(this,o.args)}original_stroke.call(this)}else original_stroke.call(this)},CanvasRenderingContext2D.prototype.clip=function(r){if(r instanceof t){this.beginPath();for(var n=0,e=r.ops_.length;e>n;n++){var o=r.ops_[n];CanvasRenderingContext2D.prototype[o.type].apply(this,o.args)}original_clip.apply(this,Array.prototype.slice.call(arguments,1))}else original_clip.apply(this,arguments)},CanvasRenderingContext2D.prototype.isPointInPath=function(r){if(r instanceof t){this.beginPath();for(var n=0,e=r.ops_.length;e>n;n++){var o=r.ops_[n];CanvasRenderingContext2D.prototype[o.type].apply(this,o.args)}return original_is_point_in_path.apply(this,Array.prototype.slice.call(arguments,1))}return original_is_point_in_path.apply(this,arguments)},CanvasRenderingContext2D.prototype.isPointInStroke=function(r){if(r instanceof t){this.beginPath();for(var n=0,e=r.ops_.length;e>n;n++){var o=r.ops_[n];CanvasRenderingContext2D.prototype[o.type].apply(this,o.args)}return original_is_point_in_stroke.apply(this,Array.prototype.slice.call(arguments,1))}return original_is_point_in_stroke.apply(this,arguments)},Path2D=t}();
function CanvasGlobe(ctx, width, height, cfg, overlay) {
'use strict';
var maxScale = 3;
var self;
var projectionGlobe = d3.geo
.orthographic()
.clipAngle(90)
.precision(0)
.translate([height / 2, height / 2])
.scale(height / 2);
var projectionRaw = d3.geo
.orthographic()
.precision(1)
.clipAngle(90)
.translate([height / 2, height / 2])
.scale(height / 2);
var projectionGlobeCalc = d3.geo
.orthographic()
.clipAngle(90)
.translate([height / 2, height / 2])
.scale(height / 2);
var projectionLinesGlobe = d3.geo
.orthographic()
.rotate([cfg.shift, 0, 0])
.precision(1)
.clipAngle(90)
.translate([height / 2, height / 2])
.scale(height / 2);
var pathRaw = d3.geo.path()
.context(ctx)
.projection(projectionRaw);
var pathLines = d3.geo.path()
.context(ctx)
.projection(projectionLinesGlobe);
var zoom = d3.behavior.zoom().scaleExtent([1, maxScale])
.on('zoomstart', zoomed)
.on('zoom', zoomed)
.on('zoomend', zoomed);
var graticuleData = d3.geo.graticule()();
var h = d3.geo.hexakisIcosahedron;
var coolLinesData = h.icosahedronEdges();
var hotLinesData = h.hexakisCenterEdges();
var balancedLinesData = h.hexakisSideEdges();
overlay.call(zoom);
function draw(running) {
projectionRaw.precision(running ? 2 : 1);
projectionLinesGlobe.precision(running ? 2 : 1);
ctx.save();
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.lineWidth = 1;
ctx.translate(ctx.canvas.width - width, ctx.canvas.height - height);
// Water
ctx.beginPath();
ctx.arc(width / 2, height / 2, width / 2 - cfg.frameLineWidth, 0, Math.PI * 2);
ctx.closePath();
ctx.fillStyle = !cfg.filter.map.off ? cfg.colors.water : cfg.colors.bg;
ctx.fill();
// Frame
ctx.beginPath();
ctx.arc(width / 2, height / 2, width / 2 - cfg.frameLineWidth / 2, 0, Math.PI * 2);
ctx.closePath();
ctx.strokeStyle = cfg.colors.frame;
ctx.lineWidth = cfg.frameLineWidth;
ctx.stroke();
// Clip
ctx.beginPath();
ctx.arc(width / 2, height / 2, width / 2 - cfg.frameLineWidth, 0, Math.PI * 2);
ctx.clip();
ctx.lineWidth = 0.5;
if (!cfg.filter.graticule.off) {
ctx.beginPath();
pathRaw(graticuleData);
ctx.strokeStyle = cfg.colors.graticule;
ctx.setLineDash(cfg.dash);
ctx.stroke();
ctx.setLineDash([]);
}
if (!cfg.filter.map.off && self.landDatum) {
ctx.beginPath();
pathRaw(self.landDatum);
ctx.fillStyle = cfg.colors.land;
ctx.strokeStyle = cfg.colors.border;
ctx.fill();
ctx.stroke();
}
ctx.lineWidth = 1;
if (!cfg.filter['cool-line'].off) {
ctx.beginPath();
pathLines(coolLinesData);
ctx.strokeStyle = cfg.colors.cool;
ctx.stroke();
}
if (!cfg.filter['hot-line'].off) {
ctx.beginPath();
pathLines(hotLinesData);
ctx.strokeStyle = cfg.colors.hot;
ctx.stroke();
}
if (!cfg.filter['balanced-line'].off) {
ctx.beginPath();
pathLines(balancedLinesData);
ctx.strokeStyle = cfg.colors.balanced;
ctx.stroke();
}
if (self.selection) {
var p = pathRaw.centroid(self.selection.geometry);
if (!isNaN(p[0]) && !isNaN(p[1])) {
ctx.beginPath();
ctx.arc(p[0], p[1], 7, 0, 2 * Math.PI);
ctx.strokeStyle = cfg.colors.focus;
ctx.lineWidth = 3;
ctx.fillStyle = cfg.colors.selection;
ctx.fill();
ctx.stroke();
}
}
ctx.restore();
}
var a, pG, pGL;
function zoomed() {
var m = d3.mouse(this);
if (d3.event && d3.event.sourceEvent) {
d3.event.sourceEvent.stopPropagation();
d3.event.sourceEvent.preventDefault();
}
({
zoomstart: function () {
pG = projectionGlobe.rotate();
pGL = projectionLinesGlobe.rotate();
projectionGlobeCalc.rotate(pG);
a = projectionGlobeCalc.invert(m);
},
zoom: function () {
var b = projectionGlobeCalc.invert(m);
var pgR = [pG[0] + b[0] - a[0], pG[1] + b[1] - a[1]];
var plgR = [pGL[0] + b[0] - a[0], pGL[1] + b[1] - a[1]];
if (self.canZoom && !self.canZoom(pgR)) {
return;
}
if (!isNaN(b[0]) && !isNaN(b[1])) {
projectionRaw.rotate(pgR);
projectionGlobe.rotate(pgR);
projectionLinesGlobe.rotate(plgR);
if (self.onZoomed) {
var s = zoom.scale();
if (cfg.mraf) {
requestAnimationFrame(function () {
self.onZoomed(null, s, pgR);
});
} else {
self.onZoomed(null, s, pgR);
}
}
}
},
zoomend: function () {
}
})[d3.event.type]();
}
function setZoom(t, s, i) {
zoom.scale(s);
projectionLinesGlobe.rotate([i[0] + cfg.shift, i[1]]);
projectionGlobe.rotate(i);
projectionRaw.rotate(i);
}
self = {
draw: draw,
pathRaw: pathRaw,
setZoom: setZoom
};
return self;
}
<!DOCTYPE html>
<meta charset="utf-8">
<title>Planetary Grid II</title>
<link rel="stylesheet" href="/darosh/raw/14e2e4e14898f13e13c7/style.css">
<body>
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/topojson/1.6.19/topojson.min.js"></script>
<script src="canvas.min.js"></script>
<script src="/darosh/raw/2d12a584a14910032ab8/togeojson.js"></script>
<script src="/darosh/raw/2fe464efd794bde5ed68/hexakis-icosahedron.js"></script>
<script src="/darosh/raw/14e2e4e14898f13e13c7/config.js"></script>
<script src="/darosh/raw/14e2e4e14898f13e13c7/utils.js"></script>
<script src="/darosh/raw/14e2e4e14898f13e13c7/legend.js"></script>
<script src="/darosh/raw/14e2e4e14898f13e13c7/list.js"></script>
<script src="/darosh/raw/14e2e4e14898f13e13c7/info.js"></script>
<script src="globe.js"></script>
<script src="map.js"></script>
<script>
(function () {
'use strict';
var widthList = 220;
var margin = 12;
var widthScrollBar = self.frameElement ? 0 : 20;
var widthScreen = Math.floor(Math.max(document.body.clientWidth || 0, 960)) - widthScrollBar;
widthScreen = Math.min(1480, widthScreen);
var heightScreen = Math.max(500, widthScreen / (960 / 480));
margin = widthScreen > 960 ? margin * 2 : margin;
var widthGlobe = 200;
var heightGlobe = 200;
if (heightScreen > 660) {
widthGlobe = 280;
heightGlobe = 280;
}
var widthLegend = 140;
var widthMap = widthScreen - Math.max(widthLegend, widthGlobe / 2) - 2 * margin;
var heightMap = heightScreen - heightGlobe / 2 - 2 * margin;
var selected;
var list;
var cfg = new Config();
cfg.globeRadius = widthGlobe / 2;
cfg.url = 'http://bl.ocks.org/darosh/7b816a50e66bb62208a7';
var canvas = d3.select('body').append('canvas')
.attr('class', 'no-select')
.attr('width', widthMap + widthGlobe / 2)
.attr('height', heightMap + heightGlobe / 2)
.style('margin-left', margin + 'px')
.style('margin-top', margin + 'px');
var ctx = canvas.node().getContext('2d', {alpha: false});
var map = new CanvasMap(ctx, widthMap, heightMap, margin, cfg);
var globeOverlay = d3.select('body')
.append('div')
.attr('class', 'no-select')
.style('position', 'absolute')
.style('border-radius', (widthGlobe / 2) + 'px')
.style('left', (widthMap - widthGlobe / 2 + margin) + 'px')
.style('top', (heightMap - widthGlobe / 2 + margin) + 'px')
.style('width', (widthGlobe) + 'px')
.style('height', (heightGlobe) + 'px');
var globe = new CanvasGlobe(ctx, widthGlobe, heightGlobe, cfg, globeOverlay);
map.drawGlobe = globe.draw;
var svgLegendWrap = d3.select('body').append('svg')
.attr('width', widthLegend)
.attr('height', heightMap - heightGlobe / 2)
.style('position', 'absolute')
.style('left', (widthMap + margin + cfg.lineMid) + 'px')
.style('top', margin + 'px');
var legend = new SvgLegend(svgLegendWrap, cfg, clickedLegend);
cfg.filter = legend.lookFilter;
var svgListWrap = d3.select('body').append('svg')
.attr('width', widthMap - widthGlobe / 2)
.attr('height', 0)
.style('position', 'absolute')
.style('left', margin + 'px')
.style('top', (heightMap + margin + cfg.lineMid) + 'px');
list = new SvgList(svgListWrap,
cfg, widthMap - widthGlobe / 2, widthList,
function (h) {
h = h + margin;
svgListWrap.attr('height', h);
d3.select(self.frameElement).style('height', (h + heightMap + 2 * margin) + 'px');
},
selectedPlace
);
var info = new HtmlInfo(d3.select('body').append('div')
.style('position', 'absolute')
.style('left', (2 * margin) + 'px')
.style('top', (2 * margin) + 'px')
.style('border', cfg.frameLineWidth + 'px solid ' + cfg.colors.frame)
.style('padding', (cfg.titleSize * 0.75) + 'px')
.style('min-width', (cfg.titleSize * 8) + 'px')
.style('background-color', cfg.colors.bg),
cfg
);
var controls = d3.select('body').append('svg')
.attr('width', 70)
.attr('height', 22)
.style('position', 'absolute')
.style('left', (margin * 1.5) + 'px')
.style('top', (heightMap + margin * .5 - 22) + 'px');
Utils.svgControls(controls, setIndex, map.reset);
map.clickedPoint = function (p) {
selectedPlace(p);
};
map.onZoomed = globe.setZoom;
globe.onZoomed = map.setZoom;
globe.canZoom = function (i) {
var p = map.projection(i);
return !isNaN(p[0]) && !isNaN(p[1]);
};
if (self.frameElement) {
self.frameElement.focus();
}
d3.select('body').on('keydown', function () {
var i = null;
if (d3.event.keyCode === 37) {
i = -1;
} else if (d3.event.keyCode === 39) {
i = +1;
} else if (d3.event.keyCode === 27 && selected) {
selectedPlace(selected);
map.update(1);
d3.event.stopPropagation();
d3.event.preventDefault();
}
if (i !== null) {
setIndex(i);
d3.event.stopPropagation();
d3.event.preventDefault();
}
});
map.update(1);
d3.json('/darosh/raw/14e2e4e14898f13e13c7/mercator-countries.json', function (topo) {
topojson.presimplify(topo);
map.countriesDatum = topojson.mesh(topo, topo.objects.countries);
map.update(1);
});
d3.json('/darosh/raw/14e2e4e14898f13e13c7/mercator-land.json', function (topo) {
topojson.presimplify(topo);
map.landDatum = topojson.feature(topo, topo.objects.land);
map.update(1);
});
d3.json('/darosh/raw/14e2e4e14898f13e13c7/land.json', function (topo) {
globe.landDatum = topojson.feature(topo, topo.objects.land);
map.update(1);
});
d3.xml('/darosh/raw/2d12a584a14910032ab8/places.kml', function (xml) {
var geo = toGeoJSON.kml(xml);
Utils.parsePlaces(geo);
map.geo = geo;
if (list) {
list.update(geo);
}
cfg.filtered = geo.features;
map.update(1);
});
function clickedLegend() {
map.update(1);
list.filter(legend.lookShapes);
}
function selectedPlace(d) {
if (list) {
list.selection(selected, d);
}
if (d === selected) {
d = null;
}
globe.selection = d;
info.update(d);
selected = d;
map.zoomTo(d, info.size);
}
function setIndex(i) {
var f = cfg.filtered;
var c = f.indexOf(selected);
c = c === -1 ? 0 : (c + i);
c = (c + f.length) % f.length;
if (list) {
list.selection(selected, f[c]);
}
info.update(f[c]);
globe.selection = f[c];
setTimeout(function () {
map.zoomTo(f[c], info.size);
}, 50);
selected = f[c];
}
})();
</script>
</body>
function CanvasMap(ctx, width, height, margin, cfg) {
'use strict';
var maxScale = 3;
var self;
var focused;
var focusedFeature;
var cache = {};
var cachePath = false;
var cachePathLines = [];
var translate;
var scale;
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.fillStyle = cfg.colors.bg;
ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
var referenceSize = Math.sqrt(height * height + width * width) * cfg.durationScale;
var clickMapCanvas = document.createElement('canvas');
clickMapCanvas.width = width;
clickMapCanvas.height = height;
var clickCtx = clickMapCanvas.getContext('2d', {alpha: false});
var clip = d3.geo.clipExtent();
var projection = d3.geo.mercator()
.translate([0, 0])
.precision(0)
.scale(width / 2 / Math.PI);
var simplify = d3.geo.transform({
point: function (x, y, z) {
if (z >= simplifiedProjection.area) {
this.stream.point(x * width | 0, y * width | 0);
}
}
});
var simplifiedProjection = {
stream: function (s) {
return simplify.stream(clip.stream(s));
},
baseArea: 4e-3 / width
};
simplifiedProjection.area = simplifiedProjection.baseArea;
var projectionLines = d3.geo.mercator()
.rotate([cfg.shift, 0, 0])
.translate([0, 0])
.precision(1)
.scale(width / 2 / Math.PI);
var path = d3.geo.path()
.context(!cachePath ? ctx : null)
.projection(simplifiedProjection);
var pathRaw = d3.geo.path()
.context(/*!cachePathLines ? */ctx/* : null*/)
.projection(projection);
var pathLines = d3.geo.path()
.context(!cachePathLines ? ctx : null)
.projection(projectionLines);
var zoom = d3.behavior.zoom()
.scaleExtent([1, maxScale])
.on('zoom', zoomed);
var graticuleDatum = d3.geo.graticule()();
var h = d3.geo.hexakisIcosahedron;
var coolLinesData = h.icosahedronEdges();
var hotLinesData = h.hexakisCenterEdges();
var balancedLinesData = h.hexakisSideEdges();
var coolPointsData = Utils.pointsToFeatures(h.icosahedronPoints(), 'cool-point', 'Cool point', cfg.shift);
var hotPointsData = Utils.pointsToFeatures(h.hexakisCenterPoints(), 'hot-point', 'Hot point', cfg.shift);
var balancedPointsData = Utils.pointsToFeatures(h.hexakisCrossPoints(), 'balanced-point', 'Balanced point', cfg.shift);
d3.select(ctx.canvas).call(zoom).on('mouseup', click);
function draw() {
var t = zoom.translate();
var s = zoom.scale();
ctx.save();
ctx.setTransform(1, 0, 0, 1, 0, 0);
// Clip
ctx.beginPath();
ctx.moveTo(cfg.frameLineWidth, cfg.frameLineWidth);
ctx.lineTo(cfg.frameLineWidth, height - cfg.frameLineWidth);
ctx.lineTo(width - cfg.globeRadius, height - cfg.frameLineWidth);
ctx.arcTo(width - cfg.globeRadius, height - cfg.globeRadius,
width - cfg.frameLineWidth, height - cfg.globeRadius,
cfg.globeRadius);
ctx.lineTo(width - cfg.frameLineWidth, height - cfg.globeRadius);
ctx.lineTo(width - cfg.frameLineWidth, cfg.frameLineWidth);
ctx.lineTo(cfg.frameLineWidth, cfg.frameLineWidth);
ctx.clip();
// Water
if (!cfg.filter.map.off) {
ctx.fillStyle = cfg.colors.water;
ctx.fillRect(cfg.frameLineWidth, cfg.frameLineWidth, width - cfg.frameLineWidth * 2, height - cfg.frameLineWidth * 2);
} else {
ctx.fillStyle = cfg.colors.bg;
ctx.fillRect(cfg.frameLineWidth, cfg.frameLineWidth, width - cfg.frameLineWidth * 2, height - cfg.frameLineWidth * 2);
}
// Transform
ctx.translate(Math.round(s * width / 2 + t[0]), Math.round(s * height / 2 + t[1]));
ctx.scale(s, s);
// Clip
var c = [-t[0] / s - (s - 1) * (width / s) / 2, -t[1] / s - (s - 1) * (height / s) / 2];
var ce = [[-width / 2 / s + c[0], -height / 2 / s + c[1]], [width / 2 / s + c[0], height / 2 / s + c[1]]];
clip.extent(ce);
if (!cachePathLines) {
projectionLines.clipExtent(ce);
}
projection.clipExtent(ce);
ctx.lineWidth = 0.5 / s;
// Graticule
if (!cfg.filter.graticule.off) {
ctx.beginPath();
ctx.setLineDash([cfg.dash[0] / s, cfg.dash[1] / s]);
ctx.strokeStyle = cfg.colors.graticule;
if (false && cachePathLines) {
ctx.stroke(cachePathLines[3] = cachePathLines[3] || new Path2D(pathRaw(graticuleDatum)));
} else {
pathRaw(graticuleDatum);
ctx.stroke();
}
ctx.setLineDash([]);
}
// Land
if (!cachePath && !cfg.filter.map.off && self.landDatum) {
ctx.beginPath();
ctx.fillStyle = cfg.colors.land;
path(self.landDatum);
ctx.fill();
} else if (cachePath && !cfg.filter.map.off && self.landDatum) {
ctx.beginPath();
ctx.fillStyle = cfg.colors.land;
path(self.landDatum);
ctx.fill(cachePath[10 + Math.round(s * 10)] = cachePath[10 + Math.round(s * 2)] || new Path2D(path(self.landDatum)));
}
// Borders
if (!cachePath && !cfg.filter.map.off && self.countriesDatum) {
ctx.beginPath();
path(self.countriesDatum);
ctx.strokeStyle = cfg.colors.border;
ctx.stroke();
}
ctx.lineWidth = 1 / s;
// Lines
if (!cfg.filter['cool-line'].off) {
ctx.beginPath();
ctx.strokeStyle = cfg.colors.cool;
if (cachePathLines) {
ctx.stroke(cachePathLines[0] = cachePathLines[0] || new Path2D(pathLines(coolLinesData)));
} else {
pathLines(coolLinesData);
ctx.stroke();
}
}
if (!cfg.filter['hot-line'].off) {
ctx.beginPath();
ctx.strokeStyle = cfg.colors.hot;
if (cachePathLines) {
ctx.stroke(cachePathLines[1] = cachePathLines[1] || new Path2D(pathLines(hotLinesData)));
} else {
pathLines(hotLinesData);
ctx.stroke();
}
}
if (!cfg.filter['balanced-line'].off) {
ctx.beginPath();
ctx.strokeStyle = cfg.colors.balanced;
if (cachePathLines) {
ctx.stroke(cachePathLines[2] = cachePathLines[2] || new Path2D(pathLines(balancedLinesData)));
} else {
pathLines(balancedLinesData);
ctx.stroke();
}
}
// Points
if (!cfg.filter['cool-point'].off) {
coolPointsData.forEach(function (d) {
drawPoint(d, ctx, projectionLines, cfg.colors.cool, 10 / s);
});
}
if (!cfg.filter['hot-point'].off) {
hotPointsData.forEach(function (d) {
drawPoint(d, ctx, projectionLines, cfg.colors.hot, 10 / s);
});
}
if (!cfg.filter['balanced-point'].off) {
balancedPointsData.forEach(function (d) {
drawPoint(d, ctx, projectionLines, cfg.colors.balanced, 10 / s);
});
}
// Symbols
if (self.geo) {
ctx.lineWidth = cfg.symbolLine / s;
ctx.strokeStyle = cfg.colors.shape;
self.geo.features.forEach(function (d) {
drawSymbol(d, s, ctx, cfg.shapes[d.properties.description]);
});
}
if (focused) {
ctx.beginPath();
ctx.arc(focused[0], focused[1], 25 / s, 0, 2 * Math.PI);
ctx.strokeStyle = cfg.colors.focus;
ctx.fillStyle = cfg.colors.selection;
ctx.lineWidth = 6 / s;
ctx.fill();
ctx.stroke();
if (focusedFeature && focusedFeature.type === 'Feature') {
ctx.lineWidth = cfg.symbolLine / s;
ctx.strokeStyle = cfg.colors.shape;
drawSymbol(focusedFeature, s, ctx, cfg.shapes[focusedFeature.properties.description]);
}
var n = translateToCenter(t, s);
var l = [[n[0] + (-width / 2 + margin + self.size.width / 2) / s,
n[1] + (-height / 2 + margin + self.size.height / 2) / s],
[focused[0], focused[1]]];
var d = Math.sqrt(Math.pow(l[1][0] - l[0][0], 2) + Math.pow(l[1][1] - l[0][1], 2));
var r = (d - 25 / s) / d;
l[1][0] = l[0][0] + r * (l[1][0] - l[0][0]);
l[1][1] = l[0][1] + r * (l[1][1] - l[0][1]);
ctx.beginPath();
ctx.moveTo(l[0][0], l[0][1]);
ctx.lineTo(l[1][0], l[1][1]);
ctx.lineWidth = 3 / s;
ctx.strokeStyle = cfg.colors.frame;
ctx.stroke();
}
ctx.restore();
// Frame
ctx.beginPath();
ctx.moveTo(width - cfg.frameLineWidth / 2, height - cfg.globeRadius);
ctx.lineTo(width - cfg.frameLineWidth / 2, cfg.frameLineWidth / 2);
ctx.lineTo(cfg.frameLineWidth / 2, cfg.frameLineWidth / 2);
ctx.lineTo(cfg.frameLineWidth / 2, height - cfg.frameLineWidth / 2);
ctx.lineTo(width - cfg.globeRadius, height - cfg.frameLineWidth / 2);
ctx.strokeStyle = cfg.colors.frame;
ctx.lineWidth = cfg.frameLineWidth;
ctx.stroke();
}
function drawSymbol(d, s, ctx, color) {
if (cfg.filter[d.properties.description].off) {
return;
}
var t = pathRaw.centroid(d);
if (isNaN(t[0]) || isNaN(t[1])) {
return;
}
var symbolSize = cfg.sizes[d.properties.description] / s;
var cp;
if (cache[cfg.symbols[d.properties.description] + symbolSize]) {
cp = cache[cfg.symbols[d.properties.description] + symbolSize]
} else {
var p = d3.svg.symbol().size(symbolSize).type(cfg.symbols[d.properties.description])();
cp = cache[cfg.symbols[d.properties.description] + symbolSize] = new Path2D(p);
}
ctx.save();
ctx.translate(t[0], t[1]);
ctx.stroke(cp);
ctx.fillStyle = color;
ctx.fill(cp);
ctx.restore();
}
function drawPoint(d, ctx, projection, color, s) {
var p = projection(d.coordinates);
if (Math.abs(p[0]) !== Infinity && Math.abs(p[1]) !== Infinity) {
ctx.beginPath();
ctx.arc(p[0], p[1], s, 0, 2 * Math.PI);
ctx.strokeStyle = color;
ctx.stroke();
}
}
function numberToColor(n) {
var b = 0xff & n;
n >>= 8;
var g = 0xff & n;
n >>= 8;
return 'rgb(' + n + ',' + g + ',' + b + ')';
}
function pixelToNumber(p) {
return (p[0] << 16) + (p[1] << 8) + p[2];
}
function click() {
var m = d3.mouse(this);
var t = zoom.translate();
var s = zoom.scale();
var n = 1;
var map = {};
var c;
clickCtx.setTransform(1, 0, 0, 1, 0, 0);
clickCtx.clearRect(0, 0, width, height);
clickCtx.translate(s * width / 2 + t[0], s * height / 2 + t[1]);
clickCtx.scale(s, s);
if (!cfg.filter['cool-point'].off) {
coolPointsData.forEach(function (d) {
map[n] = d;
c = numberToColor(n++);
drawPoint(d, clickCtx, projectionLines, c, 10 / s);
clickCtx.fillStyle = c;
clickCtx.fill();
});
}
if (!cfg.filter['hot-point'].off) {
hotPointsData.forEach(function (d) {
map[n] = d;
c = numberToColor(n++);
drawPoint(d, clickCtx, projectionLines, c, 10 / s);
clickCtx.fillStyle = c;
clickCtx.fill();
});
}
if (!cfg.filter['balanced-point'].off) {
balancedPointsData.forEach(function (d) {
map[n] = d;
c = numberToColor(n++);
drawPoint(d, clickCtx, projectionLines, c, 10 / s);
clickCtx.fillStyle = c;
clickCtx.fill();
});
}
if (self.geo) {
clickCtx.lineWidth = cfg.symbolLine / s;
self.geo.features.forEach(function (d) {
map[n] = d;
c = numberToColor(n++);
clickCtx.strokeStyle = c;
drawSymbol(d, s, clickCtx, c);
});
if (focusedFeature && focusedFeature.type === 'Feature') {
map[n] = focusedFeature;
c = numberToColor(n++);
clickCtx.strokeStyle = c;
drawSymbol(focusedFeature, s, clickCtx, c);
}
}
var p = clickCtx.getImageData(m[0], m[1], 1, 1).data;
var b = pixelToNumber(p);
if (map[b] && self.clickedPoint) {
self.clickedPoint(map[b]);
}
}
function getFixedZoom(t, s) {
var S = (width - height) / 2;
t[0] = Math.min(0, Math.max(-width * (s - 1), t[0]));
t[1] = Math.min(+S * s, Math.max(-S * s - height * (s - 1), t[1]));
}
function fixZoom() {
var t = zoom.translate();
var s = zoom.scale();
getFixedZoom(t, s);
zoom.translate(t);
}
function zoomed() {
if (d3.event && d3.event.sourceEvent) {
d3.event.sourceEvent.stopPropagation();
d3.event.sourceEvent.preventDefault();
}
fixZoom();
var t = zoom.translate();
var s = zoom.scale();
if ((s === scale) && translate && (t[0] === translate[0]) && (t[1] === translate[1])) {
return;
}
translate = t;
scale = s;
var c = translateToCenter(t, s);
var i = projection.invert([-c[0], -c[1]]);
if (cfg.mraf) {
requestAnimationFrame(function () {
update();
if (self.onZoomed) {
self.onZoomed(t, s, i);
}
});
} else {
update();
if (self.onZoomed) {
self.onZoomed(t, s, i);
}
}
}
function update(f, running) {
var s = (running === true) ? 1 : zoom.scale();
projectionLines.precision(cachePathLines ? Math.sqrt(1 / 2) / maxScale / maxScale : Math.sqrt(1 / 2) / s / s);
simplifiedProjection.area = simplifiedProjection.baseArea / s / s;
draw();
if (self.drawGlobe) {
self.drawGlobe(running);
}
}
function setZoom(t, s, i) {
var p = projection(i);
zoom.scale(s);
t = centerToTranslate([-p[0], -p[1]], s);
zoom.translate(t);
fixZoom();
update();
}
function translateToCenter(t, s) {
return [-t[0] / s - (s - 1) * (width / s) / 2, -t[1] / s - (s - 1) * (height / s) / 2];
}
function centerToTranslate(c, s) {
return [-(c[0] + (s - 1) * (width / s) / 2) * s, -(c[1] + (s - 1) * (height / s) / 2) * s];
}
function zoomTransition(p, toScale) {
var fromScale = zoom.scale();
var fromTranslate = zoom.translate();
var toTranslate = toScale === 1 ? p : [-p[0] * toScale - width, -p[1] * toScale - height];
var from = translateToCenter(fromTranslate, fromScale);
from[2] = referenceSize / fromScale;
getFixedZoom(toTranslate, toScale);
var to = translateToCenter(toTranslate, toScale);
to[2] = referenceSize / toScale;
var zi = d3.interpolateZoom(from, to);
var dur = Math.min(cfg.durationMax, Math.max(cfg.durationMin, zi.duration * cfg.durationSpeed));
d3.transition().duration(dur).tween('tween', tween);
function tween() {
return function (t) {
var z = zi(t);
var s = referenceSize / z[2];
var translate = centerToTranslate(z, s);
getFixedZoom(translate, s);
zoom.translate(translate);
zoom.scale(s);
update(t === 1 ? 1 : 0, t < 1);
if (self.onZoomed) {
var c = translateToCenter(translate, s);
var i = projection.invert([-c[0], -c[1]]);
self.onZoomed(translate, s, i);
}
};
}
}
function zoomTo(d, infoSize) {
self.size = infoSize;
if (!d) {
focusedFeature = null;
focused = null;
update();
return;
}
var c = d.geometry ? [d.geometry.coordinates[0], d.geometry.coordinates[1]] : [d[0] + cfg.shift, d[1]];
var p = projection(c);
focusedFeature = d;
focused = p;
zoomTransition(p, maxScale);
}
function reset() {
zoomTransition(projection([0, 0]), 1);
}
return self = {
ctx: ctx,
path: path,
pathRaw: pathRaw,
projection: projection,
update: update,
setZoom: setZoom,
zoomTo: zoomTo,
reset: reset
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment