Synchronization of d3 and tree.js globes with quaternion/versor drag.
An answer to d3/d3-geo#74
Forked from mbostock's block: Versor Dragging
license: gpl-3.0 | |
height: 600 | |
border: no |
Synchronization of d3 and tree.js globes with quaternion/versor drag.
An answer to d3/d3-geo#74
Forked from mbostock's block: Versor Dragging
<!DOCTYPE html> | |
<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> |
�PNG | |