function drag(projection) {
let v0, q0, r0, a0, l;
function pointer(event, that) {
const t = d3.pointers(event, that);
if (t.length !== l) {
l = t.length;
if (l > 1) a0 = Math.atan2(t[1][1] - t[0][1], t[1][0] - t[0][0]);
dragstarted.apply(that, [event, that]);
}
if (l > 1) {
const x = d3.mean(t, p => p[0]);
const y = d3.mean(t, p => p[1]);
const a = Math.atan2(t[1][1] - t[0][1], t[1][0] - t[0][0]);
return [x, y, a];
}
return t[0];
}
function dragstarted({x, y}) {
v0 = versor.cartesian(projection.invert([x, y]));
q0 = versor(r0 = projection.rotate());
}
function dragged(event) {
const v1 = versor.cartesian(projection.rotate(r0).invert([event.x, event.y]));
const delta = versor.delta(v0, v1);
let q1 = versor.multiply(q0, delta);
const p = pointer(event, this);
if (p[2]) {
const d = (p[2] - a0) / 2;
const s = -Math.sin(d);
const c = Math.sign(Math.cos(d));
q1 = versor.multiply([Math.sqrt(1 - s * s), 0, 0, c * s], q1);
}
projection.rotate(versor.rotation(q1));
if (delta[0] < 0.7) dragstarted.apply(this, [event, this]);
}
return d3.drag()
.on("start", dragstarted)
.on("drag", dragged);
}