Skip to content
Shapemetry

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.5

The 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 resolution

Path2d.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
)
Last updated: 📖 1 min readEdit on GitHub