Skip to content

Instantly share code, notes, and snippets.

@tylerball
Last active August 29, 2015 14:10
Show Gist options
  • Save tylerball/4c8e2b6e769aa69ad4ea to your computer and use it in GitHub Desktop.
Save tylerball/4c8e2b6e769aa69ad4ea to your computer and use it in GitHub Desktop.
3d piechart

3d piechart using THREE.js and some lodash.

The Pie constructor takes two arguments, the id of the element to place it and a data object. The data object needs some values:

  • divisions: An array of floats representing their percentage share of the graph. The remainder will be automatically filled.
  • colors: An array of colours. Should be one element longer than divisions to accommodate colouring the remainder.
<!DOCTYPE html>
<meta charset="utf-8">
<body>
<div id="Pie" style="width: 500px; height: 500px; margin: 0 auto;"></div>
<script src="//cdnjs.cloudflare.com/ajax/libs/lodash.js/2.4.1/lodash.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/three.js/r69/three.min.js"></script>
<script src="pie.js"></script>
<script>
new Pie('Pie', {
divisions: [0.25, 0.6],
colors: [0xEEEEEE, 0xDDDDDD, 0xCCCCCC]
});
</script>
</body>
(function (_, THREE) {
var Pie = window.Pie = function (id, data) {
this.el = document.getElementById(id);
this.colors = data.colors;
this.divisions = data.divisions;
this.init();
};
Pie.prototype.init = function () {
var scene = this.scene = new THREE.Scene();
var fov = 45; // camera's field of view
var viewWidth = this.el.offsetWidth;
var viewHeight = this.el.offsetHeight;
var camera = this.camera = new THREE.PerspectiveCamera(
fov, viewWidth / viewHeight, 1, 1000
);
var renderer;
if (window.WebGLRenderingContext) {
renderer = new THREE.WebGLRenderer({ antialiasing: true });
} else {
renderer = new THREE.CanvasRenderer();
}
this.renderer = renderer;
renderer.setSize(viewWidth, viewHeight);
renderer.setClearColor(0xffffff, 1); // white bg
this.el.appendChild(renderer.domElement);
var pie = this.pie = this.build();
scene.add(pie);
pie.rotation.x = Math.PI * 0.6;
camera.lookAt(pie.position);
// dynamically calcuate the camera position in order to fit the pie in view
// http://stackoverflow.com/a/2866471
//camera.position.z = pie.scale.x / Math.tan(Math.PI * fov / 360);
camera.position.z = 6;
var render = function () {
window.requestAnimationFrame(render);
pie.rotation.z += 0.005;
renderer.render(scene, camera);
};
render();
};
Pie.prototype.build = function () {
var pie = new THREE.Group();
var total = 2 * Math.PI;
var reducer = function (memo, num) { return memo + num; };
// fill the first segment
pie.add(this.buildSegment(0, total * this.divisions[0], this.colors[0]));
for (var i = 1; i < this.divisions.length; i++) {
pie.add(this.buildSegment(
// get the sum of all radii before this
total * _.reduce(_.first(this.divisions, i), reducer, 0),
total * this.divisions[i],
this.colors[i]
));
}
// fill the rest of the pie
var remainder = total * _.reduce(this.divisions, reducer, 0);
pie.add(this.buildSegment(remainder, total - remainder, _.last(this.colors)));
return pie;
};
Pie.prototype.buildSegment = function (start, end, color) {
var points = [];
points.push(new THREE.Vector3(0, 0, 0));
points.push(new THREE.Vector3(0, 2, 0));
points.push(new THREE.Vector3(0, 2, 1));
points.push(new THREE.Vector3(0, 0, 1));
var geometry = new THREE.LatheGeometry(points, 24, start, end);
var material = new THREE.MeshBasicMaterial({ color: color });
return new THREE.Mesh(geometry, material);
};
})(window._, window.THREE);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment