/** * The style-agnostic chassis a stair guard is laid out on, as pure data (no * Three.js, no React). A guard — baluster (`baluster-guard.ts`) or deck * post-and-rail (`post-and-rail-guard.ts`) — differs only in the sections and * y of its rails, its newel and its infill; where those members *go* is the * same problem for both: sample the rail path by run so density tracks the run * rather than the path's tessellation, extend it `reach` past the top vertex, * stand newels by spacing, and close a plan corner with a fitting block. That * shared geometry lives here so there is one path/corner/station convention to * maintain and no duplicated arc sampling between the styles. */ export type Vec3 = [number, number, number]; /** An oriented box in the guard's frame; see `GuardBox`'s consumers for UVs. */ export type GuardBox = { /** Box centre. */ center: Vec3; /** Local box size: x across the run, y vertical, z along the run. */ size: Vec3; /** Unit direction the box's z axis runs along (for sloped/plan-turned bars). */ direction: Vec3; /** Render as a round member (a cylinder about `direction`); `size[0]` is its * diameter. Cables and their terminal sleeves set this; everything else is a * rectangular bar. */ round?: boolean; }; export declare const UP: Vec3; export declare const add: (a: Vec3, b: Vec3) => Vec3; export declare const sub: (a: Vec3, b: Vec3) => Vec3; export declare const scale: (a: Vec3, s: number) => Vec3; export declare const lerp: (a: Vec3, b: Vec3, t: number) => Vec3; export declare const horizontalLength: (d: Vec3) => number; /** An oriented bar between two points; `width` is across the run, `depth` vertical. */ export declare function barBox(a: Vec3, b: Vec3, width: number, depth: number): GuardBox | null; /** A plumb box standing from `base` up `height`, square section `width`. */ export declare function postBox(base: Vec3, height: number, width: number): GuardBox; /** A guard rail: its centre `y` off the nosing line and its section (`across` * the run, `vertical`), bottom-to-top. */ export type GuardRail = { y: number; across: number; vertical: number; }; /** The rails of a guard following `railPoints`, with a cube fitting block * closing every real plan corner — the shared joinery for both guard styles. */ export declare function railBars(railPoints: Vec3[], isCorner: (i: number) => boolean, rails: GuardRail[]): GuardBox[]; /** A plan direction change past this at an interior vertex gets a corner fitting * block; collinear points and curve tessellation stay under it, so a dense path * does not bulge a block at every vertex. */ export declare const CORNER_MIN_TURN: number; export type GuardPathOptions = { /** Metres between newels along the run; omit (or Infinity) for ends only. */ postSpacing?: number; cornerPosts?: boolean; topPost?: boolean; /** Metres the rails run past the top vertex, along the final slope. */ reach?: number; /** The requested infill pitch, clamped into [min, max] below. */ picketPitch?: number; picketMinPitch?: number; picketMaxPitch?: number; }; export type GuardPath = /** A path with no horizontal run — a zero-radius winder's inner pivot: the * caller stands one plumb newel over the vertical rise `lo`→`hi`. */ { kind: 'pivot'; lo: Vec3; hi: Vec3; } | { kind: 'path'; /** Total horizontal run, including `reach`. */ run: number; /** The vertices the rails follow, extended `reach` past the top. */ railPoints: Vec3[]; /** The point at horizontal station `s` along the run. */ pointAt: (s: number) => Vec3; /** Whether interior vertex `i` of `railPoints` is a real plan corner. */ isCorner: (i: number) => boolean; /** Newel foot positions, bottom-to-top along the run. */ postPositions: Vec3[]; /** Horizontal stations of `postPositions`, bottom-to-top. */ postStations: number[]; /** Infill (picket) foot positions, newel clashes left to the caller. */ picketStations: Vec3[]; }; /** * Resolve the shared chassis for a guard laid along `points` (bottom first). * Returns `null` for a degenerate path (fewer than two points, or a vertical * run with no rise). The caller turns the stations into its own members. */ export declare function resolveGuardPath(points: Vec3[], options: GuardPathOptions): GuardPath | null; export declare const GUARD_POST_SPACING = 1.2192; export declare const GUARD_PICKET_PITCH = 0.127; export type GuardOptions = { railHeight: number; postSpacing?: number; topPost?: boolean; postThrough?: boolean; reach?: number; }; type PostProfile = { width: number; embed: number; top: number; throughTop: number; capThickness: number; capOverhang: number; foot?: (x: number, y: number, z: number) => GuardBox; }; /** Posts, caps and the vertical-pivot case have the same assembly in every style. */ export declare function buildGuardChassis(points: Vec3[], options: GuardOptions, rails: GuardRail[], profile: PostProfile, sampling?: Pick): { path: GuardPath; boxes: GuardBox[]; } | null; export declare function guardPickets(path: Extract, width: number, postWidth: number, bottom: number, top: number): GuardBox[]; export {}; //# sourceMappingURL=guard-path.d.ts.map