2D Contours, Offset, and Nesting
2D profile work happens on Path2d documents — JSON strings again. A Path2d is one or more ordered plane chains ({ curves: PolyCurve2d[] }), where each composite is { segments: Curve2d[] } of tagged line / arc / ellipse / nurbs segments over [x, y] pairs. You rarely hand-write one: the Path2d namespace has builders.
Build a profile
import { Path2d, Transformation } from '@huukhanhnguyen/geometry'
const IDENTITY = Transformation.identity() // builders take an optional placement matrix
const plate = Path2d.rectangle(100, 50, 200, 100, false, IDENTITY) // CENTER + size, JSON string out
const washer = Path2d.circle(50, 50, 30, false, IDENTITY) // center + radius
// Holes are explicit second loops — outer ring first:
const ringed = Path2d.fromRings(
JSON.stringify([
[[0, 0], [200, 0], [200, 100], [0, 100]], // outer
[[50, 25], [150, 25], [150, 75], [50, 75]], // hole
]),
false,
IDENTITY,
)Offset
Path2d.offset(shape_json, distance, cap) — positive dilates, negative erodes. An open chain is stroked by |distance| with the given end cap:
const grown = Path2d.offset(plate, 5, 'round') // dilate by 5
const shrunk = Path2d.offset(plate, -2.5, 'round') // erode by 2.5The cap/join vocabulary is exported as string unions from the geometry root:
import type { OffsetEndType, OffsetJoinType } from '@huukhanhnguyen/geometry'
// OffsetEndType = 'round' | 'square' | 'butt' — line cap for open ends
// OffsetJoinType = 'round' | 'miter' | 'square' — corner join resolutionPath2d.offset takes a cap only. When you need join control (miter limits, etc.), use Path2d.thicken(shape_json, distance, join_type, end_type, miter_limit, merge_connected).
Nesting
Nesting.nest packs parts into a bin sheet — Path2d documents in, a placement document out:
import { Nesting, Path2d } from '@huukhanhnguyen/geometry'
const bin = Path2d.rectangle(500, 250, 1000, 500, false, IDENTITY)
const parts = JSON.stringify([
JSON.parse(Path2d.rectangle(100, 50, 200, 100, false, IDENTITY)),
JSON.parse(Path2d.circle(0, 0, 60, false, IDENTITY)),
])
const result = Nesting.nest(
bin,
parts,
JSON.stringify({ rotations: 4 }), // '{}' = all defaults
10, // generations — genetic-algorithm iterations
0, // seed — same seed, same layout
)