Skip to content

Instantly share code, notes, and snippets.

@mbostock
Last active November 21, 2016 03:40
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save mbostock/85d7dce6fca6277f75fe to your computer and use it in GitHub Desktop.
Save mbostock/85d7dce6fca6277f75fe to your computer and use it in GitHub Desktop.
Colorcomb II
license: gpl-3.0
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
background: #000;
}
</style>
<canvas width="960" height="500"></canvas>
<script>
/* https://github.com/d3/d3-voronoi Copyright 2015 Mike Bostock */
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t(e.voronoi={})}(this,function(e){"use strict";function t(e){return function(){return e}}function n(e,t,n){return(e.x-n.x)*(t.y-e.y)-(e.x-t.x)*(n.y-e.y)}function i(e,t){return t.angle-e.angle}function r(e,t){this.l=e,this.r=t,this.a=this.b=null}function s(e,t,n){var i=new r(e,null);return i.a=t,i.b=n,D.push(i),i}function u(e,t,n){this.edge=e,this.site=t,this.angle=n}function f(e,t,n){var i=e.a,r=e.b;return new u(e,t,n?Math.atan2(n.y-t.y,n.x-t.x):e.l===t?Math.atan2(r.x-i.x,i.y-r.y):Math.atan2(i.x-r.x,r.y-i.y))}function a(e,t,n,i){for(var r,u,a,l,o,c,h,y,x,v,g=B.length;g--;)if(o=B[g],o&&o.prepare())for(h=o.edges,y=h.length,c=0;y>c;)v=h[c].end(),a=v.x,l=v.y,x=h[++c%y].start(),r=x.x,u=x.y,(Math.abs(a-r)>I||Math.abs(l-u)>I)&&(h.splice(c,0,f(s(o.site,v,Math.abs(a-e)<I&&i-l>I?{x:e,y:Math.abs(r-e)<I?u:i}:Math.abs(l-i)<I&&n-a>I?{x:Math.abs(u-i)<I?r:n,y:i}:Math.abs(a-n)<I&&l-t>I?{x:n,y:Math.abs(r-n)<I?u:t}:Math.abs(l-t)<I&&a-e>I?{x:Math.abs(u-t)<I?r:e,y:t}:null),o.site,null)),++y)}function l(e,t,n,i,r){var s,u=e.a,f=e.b,a=u.x,l=u.y,o=f.x,c=f.y,h=0,y=1,x=o-a,v=c-l;if(s=t-a,x||!(s>0)){if(s/=x,0>x){if(h>s)return;y>s&&(y=s)}else if(x>0){if(s>y)return;s>h&&(h=s)}if(s=i-a,x||!(0>s)){if(s/=x,0>x){if(s>y)return;s>h&&(h=s)}else if(x>0){if(h>s)return;y>s&&(y=s)}if(s=n-l,v||!(s>0)){if(s/=v,0>v){if(h>s)return;y>s&&(y=s)}else if(v>0){if(s>y)return;s>h&&(h=s)}if(s=r-l,v||!(0>s)){if(s/=v,0>v){if(s>y)return;s>h&&(h=s)}else if(v>0){if(h>s)return;y>s&&(y=s)}return h>0&&(e.a={x:a+h*x,y:l+h*v}),1>y&&(e.b={x:a+y*x,y:l+y*v}),e}}}}}function o(e,t,n,i,r){var s=e.b;if(s)return!0;var u,f,a=e.a,l=e.l,o=e.r,c=l.x,h=l.y,y=o.x,x=o.y,v=(c+y)/2,g=(h+x)/2;if(x===h){if(t>v||v>=i)return;if(c>y){if(a){if(a.y>=r)return}else a={x:v,y:n};s={x:v,y:r}}else{if(a){if(a.y<n)return}else a={x:v,y:r};s={x:v,y:n}}}else if(u=(c-y)/(x-h),f=g-u*v,-1>u||u>1)if(c>y){if(a){if(a.y>=r)return}else a={x:(n-f)/u,y:n};s={x:(r-f)/u,y:r}}else{if(a){if(a.y<n)return}else a={x:(r-f)/u,y:r};s={x:(n-f)/u,y:n}}else if(x>h){if(a){if(a.x>=i)return}else a={x:t,y:u*t+f};s={x:i,y:u*i+f}}else{if(a){if(a.x<t)return}else a={x:i,y:u*i+f};s={x:t,y:u*t+f}}return e.a=a,e.b=s,!0}function c(e,t,n,i){for(var r,s=D.length;s--;)r=D[s],(!o(r,e,t,n,i)||!l(r,e,t,n,i)||Math.abs(r.a.x-r.b.x)<I&&Math.abs(r.a.y-r.b.y)<I)&&(r.a=r.b=null,D.splice(s,1))}function h(e){e.U=e.C=e.L=e.R=e.P=e.N=null}function y(e){var t=e.circle;t&&(t.P||(J=t.N),F.remove(t),K.push(t),h(t),e.circle=null)}function x(){h(this),this.x=this.y=this.arc=this.site=this.cy=null}function v(e){var t=e.P,n=e.N;if(t&&n){var i=t.site,r=e.site,s=n.site;if(i!==s){var u=r.x,f=r.y,a=i.x-u,l=i.y-f,o=s.x-u,c=s.y-f,h=2*(a*c-l*o);if(!(h>=-O)){var y=a*a+l*l,v=o*o+c*c,g=(c*y-l*v)/h,C=(a*v-o*y)/h,c=C+f,d=K.pop()||new x;d.arc=e,d.site=r,d.x=g+u,d.y=c+Math.sqrt(g*g+C*C),d.cy=c,e.circle=d;for(var p=null,L=F._;L;)if(d.y<L.y||d.y===L.y&&d.x<=L.x){if(!L.L){p=L.P;break}L=L.L}else{if(!L.R){p=L;break}L=L.R}F.insert(p,d),p||(J=d)}}}}function g(e,t,n,i){e.a||e.b?e.l===n?e.b=i:e.a=i:(e.a=i,e.l=t,e.r=n)}function C(e,t,n,i){var s=new r(e,t);return D.push(s),n&&g(s,e,t,n),i&&g(s,t,e,i),B[e.i].edges.push(f(s,e,t)),B[t.i].edges.push(f(s,t,e)),s}function d(){h(this),this.edge=this.site=this.circle=null}function p(e){var t=Q.pop()||new d;return t.site=e,t}function L(e){y(e),G.remove(e),Q.push(e),h(e)}function b(e){var t=e.circle,n=t.x,i=t.cy,r={x:n,y:i},s=e.P,u=e.N,f=[e];L(e);for(var a=s;a.circle&&Math.abs(n-a.circle.x)<I&&Math.abs(i-a.circle.cy)<I;)s=a.P,f.unshift(a),L(a),a=s;f.unshift(a),y(a);for(var l=u;l.circle&&Math.abs(n-l.circle.x)<I&&Math.abs(i-l.circle.cy)<I;)u=l.N,f.push(l),L(l),l=u;f.push(l),y(l);var o,c=f.length;for(o=1;c>o;++o)l=f[o],a=f[o-1],g(l.edge,a.site,l.site,r);a=f[0],l=f[c-1],l.edge=C(a.site,l.site,null,r),v(a),v(l)}function R(e){this.site=e,this.edges=[]}function M(e){return B[e.i]=new R(e)}function U(e,t){var n=e.site,i=n.x,r=n.y,s=r-t;if(!s)return i;var u=e.P;if(!u)return-(1/0);n=u.site;var f=n.x,a=n.y,l=a-t;if(!l)return f;var o=f-i,c=1/s-1/l,h=o/l;return c?(-h+Math.sqrt(h*h-2*c*(o*o/(-2*l)-a+l/2+r-s/2)))/c+i:(i+f)/2}function N(e,t){var n=e.N;if(n)return U(n,t);var i=e.site;return i.y===t?i.x:1/0}function P(e){for(var t,n,i,r,s=e.x,u=e.y,f=G._;f;)if(i=U(f,u)-s,i>I)f=f.L;else{if(r=s-N(f,u),!(r>I)){i>-I?(t=f.P,n=f):r>-I?(t=f,n=f.N):t=n=f;break}if(!f.R){t=f;break}f=f.R}M(e);var a=p(e);if(G.insert(t,a),t||n){if(t===n)return y(t),n=p(t.site),G.insert(a,n),a.edge=n.edge=C(t.site,a.site),v(t),void v(n);if(!n)return void(a.edge=C(t.site,a.site));y(t),y(n);var l=t.site,o=l.x,c=l.y,h=e.x-o,x=e.y-c,d=n.site,L=d.x-o,b=d.y-c,R=2*(h*b-x*L),P=h*h+x*x,_=L*L+b*b,m={x:(b*P-x*_)/R+o,y:(h*_-L*P)/R+c};g(n.edge,l,d,m),a.edge=C(l,e,null,m),n.edge=C(e,d,null,m),v(t),v(n)}}function _(){this._=null}function m(e,t){var n=t,i=t.L,r=n.U;r?r.L===n?r.L=i:r.R=i:e._=i,i.U=r,n.U=i,n.L=i.R,n.L&&(n.L.U=n),i.R=n}function w(e,t){var n=t,i=t.R,r=n.U;r?r.L===n?r.L=i:r.R=i:e._=i,i.U=r,n.U=i,n.R=i.L,n.R&&(n.R.U=n),i.L=n}function k(e){for(;e.L;)e=e.L;return e}function q(e,t){return t.y-e.y||t.x-e.x}function A(e,t){var n,i,r,s=e.sort(q).pop();for(D=[],B=new Array(e.length),G=new _,F=new _;;)if(r=J,s&&(!r||s.y<r.y||s.y===r.y&&s.x<r.x))(s.x!==n||s.y!==i)&&(P(s),n=s.x,i=s.y),s=e.pop();else{if(!r)break;b(r.arc)}if(t){var n=t[0][0],i=t[0][1],u=t[1][0],f=t[1][1];c(n,i,u,f),a(n,i,u,f)}var l={cells:B,edges:D};return G=F=D=B=null,l}function E(e){return e[1]}function j(e){return e[0]}function z(){function e(e){var t=new Array(e.length),n=l[0][0],i=l[0][1],s=l[1][0],u=l[1][1];return A(r(e),l).cells.forEach(function(r,f){var a=r.edges,l=r.site,o=t[f]=a.length?a.map(function(e){var t=e.start();return[t.x,t.y]}):l.x>=n&&l.x<=s&&l.y>=i&&l.y<=u?[[n,u],[s,u],[s,i],[n,i]]:[];o.point=e[f]}),t}function r(e){return e.map(function(e,t){return{x:Math.round(f(e,t)/I)*I,y:Math.round(a(e,t)/I)*I,i:t}})}var s=j,u=E,f=s,a=u,l=H;return e.links=function(e){return A(r(e)).edges.filter(function(e){return e.l&&e.r}).map(function(t){return{source:e[t.l.i],target:e[t.r.i]}})},e.triangles=function(e){var t=[];return A(r(e)).cells.forEach(function(r,s){for(var u,f,a=r.site,l=r.edges.sort(i),o=-1,c=l.length,h=l[c-1].edge,y=h.l===a?h.r:h.l;++o<c;)u=h,f=y,h=l[o].edge,y=h.l===a?h.r:h.l,s<f.i&&s<y.i&&n(a,f,y)<0&&t.push([e[s],e[f.i],e[y.i]])}),t},e.x=function(n){return arguments.length?(s=n,f="function"==typeof n?s:t(s),e):s},e.y=function(n){return arguments.length?(u=n,a="function"==typeof n?u:t(u),e):u},e.extent=function(t){return arguments.length?(l=null==t?H:t,e):l===H?null:l},e.size=function(t){return arguments.length?e.extent(t&&[[0,0],t]):l===H?null:l&&l[1]},e}var B,D,F,G,H=[[-1e6,-1e6],[1e6,1e6]],I=1e-6;u.prototype={start:function(){return this.edge.l===this.site?this.edge.a:this.edge.b},end:function(){return this.edge.l===this.site?this.edge.b:this.edge.a}};var J,K=[],O=1e-12,Q=[];R.prototype.prepare=function(){for(var e,t=this.edges,n=t.length;n--;)e=t[n].edge,e.b&&e.a||t.splice(n,1);return t.sort(i),t.length},_.prototype={insert:function(e,t){var n,i,r;if(e){if(t.P=e,t.N=e.N,e.N&&(e.N.P=t),e.N=t,e.R){for(e=e.R;e.L;)e=e.L;e.L=t}else e.R=t;n=e}else this._?(e=k(this._),t.P=null,t.N=e,e.P=e.L=t,n=e):(t.P=t.N=null,this._=t,n=null);for(t.L=t.R=null,t.U=n,t.C=!0,e=t;n&&n.C;)i=n.U,n===i.L?(r=i.R,r&&r.C?(n.C=r.C=!1,i.C=!0,e=i):(e===n.R&&(w(this,n),e=n,n=e.U),n.C=!1,i.C=!0,m(this,i))):(r=i.L,r&&r.C?(n.C=r.C=!1,i.C=!0,e=i):(e===n.L&&(m(this,n),e=n,n=e.U),n.C=!1,i.C=!0,w(this,i))),n=e.U;this._.C=!1},remove:function(e){e.N&&(e.N.P=e.P),e.P&&(e.P.N=e.N),e.N=e.P=null;var t,n,i,r=e.U,s=e.L,u=e.R;if(n=s?u?k(u):s:u,r?r.L===e?r.L=n:r.R=n:this._=n,s&&u?(i=n.C,n.C=e.C,n.L=s,s.U=n,n!==u?(r=n.U,n.U=e.U,e=n.R,r.L=e,n.R=u,u.U=n):(n.U=r,r=n,e=n.R)):(i=e.C,e=n),e&&(e.U=r),!i){if(e&&e.C)return void(e.C=!1);do{if(e===this._)break;if(e===r.L){if(t=r.R,t.C&&(t.C=!1,r.C=!0,w(this,r),t=r.R),t.L&&t.L.C||t.R&&t.R.C){t.R&&t.R.C||(t.L.C=!1,t.C=!0,m(this,t),t=r.R),t.C=r.C,r.C=t.R.C=!1,w(this,r),e=this._;break}}else if(t=r.L,t.C&&(t.C=!1,r.C=!0,m(this,r),t=r.L),t.L&&t.L.C||t.R&&t.R.C){t.L&&t.L.C||(t.R.C=!1,t.C=!0,w(this,t),t=r.L),t.C=r.C,r.C=t.L.C=!1,m(this,r),e=this._;break}t.C=!0,e=r,r=r.U}while(!e.C);e&&(e.C=!1)}}},e.voronoi=z});
/* https://github.com/d3/d3-timer Copyright 2015 Mike Bostock */
"undefined"==typeof requestAnimationFrame&&(requestAnimationFrame="undefined"!=typeof window&&(window.msRequestAnimationFrame||window.mozRequestAnimationFrame||window.webkitRequestAnimationFrame||window.oRequestAnimationFrame)||function(e){return setTimeout(e,17)}),function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n(e.timer={})}(this,function(e){"use strict";function n(){r=m=0,c=1/0,t(u())}function t(e){if(!r){var t=e-Date.now();t>24?c>e&&(m&&clearTimeout(m),m=setTimeout(n,t),c=e):(m&&(m=clearTimeout(m),c=1/0),r=requestAnimationFrame(n))}}function i(e,n,i){i=null==i?Date.now():+i,null!=n&&(i+=+n);var o={callback:e,time:i,flush:!1,next:null};a?a.next=o:f=o,a=o,t(i)}function o(e,n,t){t=null==t?Date.now():+t,null!=n&&(t+=+n),l.callback=e,l.time=t}function u(e){e=null==e?Date.now():+e;var n=l;for(l=f;l;)e>=l.time&&(l.flush=l.callback(e-l.time,e)),l=l.next;l=n,e=1/0;for(var t,i=f;i;)i.flush?i=t?t.next=i.next:f=i.next:(i.time<e&&(e=i.time),i=(t=i).next);return a=t,e}var a,m,r,f,l,c=1/0;e.timer=i,e.timerReplace=o,e.timerFlush=u});
/* https://github.com/d3/d3-color Copyright 2015 Mike Bostock */
"undefined"==typeof Map?(Map=function(){this.clear()},Map.prototype={set:function(t,e){return this._[t]=e,this},get:function(t){return this._[t]},has:function(t){return t in this._},"delete":function(t){return t in this._&&delete this._[t]},clear:function(){this._=Object.create(null)},get size(){var t=0;for(var e in this._)++t;return t},forEach:function(t){for(var e in this._)t(this._[e],e,this)}}):function(){var t=new Map;t.set(0,0)!==t&&(t=t.set,Map.prototype.set=function(){return t.apply(this,arguments),this})}(),function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e(t.color={})}(this,function(t){"use strict";function e(){}function s(t,e,s){this.r=Math.max(0,Math.min(255,Math.round(t))),this.g=Math.max(0,Math.min(255,Math.round(e))),this.b=Math.max(0,Math.min(255,Math.round(s)))}function n(t,e,s){return isNaN(t)&&(t=0),isNaN(e)&&(e=0),isNaN(s)&&(s=0),"#"+(16>t?"0"+t.toString(16):t.toString(16))+(16>e?"0"+e.toString(16):e.toString(16))+(16>s?"0"+s.toString(16):s.toString(16))}function r(t,n,r){return 1===arguments.length&&(t instanceof e||(t=l(t)),t?(t=t.rgb(),r=t.b,n=t.g,t=t.r):t=n=r=NaN),new s(t,n,r)}function i(t){return r(t>>16&255,t>>8&255,255&t)}function a(t,e,s){this.h=+t,this.s=Math.max(0,Math.min(1,+e)),this.l=Math.max(0,Math.min(1,+s))}function o(t,e,s){return 255*(60>t?e+(s-e)*t/60:180>t?s:240>t?e+(s-e)*(240-t)/60:e)}function h(t,s,n){if(1===arguments.length)if(t instanceof a)n=t.l,s=t.s,t=t.h;else if(t instanceof e||(t=l(t)),t){if(t instanceof a)return t;t=t.rgb();var r=t.r/255,i=t.g/255,o=t.b/255,h=Math.min(r,i,o),u=Math.max(r,i,o),c=u-h;n=(u+h)/2,c?(s=.5>n?c/(u+h):c/(2-u-h),t=r===u?(i-o)/c+6*(o>i):i===u?(o-r)/c+2:(r-i)/c+4,t*=60):(t=NaN,s=n>0&&1>n?0:t)}else t=s=n=NaN;return new a(t,s,n)}function l(t){var e;return t=(t+"").trim().toLowerCase(),(e=R.exec(t))?(e=parseInt(e[1],16),r(e>>8&15|e>>4&240,e>>4&15|240&e,(15&e)<<4|15&e)):(e=E.exec(t))?i(parseInt(e[1],16)):(e=z.exec(t))?r(e[1],e[2],e[3]):(e=P.exec(t))?r(2.55*e[1],2.55*e[2],2.55*e[3]):(e=O.exec(t))?h(e[1],.01*e[2],.01*e[3]):L.has(t)?i(L.get(t)):null}function u(t,e,s){this.l=+t,this.a=+e,this.b=+s}function c(t){return 255*(.0031308>=t?12.92*t:1.055*Math.pow(t,1/2.4)-.055)}function g(t){return t>F?t*t*t:G*(t-D)}function f(t){return t>T?Math.pow(t,1/3):t/G+D}function d(t){return(t/=255)<=.04045?t/12.92:Math.pow((t+.055)/1.055,2.4)}function p(t,e,s){this.h=+t,this.c=+e,this.l=+s}function b(t,e,n){if(1===arguments.length)if(t instanceof u)n=t.b,e=t.a,t=t.l;else if(t instanceof p){var i=t.h*U;n=Math.sin(i)*t.c,e=Math.cos(i)*t.c,t=t.l}else{t instanceof s||(t=r(t));var a=d(t.r),o=d(t.g),n=d(t.b),h=f((.4124564*a+.3575761*o+.1804375*n)/K),l=f((.2126729*a+.7151522*o+.072175*n)/Q),c=f((.0193339*a+.119192*o+.9503041*n)/J);n=200*(l-c),e=500*(h-l),t=116*l-16}return new u(t,e,n)}function N(t,e,s){return 1===arguments.length&&(t instanceof p?(s=t.l,e=t.c,t=t.h):(t instanceof u||(t=b(t)),s=t.l,e=Math.sqrt(t.a*t.a+t.b*t.b),t=Math.atan2(t.b,t.a)*W,0>t&&(t+=360))),new p(t,e,s)}function m(t,e,s){this.h=+t,this.s=+e,this.l=+s}function w(t,e,n){if(1===arguments.length)if(t instanceof m)n=t.l,e=t.s,t=t.h;else{t instanceof s||(t=r(t));var i=t.r/255,a=t.g/255,o=t.b/255;n=(at*o+it*i-rt*a)/(at+it-rt);var h=o-n,l=(Y*(a-n)-tt*h)/Z,u=Math.pow(n,nt);e=Math.sqrt(l*l+h*h)/(Y*u*(1-u)),t=e?Math.atan2(l,h)*W-120:NaN,0>t&&(t+=360)}return new m(t,e,n)}function y(t,e){t=w(t),e=w(e);var s=isNaN(t.h)?e.h:t.h,n=isNaN(t.s)?e.s:t.s,r=t.l,i=isNaN(e.h)?0:e.h-s,a=isNaN(e.s)?0:e.s-n,o=e.l-r;return function(e){return t.h=s+i*e,t.s=n+a*e,t.l=r+o*e,t+""}}function v(t,e){var s=(t-e)%360;return s+(s>180?-360:-180>s?360:0)}function M(t,e){t=w(t),e=w(e);var s=isNaN(t.h)?e.h:t.h,n=isNaN(t.s)?e.s:t.s,r=t.l,i=isNaN(e.h)?0:v(e.h,s),a=isNaN(e.s)?0:e.s-n,o=e.l-r;return function(e){return t.h=s+i*e,t.s=n+a*e,t.l=r+o*e,t+""}}function k(t,e){t=N(t),e=N(e);var s=isNaN(t.h)?e.h:t.h,n=isNaN(t.c)?e.c:t.c,r=t.l,i=isNaN(e.h)?0:e.h-s,a=isNaN(e.c)?0:e.c-n,o=e.l-r;return function(e){return t.h=s+i*e,t.c=n+a*e,t.l=r+o*e,t+""}}function x(t,e){t=N(t),e=N(e);var s=isNaN(t.h)?e.h:t.h,n=isNaN(t.c)?e.c:t.c,r=t.l,i=isNaN(e.h)?0:v(e.h,s),a=isNaN(e.c)?0:e.c-n,o=e.l-r;return function(e){return t.h=s+i*e,t.c=n+a*e,t.l=r+o*e,t+""}}function q(t,e){t=b(t),e=b(e);var s=t.l,n=t.a,r=t.b,i=e.l-s,a=e.a-n,o=e.b-r;return function(e){return t.l=s+i*e,t.a=n+a*e,t.b=r+o*e,t+""}}function _(t,e){t=h(t),e=h(e);var s=isNaN(t.h)?e.h:t.h,n=isNaN(t.s)?e.s:t.s,r=t.l,i=isNaN(e.h)?0:e.h-s,a=isNaN(e.s)?0:e.s-n,o=e.l-r;return function(e){return t.h=s+i*e,t.s=n+a*e,t.l=r+o*e,t+""}}function S(t,e){t=h(t),e=h(e);var s=isNaN(t.h)?e.h:t.h,n=isNaN(t.s)?e.s:t.s,r=t.l,i=isNaN(e.h)?0:v(e.h,s),a=isNaN(e.s)?0:e.s-n,o=e.l-r;return function(e){return t.h=s+i*e,t.s=n+a*e,t.l=r+o*e,t+""}}function j(t,e){t=r(t),e=r(e);var s=t.r,i=t.g,a=t.b,o=e.r-s,h=e.g-i,l=e.b-a;return function(t){return n(Math.round(s+o*t),Math.round(i+h*t),Math.round(a+l*t))}}e.prototype={toString:function(){return this.rgb()+""}};var L=(new Map).set("aliceblue",15792383).set("antiquewhite",16444375).set("aqua",65535).set("aquamarine",8388564).set("azure",15794175).set("beige",16119260).set("bisque",16770244).set("black",0).set("blanchedalmond",16772045).set("blue",255).set("blueviolet",9055202).set("brown",10824234).set("burlywood",14596231).set("cadetblue",6266528).set("chartreuse",8388352).set("chocolate",13789470).set("coral",16744272).set("cornflowerblue",6591981).set("cornsilk",16775388).set("crimson",14423100).set("cyan",65535).set("darkblue",139).set("darkcyan",35723).set("darkgoldenrod",12092939).set("darkgray",11119017).set("darkgreen",25600).set("darkgrey",11119017).set("darkkhaki",12433259).set("darkmagenta",9109643).set("darkolivegreen",5597999).set("darkorange",16747520).set("darkorchid",10040012).set("darkred",9109504).set("darksalmon",15308410).set("darkseagreen",9419919).set("darkslateblue",4734347).set("darkslategray",3100495).set("darkslategrey",3100495).set("darkturquoise",52945).set("darkviolet",9699539).set("deeppink",16716947).set("deepskyblue",49151).set("dimgray",6908265).set("dimgrey",6908265).set("dodgerblue",2003199).set("firebrick",11674146).set("floralwhite",16775920).set("forestgreen",2263842).set("fuchsia",16711935).set("gainsboro",14474460).set("ghostwhite",16316671).set("gold",16766720).set("goldenrod",14329120).set("gray",8421504).set("green",32768).set("greenyellow",11403055).set("grey",8421504).set("honeydew",15794160).set("hotpink",16738740).set("indianred",13458524).set("indigo",4915330).set("ivory",16777200).set("khaki",15787660).set("lavender",15132410).set("lavenderblush",16773365).set("lawngreen",8190976).set("lemonchiffon",16775885).set("lightblue",11393254).set("lightcoral",15761536).set("lightcyan",14745599).set("lightgoldenrodyellow",16448210).set("lightgray",13882323).set("lightgreen",9498256).set("lightgrey",13882323).set("lightpink",16758465).set("lightsalmon",16752762).set("lightseagreen",2142890).set("lightskyblue",8900346).set("lightslategray",7833753).set("lightslategrey",7833753).set("lightsteelblue",11584734).set("lightyellow",16777184).set("lime",65280).set("limegreen",3329330).set("linen",16445670).set("magenta",16711935).set("maroon",8388608).set("mediumaquamarine",6737322).set("mediumblue",205).set("mediumorchid",12211667).set("mediumpurple",9662683).set("mediumseagreen",3978097).set("mediumslateblue",8087790).set("mediumspringgreen",64154).set("mediumturquoise",4772300).set("mediumvioletred",13047173).set("midnightblue",1644912).set("mintcream",16121850).set("mistyrose",16770273).set("moccasin",16770229).set("navajowhite",16768685).set("navy",128).set("oldlace",16643558).set("olive",8421376).set("olivedrab",7048739).set("orange",16753920).set("orangered",16729344).set("orchid",14315734).set("palegoldenrod",15657130).set("palegreen",10025880).set("paleturquoise",11529966).set("palevioletred",14381203).set("papayawhip",16773077).set("peachpuff",16767673).set("peru",13468991).set("pink",16761035).set("plum",14524637).set("powderblue",11591910).set("purple",8388736).set("rebeccapurple",6697881).set("red",16711680).set("rosybrown",12357519).set("royalblue",4286945).set("saddlebrown",9127187).set("salmon",16416882).set("sandybrown",16032864).set("seagreen",3050327).set("seashell",16774638).set("sienna",10506797).set("silver",12632256).set("skyblue",8900331).set("slateblue",6970061).set("slategray",7372944).set("slategrey",7372944).set("snow",16775930).set("springgreen",65407).set("steelblue",4620980).set("tan",13808780).set("teal",32896).set("thistle",14204888).set("tomato",16737095).set("turquoise",4251856).set("violet",15631086).set("wheat",16113331).set("white",16777215).set("whitesmoke",16119285).set("yellow",16776960).set("yellowgreen",10145074),$=s.prototype=new e,H=.7;$.darker=function(t){return t=null==t?H:Math.pow(H,t),new s(this.r*t,this.g*t,this.b*t)};var I=1/H;$.brighter=function(t){return t=null==t?I:Math.pow(I,t),new s(this.r*t,this.g*t,this.b*t)},$.rgb=function(){return this},$.toString=function(){return n(this.r,this.g,this.b)};var C=a.prototype=new e;C.brighter=function(t){return t=null==t?I:Math.pow(I,t),new a(this.h,this.s,this.l*t)},C.darker=function(t){return t=null==t?H:Math.pow(H,t),new a(this.h,this.s,this.l*t)},C.rgb=function(){var t=this.h%360+360*(this.h<0),e=isNaN(t)||isNaN(this.s)?0:this.s,n=this.l,r=.5>=n?n*(1+e):n+e-n*e,i=2*n-r;return new s(o(t>=240?t-240:t+120,i,r),o(t,i,r),o(120>t?t+240:t-120,i,r))};var O=/^hsl\(\s*([-+]?\d+(?:\.\d+)?)\s*,\s*([-+]?\d+(?:\.\d+)?)%\s*,\s*([-+]?\d+(?:\.\d+)?)%\s*\)$/,P=/^rgb\(\s*([-+]?\d+(?:\.\d+)?)%\s*,\s*([-+]?\d+(?:\.\d+)?)%\s*,\s*([-+]?\d+(?:\.\d+)?)%\s*\)$/,z=/^rgb\(\s*([-+]?\d+)\s*,\s*([-+]?\d+)\s*,\s*([-+]?\d+)\s*\)$/,E=/^#([0-9a-f]{6})$/,R=/^#([0-9a-f]{3})$/;l.prototype=e.prototype,r.prototype=s.prototype,h.prototype=a.prototype;var A=u.prototype=new e,B=18;A.brighter=function(t){return new u(this.l+B*(null==t?1:t),this.a,this.b)},A.darker=function(t){return new u(this.l-B*(null==t?1:t),this.a,this.b)};var D=4/29,F=6/29,G=3*F*F,J=1.08883,K=.95047,Q=1;A.rgb=function(){var t=(this.l+16)/116,e=isNaN(this.a)?t:t+this.a/500,n=isNaN(this.b)?t:t-this.b/200;return t=Q*g(t),e=K*g(e),n=J*g(n),new s(c(3.2404542*e-1.5371385*t-.4985314*n),c(-.969266*e+1.8760108*t+.041556*n),c(.0556434*e-.2040259*t+1.0572252*n))};var T=F*F*F,U=Math.PI/180,V=p.prototype=new e;V.brighter=function(t){return new p(this.h,this.c,this.l+B*(null==t?1:t))},V.darker=function(t){return new p(this.h,this.c,this.l-B*(null==t?1:t))},V.rgb=function(){return b(this).rgb()},b.prototype=u.prototype;var W=180/Math.PI;N.prototype=p.prototype;var X=m.prototype=new e;X.brighter=function(t){return t=null==t?I:Math.pow(I,t),new m(this.h,this.s,this.l*t)},X.darker=function(t){return t=null==t?H:Math.pow(H,t),new m(this.h,this.s,this.l*t)};var Y=1.97294,Z=-.90649,tt=-.29227,et=1.78277,st=-.14861,nt=1,rt=Y*et,it=Y*Z,at=et*tt-Z*st;X.rgb=function(){var t=isNaN(this.h)?0:(this.h+120)*U,e=Math.pow(this.l,nt),n=isNaN(this.s)?0:this.s*e*(1-e),r=Math.cos(t),i=Math.sin(t);return new s(255*(e+n*(st*r+et*i)),255*(e+n*(tt*r+Z*i)),255*(e+n*Y*r))},w.prototype=m.prototype,t.color=l,t.rgb=r,t.hsl=h,t.lab=b,t.hcl=N,t.cubehelix=w,t.interpolateRgb=j,t.interpolateHsl=S,t.interpolateHslLong=_,t.interpolateLab=q,t.interpolateHcl=x,t.interpolateHclLong=k,t.interpolateCubehelix=M,t.interpolateCubehelixLong=y});
var canvas = document.querySelector("canvas"),
width = canvas.width,
height = canvas.height,
context = canvas.getContext("2d"),
voro = voronoi.voronoi().extent([[0.5, 0.5], [width - 0.5, height - 0.5]]);
var rainbow1 = color.interpolateCubehelixLong(color.cubehelix(-100, 0.75, 0.35), color.cubehelix(80, 1.50, 0.8)),
rainbow2 = color.interpolateCubehelixLong(color.cubehelix(80, 1.50, 0.8), color.cubehelix(260, 0.75, 0.35));
var n = 50,
particles = new Array(n),
radius = 20;
for (var i = 0; i < n; ++i) particles[i] = {0: Math.random() * width, 1: Math.random() * height, vx: 0, vy: 0};
timer.timer(function(elapsed) {
for (var i = 0; i < n; ++i) {
var p = particles[i];
p[0] += p.vx; if (p[0] < 0) p[0] = p.vx *= -1; else if (p[0] > width) p[0] = width + (p.vx *= -1);
p[1] += p.vy; if (p[1] < 0) p[1] = p.vy *= -1; else if (p[1] > height) p[1] = height + (p.vy *= -1);
p.vx += 0.1 * (Math.random() - .5) - 0.01 * p.vx;
p.vy += 0.1 * (Math.random() - .5) - 0.01 * p.vy;
}
var cells = voro(particles);
context.beginPath();
cells.forEach(function(cell) { drawRoundedPolygon(cell, radius); });
context.globalAlpha = 0.2;
context.lineWidth = 4;
context.strokeStyle = "#000";
context.stroke();
context.beginPath();
cells.forEach(function(cell) { drawRoundedPolygon(cell, radius); });
context.globalAlpha = 1;
var t = (elapsed / 5000) % 2;
context.strokeStyle = t > 1 ? rainbow2(t - 1) : rainbow1(t);
context.lineWidth = 1.5;
context.stroke();
});
function drawPoint(point) {
context.moveTo(point[0] + 1.5, point[1]);
context.arc(point[0], point[1], 1.5, 0, 2 * Math.PI);
}
function drawPolygon(points) {
context.moveTo(points[0][0], points[0][1]);
for (var i = 1, n = points.length; i < n; ++i) context.lineTo(points[i][0], points[i][1]);
context.closePath();
}
function drawRoundedPolygon(points, r) {
var i,
n = points.length,
p0,
p1,
p2,
p3,
n1 = 0,
t012,
t123,
x21, y21,
x4, y4,
x5, y5,
moved,
circle = polygonIncircle(points);
// Build a linked list from the array of vertices so we can splice.
for (i = 0, p1 = points[n - 2], p2 = points[n - 1]; i < n; ++i) {
p0 = p1, p1 = p2, p2 = points[i];
p1.previous = p0;
p1.next = p2;
}
// The rounding radius can’t be bigger than the polygon’s incircle.
// The fudge factor of 1px lets the rounded polygon get squished a bit.
// TODO Abort the search for the incircle if one is found larger than r.
r = Math.min(r, circle.radius - 1);
if (r <= 0) return;
// TODO do we need to make all these extra passes?
for (i = 0, p3 = p2.next; n1 <= n; ++n1) {
p0 = p1, p1 = p2, p2 = p3, p3 = p3.next;
t012 = cornerTangent(p0[0], p0[1], p1[0], p1[1], p2[0], p2[1], r);
t123 = 1 - cornerTangent(p3[0], p3[1], p2[0], p2[1], p1[0], p1[1], r);
// If the following corner’s tangent is before this corner’s tangent,
// replace p1 and p2 with the intersection of the lines 01 and 23.
if (t012 >= t123) {
p2 = p0.next = p3.previous = lineLineIntersection(p0[0], p0[1], p1[0], p1[1], p2[0], p2[1], p3[0], p3[1]);
p2.previous = p0;
p2.next = p3;
p3 = p2;
p2 = p3.previous;
p1 = p2.previous;
p0 = p1.previous;
n1 = 0;
if (--n < 3) break;
}
}
// If we removed too many points, just draw the previously computed incircle.
if (n < 3) {
context.moveTo(circle[0] + circle.radius, circle[1]);
context.arc(circle[0], circle[1], circle.radius, 0, 2 * Math.PI);
return;
}
// Draw the rounded polygon, compting the corner tangents.
for (i = 0; i <= n; ++i) {
p0 = p1, p1 = p2, p2 = p3, p3 = p3.next;
t012 = cornerTangent(p0[0], p0[1], p1[0], p1[1], p2[0], p2[1], r);
t123 = 1 - cornerTangent(p3[0], p3[1], p2[0], p2[1], p1[0], p1[1], r);
x21 = p2[0] - p1[0], y21 = p2[1] - p1[1];
x4 = p1[0] + t012 * x21, y4 = p1[1] + t012 * y21;
x5 = p1[0] + t123 * x21, y5 = p1[1] + t123 * y21;
if (moved) context.arcTo(p1[0], p1[1], x4, y4, r);
else moved = true, context.moveTo(x4, y4);
context.lineTo(x5, y5);
}
}
// Given a circle of radius r that is tangent to the line segments 01 and 12,
// returns the parameter t of the tangent along the line segment 12.
function cornerTangent(x0, y0, x1, y1, x2, y2, r) {
var theta = innerAngle(x0, y0, x1, y1, x2, y2),
x21 = x2 - x1, y21 = y2 - y1,
l21 = Math.sqrt(x21 * x21 + y21 * y21),
l14 = r / Math.tan(theta / 2);
return l14 / l21;
}
// A horrible brute-force algorithm for determining the largest circle that can
// fit inside a convex polygon. For each distinct set of three sides of the
// polygon, compute the tangent circle. Then reduce the circle’s radius against
// the remaining sides of the polygon.
function polygonIncircle(points) {
var circle = {radius: 0};
for (var i = 0, n = points.length; i < n; ++i) {
var pi0 = points[i],
pi1 = points[(i + 1) % n];
for (var j = i + 1; j < n; ++j) {
var pj0 = points[j],
pj1 = points[(j + 1) % n],
pij = j === i + 1 ? pj0 : lineLineIntersection(pi0[0], pi0[1], pi1[0], pi1[1], pj0[0], pj0[1], pj1[0], pj1[1]);
search: for (var k = j + 1; k < n; ++k) {
var pk0 = points[k],
pk1 = points[(k + 1) % n],
pik = lineLineIntersection(pi0[0], pi0[1], pi1[0], pi1[1], pk0[0], pk0[1], pk1[0], pk1[1]),
pjk = k === j + 1 ? pk0 : lineLineIntersection(pj0[0], pj0[1], pj1[0], pj1[1], pk0[0], pk0[1], pk1[0], pk1[1]),
candidate = triangleIncircle(pij[0], pij[1], pik[0], pik[1], pjk[0], pjk[1]),
radius = candidate.radius;
for (var l = 0; l < n; ++l) {
var pl0 = points[l],
pl1 = points[(l + 1) % n],
r = pointLineDistance(candidate[0], candidate[1], pl0[0], pl0[1], pl1[0], pl1[1]);
if (r < circle.radius) continue search;
if (r < radius) radius = r;
}
circle = candidate;
circle.radius = radius;
}
}
}
return circle;
}
// Returns the angle between segments 01 and 12.
function innerAngle(x0, y0, x1, y1, x2, y2) {
var x01 = x0 - x1, y01 = y0 - y1,
x12 = x1 - x2, y12 = y1 - y2,
x02 = x0 - x2, y02 = y0 - y2,
l01_2 = x01 * x01 + y01 * y01,
l12_2 = x12 * x12 + y12 * y12,
l02_2 = x02 * x02 + y02 * y02;
return Math.acos((l12_2 + l01_2 - l02_2) / (2 * Math.sqrt(l12_2 * l01_2)));
}
// Returns the intersection of the infinite lines 01 and 23.
function lineLineIntersection(x0, y0, x1, y1, x2, y2, x3, y3) {
var x02 = x0 - x2, y02 = y0 - y2,
x10 = x1 - x0, y10 = y1 - y0,
x32 = x3 - x2, y32 = y3 - y2,
t = (x32 * y02 - y32 * x02) / (y32 * x10 - x32 * y10);
return [x0 + t * x10, y0 + t * y10];
}
// Returns the signed distance from point 0 to the infinite line 12.
function pointLineDistance(x0, y0, x1, y1, x2, y2) {
var x21 = x2 - x1, y21 = y2 - y1;
return (y21 * x0 - x21 * y0 + x2 * y1 - y2 * x1) / Math.sqrt(y21 * y21 + x21 * x21);
}
// Returns the largest circle inside the triangle 012.
function triangleIncircle(x0, y0, x1, y1, x2, y2) {
var x01 = x0 - x1, y01 = y0 - y1,
x02 = x0 - x2, y02 = y0 - y2,
x12 = x1 - x2, y12 = y1 - y2,
l01 = Math.sqrt(x01 * x01 + y01 * y01),
l02 = Math.sqrt(x02 * x02 + y02 * y02),
l12 = Math.sqrt(x12 * x12 + y12 * y12),
k0 = l01 / (l01 + l02),
k1 = l12 / (l12 + l01),
center = lineLineIntersection(x0, y0, x1 - k0 * x12, y1 - k0 * y12, x1, y1, x2 + k1 * x02, y2 + k1 * y02);
center.radius = Math.sqrt((l02 + l12 - l01) * (l12 + l01 - l02) * (l01 + l02 - l12) / (l01 + l02 + l12)) / 2;
return center;
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment