@rougher-stuff/perfect-arrows

Renderer-independent arrow geometry for points and rectangles


License
MIT
Install
npm install @rougher-stuff/perfect-arrows@0.0.1

Documentation

Rougher Stuff

CI Deploy License: MIT

Rougher Stuff is a shared geometry, rendering, routing, animation, and annotation stack for expressive web interfaces. It evolves Rough.js, Perfect Arrows, and Rough Notation together while keeping each package useful on its own.

Interactive playground · Documentation · Contributing

Packages

Package Purpose
roughjs Canvas/SVG rough rendering, semantic drawables, retained updates, and animation
@rougher-stuff/perfect-arrows Renderer-independent arrow routes, bounds, and decorations
@rougher-stuff/rough-notation DOM/Range annotations, shared layers, connectors, and callouts
@rougher-stuff/geometry Canonical paths, transforms, metrics, intersections, and rounded contours
@rougher-stuff/animation Framework-neutral progress controllers and timelines
@rougher-stuff/markdown Markdown directives, semantic themes, block annotations, and callouts

Quick Start

Install only the layer you need:

pnpm add roughjs
pnpm add @rougher-stuff/perfect-arrows
pnpm add @rougher-stuff/rough-notation roughjs
pnpm add @rougher-stuff/markdown roughjs

Draw to Canvas:

import rough from 'roughjs';

const canvas = document.querySelector('canvas')!;
const drawing = rough.canvas(canvas);

drawing.roundedRectangle(20, 20, 220, 120, {
  cornerRadius: 18,
  fill: '#f0c84b',
  fillStyle: 'hachure',
  seed: 42,
});

Create renderer-independent arrow geometry:

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);

Annotate an interface:

import { annotate } from '@rougher-stuff/rough-notation';

const annotation = annotate(document.querySelector('#important')!, {
  type: 'highlight',
  color: '#f0c84b',
  seed: 42,
});

annotation.show();

Read the getting started guide for SVG, retained rendering, connectors, and foundation APIs.

Architecture

Rougher Stuff separates nominal geometry from rough compilation, paint, rendering, and progress:

nominal geometry → rough compilation → render plan → SVG / Canvas → interaction

The foundation packages provide exact geometry and normalized time. Product packages add rendering, arrow routing, and DOM-aware annotations without reversing that dependency direction. See the architecture guide for details.

Development

This repository uses pnpm workspaces, Turbo, Vitest, Playwright, Oxlint, and Oxfmt. Node.js 22 matches CI.

corepack enable
pnpm install --frozen-lockfile
pnpm dev          # product site and playground
pnpm docs:dev     # documentation site

Before opening a pull request, run the same quality gate as GitHub Actions:

pnpm format
pnpm lint
pnpm test
pnpm build

Visual tests and benchmarks remain explicit because they depend on browsers or stable machine conditions:

pnpm test:visual
pnpm bench
pnpm bench:roughjs:compare
pnpm bench:perfect-arrows:compare
pnpm bench:notation:compare

Repository Layout

apps/playground   product site and interactive demos
apps/docs         VitePress documentation
packages/*        publishable libraries
scripts           benchmark and maintenance scripts
visual-tests      upstream Rough.js visual fixtures

Deployment

Pushes to main run CI and publish the playground plus documentation to GitHub Pages. The deployment workflow assembles apps/playground/dist at the site root and apps/docs/.vitepress/dist under /docs/.

To reproduce the Pages build locally:

pnpm build:site

Repository maintainers must select GitHub Actions as the Pages source once in Settings → Pages. Dependabot checks workspace dependencies weekly and Actions monthly.

Heritage

Rougher Stuff builds on the original work of Preet Shihn (Rough.js and Rough Notation) and Steve Ruiz (Perfect Arrows). Consolidated geometry algorithms retain their upstream notices in packages/geometry/THIRD_PARTY_NOTICES.md.

Financial contributions to the upstream Rough.js project are accepted through Open Collective.

License

MIT. Package-level upstream attribution and notices live beside each package.