export type Point2 = [number, number]; export declare const POLYGON_EPSILON = 0.000001; export declare const POLYGON_AREA_EPSILON = 0.000001; export type Winding = "CW" | "CCW"; export declare function pointsEqualXZ(a: Point2, b: Point2, eps?: number): boolean; /** Shoelace area on the XZ plane. Positive = CCW, negative = CW. */ export declare function signedAreaXZ(ring: Point2[]): number; export declare function orientationXZ(a: Point2, b: Point2, c: Point2): number; export declare function onSegmentXZ(a: Point2, b: Point2, p: Point2, eps?: number): boolean; export declare function segmentsIntersectXZ(a1: Point2, a2: Point2, b1: Point2, b2: Point2): boolean; export declare function hasSelfIntersectionXZ(ring: Point2[]): boolean; /** Drop consecutive duplicate points and any trailing point that closes the * ring, returning a "unique vertex" list. Caller passes either an open ring * or one that already closes — either way the result is the canonical * unique-vertex form (no closing duplicate). */ export declare function dedupeAndAutoClose(ring: Point2[], eps?: number): Point2[]; export declare function ensureWinding(ring: Point2[], desired: Winding): Point2[]; /** Crossing-number point-in-polygon test on the XZ plane. Boundary points may * return either true or false; callers needing strict-interior should * combine this with an edge-distance check. */ export declare function pointInPolygonXZ(p: Point2, ring: Point2[]): boolean; /** True if every vertex of `inner` lies inside `outer` AND no edge of `inner` * crosses any edge of `outer`. Both rings are unique-vertex (no closing dup). */ export declare function ringStrictlyInside(inner: Point2[], outer: Point2[]): boolean; /** True if any vertex of one ring lies inside the other, OR any pair of edges * crosses. Both rings are unique-vertex (no closing dup). */ export declare function ringsOverlap(a: Point2[], b: Point2[]): boolean; /** Average of all points. Returns [0, 0] for empty input. */ export declare function centroidXZ(ring: Point2[]): Point2; /** Translate every point. Returns a new array. */ export declare function translatePointsXZ(ring: Point2[], dx: number, dz: number): Point2[]; /** Rotate every point around `anchor` by `angleRad` (right-handed about +Y, * i.e. angle increases counter-clockwise when viewed from above). */ export declare function rotatePointsXZ(ring: Point2[], angleRad: number, anchor: Point2): Point2[]; /** Closed polygon profile in the world XZ plane. `outerRing` is the outer * boundary; `holes` are interior cutouts. Both are auto-closed at validation * time and normalized to the winding `opengeometry.Polygon` expects (CW outer, * CCW holes). Used by `Floor`, `Slab`, and any other extruded-profile element. */ export interface PolygonProfile { outerRing: Point2[]; holes?: Point2[][]; } export interface NormalizedPolygonProfile { outer: Point2[]; holes: Point2[][]; } /** Deep-clone a profile so caller mutations don't bleed into element state. */ export declare function clonePolygonProfile(profile: PolygonProfile): PolygonProfile; /** Validate, dedupe/auto-close, and normalize winding of a polygon profile. * * Throws on invalid input: * - non-array / non-finite coordinates * - <3 unique vertices after dedupe * - zero-area (collinear) outer ring or hole * - self-intersecting outer ring or hole * - hole not strictly inside the outer ring * - overlapping holes * * `label` is prefixed onto error messages so the caller's element type is * visible in the thrown message (e.g. "Floor: profile.outerRing * self-intersects."). */ export declare function validateAndNormalizeProfile(profile: PolygonProfile, label: string): NormalizedPolygonProfile;