Drawing an arrow

logo

This applet simply draws an arrow when you click and drag on it. That's all.

Suppose we want to draw an arrow between p1(x1,y1)p_1(x_1, y_1) and p2(x2,y2)p_2(x_2, y_2). The basic idea is that we determine two points L and R a fixed distance below p2p_2 on either side of the line from p1p_1 to p2p_2 along a perpendicular direction to it. We then connect L, p2p_2, and R with lines and we have our arrow.

arrow design

Determining a direction perpendicular to a line or a vector is straightforward. If the vector drawn from p1p_1 to p2p_2 is (x2x1)i^+(y2y1)j^(x_2 - x_1)\hat{i} + (y_2 - y_1)\hat{j} the vector perpendicular to it is (y2y1)i^(x2x1)j^(y_2 - y_1)\hat{i} - (x_2 - x_1)\hat{j}, that is, you swap the x and y components and make one of them negative(this makes the dot product of the two vectors zero).

Once you have the points L, R and the bottom of the arrowhead B you can draw the arrowhead in other ways. Shown below is an arrow that looks more like an actual arrow. This was obtained by connecting B, L, p2p_2, and R with quadratic bezier curves instead of lines.

primitive arrow

Here is the relevant code. The commented-out portion draws the arrow in the above figure.

function drawArrow(x1, y1, x2, y2) {
const dx = x2 - x1;
const dy = y2 - y1;
const length = Math.sqrt(dx * dx + dy * dy);
const unit_vec = { x: dx / length, y: dy / length };
const perp_unit_vec = { x: unit_vec.y, y: -unit_vec.x };
const head_back = { x: x2 - _head * unit_vec.x, y: y2 - _head * unit_vec.y };
const head_left = {
x: head_back.x + (_base / 2) * perp_unit_vec.x,
y: head_back.y + (_base / 2) * perp_unit_vec.y,
};
const head_right = {
x: head_back.x - (_base / 2) * perp_unit_vec.x,
y: head_back.y - (_base / 2) * perp_unit_vec.y,
};
_ctx.beginPath();
_ctx.moveTo(x1, y1);
_ctx.lineTo(x2, y2);
_ctx.moveTo(head_left.x, head_left.y);
_ctx.lineTo(x2, y2);
_ctx.lineTo(head_right.x, head_right.y);
_ctx.stroke();
/*
fill(0);
beginShape();
vertex(head_back.x, head_back.y);
quadraticVertex(head_left.x, head_left.y, x2, y2);
quadraticVertex(head_right.x, head_right.y,head_back.x, head_back.y);
endShape();
*/

}