Skip to content

Instantly share code, notes, and snippets.

@mbostock mbostock/.block
Last active May 9, 2017

Embed
What would you like to do?
Versor Dragging II
license: gpl-3.0
height: 600
<!DOCTYPE html>
<canvas width="960" height="600"></cavnas>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://unpkg.com/topojson-client@2"></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.geoMercator()
.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();
}
d3.json("https://unpkg.com/world-atlas@1/world/110m.json", function(error, world) {
if (error) throw error;
var land = topojson.feature(world, world.objects.land);
render = function() {
context.fillStyle = "#fff", context.fillRect(0, 0, width, height);
context.beginPath(), path(land), context.fillStyle = "#000", context.fill();
};
render();
});
</script>
// Version 0.0.0. Copyright 2017 Mike Bostock.
(function(global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global.versor = factory());
}(this, (function() {'use strict';
var acos = Math.acos,
asin = Math.asin,
atan2 = Math.atan2,
cos = Math.cos,
max = Math.max,
min = Math.min,
PI = Math.PI,
sin = Math.sin,
sqrt = Math.sqrt,
radians = PI / 180,
degrees = 180 / PI;
// Returns the unit quaternion for the given Euler rotation angles [λ, φ, γ].
function versor(e) {
var l = e[0] / 2 * radians, sl = sin(l), cl = cos(l), // λ / 2
p = e[1] / 2 * radians, sp = sin(p), cp = cos(p), // φ / 2
g = e[2] / 2 * radians, sg = sin(g), cg = cos(g); // γ / 2
return [
cl * cp * cg + sl * sp * sg,
sl * cp * cg - cl * sp * sg,
cl * sp * cg + sl * cp * sg,
cl * cp * sg - sl * sp * cg
];
}
// Returns Cartesian coordinates [x, y, z] given spherical coordinates [λ, φ].
versor.cartesian = function(e) {
var l = e[0] * radians, p = e[1] * radians, cp = cos(p);
return [cp * cos(l), cp * sin(l), sin(p)];
};
// Returns the Euler rotation angles [λ, φ, γ] for the given quaternion.
versor.rotation = function(q) {
return [
atan2(2 * (q[0] * q[1] + q[2] * q[3]), 1 - 2 * (q[1] * q[1] + q[2] * q[2])) * degrees,
asin(max(-1, min(1, 2 * (q[0] * q[2] - q[3] * q[1])))) * degrees,
atan2(2 * (q[0] * q[3] + q[1] * q[2]), 1 - 2 * (q[2] * q[2] + q[3] * q[3])) * degrees
];
};
// Returns the quaternion to rotate between two cartesian points on the sphere.
versor.delta = function(v0, v1) {
var w = cross(v0, v1), l = sqrt(dot(w, w));
if (!l) return [1, 0, 0, 0];
var t = acos(max(-1, min(1, dot(v0, v1)))) / 2, s = sin(t); // t = θ / 2
return [cos(t), w[2] / l * s, -w[1] / l * s, w[0] / l * s];
};
// Returns the quaternion that represents q0 * q1.
versor.multiply = function(q0, q1) {
return [
q0[0] * q1[0] - q0[1] * q1[1] - q0[2] * q1[2] - q0[3] * q1[3],
q0[1] * q1[0] + q0[0] * q1[1] + q0[2] * q1[3] - q0[3] * q1[2],
q0[0] * q1[2] - q0[1] * q1[3] + q0[2] * q1[0] + q0[3] * q1[1],
q0[0] * q1[3] + q0[1] * q1[2] - q0[2] * q1[1] + q0[3] * q1[0]
];
};
function cross(v0, v1) {
return [
v0[1] * v1[2] - v0[2] * v1[1],
v0[2] * v1[0] - v0[0] * v1[2],
v0[0] * v1[1] - v0[1] * v1[0]
];
}
function dot(v0, v1) {
return v0[0] * v1[0] + v0[1] * v1[1] + v0[2] * v1[2];
}
return versor;
})));
@Fil

This comment has been minimized.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.