Skip to content

Instantly share code, notes, and snippets.

@davo
Forked from mbostock/.block
Last active October 24, 2016 22:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save davo/490f38ed9b559f61d1401f64f0bc450d to your computer and use it in GitHub Desktop.
Save davo/490f38ed9b559f61d1401f64f0bc450d to your computer and use it in GitHub Desktop.
Geodesic Rainbow
license: gpl-3.0

Use the range slider to change the degree of subdivision in this geodesic sphere. The base shape, visible when subdivision is disabled, is the icosahedron. Built with the d3.geodesic plugin.

!function(){function n(n){return d3.merge(e.map(function(t){var o=r(t[0],t[1]),u=r(t[0],t[2]),i=[];i.push([t[0],o(1/n),u(1/n)]);for(var a=1;n>a;++a){for(var e=r(o(a/n),u(a/n)),c=r(o((a+1)/n),u((a+1)/n)),f=0;a>=f;++f)i.push([e(f/a),c(f/(a+1)),c((f+1)/(a+1))]);for(var f=0;a>f;++f)i.push([e(f/a),c((f+1)/(a+1)),e((f+1)/a)])}return i}))}function t(t){function r(n,t){var r;(n[0]<t[0]||n[0]==t[0]&&(n[1]<t[1]||n[1]==t[1]&&n[2]<t[2]))&&(r=n,n=t,t=r),u[n.map(o)+" "+t.map(o)]=[n,t]}function o(n){return d3.round(n,4)}var u={};return n(t).forEach(function(n){r(n[0],n[1]),r(n[1],n[2]),r(n[2],n[0])}),d3.values(u)}function r(n,t){var r=n[0],o=n[1],u=n[2],i=t[0]-r,a=t[1]-o,e=t[2]-u;return function(n){return[r+n*i,o+n*a,u+n*e]}}function o(n){var t=n[0],r=n[1],o=n[2];return[Math.atan2(r,t)*i,Math.acos(o/Math.sqrt(t*t+r*r+o*o))*i-90]}var u=1.618033988749895,i=180/Math.PI,a=[[1,u,0],[-1,u,0],[1,-u,0],[-1,-u,0],[0,1,u],[0,-1,u],[0,1,-u],[0,-1,-u],[u,0,1],[-u,0,1],[u,0,-1],[-u,0,-1]],e=[[0,1,4],[1,9,4],[4,9,5],[5,9,3],[2,3,7],[3,2,5],[7,10,2],[0,8,10],[0,4,8],[8,2,10],[8,4,5],[8,5,2],[1,0,6],[11,1,6],[3,9,11],[6,10,7],[3,11,7],[11,6,7],[6,0,10],[9,1,11]].map(function(n){return n.map(function(n){return a[n]})});d3.geodesic={multipolygon:function(t){return{type:"MultiPolygon",coordinates:n(~~t).map(function(n){return n=n.map(o),n.push(n[0]),n=[n]})}},polygons:function(n){return d3.geodesic.multipolygon(~~n).coordinates.map(function(n){return{type:"Polygon",coordinates:n}})},multilinestring:function(n){return{type:"MultiLineString",coordinates:t(~~n).map(function(n){return n.map(o)})}}}}();
<!DOCTYPE html>
<meta charset="utf-8">
<style>
#subdivision {
position: absolute;
top: 20px;
left: 20px;
}
#subdivision input {
width: 200px;
}
</style>
<div id="subdivision">
<input type="range" min="1" max="12" value="8">
<output name="subdivision"></output>
</div>
<script src="//d3js.org/d3.v3.min.js"></script>
<script src="d3.geodesic.min.js"></script>
<script>
var width = 960,
height = 500;
var velocity = [.010, .005],
t0 = Date.now();
var projection = d3.geo.orthographic()
.scale(height / 2 - 10);
var canvas = d3.select("body").append("canvas")
.attr("width", width)
.attr("height", height);
var context = canvas.node().getContext("2d");
context.strokeStyle = "#000";
context.lineWidth = .5;
var faces;
var output = d3.select("output");
var input = d3.select("input")
.on("input", function() { geodesic(+this.value); })
.on("change", function() { geodesic(+this.value); })
.each(function() { geodesic(+this.value); });
d3.timer(function() {
var time = Date.now() - t0;
projection.rotate([time * velocity[0], time * velocity[1]]);
redraw();
});
function redraw() {
context.clearRect(0, 0, width, height);
faces.forEach(function(d) {
d.polygon[0] = projection(d[0]);
d.polygon[1] = projection(d[1]);
d.polygon[2] = projection(d[2]);
if (d.visible = d.polygon.area() > 0) {
context.fillStyle = rgba(0,0,0,.2);
context.beginPath();
drawTriangle(d.polygon);
context.fill();
}
});
context.beginPath();
faces.forEach(function(d) {
if (d.visible) {
drawTriangle(d.polygon);
}
});
context.stroke();
}
function drawTriangle(triangle) {
context.moveTo(triangle[0][0], triangle[0][1]);
context.lineTo(triangle[1][0], triangle[1][1]);
context.lineTo(triangle[2][0], triangle[2][1]);
context.closePath();
}
function geodesic(subdivision) {
output.text(subdivision);
faces = d3.geodesic.polygons(subdivision).map(function(d) {
d = d.coordinates[0];
d.pop(); // use an open polygon
d.fill = d3.hsl(d[0][0], 1, .5) + "";
d.polygon = d3.geom.polygon(d.map(projection));
return d;
});
redraw();
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment