|
<!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 - 180) / 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(); |
|
|
|
// reposition the three.js camera according to the versor |
|
// the order of arguments reflects the fact that d3 and three.js |
|
// use different referentials (x,y,z) |
|
//var q = new THREE.Quaternion(-q1[2], q1[1], q1[3], q1[0]) |
|
//q.normalize() // not needed since our versor norm = 1 |
|
//scene.setRotationFromQuaternion(q) |
|
|
|
var v2 = versor.cartesian(r1) |
|
|
|
camera.position.x = -v2[1] * 400; |
|
camera.position.y = -v2[2] * 400; |
|
camera.position.z = v2[0] * 400; |
|
camera.lookAt(scene.position); |
|
|
|
camera.rotation.z = -2 * Math.PI * Math.random(); |
|
|
|
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, 500) |
|
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.MeshLambertMaterial({ |
|
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.MeshStandardMaterial({ |
|
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.MeshLambertMaterial({ |
|
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.MeshLambertMaterial({ |
|
color: 'green' |
|
}) |
|
var dot3Mesh = new THREE.Mesh(dot3, dot3Material) |
|
var sphereObject = new THREE.Object3D() |
|
sphereObject.add(sphereMesh, dot1Mesh, dot2Mesh, dot3Mesh) |
|
|
|
// Add light to scene. |
|
var light = new THREE.HemisphereLight('#fff', '#666', 1.5) |
|
light.position.set(500, 500, 200) |
|
scene.add(light) |
|
|
|
scene.add(sphereObject) |
|
function drawShadeLayer() { |
|
renderer.render(scene, camera) |
|
} |
|
drawShadeLayer(); |
|
|
|
</script> |