(function() { // ****** icosahedron ****** var φ = 1.618033988749895, ρ = 180 / Math.PI; var vertices_icosahedron = [ [1,φ,0], [-1,φ,0], [1,-φ,0], [-1,-φ,0], [0,1,φ], [0,-1,φ], [0,1,-φ], [0,-1,-φ], [φ,0,1], [-φ,0,1], [φ,0,-1], [-φ,0,-1] ]; // icosahedron faces var faces_icosahedron = [ [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(face) { return face.map(function(i) { return vertices_icosahedron[i]; }); }); // ****** tetraahedron ****** var pi = Math.PI; var degrees = 180 / pi; var asin1_3 = Math.asin(1 / 3); var vertices_tetrahedron = [ [0 , 0 , 1], [Math.sqrt(8/9), 0 , -1/3], [-Math.sqrt(2/9), Math.sqrt(2/3), -1/3], [-Math.sqrt(2/9), -Math.sqrt(2/3), -1/3] // [ 0, pi / 2], // [-180, -asin1_3], // [ -60, -asin1_3], // [ 60, -asin1_3] ].map(function(vertex){ return vertex; // return cartesian(vertex); }); var faces_tetrahedron = [ [0,2,1], [0,3,2], [0,1,3], [1,2,3] ].map(function(face) { return face.map(function(i) { return vertices_tetrahedron[i]; }); }); d3.icosahedron = { vertices: function() {return vertices_icosahedron}, faces: function() { return faces_icosahedron; }, multipolygon: function(n) { return { type: "MultiPolygon", coordinates: subdivideFaces(~~n, this).map(function(face) { face = face.map(project); face.push(face[0]); face = [face]; return face; }) }; }, polygons: function(n) { return d3.icosahedron.multipolygon(~~n).coordinates.map(function(face) { return {type: "Polygon", coordinates: face}; }); }, multilinestring: function(n) { return { type: "MultiLineString", coordinates: subdivideEdges(~~n, this).map(function(edge) { return edge.map(project); }) }; } }; d3.tetrahedron = { vertices: function() {return vertices_tetrahedron}, faces: function() { return faces_tetrahedron; }, multipolygon: function(n) { return { type: "MultiPolygon", coordinates: subdivideFaces(~~n, this).map(function(face) { face = face.map(project); face.push(face[0]); face = [face]; return face; }) }; }, polygons: function(n) { return d3.tetrahedron.multipolygon(~~n).coordinates.map(function(face) { return {type: "Polygon", coordinates: face}; }); }, multilinestring: function(n) { return { type: "MultiLineString", coordinates: subdivideEdges(~~n, this).map(function(edge) { return edge.map(project); }) }; } }; function subdivideFaces(n, polyhedron) { return d3.merge(polyhedron.faces().map(function(face) { var i01 = interpolate(face[0], face[1]), i02 = interpolate(face[0], face[2]), faces = []; faces.push([ face[0], i01(1 / n), i02(1 / n) ]); for (var i = 1; i < n; ++i) { var i1 = interpolate(i01(i / n), i02(i / n)), i2 = interpolate(i01((i + 1) / n), i02((i + 1) / n)); for (var j = 0; j <= i; ++j) { faces.push([ i1(j / i), i2(j / (i + 1)), i2((j + 1) / (i + 1)) ]); } for (var j = 0; j < i; ++j) { faces.push([ i1(j / i), i2((j + 1) / (i + 1)), i1((j + 1) / i) ]); } } return faces; })); } function subdivideEdges(n, polyhedron) { var edges = {}; subdivideFaces(n, polyhedron).forEach(function(face) { add(face[0], face[1]); add(face[1], face[2]); add(face[2], face[0]); }); function add(p0, p1) { var t; if (p0[0] < p1[0] || (p0[0] == p1[0] && (p0[1] < p1[1] || (p0[1] == p1[1] && p0[2] < p1[2])))) t = p0, p0 = p1, p1 = t; polyhedron.edges()[p0.map(round) + " " + p1.map(round)] = [p0, p1]; } function round(d) { return d3.round(d, 4); } return d3.values(edges); } function interpolate(p0, p1) { var x0 = p0[0], y0 = p0[1], z0 = p0[2], x1 = p1[0] - x0, y1 = p1[1] - y0, z1 = p1[2] - z0; return function(t) { return [ x0 + t * x1, y0 + t * y1, z0 + t * z1 ]; }; } function cartesian(spherical) { var lambda = spherical[0], phi = spherical[1], cosPhi = Math.cos(phi); return [cosPhi * Math.cos(lambda), cosPhi * Math.sin(lambda), Math.sin(phi)]; } function project(p) { var x = p[0], y = p[1], z = p[2]; return [ Math.atan2(y, x) * ρ, Math.acos(z / Math.sqrt(x * x + y * y + z * z)) * ρ - 90 ]; } })();