function arrow() {
class Arrow extends Plot.Link {
constructor(_, opts) {
const R = super(...arguments);
this.opts = opts;
}
render() {
const opts = this.opts;
const node = super.render(...arguments);
const lines = d3.select(node).attr("fill", "none").selectAll("path");
lines.select(function() {
const [x1, y1, x2, y2] = this.getAttribute("d").split(/[ML,]/g).slice(1).map(Number);
this.setAttribute("d", getPath(x1, y1, x2, y2, opts));
});
return node;
}
}
return new Arrow(...arguments);
function getPath(x1, y1, x2, y2, {angle = Math.PI/4, arrowheadlen = 10, arrowheadangle = 0.4}) {
const CLOCKWISE = +(+angle < 0);
const ANGLE = Math.abs(+angle);
const D = 1 / (2 * Math.tan(Math.max(1e-7, ANGLE / 2)));
const dx = x2 - x1, dy = y2 - y1;
const h = Math.hypot(dy, dx);
const d = h * D;
const r = Math.hypot(d, h / 2);
const l = Math.min(arrowheadlen, h * 0.3), a = arrowheadangle;
const tangentAngle = Math.atan2(dy, dx)
+ (CLOCKWISE ? 1 : -1) * ANGLE / 2 * (1 - (l / r / Math.PI));
const x3 = x2 - l * Math.cos(tangentAngle + a), y3 = y2 - l * Math.sin(tangentAngle + a);
const x4 = x2 - l * Math.cos(tangentAngle - a), y4 = y2 - l * Math.sin(tangentAngle - a);
return r < 1e5
? `M ${x1} ${y1} A ${r} ${r} 0 0 ${CLOCKWISE} ${x2} ${y2} L ${x3} ${y3} M ${x2} ${y2} L ${x4} ${y4}`
: `M ${x1} ${y1} L ${x2} ${y2} L ${x3} ${y3} M ${x2} ${y2} L ${x4} ${y4}`
}
}