Appearance
geom.polylineFrame
Arc-length frame along a plan polyline. Every "distance along a wall / edge / road" question in the pack modules goes through this so a curve needs no special case anywhere downstream. Plan coordinates are x/z in the caller's length unit, Y is up and never appears here.
The second half of the file offsets that frame. One frame, one band: a centreline becomes an arc-length frame with a mitred offset direction at every vertex, so a boundary at arc position d is a pure function of d — two neighbouring cells share the identical wall by construction and a gap between them is not representable. Offsetting the frame to t0/t1 and walking it from d0 to d1 gives the band polygon for any arc span, mitred through the vertices it crosses.
An envelope is honoured in the arc domain, not by polygon clipping: ask how deep the band may go at an arc position ({@link maxInsideOffset}) and which arc spans are buildable at all ({@link contiguousSpans}). Both are sampled and then bisected to the boundary, which is exact enough for plan work and cannot produce the self-intersecting rings a general clipper does. For real polygon booleans see geom/polygon-boolean.ts.
Units: every length is in whatever unit the caller's coordinates are in; the algorithms compute natively on the caller's numbers. The module's own tolerances are defined in millimetres and scaled to the optional trailing unit parameter (default "mm") with constMm, so they keep one physical meaning in every unit. Pass the unit your coordinates are in.
Pure module: no host, no DOM, no clock, no RNG. Inputs and outputs are plain data (structuredClone-safe).
ts
// plugin worker
import { polylineFrame, straightFrame, isBentPolyline, frameAlong, projectOntoPolyline, pointInEnvelope } from "@snaptrude/plugin-toolkit/geom-polyline-frame";
// agent run_worker
modules.geom.polylineFrame.polylineFrame(...)
modules.geom.polylineFrame.straightFrame(...)
modules.geom.polylineFrame.isBentPolyline(...)Every input carries unit ("mm" | "cm" | "m" | "ft" | "in"). All lengths in that call — input and output — are in it; angles are degrees, areas are that unit squared.
When to use it
| Use it for | Not for |
|---|---|
| Turning a distance along a wall, edge or road into a world point, tangent and normal | Polygon booleans and ring offsets — see geom.polygonBoolean |
| Offsetting a centreline into mitred bands, so two neighbouring cells share the identical wall | Closed-ring primitives — see geom.polygon |
| Asking how deep a band may reach inside an envelope, and which arc spans are buildable at all | Doubly-curved or three-dimensional paths |
Inputs and outputs
The shapes every function below speaks. Each appears once; a field's note says what changing it does to the result.
PlanPoint
A point in plan, in the caller's length unit.
x:numberz:number
PolylineFrame
A polyline with its cumulative arc length precomputed. Build one with polylineFrame; treat it as read-only afterwards.
pts:PlanPoint[]— The sampled points. A straight edge has two; an arc has as many as it needs.cum:number[]— Arc length frompts[0]to each point.cum[0] === 0.length:number— Total arc length,cum[cum.length - 1].curved:boolean— True when the polyline runs measurably longer than its chord (seeisBentPolyline).
PlanVector
A unit-ish direction in plan. Never carries units.
x:numberz:number
FrameSample
Position and local axes at a distance along the polyline.
x:numberz:numbertangent:PlanVector— Unit tangent, pointing in the direction of increasingd.normal:PlanVector— Unit normal. Points to theoutwardside when one was given, else to the tangent's right ({z, -x}).
OffsetFrame
A polyline ready to be offset: the arc-length frame, the unit direction of each leg, the offset direction at each vertex (mitred, capped) and the signed turn at each vertex.
frame:PolylineFramedirs:PlanVector[]— One per leg,pts.length - 1entries.offsets:PlanVector[]— One per point. Ends carry their leg's left normal; interior vertices carry the mitre direction, scaled so an offset oftreaches the mitre point. On a closed ring both ends carry the seam's mitre, which is the same direction at both, so the band closes on itself without a notch.turnDeg:number[]— Signed turn at each point in degrees,0at both ends — except on a closed ring, where both ends carry the seam vertex's own turn. Positive means the path turns towards its left normal, so the left side is the concave one.closed:boolean— True when the last point returns to the first: the seam is a vertex like any other, andd = 0andd = lengthare the same place on the ground.envelope?:Envelope— When given, a mitre point that would fall outside it is pulled back along the mitre until it is inside, so no band polygon leaves the envelope at a bend even though its perpendicular offsets are all inside.
Span
An arc-length interval along a frame.
d0:numberd1:number
Functions
polylineFrame(ptsIn, dedupeTol, unit)
Build a frame from plan points. Consecutive duplicates (within dedupeTol, default 0.01 mm scaled to unit) are dropped so zero-length pieces never appear in the arc length.
Tolerances are defined in mm and scaled to unit; pass the unit your coordinates are in.
- Parameters:
ptsIn:PlanPoint[]dedupeTol?:numberunit?:LengthUnit— Default"mm".
- Returns:
PolylineFrame - Throws: when fewer than two distinct points remain.
straightFrame(start, direction, length, unit)
A straight frame from a start point, a direction and a length. Tolerances are defined in mm and scaled to unit; pass the unit your coordinates are in.
- Parameters:
start:PlanPointdirection:PlanVectorlength:numberunit?:LengthUnit— Default"mm".
- Returns:
PolylineFrame
isBentPolyline(pts)
A polyline is curved when it runs measurably longer (0.2 %) than the straight line between its ends. Asks the points, not the source geometry, so it cannot disagree with the arc length used everywhere else.
- Parameters:
pts:PlanPoint[]
- Returns:
boolean
frameAlong(frame, d, outward)
Position, tangent and normal at arc length d. d is clamped to the polyline. When outward is given the normal is flipped to agree with it, which is how a run's outward side propagates to every sample on a curve.
- Parameters:
frame:PolylineFramed:numberoutward?:PlanVector
- Returns:
FrameSample
projectOntoPolyline(frame, p)
Arc-length distance along the polyline to the point nearest p, and how far p sits off the polyline. Measured by arc length, not by chord: projecting onto the chord is only right for a straight edge and compresses an arc towards its middle.
- Parameters:
frame:PolylineFramep:PlanPoint
- Returns:
{ d: number; perp: number }
pointInEnvelope(p, env)
Inside the envelope's ring and outside every hole, by ray parity.
- Parameters:
p:PlanPointenv:Envelope
- Returns:
boolean
Effect: a ring of fewer than three points is treated as no constraint at all rather than as empty ground, so an envelope may carry holes without an outer ring.
offsetFrame(ptsIn, envelope, unit)
Build an offsettable frame from centreline points. Duplicate points are dropped by polylineFrame.
A path whose last point returns to its first is a closed ring: closed is set, and the seam gets the same mitred offset and signed turn as any other vertex, so a band built across it meets itself instead of butting two square ends together.
Tolerances are defined in mm and scaled to unit; pass the unit your coordinates are in.
- Parameters:
ptsIn:PlanPoint[]envelope?:Envelopeunit?:LengthUnit— Default"mm".
- Returns:
OffsetFrame - Throws: when fewer than two distinct points remain.
offsetPointAt(cf, d, t, unit)
The point t to the left of the centreline at arc position d. Negative t is the right side. At a vertex the offset runs along the mitre, which is what makes a wall at a bend shared by the cells on both sides of it.
Tolerances are defined in mm and scaled to unit; pass the unit your coordinates are in.
- Parameters:
cf:OffsetFramed:numbert:numberunit?:LengthUnit— Default"mm".
- Returns:
PlanPoint
offsetChain(cf, d0, d1, t, unit)
The offset boundary from d0 to d1 at offset t, including every vertex the span crosses.
Tolerances are defined in mm and scaled to unit; pass the unit your coordinates are in.
- Parameters:
cf:OffsetFramed0:numberd1:numbert:numberunit?:LengthUnit— Default"mm".
- Returns:
PlanPoint[]
bandRing(cf, d0, d1, t0, t1, unit)
The band polygon over [d0, d1] between offsets t0 and t1. Both offsets take the same sign: positive is the left side, negative the right.
Tolerances are defined in mm and scaled to unit; pass the unit your coordinates are in.
- Parameters:
cf:OffsetFramed0:numberd1:numbert0:numbert1:numberunit?:LengthUnit— Default"mm".
- Returns:
PlanPoint[]
Effect: the ring is the cell outline a caller draws. Winding follows the sign of the offsets, so read areas through an unsigned area function.
maxInsideOffset(cf, d, t0, t1, env, unit)
How far the band may reach at arc position d before it leaves the envelope: t1 when the full depth fits, t0 when even the corridor edge is outside, else the bisected boundary.
Tolerances are defined in mm and scaled to unit; pass the unit your coordinates are in.
- Parameters:
cf:OffsetFramed:numbert0:numbert1:numberenv:Envelopeunit?:LengthUnit— Default"mm".
- Returns:
number
contiguousSpans(length, step, test, unit)
The arc spans of [0, length] where test holds, longest first. Sampled every step and then bisected to the flip, so a span's ends are the real boundary rather than a sample.
The step floor is defined in mm and scaled to unit; pass the unit your lengths are in.
- Parameters:
length:numberstep:numbertest:(d: number) => booleanunit?:LengthUnit— Default"mm".
- Returns:
Span[]
Effect: a feature narrower than step can be missed entirely; pick a step below the smallest span that matters.
Examples
A world point, tangent and normal at a distance along a wall (both)
ts
const frame = polylineFrame(wall);
return frameAlong(frame, d);One cell outline off a corridor centreline, mitred through the bends (both)
ts
const cf = offsetFrame(centreline);
return bandRing(cf, d0, d1, corridorHalfWidth, corridorHalfWidth + cellDepth);Errors
| Condition | Result |
|---|---|
polylineFrame() — fewer than two distinct points remain. | throws |
offsetFrame() — fewer than two distinct points remain. | throws |
Related
geom.polygon— ring primitivespack.linearArray— runs are arc-length framespack.corridorRuns— bands are offsets of this frame