Perfect Arrows
@rougher-stuff/perfect-arrows computes arrow bodies, bounds, and endpoint decorations without coupling them to a rendering technology.
Point to point
import { createArrow, toSvgPath } from '@rougher-stuff/perfect-arrows';
const arrow = createArrow({
from: { x: 20, y: 40 },
to: { x: 240, y: 160 },
route: { type: 'swoop', bend: 0.4 },
end: { type: 'open-arrow', size: 12 },
});
const pathData = toSvgPath(arrow.body);Use createArrowWithoutBounds() when only the path and decorations are needed.
Between rectangles
Oriented boxes preserve their local coordinate systems rather than collapsing to axis-aligned bounds:
import { createArrowBetweenOrientedRects } from '@rougher-stuff/perfect-arrows';
const arrow = createArrowBetweenOrientedRects({
from: { center: { x: 60, y: 30 }, width: 120, height: 60, rotation: Math.PI / 4 },
to: { center: { x: 300, y: 180 }, width: 80, height: 50, rotation: -Math.PI / 6 },
});Routes and decorations
Routes include straight, arc, swoop, s-curve, elbow, wave, and pigtail.
Endpoint decorations include open-arrow, triangle, needle, dot, circle, and bar. Routing hints can set start and end directions when a route must leave or enter a particular side.
Legacy compatibility
The original getArrow() and getBoxToBoxArrow() functions remain available from the package root and from @rougher-stuff/perfect-arrows/legacy.
See the package README for the complete legacy API.
