Skip to content

Instantly share code, notes, and snippets.

@dongli
Last active January 19, 2017 03:07
Show Gist options
  • Save dongli/e9291c411cd7ed409e71bcd14d681952 to your computer and use it in GitHub Desktop.
Save dongli/e9291c411cd7ed409e71bcd14d681952 to your computer and use it in GitHub Desktop.
Synchronize rotations of D3.js and Three.js (V2)
license: mit

This example shows how to overlay a sphere rendered by Three.js onto a geo projection rendered by D3.js. The drag event is handled by D3.js, and Three.js is synchronized with it. The black cross is from D3 layer. Blue, red and green small balls are from Three layer.

The main focus is on this synchronization. In this version, only two axes rotation is applied. The black cross and blue ball are binded together. There is a failure example which use three axes rotation by using quaternion.

<!doctype html>
<html>
<head>
<meta charset='utf-8'>
<script src='https://d3js.org/d3.v4.min.js'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/three.js/r83/three.min.js'></script>
<style>
#map-layer {
position: absolute;
left: 0;
top: 0;
z-index: 2;
}
#shade-layer {
position: absolute;
left: 0;
top: 0;
z-index: 1;
}
</style>
</head>
<body>
<script>
// Set parameters.
const WIDTH = 512, HEIGHT = 512
const RADIUS = 120
const INITIAL_ROTATE = [0, 0]
const SCALE = RADIUS
const BACKGROUND_COLOR = 'white'
const TO_RADIAN = Math.PI / 180
const TO_DEGREE = 180 / Math.PI
const ROTATION_SCALE = 0.25
// create projection object.
var projection = d3.geoOrthographic()
.translate([WIDTH / 2, HEIGHT / 2])
.scale(SCALE)
.clipAngle(90)
.rotate(INITIAL_ROTATE)
// Create map layers at multiple resolutions.
var mapLayer = d3.select('body').append('canvas').attr('id', 'map-layer')
.attr('width', WIDTH).attr('height', HEIGHT)
var mapContext = mapLayer.node().getContext('2d')
mapContext.strokeStyle = 'black'
var mapPath = d3.geoPath().projection(projection).context(mapContext)
var mapMarks = {
type: 'FeatureCollection',
features: [{
type: 'Feature',
geometry: {
type: 'LineString',
coordinates: [
[-5, 0],
[ 5, 0]
]
}
}, {
type: 'Feature',
geometry: {
type: 'LineString',
coordinates: [
[0, -5],
[0, 5]
]
}
}]
}
function drawMapLayer() {
mapContext.setTransform(1, 0, 0, 1, 0, 0)
mapContext.clearRect(0, 0, WIDTH, HEIGHT)
mapContext.beginPath()
mapPath(mapMarks)
mapContext.lineWidth = 2
mapContext.stroke()
}
drawMapLayer()
// Handle drag event.
var drag = d3.drag().on('drag', function() {
// Skip spurious drag event.
if (d3.event.dx === 0 && d3.event.dy === 0) return
var r = projection.rotate()
var dr = [d3.event.dx * ROTATION_SCALE, - d3.event.dy * ROTATION_SCALE]
projection.rotate([r[0] + dr[0], r[1] + dr[1]])
drawMapLayer()
// Rotate Three.js objects.
sphereObject.rotation.x = - projection.rotate()[1] * TO_RADIAN
sphereObject.rotation.y = projection.rotate()[0] * TO_RADIAN
sphereObject.rotation.z = projection.rotate()[2] * TO_RADIAN
drawShadeLayer()
})
mapLayer.call(drag)
// Create 3D scene and camera objects.
var scene = new THREE.Scene()
var camera = new THREE.OrthographicCamera(-WIDTH / 2, WIDTH / 2, HEIGHT / 2, -HEIGHT / 2, 0.1, 10000)
camera.position.z = 500
// Create renderer object.
var renderer = new THREE.WebGLRenderer({
antialias: true
})
renderer.domElement.id = 'shade-layer'
renderer.setClearColor(BACKGROUND_COLOR, 1)
renderer.setSize(WIDTH, HEIGHT)
document.body.appendChild(renderer.domElement)
// Add light to scene.
var light = new THREE.HemisphereLight('#fff', '#666', 1.5)
light.position.set(0, 500, 0)
scene.add(light)
// Create sphere.
var sphere = new THREE.SphereGeometry(120, 100, 100)
var sphereMaterial = new THREE.MeshNormalMaterial({
wireframe: false
})
var sphereMesh = new THREE.Mesh(sphere, sphereMaterial)
// For debug ...
var dot1 = new THREE.SphereGeometry(5, 10, 10)
dot1.translate(0, 0, 120)
var dot1Material = new THREE.MeshBasicMaterial({
color: 'blue'
})
var dot1Mesh = new THREE.Mesh(dot1, dot1Material)
var dot2 = new THREE.SphereGeometry(5, 10, 10)
dot2.translate(120, 0, 0)
var dot2Material = new THREE.MeshBasicMaterial({
color: 'red'
})
var dot2Mesh = new THREE.Mesh(dot2, dot2Material)
var dot3 = new THREE.SphereGeometry(5, 10, 10)
dot3.translate(0, -120, 0)
var dot3Material = new THREE.MeshBasicMaterial({
color: 'green'
})
var dot3Mesh = new THREE.Mesh(dot3, dot3Material)
var sphereObject = new THREE.Object3D()
sphereObject.add(sphereMesh, dot1Mesh, dot2Mesh, dot3Mesh)
scene.add(sphereObject)
function drawShadeLayer() {
renderer.render(scene, camera)
}
drawShadeLayer()
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment