Stroke outline generation, branch junctions, and mark-making for genart.dev.
Transforms centerline geometry into production-quality illustrated output — smooth outlines, clean branch junctions, and medium-specific mark strategies (ink, pencil, engraving, woodcut, brush).
npm install @genart-dev/illustrationimport {
segmentToProfile,
generateStrokeOutline,
inkMark,
} from "@genart-dev/illustration";
// 1. Convert geometry to a stroke profile
const profile = segmentToProfile({ x1: 0, y1: 0, x2: 200, y2: 0, width: 10 });
// 2. Generate the outline polygon
const outline = generateStrokeOutline(profile);
// 3. Apply a mark strategy
const marks = inkMark.generateMarks(outline, profile, { density: 1, weight: 1 }, Math.random);
// → Mark[] — polylines with width and opacity, ready for your rendererConvert a centerline with per-point widths into a closed polygon (left/right edges + caps).
generateStrokeOutline(profile, options?)— full outline with left, right, startCap, endCapgenerateStrokePolygon(profile, options?)— flattened polygon (single Point2D[])taperScale(t, taper)— width multiplier at position t given a TaperSpecgenerateCap(center, tangent, halfWidth, style)— cap geometry
Convert from common formats to StrokeProfile:
segmentToProfile(segment)— single TurtleSegment (x1/y1/x2/y2/width)segmentsToProfiles(segments)— batch conversionalgorithmPathToProfile(path)— algorithm stroke paths with per-point data
Merge branching outlines into smooth, continuous forms — eliminating the arrowhead artifacts that plague naive stroke rendering.
mergeYJunction(parent, child, attachment)— merge two outlines at a forkmergeSegmentTree(segments, options?)— merge an entire segment treeenforceG1AtJunctions(points, junctions)— tangent continuity at joinssmoothAtIndex(points, index, radius?)— local smoothingsubdivideSharpCorners(points, maxAngle?)— break up acute angles
Each strategy implements MarkStrategy.generateMarks(outline, profile, config, rng) → Mark[]:
| Strategy | Character | Reference |
|---|---|---|
technicalMark |
Uniform weight, no variation — CAD/architectural | Technical pen |
inkMark |
Pressure-based width, endpoint pooling | Bamboo & Rock (Deng Yu) |
pencilMark |
Multi-pass offset strokes, cross-hatching | Graphite landscape |
engravingMark |
Contour outline + clipped parallel hatching | Copper-plate engraving |
woodcutMark |
Bold fill + carved gouge marks | Block print |
brushMark |
Thick→thin taper, dry-brush filaments, wet pooling | Sumi-e ink painting |
Each implements FillStrategy.generateMarks(polygon, config, rng) → Mark[]:
hatchFill— parallel lines at configurable angle and densitycrosshatchFill— two perpendicular hatch layersstippleFill— quasi-random dot distribution
Vector math, arc-length parameterization, Catmull-Rom interpolation, and polygon operations (point-in-polygon, area, bounds, offset, outline flattening).
- Zero dependencies on
@genart-dev/format— uses structural typing throughout - Declarative output — strategies return
Mark[]data; rendering to canvas/SVG is the consumer's job - Composable — adapters → outlines → junctions → marks, mix and match at each stage
See QUALITY.md for the 24 visual quality bars that define "done" for each phase.
MIT