tldraw
Version:
A tiny little drawing editor.
120 lines (119 loc) • 4.19 kB
JavaScript
import {
HALF_PI,
PI,
Vec,
clamp,
exhaustiveSwitchError,
intersectCircleCircle
} from "@tldraw/editor";
function getArrowPoints(info, side, strokeWidth) {
const point = side === "end" ? info.end.point : info.start.point;
let int;
switch (info.type) {
case "straight": {
const opposite = side === "end" ? info.start.point : info.end.point;
const compareLength = Vec.Dist(opposite, point);
const length = clamp(compareLength / 5, strokeWidth, strokeWidth * 3);
int = Vec.Nudge(point, opposite, length);
break;
}
case "arc": {
const compareLength = Math.abs(info.bodyArc.length);
const length = clamp(compareLength / 5, strokeWidth, strokeWidth * 3);
const intersections = intersectCircleCircle(
point,
length,
info.handleArc.center,
info.handleArc.radius
);
int = side === "end" ? info.handleArc.sweepFlag ? intersections[0] : intersections[1] : info.handleArc.sweepFlag ? intersections[1] : intersections[0];
break;
}
case "elbow": {
const previousPoint = side === "end" ? info.route.points[info.route.points.length - 2] : info.route.points[1];
const previousSegmentLength = Vec.ManhattanDist(previousPoint, point);
const length = clamp(previousSegmentLength / 2, strokeWidth, strokeWidth * 3);
int = previousPoint ? Vec.Nudge(point, previousPoint, length) : point;
break;
}
default:
exhaustiveSwitchError(info, "type");
}
if (Vec.IsNaN(int)) {
int = point;
}
return { point, int };
}
function getArrowhead({ point, int }) {
const PL = Vec.RotWith(int, point, PI / 6);
const PR = Vec.RotWith(int, point, -PI / 6);
return `M ${PL.x} ${PL.y} L ${point.x} ${point.y} L ${PR.x} ${PR.y}`;
}
function getTriangleHead({ point, int }) {
const PL = Vec.RotWith(int, point, PI / 6);
const PR = Vec.RotWith(int, point, -PI / 6);
return `M ${PL.x} ${PL.y} L ${PR.x} ${PR.y} L ${point.x} ${point.y} Z`;
}
function getInvertedTriangleHead({ point, int }) {
const d = Vec.Sub(int, point).div(2);
const PL = Vec.Add(point, Vec.Rot(d, HALF_PI));
const PR = Vec.Sub(point, Vec.Rot(d, HALF_PI));
return `M ${PL.x} ${PL.y} L ${int.x} ${int.y} L ${PR.x} ${PR.y} Z`;
}
function getDotHead({ point, int }) {
const A = Vec.Lrp(point, int, 0.45);
const r = Vec.Dist(A, point);
return `M ${A.x - r},${A.y}
a ${r},${r} 0 1,0 ${r * 2},0
a ${r},${r} 0 1,0 -${r * 2},0 `;
}
function getDiamondHead({ point, int }) {
const PB = Vec.Lrp(point, int, 0.75);
const PL = Vec.RotWith(PB, point, PI / 4);
const PR = Vec.RotWith(PB, point, -PI / 4);
const PQ = Vec.Lrp(PL, PR, 0.5);
PQ.add(Vec.Sub(PQ, point));
return `M ${PQ.x} ${PQ.y} L ${PR.x} ${PR.y} ${point.x} ${point.y} L ${PL.x} ${PL.y} Z`;
}
function getSquareHead({ int, point }) {
const PB = Vec.Lrp(point, int, 0.85);
const d = Vec.Sub(PB, point).div(2);
const PL1 = Vec.Add(point, Vec.Rot(d, HALF_PI));
const PR1 = Vec.Sub(point, Vec.Rot(d, HALF_PI));
const PL2 = Vec.Add(PB, Vec.Rot(d, HALF_PI));
const PR2 = Vec.Sub(PB, Vec.Rot(d, HALF_PI));
return `M ${PL1.x} ${PL1.y} L ${PL2.x} ${PL2.y} L ${PR2.x} ${PR2.y} L ${PR1.x} ${PR1.y} Z`;
}
function getBarHead({ int, point }) {
const d = Vec.Sub(int, point).div(2);
const PL = Vec.Add(point, Vec.Rot(d, HALF_PI));
const PR = Vec.Sub(point, Vec.Rot(d, HALF_PI));
return `M ${PL.x} ${PL.y} L ${PR.x} ${PR.y}`;
}
function getArrowheadPathForType(info, side, strokeWidth) {
const type = side === "end" ? info.end.arrowhead : info.start.arrowhead;
if (type === "none") return;
const points = getArrowPoints(info, side, strokeWidth);
if (!points) return;
switch (type) {
case "bar":
return getBarHead(points);
case "square":
return getSquareHead(points);
case "diamond":
return getDiamondHead(points);
case "dot":
return getDotHead(points);
case "inverted":
return getInvertedTriangleHead(points);
case "arrow":
return getArrowhead(points);
case "triangle":
return getTriangleHead(points);
}
return "";
}
export {
getArrowheadPathForType
};
//# sourceMappingURL=arrowheads.mjs.map