D3
OG
Old school D3 from simpler times
All examples
By author
By category
About
armollica
Full window
Github gist
Isometric Projection
Isometric projection (Wikipedia)
<html> <head> <style> html, body { font-family: monospace; } .cube .side { fill-opacity: 0.4; stroke: #666; stroke-width: 0.5; } .top { fill: steelblue; } .bottom { fill: yellow; } .left { fill: brown; } .right { fill: forestgreen; } .axis { fill: none; stroke: #000; } </style> </head> <body> <script src="https://d3js.org/d3.v4.min.js"></script> <script> var width = 960, height = 500; var svg = d3.select('body').append('svg') .attr('width', width) .attr('height', height); var projection = isometricProjection(); var path = isometricPath() .projection(projection); svg.append('defs').append('marker') .attr('id', 'arrow') .attr('markerWidth', 10) .attr('markerHeight', 10) .attr('refX', 0) .attr('refY', 3) .attr('orient', 'auto') .attr('markerUnits', 'strokeWidth') .append('path') .attr('d', 'M0,0L0,6L9,3Z'); var g = svg.append('g') .attr('transform', function(d) { return 'translate(' + (width / 2) + ',' + (height / 2) + ')'; }); var axes = g.append('g').attr('class', 'axes'); var axis = axes.selectAll('.axis').data(axesData()) .enter().append('g') .attr('class', 'axis'); axis.append('path') .attr('d', function(d) { return path(d.points); }) .attr('marker-end', 'url(#arrow)'); axis.append('text') .attr('transform', function(d) { var p = projection(d.labelLocation); return 'translate(' + p[0] + ',' + p[1] + ')'; }) .attr('dy', '0.33em') .style('text-anchor', 'middle') .text(function(d) { return d.label; }); var cube = g.append('g').attr('class', 'cube'); var side = cube.selectAll('path').data(cubeData()) .enter().append('path') .attr('class', function(d) { return 'side ' + d.className; }) .attr('d', function(d) { return path(d.points) + 'Z'; }); function update() { side.attr('d', function(d) { return path(d.points) + 'Z'; }); axis.select('path') .attr('d', function(d) { return path(d.points); }); axis.select('text') .attr('transform', function(d) { var p = projection(d.labelLocation); return 'translate(' + p[0] + ',' + p[1] + ')'; }); } d3.timer(function(elapsed) { projection .pitch((Math.PI / 6) * Math.sin(elapsed / 10000)) .yaw((elapsed / 2000)); update(); }); function cubeData() { return [ { className: 'bottom', points: [ [-50, -50, -50], [-50, +50, -50], [+50, +50, -50], [+50, -50, -50] ] }, { className: 'left', points: [ [+50, -50, +50], [+50, +50, +50], [+50, +50, -50], [+50, -50, -50] ] }, { className: 'right', points: [ [-50, +50, +50], [+50, +50, +50], [+50, +50, -50], [-50, +50, -50] ] }, { className: 'left', points: [ [-50, -50, +50], [-50, +50, +50], [-50, +50, -50], [-50, -50, -50] ] }, { className: 'right', points: [ [-50, -50, +50], [+50, -50, +50], [+50, -50, -50], [-50, -50, -50] ] }, { className: 'top', points: [ [-50, -50, +50], [-50, +50, +50], [+50, +50, +50], [+50, -50, +50] ] }, ]; } function axesData() { return [ { label: 'x', labelLocation: [225, 0, 0], points: [ [0, 0, 0], [200, 0, 0] ] }, { label: 'y', labelLocation: [0, 225, 0], points: [ [0, 0, 0], [0, 200, 0] ] }, { label: 'z', labelLocation: [0, 0, 225], points: [ [0, 0, 0], [0, 0, 200] ] } ]; } function isometricProjection() { var sin = Math.sin, cos = Math.cos, asin = Math.asin, tan = Math.atan, PI = Math.PI; var pitch = PI / 6, yaw = PI / 4, alpha = asin(tan(pitch)), beta = yaw; // See https://en.wikipedia.org/wiki/Isometric_projection // TODO: Figure out why ax, ay and az needed to be flipped around. function project(point) { var ax = point[1], ay = -point[2], az = point[0]; var x = cos(beta) * ax - sin(beta) * az, y = cos(alpha) * ay + sin(alpha) * (sin(beta) * ax + cos(beta) * az); return [x, y]; } project.pitch = function(x) { if (!arguments.length) return alpha; pitch = x; alpha = Math.asin(Math.tan(pitch)); return project; }; project.yaw = function(x) { if (!arguments.length) return beta; yaw = x; beta = yaw; return project; }; return project; } function isometricPath() { var projection = function(d) { return d.slice(0, 2); }; function path(points) { return 'M' + points .map(function(point) { return projection(point).join(','); }) .join('L'); } path.projection = function(x) { if (!arguments.length) return projection; projection = x; return path; }; return path; } function radiansToDegrees(radians) { return radians * 180 / Math.PI; } function degreesToRadians(degrees) { return degrees * Math.PI / 180; } </script> </body> </html>
https://d3js.org/d3.v4.min.js