Synchronization of d3 and tree.js globes with quaternion/versor drag.
An answer to https://github.com/d3/d3-geo/issues/74
Forked from mbostock's block: Versor Dragging
xxxxxxxxxx
<style>
#map-layer {
position: absolute;
left: 0;
top: 0;
z-index: 2;
}
#shade-layer {
position: absolute;
left: 0;
top: 0;
z-index: 1;
}
</style>
<canvas width="960" height="600" id="map-layer"></canvas>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://unpkg.com/topojson-client@2"></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/three.js/r83/three.min.js'></script>
<script src="versor.js"></script>
<script>
var canvas = d3.select("canvas"),
width = canvas.property("width"),
height = canvas.property("height"),
context = canvas.node().getContext("2d");
var projection = d3.geoOrthographic()
.scale((height - 40) / 2)
.translate([width / 2, height / 2])
.precision(0.1);
var path = d3.geoPath()
.projection(projection)
.context(context);
canvas.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged));
var render = function() {},
v0, // Mouse position in Cartesian coordinates at start of drag gesture.
r0, // Projection rotation as Euler angles at start.
q0; // Projection rotation as versor at start.
function dragstarted() {
v0 = versor.cartesian(projection.invert(d3.mouse(this)));
r0 = projection.rotate();
q0 = versor(r0);
}
function dragged() {
var v1 = versor.cartesian(projection.rotate(r0).invert(d3.mouse(this))),
q1 = versor.multiply(q0, versor.delta(v0, v1)),
r1 = versor.rotation(q1);
projection.rotate(r1);
render();
// Rotate Three.js objects according to the versor
// for some strange reason the order of arguments is not the same
var q = new THREE.Quaternion(-q1[2], q1[1], q1[3], q1[0])
//q.normalize() // not needed since our versor norm = 1
sphereObject.setRotationFromQuaternion(q)
drawShadeLayer()
}
d3.json("https://unpkg.com/world-atlas@1/world/110m.json", function(error, world) {
if (error) throw error;
var sphere = {type: "Sphere"},
land = topojson.feature(world, world.objects.land);
render = function() {
context.clearRect(0, 0, width, height);
context.beginPath(), path(land), context.strokeStyle = "#000", context.stroke();
// context.beginPath(), path(sphere), context.stroke();
};
render();
});
// Create 3D scene and camera objects.
const WIDTH=width, HEIGHT=height;
const RADIUS = projection.scale()
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
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 // (higher than RADIUS + size of the bubble)
// 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)
// Create sphere.
var sphere = new THREE.SphereGeometry(SCALE, 100, 100)
var sphereMaterial = new THREE.MeshNormalMaterial({
wireframe: false
})
var sphereMesh = new THREE.Mesh(sphere, sphereMaterial)
// For debug ...
var dot1 = new THREE.SphereGeometry(30, 10, 10)
dot1.translate(0, 0, SCALE)
var dot1Material = new THREE.MeshBasicMaterial({
color: 'blue'
})
var dot1Mesh = new THREE.Mesh(dot1, dot1Material)
var dot2 = new THREE.SphereGeometry(30, 10, 10)
dot2.translate(SCALE, 0, 0)
var dot2Material = new THREE.MeshBasicMaterial({
color: 'red'
})
var dot2Mesh = new THREE.Mesh(dot2, dot2Material)
var dot3 = new THREE.SphereGeometry(30, 10, 10)
dot3.translate(0, -SCALE, 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>
https://d3js.org/d3.v4.min.js
https://unpkg.com/topojson-client@2
https://cdnjs.cloudflare.com/ajax/libs/three.js/r83/three.min.js