Use the range slider to change the degree of subdivision in this geodesic sphere.
The base shape, visible when subdivision is disabled, is one of the 5 regular convex polyhedra (with triangularized faces for cube and dodecahedron), a.k.a. Platonic solids:
Built with a modified version of the d3.geodesic plugin which adds all Platonic solids.
Adapted to use D3v4.
Evolved from my previous block which was as well inspired by Mike Bostock's Geodesic Rainbow.
xxxxxxxxxx
<meta charset="utf-8">
<style>
#instructions {
position: absolute;
top: 20px;
left: 250px;
}
#subdivision {
position: absolute;
top: 10px;
left: 20px;
}
#subdivision input {
width: 200px;
}
#polyhedrontype {
position: absolute;
top: 40px;
left: 20px;
}
</style>
<div id="subdivision">
<input type="range" min="1" max="16" value="4">
<output name="subdivision"></output>
</div>
<div id="polyhedrontype">
<input type="radio" name="polyhedron" value="tetrahedron">tetrahedron<br>
<input type="radio" name="polyhedron" value="octahedron">octaedron<br>
<input type="radio" name="polyhedron" value="hexahedron">hexahedron (cube)<br>
<input type="radio" name="polyhedron" value="dodecahedron">dodecahedron<br>
<input type="radio" name="polyhedron" value="icosahedron" checked="checked">icosahedron<br>
</div>
<div id="instructions"><p>Drag to rotate!</p></div>
<script src="//d3js.org/d3.v4.min.js"></script>
<!-- <script src="d3.min.js"></script> -->
<script src="d3.geodesic.js"></script>
<script src="versor.js"></script>
<script>
var width = 960,
height = 500;
// var velocity = [.030, .005],
// t0 = Date.now();
var projection = d3.geoOrthographic()
.scale(height / 2 - 10);
var canvas = d3.select("body").append("canvas")
.attr("width", width)
.attr("height", height);
canvas.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged));
var render = function() {},
v0, // Mouse position in Cartesian coordinates at start of drag gesture.
r0, // Projection rotation as Euler angles at start.
q0; // Projection rotation as versor at start.
function dragstarted() {
v0 = versor.cartesian(projection.invert(d3.mouse(this)));
r0 = projection.rotate();
q0 = versor(r0);
}
function dragged() {
var v1 = versor.cartesian(projection.rotate(r0).invert(d3.mouse(this))),
q1 = versor.multiply(q0, versor.delta(v0, v1)),
r1 = versor.rotation(q1);
projection.rotate(r1);
redraw();
}
var context = canvas.node().getContext("2d");
context.strokeStyle = "#000";
context.lineWidth = .5;
var faces;
var output = d3.select("output");
var poly = "icosahedron";
var subdivision = 1;
var input_subdivision = d3.select("#subdivision input")
.on("change", function() {
subdivision = +this.value;
geodesic(subdivision, poly);
})
.each(function() {
subdivision = +this.value;
geodesic(subdivision, poly);
});
var input_poly = d3.selectAll("input[name='polyhedron']")
.on("change", function() {
poly = this.value;
geodesic(subdivision, poly);
});
// 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 = d3.polygonArea(d.polygon) > 0) {
context.fillStyle = d.fill;
context.beginPath();
drawTriangle(d.polygon);
context.fill();
}
});
context.beginPath();
faces.forEach(function(d) {
if (d.visible) {
drawTriangle(d.polygon);
}
});
context.strokeStyle = "black";
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, poly) {
output.text(subdivision);
var polyhedron;
switch(poly) {
case "tetrahedron":
polyhedron = d3.tetrahedron;
break;
case "hexahedron":
polyhedron = d3.hexahedron;
break;
case "octahedron":
polyhedron = d3.octahedron;
break;
case "dodecahedron":
polyhedron = d3.dodecahedron;
break;
default:
polyhedron = d3.icosahedron;
}
faces = polyhedron.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 = d.map(projection);
return d;
});
redraw();
}
</script>
https://d3js.org/d3.v4.min.js