A threejs version of this d3 contour plot. A 2D array of values
is defined on grid with coordinates xgrid
and ygrid
. threejs splits each cell into 2 triangle faces. The color of each vertex is set using the same d3 color scale as before.
Last active
February 17, 2019 20:26
-
-
Save grahampullan/b3beb793b10382c13f7a34c843156a8c to your computer and use it in GitHub Desktop.
threejs surface from a structured grid of data
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
license: mit |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<div id="container"></div> | |
<script src="https://threejs.org/build/three.js"></script> | |
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script> | |
<script src="https://d3js.org/d3.v4.min.js"></script> | |
<script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script> | |
<script> | |
// Structured (n * m) grid of data. Point coordinates are (xgrid, ygrid) | |
var n = 101, m = 101; | |
var nverts = n*m; | |
var values = new Array(n * m); | |
var xgrid = new Array(n * m); | |
var ygrid = new Array(n * m); | |
for (var j = 0., k = 0; j < m; ++j) { | |
for (var i = 0.; i < n; ++i, ++k) { | |
xgrid[k] = i; | |
ygrid[k] = 25*Math.sin(i*Math.PI/50) + j; | |
values[k] = Math.pow( (xgrid[k]-50), 2 ) + Math.pow( (ygrid[k]-50), 2 ); | |
} | |
} | |
// Obtain centre of grid and scale factors | |
var xmin = d3.min(xgrid); | |
var xmax = d3.max(xgrid); | |
var xmid = 0.5*(xmin+xmax); | |
var xrange = xmax-xmin; | |
var ymin = d3.min(ygrid); | |
var ymax = d3.max(ygrid); | |
var ymid = 0.5*(ymin+ymax); | |
var yrange = ymax-ymin; | |
var zmin = d3.min(values); | |
var zmax = d3.max(values); | |
var zmid = 0.5*(zmin+zmax); | |
var zrange = zmax-zmin; | |
var scalefac = 1.2/Math.max(xrange, yrange); | |
var scalefacz = 0.5/zrange; | |
// Use d3 for color scale | |
var color = d3.scaleLinear() | |
.domain(d3.extent(values)) | |
.interpolate(function() { return d3.interpolateRdBu; }); | |
// Initialise threejs geometry | |
var geometry = new THREE.Geometry(); | |
// Add grid vertices to geometry | |
for (var k = 0; k < nverts; ++k) { | |
var newvert= new THREE.Vector3( (xgrid[k] - xmid) * scalefac, (ygrid[k] - ymid) * scalefac, (values[k] - zmid) * scalefacz); | |
geometry.vertices.push(newvert); | |
} | |
// Add cell faces (2 traingles per cell) to geometry | |
for (var j = 0; j < m-1; j++){ | |
for (var i = 0; i < n-1; i++){ | |
var n0 = j*n + i; | |
var n1 = n0 + 1; | |
var n2 = (j+1)*n + i + 1; | |
var n3 = n2 - 1; | |
face1= new THREE.Face3(n0,n1,n2); | |
face2= new THREE.Face3(n2,n3,n0); | |
face1.vertexColors[0]=new THREE.Color(color(values[n0])); | |
face1.vertexColors[1]=new THREE.Color(color(values[n1])); | |
face1.vertexColors[2]=new THREE.Color(color(values[n2])); | |
face2.vertexColors[0]=new THREE.Color(color(values[n2])); | |
face2.vertexColors[1]=new THREE.Color(color(values[n3])); | |
face2.vertexColors[2]=new THREE.Color(color(values[n0])); | |
geometry.faces.push(face1); | |
geometry.faces.push(face2); | |
} | |
} | |
// Compute normals for shading | |
geometry.computeFaceNormals(); | |
geometry.computeVertexNormals(); | |
// Use MeshPhongMaterial for a reflective surface | |
var material = new THREE.MeshPhongMaterial( { | |
side: THREE.DoubleSide, | |
color: 0xffffff, | |
vertexColors: THREE.VertexColors, | |
specular: 0x050505, | |
shininess: 100., | |
emissive: 0x111111, | |
}); | |
// Initialise threejs scene | |
var scene = new THREE.Scene(); | |
// Add Mesh to scene | |
scene.add( new THREE.Mesh( geometry, material ) ); | |
// Create renderer | |
var renderer = new THREE.WebGLRenderer({alpha:true, antialias:true}); | |
renderer.setPixelRatio( window.devicePixelRatio ); | |
renderer.setSize( 500, 500 ); | |
// Set target DIV for rendering | |
var container = document.getElementById( 'container' ); | |
container.appendChild( renderer.domElement ); | |
// Define the camera | |
var camera = new THREE.PerspectiveCamera( 45, 1, 0.1, 10 ); | |
camera.position.z = 2; | |
// Add controls | |
var controls = new THREE.OrbitControls( camera, renderer.domElement ); | |
controls.addEventListener( 'change', function(){ | |
renderer.render(scene,camera); // re-render if controls move/zoom | |
} ); | |
controls.enableZoom = true; | |
// Light above | |
var light = new THREE.PointLight( 0xffffff); | |
light.position.set( 0, 0, 3 ); | |
scene.add( light ); | |
// Light below | |
var light = new THREE.PointLight( 0xffffff); | |
light.position.set( 0, 0, -3 ); | |
scene.add( light ); | |
// Ambient light | |
var light = new THREE.AmbientLight( 0x222222 ); | |
scene.add( light ); | |
// Make initial call to render scene | |
renderer.render( scene, camera ); | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment