import { P as Polygon, V as Vec3, C as CameraState, a as PolyDirectionalLight, b as PolyAmbientLight, M as MeshResolution, c as PolyPointLight, d as PolyTextureLightingMode, e as Vec2, f as PolyTextureLeafSizing, g as PolyTextureImageSource, h as PolyTextureImageRendering, i as PolyTextureImageLighting, j as PolyTextureProjection, k as PolyTextureBackend, T as TextureTriangle, l as PolyTexturePresentation } from './camera-VU-yix11.cjs'; export { A as AutoRotateConfig, m as AutoRotateOption, B as BASE_TILE, n as CameraHandle, o as CameraStyleInput, D as DEFAULT_CAMERA_STATE, p as DEFAULT_PROJECTION, q as PolyCameraProjection, r as PolyCameraSceneTransformOptions, s as PolyCameraSnapshot, t as PolyCameraSnapshotOptions, u as PolyCameraSnapshotSource, v as PolyMaterial, w as PolyTextureAlphaMode, x as PolyTextureWrap, y as PolyTextureWrapMode, z as buildPolyCameraSceneTransform, E as capturePolyCameraSnapshot, F as createIsometricCamera, G as normalizeInvertMultiplier, H as polyCameraTargetToCss, I as resolvePolyCameraAppliedPerspectiveStyle } from './camera-VU-yix11.cjs'; /** * normalizePolygons — validates a polygon list, drops degenerate inputs, * triangulates non-coplanar N-gons, strips bad UVs, sanitizes data, and * returns the cleaned polygons + a list of human-readable warnings. * * Validation rules are encoded here and covered by the normalization tests. * * Pure: no DOM, no I/O, deterministic. Bbox is NOT computed here — that's * derived on demand by `buildSceneContext` / consumers. */ interface NormalizeResult { polygons: Polygon[]; warnings: string[]; } declare function normalizePolygons(input: Polygon[]): NormalizeResult; /** * Scene context — the top-level entry point that takes a polygon mesh * (already normalized) and returns the data the framework wrappers need * to render. * * The renderer consumes a flat polygon list plus a scene bbox; higher-level * culling or mesh optimization happens before this context is built. */ interface SceneBbox { /** Minimum corner of the axis-aligned bounding box (inclusive). */ min: Vec3; /** Maximum corner of the axis-aligned bounding box (inclusive). */ max: Vec3; } interface SceneContext { /** Validated polygon list — the renderer iterates this. */ polygons: Polygon[]; /** Polygon-mesh bbox in world space. Used to size the scene container. */ sceneBbox: SceneBbox; /** Warnings raised during normalization (already-applied fixes). */ warnings: string[]; } interface SceneContextBuildArgs { /** * Polygon list. Pass parser output directly — `buildSceneContext` runs * `normalizePolygons` for you. */ polygons: Polygon[]; /** * If true, skip the normalize pass (caller has already validated). Useful * when chaining `mergePolygons` after a manual `normalizePolygons` call. */ skipNormalize?: boolean; } interface SceneContextBuildResult { context: SceneContext; /** * Mesh-bbox dimensions. Convenience copy of `context.sceneBbox` plus a * `size` field (max - min) for callers that want a single number per axis. */ dimensions: { sceneBbox: SceneBbox; size: Vec3; }; /** Warnings raised during normalization. Mirrors `context.warnings`. */ warnings: string[]; } /** * Compute the axis-aligned bounding box across every vertex of every polygon. * Returns a zero-extent bbox at origin for empty input — callers that care * about that case should check `polygons.length` first. */ declare function computeSceneBbox(polygons: Polygon[]): SceneBbox; declare function buildSceneContext(args: SceneContextBuildArgs): SceneContextBuildResult; /** * Polygon geometry helpers — pure math operating on Polygon vertices. * These helpers feed lighting, diagnostics, and renderer metrics without * depending on browser APIs. */ interface PolygonFace { /** Vertices in CCW-from-outside order. Same as Polygon.vertices. */ v: Vec3[]; /** Original polygon's color, if any (for lighting helpers). */ color?: string; } interface TexturePaintMetricsOptions { /** CSS pixels per world X/Y unit. Matches renderer default. */ tileSize?: number; /** CSS pixels per world Z unit. Defaults to tileSize. */ layerElevation?: number; /** When true, skip untextured polygons. Defaults to true. */ texturedOnly?: boolean; } interface TexturePaintMetrics { /** Input polygon count before filtering. */ totalPolygons: number; /** Polygons included in the metric after filtering and degeneracy checks. */ measuredPolygons: number; /** Included polygons with a texture URL. */ texturedPolygons: number; /** Sum of rectangular element areas in CSS px^2. */ elementArea: number; /** Sum of projected polygon areas in CSS px^2. */ polygonArea: number; /** elementArea - polygonArea, clamped at 0. */ transparentArea: number; /** transparentArea / elementArea. */ transparentRatio: number; /** elementArea / polygonArea. */ overdrawRatio: number; /** Highest per-polygon transparentRatio. */ worstTransparentRatio: number; } /** * Surface a polygon as a single face. The returned array always has length 1; * the indirection lets face-oriented consumers keep a common loop shape. * * Returns an empty array for degenerate polygons (< 3 vertices). */ declare function polygonFaces(p: Polygon): PolygonFace[]; declare function computeTexturePaintMetrics(polygons: Polygon[], options?: TexturePaintMetricsOptions): TexturePaintMetrics; /** * Apply CSS-style chained `rotateX(rx) rotateY(ry) rotateZ(rz)` rotation * to a 3D vector. Matches the matrix composition used by PolyCSS mesh * wrapper transforms (see `buildTransform` in each PolyMesh implementation). * * CSS composes `transform: rotateX(rx) rotateY(ry) rotateZ(rz)` as the * matrix `M = Rx · Ry · Rz`, applied to a point as `M · p` — so Rz acts * first on the point, then Ry, then Rx. Compound rotations only commute * when axes coincide; getting the order wrong silently corrupts results * for any two-axis combination. * * Angles in degrees. */ declare function rotateVec3(v: Vec3, rxDeg: number, ryDeg: number, rzDeg: number): Vec3; /** * Inverse of `rotateVec3` for the same rotation tuple — transforms a * world-space vector into the mesh's local frame. Used by the baked * atlas pipeline to inverse-rotate the directional light so the * pre-multiplied Lambert shading stays correct after the mesh rotates, * and by the dynamic-mode CSS-var override for the same reason. * * The inverse of `M = Rx · Ry · Rz` is `M⁻¹ = Rz⁻¹ · Ry⁻¹ · Rx⁻¹`, so * Rx⁻¹ acts first on the vector, then Ry⁻¹, then Rz⁻¹. * * `rot` is `[rxDeg, ryDeg, rzDeg]` matching the mesh's CSS rotation prop. */ declare function inverseRotateVec3(v: Vec3, rot: Vec3): Vec3; /** * Apply the wrapper's actual CSS rotation matrix to a CSS-frame vector, * given the user-supplied world rotation tuple `[rx_w, ry_w, rz_w]`. * * The wrapper emits `rotateY(-rx_w) rotateX(-ry_w) rotateZ(-rz_w)` (the * world↔CSS reflection conjugation of three.js XYZ Euler). That matrix * is `Ry(-rx_w) · Rx(-ry_w) · Rz(-rz_w) · v` applied to a vector. Use * this anywhere downstream code needs to transform a CSS-frame normal or * point through the same rotation the wrapper applies — e.g. back-face * culling, voxel item ordering. NOT for inverse-rotating a world light * direction into mesh-local frame — that still uses `inverseRotateVec3` * with the user's world rotation, since it operates in world frame. */ declare function rotateVec3InWrapperCssFrame(v: Vec3, worldRot: Vec3): Vec3; /** * Minimal quaternion helpers for composing rotations. * * Why we need quaternions: the public PolyMesh API exposes rotation as a * Euler triple `[rx, ry, rz]` in degrees (drives CSS `rotateX rotateY * rotateZ`, applied right-to-left). Euler triples don't compose by * component addition — rotating Y after X must happen around the mesh's * NEW local-Y axis, not world-Y. The transform-controls ring drag handler * uses these helpers to compose around the mesh's local axis correctly: * * q_start = quatFromEulerXYZ(currentRotationDeg) * q_delta = quatFromAxisAngle(localAxis, deltaRadians) * q_new = quatMultiply(q_start, q_delta) // RIGHT-multiply = local frame * next = eulerXYZFromQuat(q_new) * * Convention: "XYZ" Euler means the composed rotation matrix is * `Rx(rx) · Ry(ry) · Rz(rz)`, which matches CSS `rotateX rotateY rotateZ` * (right-to-left application to a point ⇒ Z first, then Y, then X). * * Quaternion format: `[w, x, y, z]` (real-first, like three.js's internal * `_x/_y/_z/_w` reordered). Stored as plain tuples — no constructor or * runtime allocations per drag. */ /** Quaternion `[w, x, y, z]`, real component first. Unit-length is not * enforced by the type — callers normalize when needed. */ type Quat = [number, number, number, number]; /** Identity quaternion. */ declare const QUAT_IDENTITY: Quat; /** Hamilton product `q1 * q2`. Apply to a vector as `q v q⁻¹`. Right- * multiplication composes the second rotation in the LOCAL frame of the * first — that's the property the gizmo relies on for local-axis drag. */ declare function quatMultiply(q1: Quat, q2: Quat): Quat; /** Quaternion from axis-angle. `axis` must be unit length (caller's * responsibility — typically a CSS basis vector). `angleRad` in radians. */ declare function quatFromAxisAngle(axis: Vec3, angleRad: number): Quat; /** Quaternion from Euler XYZ degrees — the order CSS `rotateX rotateY * rotateZ` applies. Matches the composed matrix `Rx(rx)·Ry(ry)·Rz(rz)`. */ declare function quatFromEulerXYZ(eulerDeg: Vec3): Quat; /** Euler XYZ degrees from a quaternion — inverse of `quatFromEulerXYZ`. * Handles gimbal lock (|ry| → 90°) by collapsing rz onto rx. The output * matches the convention used by CSS `rotateX rotateY rotateZ` so it can * be written straight back into a PolyMesh rotation prop. */ declare function eulerXYZFromQuat(q: Quat): Vec3; /** * Screen → world inverse projection helpers. * * The camera transform a CameraHandle emits is: * * `translateZ(-distance) scale(zoom/tile) rotateX(rotX) rotate(rotY) translate3d(-cssX, -cssY, -cssZ)` * * (right-to-left for point transforms). For an orthographic camera the * depth component drops out at projection time, so a screen position * (mx, my) unprojects to a 3D RAY in world space — every depth value * along that ray maps to the same screen position. Callers pick a * specific 3D point by intersecting the ray with a surface they care * about (here: a sphere). * * `screenToWorldOnSphere` is the common case: given a mouse position, * find where on a fixed sphere (e.g. the draggable light marker's * sphere of constant radius) the cursor is pointing. Returns null * when the ray misses the sphere. */ interface ScreenToWorldOptions { /** Camera state — rotX/rotY/zoom/target, same shape `CameraHandle.state` exposes. */ camera: CameraState; /** World-units → CSS-px factor. Pass `DEFAULT_TILE` from the renderer. */ tileSize: number; /** Viewport center in CSS pixels (screen coordinates). The mouse position * is interpreted relative to this point. */ viewportCenterX: number; viewportCenterY: number; /** Mouse screen position (CSS pixels). */ mouseX: number; mouseY: number; } /** * Unproject the cursor to a ray in world coordinates. * * `ray(t) = origin + t · direction` * * `origin` is the world point that screen-coincides with the mouse at * depth t=0; `direction` is the camera-forward axis in world frame. * For orthographic cameras the ray is parallel — every point along it * projects to the same screen position. */ declare function screenToWorldRay(opts: ScreenToWorldOptions): { origin: Vec3; direction: Vec3; }; /** * Unproject the cursor and intersect with a sphere in world coords. * Returns the FRONT intersection (the one closer to the camera) when * the ray hits the sphere. When the ray misses (cursor outside the * projected silhouette), returns the point on the sphere's silhouette * closest to the cursor — that way callers like draggable helpers keep * tracking the cursor along the rim instead of freezing in place. */ declare function screenToWorldOnSphere(opts: ScreenToWorldOptions & { sphereCenter: Vec3; sphereRadius: number; }): Vec3 | null; interface ParsedColor { rgb: [number, number, number]; alpha: number; } declare function parseHexColor(value: string): ParsedColor | null; declare function parseRgbColor(value: string): ParsedColor | null; /** Parse hex or rgb/rgba color strings. Pure — no DOM. */ declare function parsePureColor(input: string): ParsedColor | null; declare function clampChannel(value: number): number; declare function formatColor(color: ParsedColor): string; declare function parseColor(input: string): ParsedColor | null; /** * Lighten/darken a color by a flat per-channel delta. Used by the framework * wrappers for tinted-overlay debug renderers; per-polygon Lambert shading * goes through `computeShapeLighting` instead. */ declare function shadeColor(base: string, delta: number): string; /** * Per-polygon Lambert shading. Given a polygon's outward normal and the * scene's lights, returns the shaded color as a CSS rgb string. * * Math (decoupled, three.js convention): * tint = ambient.color · ambient.intensity * + directional.color · directional.intensity · max(0, n · L) * final = baseColor × tint * * Pass `directional` and/or `ambient` undefined to fall back to defaults * (top-down white directional with intensity 1, white ambient with * intensity 0.4) — useful for static SSR/validator renders. */ declare function computeShapeLighting(normal: Vec3, baseColor: string, directional?: PolyDirectionalLight, ambient?: PolyAmbientLight): string; /** * Merge coplanar same-color adjacent triangles into N-vertex polygons. * * Each visible polygon renders as one DOM leaf — so a mesh whose triangles * came from quads or pentagons collapses back into its original face count. * * - Geodesic spheres: ~half the triangles came from quad subdivisions * - OBJ imports: many were quads/n-gons fan-triangulated by the importer * - Hand-built dodecahedra: 36 triangles → 12 pentagons * * Algorithm: * 1. For each input polygon, compute its plane (unit normal + signed * distance from origin). * 2. Build an undirected edge graph: every edge of every polygon indexes * the polygons it belongs to. * 3. Repeatedly walk shared edges and merge the two polygons sharing that * edge if they pass the merge predicate (same color, near-coplanar, * result is convex, edge is interior). Each merge replaces two * polygons with one larger polygon and updates the edge index. * 4. Iterate until no more merges fire — the fixed point grows triangles * → quads → pentagons → … as far as the geometry allows. * * Polygons with < 3 vertices are passed through unchanged (the caller is * expected to have run `normalizePolygons` first; this is a defensive copy). */ declare function mergePolygons(input: Polygon[]): Polygon[]; /** * dedupeOverlappingPolygons — drop polygons whose 3D footprint coincides * with another polygon's, within an epsilon tolerance. * * Why this exists: modelers (and importers) often emit redundant geometry * for the same visible surface — a doubled face on a wall, an inner shell * coincident with an outer shell, or two N-gons that fan-triangulate the * same region. Each duplicate is a real render leaf at render time: * it costs DOM, Lambert math, atlas budget, and redundant shadow projection * work. * * This is a separate concern from `cullInteriorPolygons` (which removes * polygons fully *enclosed* by other geometry, conservative against * false positives) and from `mergePolygons` (which joins same-color * coplanar polygons that share an edge). A polygon's exact twin * doesn't share an edge with itself and isn't enclosed by anything — * it slips through both passes. * * Algorithm: * 1. Compute each polygon's plane (normal + signed offset along * normal) and centroid. * 2. Bucket polygons by quantized plane key (rounded normal direction * with sign-folding so anti-parallel faces share a bucket, and * rounded distance from origin along the unsigned normal axis). * Polygons in different buckets cannot overlap. * 3. Within each bucket, do an O(K²) pairwise check on at most K * polygons. Two polygons overlap if their 2D projections onto * the shared plane share a significant area fraction. * 4. When a pair overlaps, drop one: prefer keeping the one whose * normal points *away* from the mesh centroid (the "outward" * face). For ties (truly identical orientation), keep the one * with greater 2D area. * * Runs once at parse time in the same pipeline as mergePolygons. Zero * cost at runtime — once it returns the polygon array is final and the * dedup logic never executes again. */ /** Tunable thresholds. Default values are conservative — only catch * duplicates that are visually identical surfaces (exact twins, * back-to-back winding flips, nested polys on the same plane). * Looser values are appropriate for shadow-casting purposes, where * any polygons whose projections land in the same place can share a * shadow without affecting the rendered model. */ interface DedupeOverlappingPolygonsOptions { /** Maximum 1 - |dot(n_a, n_b)| for normals to count as "parallel". * Default 1e-3 (strict — must be near-identical orientation). * Looser values (~5e-2 ≈ 18° off) treat near-parallel normals as * duplicates, useful for shadow dedup where small orientation * differences project to nearly the same shadow shape. */ normalTolerance?: number; /** Maximum signed-distance difference between two polygons' plane * offsets (along their shared normal) to count as coplanar. * Default 0.05 (world units). Looser values treat distinct * parallel shells (e.g. an inner cavity wall behind an outer * wall) as shadow-duplicates. */ distanceTolerance?: number; /** Minimum overlap fraction (max of A-in-B and B-in-A vertex * containment ratios) for a pair to count as a duplicate. * Default 0.7. Lower (~0.4) is liberal; higher (~0.9) is strict. */ overlapFraction?: number; /** Preserve reverse-wound faces from authored double-sided materials. * Geometry dedupe keeps these by default; shadow dedupe can disable it * because coincident front/back casters produce stacked shadows. */ preserveDoubleSidedBackfaces?: boolean; } /** Identify polygons that are duplicates within tolerance. Returns the * set of indices into the input array that should be dropped (the * losers of duplicate pairs). The "winner" of a pair is the polygon * whose normal faces away from the mesh centroid (outward), with * larger area as a tiebreaker. * * Exposed for callers that want to act on the index set directly — * e.g. shadow casting can use a looser tolerance to skip redundant caster * projections without removing them from the renderable polygon set. */ declare function findOverlappingPolygonDuplicates(input: Polygon[], options?: DedupeOverlappingPolygonsOptions): Set; declare function dedupeOverlappingPolygons(input: Polygon[], options?: DedupeOverlappingPolygonsOptions): Polygon[]; interface CoverPlanarPolygonsOptions { /** Smallest connected coplanar group worth attempting. Default 4. */ minGroupPolygons?: number; /** Maximum candidate 2D axes tested per group. Default 8. */ maxCandidateAxes?: number; /** Plane normal/distance tolerance in scene units. Default 1e-3. */ planeEpsilon?: number; } /** * Re-cover flat same-color mesh regions with generated convex polygons. * * `mergePolygons` preserves source topology: it can only combine existing * neighboring faces. This pass is more aggressive for solid-color planar * regions: it projects each connected coplanar patch into 2D, covers the * patch from its outer boundary, then lets `mergePolygons` collapse the * generated cover into large rects/quads where possible. */ declare function coverPlanarPolygons(input: Polygon[], options?: CoverPlanarPolygonsOptions): Polygon[]; interface SimplifyTriangleMeshPolygonsOptions { /** Target triangle ratio per eligible connected material group. Default 0.7. */ ratio?: number; /** Maximum accepted local plane displacement in scene units. Default 0.18. */ maxError?: number; /** Reject collapses that rotate any affected face beyond this angle. Default 65. */ maxNormalAngleDeg?: number; /** Only collapse vertices onto existing endpoint positions. Default true. */ preserveVertices?: boolean; /** Use stricter importer source-vertex keys instead of relaxed seam keys. Default "relaxed". */ vertexKeyMode?: "relaxed" | "source"; /** Keep topological/material boundary vertices fixed. Default true. */ lockBoundary?: boolean; /** Skip small groups where decimation is unlikely to repay the mutation cost. Default 80. */ minGroupTriangles?: number; /** Hard cap for rebuild passes. Default 12. */ maxPasses?: number; } /** * Import-time triangle decimation for already-solid meshes. * * This is deliberately conservative: textured polygons and material/color * boundaries are kept out of the collapse graph, endpoint-preserving collapses * mirror meshoptimizer's index-buffer simplification shape by default, and * callers can cheaply compare the returned candidate against their normal * render-cost optimizer before accepting it. */ declare function simplifyTriangleMeshPolygons(polygons: Polygon[], options?: SimplifyTriangleMeshPolygonsOptions): Polygon[]; interface OptimizeMeshPolygonsOptions { /** Public quality/resolution intent. Defaults to "lossy". */ meshResolution?: MeshResolution; /** * Return as soon as the optimizer finds a result with at most this many * polygons. Useful for candidate comparisons where the caller already knows * the maximum DOM leaf count it can accept. */ stopAtPolygonCount?: number; /** * Run the planar cover pass as an exact candidate for untextured coplanar * regions. Defaults to true. */ rectCover?: boolean | CoverPlanarPolygonsOptions; } declare function optimizeMeshPolygons(polygons: Polygon[], options?: OptimizeMeshPolygonsOptions): Polygon[]; /** * Unified parser return type. All polygon-emitting parsers (parseObj, * parseGltf, parseVox, parseStl, the loadMesh dispatcher) return this exact shape. * * The asymmetric helper `parseMtl` returns its own `MtlParseResult` (it * emits materials, not polygons) — see parseMtl.ts for the rationale. * * Lifecycle contract: callers MUST call `dispose()` when the result is no * longer needed. Idempotent — safe to call on unmount even if `objectUrls` * is empty (e.g. `parseObj`, where it's a no-op). */ interface PolyVoxelCell { x: number; y: number; z: number; color: string; } interface PolyVoxelSource { kind: "magica-vox"; cells: PolyVoxelCell[]; rows: number; cols: number; depth: number; scale: number; sourceBytes: number; } interface ParseStlTopology { componentCount: number; repairedTriangleCount: number; outwardComponentCount: number; suppliedNormalComponentCount: number; inconsistentSharedEdgeCount: number; nonManifoldSharedEdgeCount: number; } interface ParseStlColor { format: "magics"; defaultColor: string; alpha: number; coloredTriangleCount: number; defaultColorTriangleCount: number; } interface ParseStlSolid { name: string; start: number; count: number; } interface ParseAnimationClip { /** Stable numeric index in the source file's animation array. */ index: number; /** Human-readable clip name. Falls back to `animation_N` when omitted. */ name: string; /** Clip duration in seconds, derived from its sampler input accessors. */ duration: number; /** Number of glTF animation channels in the clip. */ channelCount: number; } interface ParseAnimationController { /** Animation clips exposed by the parsed mesh. Empty when none are usable. */ clips: ParseAnimationClip[]; /** * Sample a clip at `timeSeconds` and return a fresh polygon list. * `clip` accepts either the clip index or its name. Time wraps by duration. */ sample: (clip: number | string, timeSeconds: number) => Polygon[]; } interface ParseResult { /** The mesh, as a flat polygon list. Already vertex-permuted to polycss space. */ polygons: Polygon[]; /** Optional raw voxel source for `.vox` fast paths; polygon fallback remains authoritative. */ voxelSource?: PolyVoxelSource; /** Optional animation sampler for formats that carry timeline data. */ animation?: ParseAnimationController; /** * Blob/object URLs minted during parse (e.g. embedded GLB images). Pass-by- * reference — the same array is exposed on the result for visibility, and * `dispose()` revokes each one. Do NOT mutate this array externally. */ objectUrls: string[]; /** * Idempotent — revokes object URLs. Safe to call on unmount, safe to call * twice. Parsers without minted URLs (parseObj, parseMtl) supply a no-op. */ dispose: () => void; /** * Non-fatal warnings raised during parse. Empty for parsers that don't * have a warning channel; populated when downstream `normalizePolygons` * is invoked through the high-level pipeline. */ warnings: string[]; /** Optional format-specific metadata. */ metadata?: { /** Triangle count after fan-triangulation (parseObj) or post-triangulation (parseGltf). */ triangleCount?: number; /** Mesh names from the file (for glTF, from doc.meshes[].name). */ meshes?: string[]; /** Material names (in first-seen order). */ materials?: string[]; /** Animation clips from the file, mirrored from `animation.clips`. */ animations?: ParseAnimationClip[]; /** Source file size in bytes (for diagnostics). */ sourceBytes?: number; /** Voxel count for `.vox` sources. */ voxelCount?: number; /** Printable binary STL header, trimmed. */ stlHeader?: string; /** Binary STL color metadata when a supported color extension is present. */ stlColor?: ParseStlColor; /** Consecutive ASCII `solid` groups after parse/filtering. */ stlSolids?: ParseStlSolid[]; /** STL winding/connectivity diagnostics. */ stlTopology?: ParseStlTopology; }; } interface OptimizeMeshParseResultOptions { /** Render-cost optimization intent. Defaults to "lossy". */ meshResolution?: MeshResolution; /** Parse result before solid texture baking, used to identify baked swatch faces. */ source?: ParseResult; /** Merge near-identical baked swatch colors in lossy mode. Default 36. */ bakedTextureColorMergeDistance?: number; /** Try static triangle simplification before final polygon optimization. Default true. */ simplifyTriangleMeshes?: boolean; /** Options for the static triangle simplifier. */ simplifyTriangleMeshOptions?: SimplifyTriangleMeshPolygonsOptions; /** Candidate optimizer early-stop drop target. Default 0.15. */ simplifyEarlyStopDropRatio?: number; } declare function optimizeMeshParseResult(result: ParseResult, options?: OptimizeMeshParseResultOptions): ParseResult; type SeamOverlapCandidateKind = "true-gap" | "connected-facet" | "material-boundary"; interface SeamOverlapCandidate { kind: SeamOverlapCandidateKind; aPolygon: number; aEdge: number; bPolygon: number; bEdge: number; aColor?: string; bColor?: string; aMaterialKey: string; bMaterialKey: string; gapPx: number; spanPx: number; aStartPx: number; aEndPx: number; bStartPx: number; bEndPx: number; targetClosurePx: number; appliedClosurePx: number; residualGapPx: number; residualTargetPx: number; } interface SeamOverlapDiagnostics { exactPairs: number; nearPairs: number; patchedPolygons: number; patchedEdges: number; maxMeasuredGapPx: number; maxAppliedAmountPx: number; unclosedPairs: number; maxResidualGapPx: number; } interface SeamOverlapOptions { overlapPx?: number; maxGapPx?: number; capacityScale?: number; } interface SeamFacetSplitOptions { rotX?: number; rotY?: number; viewAware?: boolean; passes?: number; budget?: number; } type SeamFacetSplitCandidateReason = "component-anchor" | "global-outlier" | "local-follow-up" | "shared-polygon" | "below-threshold"; interface SeamFacetSplitCandidate { key: string; aPolygon: number; aEdge: number; bPolygon: number; bEdge: number; color?: string; materialKey: string; lengthPx: number; projectedLengthPx: number; score: number; normalRisk: number; shapeRisk: number; viewRisk: number; component: number; marginalCost: number; selected: boolean; reason: SeamFacetSplitCandidateReason; } interface SeamFacetSplitReport { candidates: SeamFacetSplitCandidate[]; selectedPolygons: number; selectedEdges: number; addedPolygons: number; } declare const DEFAULT_SEAM_OVERLAP_OPTIONS: { readonly overlapPx: 1.25; readonly maxGapPx: 14; readonly capacityScale: 1; }; declare const DEFAULT_SEAM_FACET_SPLIT_OPTIONS: { readonly budget: 40; }; declare function seamFacetSplitPolygons(polygons: Polygon[], seamOptions?: number | SeamOverlapOptions, splitOptions?: SeamFacetSplitOptions): Polygon[]; declare function seamFacetSplitReport(polygons: Polygon[], seamOptions?: number | SeamOverlapOptions, splitOptions?: SeamFacetSplitOptions): SeamFacetSplitReport; declare function seamOverlapPolygons(polygons: Polygon[], options?: number | SeamOverlapOptions): Polygon[]; declare function repairMeshSeams(polygons: Polygon[], seamOptions?: number | SeamOverlapOptions, splitOptions?: SeamFacetSplitOptions): Polygon[]; declare function seamOverlapDiagnostics(polygons: Polygon[], options?: number | SeamOverlapOptions): SeamOverlapDiagnostics; declare function seamOverlapReport(polygons: Polygon[], options?: number | SeamOverlapOptions): { diagnostics: SeamOverlapDiagnostics; candidates: SeamOverlapCandidate[]; }; /** * cullInteriorPolygons — remove polygons that are fully enclosed by other * polygons of the same mesh and therefore never visible from any external * camera direction. * * Algorithm: for each polygon p, * 1. Sample K unit directions on the hemisphere above p's normal. * 2. Cast a ray from a point just above p's centroid in each direction. * 3. If at least one ray escapes without hitting any other polygon → p * is potentially visible from some external camera → keep it. * 4. If every ray hits another polygon → p is fully surrounded → cull it. * * Acceleration: flat-array SAH-built binary BVH with slab-test AABB traversal. * All BVH data is stored in typed Float64Array / Int32Array for cache efficiency. * Ray traversal visits only the subtrees whose AABBs the ray intersects. * * Runs once at parse time inside `loadMesh`, before `mergePolygons`. Zero * runtime cost. Conservative by design — false negatives (failing to cull * a truly hidden poly) are safe; false positives would be a visual bug. */ interface CullInteriorOptions { /** Hemisphere ray samples per polygon. Higher = fewer false positives, slower. Default 8. */ samples?: number; /** Bypass the "large open topology" safety bail-out. By default a mesh * with >20% boundary edges is treated as open (terrain, partial geometry) * and culling is skipped to avoid false-positive removals on exposed faces. * Buildings imported from OBJ commonly have windows/doors that push the * boundary ratio above the threshold while STILL having clearly enclosed * interior walls that should be culled — set this when the caller has * another signal (e.g. mesh is known to be a building / room) that * interior culling is desired. Default false. */ force?: boolean; /** Fraction of sample directions that must find at least one escaping * origin for the polygon to count as visible. Default `1 / samples` * (the original "any escape keeps" behaviour). Raise to ~0.5 to * classify polygons as interior when MOST hemisphere directions are * blocked — catches interior panels of building meshes (the cottage's * porch trim, back-of-wall door frames) that have a sliver of clear * sky through windows/openings but are otherwise enclosed. */ minEscapeRatio?: number; } declare function cullInteriorPolygons(polygons: Polygon[], options?: CullInteriorOptions): Polygon[]; /** * computeLightVisibility — for each polygon of a mesh, determines whether * direct light from a given direction physically reaches it (ray from the * polygon's centroid toward the light source is unblocked) or is occluded * by other geometry of the same mesh. * * This is the CPU equivalent of one shadow-map sample per polygon from the * light's POV. Used by the baked atlas pipeline so polygons in shadow get * lit with ambient-only color, matching Three.js's depth-pass occlusion. * * Algorithm: Möller-Trumbore ray-triangle intersection per candidate, with * a flat-array BVH for any-hit traversal. Cost: O(F log F) per mesh per * light direction. Cottage at ~240 polys: ~5-10 ms. Caller should cache by * (mesh geometry version, lightDir hash) and only recompute on change. */ /** * Returns a Set of polygon indices that are OCCLUDED from the light * (a ray from the polygon's centroid in the +lightDir direction hits * another polygon of the same mesh before escaping to infinity). * * `lightDir` is the direction TO the light source (matching the * convention used by `shadePolygon`'s caller — points from the * surface toward the light). * * Caller should cache by mesh-geometry version + lightDir hash; * recompute only when either changes. */ declare function computeLightVisibility(polygons: readonly Polygon[], lightDir: Vec3, skipIndices?: ReadonlySet): Set; declare const CAMERA_BACKFACE_CULL_EPS = 0.00001; declare const VOXEL_CAMERA_CULL_AXIS_EPS = 0.001; declare const VOXEL_CAMERA_CULL_NORMAL_LIMIT = 6; interface CameraCullRotation { rotX: number; rotY: number; meshRotation?: Vec3; } interface CameraCullNormalGroup { key: string; normal: Vec3; } declare function polygonCssSurfaceNormal(polygon: Polygon): Vec3 | null; declare function cameraFacingDepth(normal: Vec3, rotation: CameraCullRotation): number; declare function normalFacesCamera(normal: Vec3, rotation: CameraCullRotation, depthThreshold?: number): boolean; declare function polygonFacesCamera(polygon: Polygon, rotation: CameraCullRotation, depthThreshold?: number): boolean; declare function cameraCullNormalKey(normal: Vec3): string; declare function cameraCullNormalGroups(normals: Iterable): CameraCullNormalGroup[]; declare function cameraCullNormalGroupsFromPolygons(polygons: readonly Polygon[]): CameraCullNormalGroup[]; declare function isAxisAlignedSurfaceNormal(normal: Vec3, axisEpsilon?: number): boolean; declare function isVoxelCameraCullableNormalGroups(groups: readonly CameraCullNormalGroup[]): boolean; declare function cameraCullVisibleSignature(groups: readonly CameraCullNormalGroup[], rotation: CameraCullRotation, depthThreshold?: number): string; /** * Geometry for the three.js-style debug axes gizmo: three thin colored * cuboids stretching along world-X, world-Y and world-Z. Mirrors the * convention `red=X, green=Y, blue=Z`. * * Returned polygons are in the standard PolyCSS world-space convention * (`+X right, +Y forward, +Z up`). Wrap with the framework's PolyMesh / * PolyScene equivalent to render. */ interface AxesHelperOptions { /** Length of each axis bar in world units. */ size?: number; /** Bar cross-section width as a fraction of `size`. */ thickness?: number; /** When true, also draws bars in the −X / −Y / −Z direction. */ negative?: boolean; /** X-axis bar color. */ xColor?: string; /** Y-axis bar color. */ yColor?: string; /** Z-axis bar color. */ zColor?: string; } /** * Build the polygons for an AxesHelper-style gizmo. Three thin cuboids, * one per world axis. Defaults match `` in the framework * packages. */ declare function axesHelperPolygons(options?: AxesHelperOptions): Polygon[]; /** * Axis-aligned box/cuboid geometry as six quad polygons. * * Returned polygons are in standard PolyCSS world space: * +X = right, +Y = front/forward, +Z = top/up. */ type BoxFace = "right" | "left" | "front" | "back" | "top" | "bottom"; type BoxFaceOptions = Pick; interface BoxPolygonsOptions extends BoxFaceOptions { /** Size along the world X/Y/Z axes. Defaults to a 1×1×1 cube. */ size?: number | Vec3; /** Center used with `size`. Defaults to the origin. */ center?: Vec3; /** Explicit minimum world-space corner. When set with `max`, bounds win over size/center. */ min?: Vec3; /** Explicit maximum world-space corner. When set with `min`, bounds win over size/center. */ max?: Vec3; /** Per-face material/data overrides. Set a face to `false` to omit it. */ faces?: Partial>; } /** Build the polygons for one axis-aligned box/cuboid. */ declare function boxPolygons(options?: BoxPolygonsOptions): Polygon[]; /** * Geometry for a single 3D arrow: a thin axis-aligned cuboid shaft * stretching from the origin along one signed axis, capped with a * 4-sided pyramid head pointing further in that direction. Used as the * drag handle for `` — same primitive recipe as * `axesHelperPolygons`, plus an arrowhead. * * Returned polygons are in standard PolyCSS world space and intended * to be wrapped in the framework's PolyMesh equivalent for rendering. */ interface ArrowPolygonsOptions { /** World axis the arrow extends along: 0=X, 1=Y, 2=Z. */ axis: 0 | 1 | 2; /** Direction along the axis: +1 (positive) or -1 (negative). Default +1. */ sign?: 1 | -1; /** Length of the rectangular shaft along the axis. */ shaftLength?: number; /** Half cross-section of the shaft (perpendicular to the axis). */ shaftHalfThickness?: number; /** Length of the pyramid head along the axis (extends past the shaft). */ headLength?: number; /** Half-extent of the pyramid base. */ headHalfThickness?: number; /** Fill color. */ color?: string; /** Emit the rectangular shaft polygons. Default `true`. Set `false` to * render just the pyramid head — used by transform-control gizmos to * declutter back-facing axes (only the head still identifies direction * while the shaft would visually overlap the front-facing arrow). */ shaft?: boolean; } /** Build the polygons for one signed-axis arrow. */ declare function arrowPolygons(options: ArrowPolygonsOptions): Polygon[]; /** * Geometry for a flat ring (annulus) lying in the plane perpendicular * to a chosen axis. Used as the rotation handle in * `` — three rings, one per axis, * each draggable to rotate the target around that axis. * * The ring is a sequence of quad segments around a circle. We don't * model a true torus (tube) — a flat annulus reads cleanly as a * "rotation circle" and keeps the polygon count proportional to the * `segments` knob. * * Returned polygons are in standard PolyCSS world space and intended * to be wrapped in the framework's PolyMesh equivalent for rendering. */ interface RingPolygonsOptions { /** World axis the ring is perpendicular to: 0=X, 1=Y, 2=Z. The ring * itself lies in the plane spanned by the other two axes. */ axis: 0 | 1 | 2; /** Mid-radius of the ring (distance from center to the middle of * the annulus band). */ radius: number; /** Half-width of the annulus band — the ring spans `radius - half` * to `radius + half`. */ halfThickness?: number; /** Number of quad segments around the circle. Higher = smoother. */ segments?: number; /** Fill color. */ color?: string; } /** Build the polygons for a flat ring (annulus). */ declare function ringPolygons(options: RingPolygonsOptions): Polygon[]; /** * One square quad covering the bounding box of a ring (annulus) in the * plane perpendicular to a chosen axis. Used by `` together with a CSS `mask: radial-gradient(...)` to * render the visible donut, replacing the segmented quad-strip approach * of `ringPolygons` with a single DOM element per ring. * * The caller is responsible for applying the mask CSS and using a donut- * shaped hit-test (the quad's bounding rect alone would over-hit the * inner hole). The recommended setup is to set the CSS custom property * `--ring-inner-ratio` on the mesh element so the mask scales with the * caller's chosen thickness ratio. */ interface RingQuadPolygonsOptions { /** World axis the ring is perpendicular to: 0=X, 1=Y, 2=Z. The quad * lies in the plane spanned by the other two axes. */ axis: 0 | 1 | 2; /** Outer radius of the ring. The quad spans ±outerRadius in both * in-plane axes. */ outerRadius: number; /** Fill color. */ color?: string; } /** Build a single 4-vertex polygon (a square) bounding the ring's outer * circle. CSS `mask` is expected to clip this to the donut shape at * render time. */ declare function ringQuadPolygons(options: RingQuadPolygonsOptions): Polygon[]; /** * A flat quad on one of the three axis-aligned planes, offset diagonally * along the two in-plane axes. Used as a planar drag handle in * `` — clicking and dragging this handle moves the * attached mesh along two axes simultaneously (XY, XZ, or YZ), instead of * the single-axis motion the arrow shafts provide. * * The polygon lives in standard PolyCSS world space; wrap it in the * framework's PolyMesh equivalent for rendering. */ interface PlanePolygonsOptions { /** Axis perpendicular to the plane: 0 = YZ plane, 1 = XZ plane, * 2 = XY plane. The quad lies on the OTHER two axes. */ axis: 0 | 1 | 2; /** Half-extent of the quad along each in-plane axis. Default `0.4`. */ size?: number; /** Center of the quad along the two in-plane axes. Pass a single number * to use the same offset on both (positive places the handle in the * +A/+B corner). Pass `[offsetA, offsetB]` to control each * independently — sign flips move the handle to a different octant. * `A = (axis+1)%3`, `B = (axis+2)%3`. Default `size * 2`. */ offset?: number | [number, number]; /** Position along the perpendicular axis. Default `0` (on the plane). */ along?: number; /** Fill color. */ color?: string; } /** Build the polygons for one axis-aligned planar drag handle. */ declare function planePolygons(options: PlanePolygonsOptions): Polygon[]; /** * Geometry for a small solid-color octahedron — the marker shape used by * `PolyDirectionalLightHelper` to indicate where a directional light is * shining from. Eight CCW-from-outside triangular faces, vertices at * `center ± (size, 0, 0)` etc. */ interface OctahedronPolygonsOptions { /** Center of the octahedron in world space. */ center: Vec3; /** Half-extent (distance from center to each pole vertex). */ size: number; /** Fill color applied to all eight faces. */ color?: string; } declare function octahedronPolygons(options: OctahedronPolygonsOptions): Polygon[]; /** * Icosphere (subdivided icosahedron) geometry — approximates a sphere with * triangular faces. Each subdivision step quadruples the face count: * subdivisions 0 → 20, 1 → 80, 2 → 320, 3 → 1280 (capped). * * Vertex coordinates use PolyCSS world space: +X right, +Y forward, +Z up. * The sphere is centered at the origin; all vertices sit at distance `radius`. * Faces wind CCW from the outside (outward normal = away from origin). */ interface SpherePolygonsOptions { /** Radius of the sphere. Default 50. */ radius?: number; /** Subdivision level (0 = bare icosahedron, 20 triangles; each +1 quadruples count). Default 1 → 80 triangles. Cap at 3 (1280 triangles). */ subdivisions?: number; /** Fill color applied to all faces. */ color?: string; } declare function spherePolygons(options?: SpherePolygonsOptions): Polygon[]; /** * Regular tetrahedron geometry — four equilateral triangular faces. * * Vertices are placed so the tetrahedron is centered at the origin. * Faces wind CCW from the outside. * * PolyCSS world space: +X right, +Y forward, +Z up. */ interface TetrahedronPolygonsOptions { /** Circumradius: distance from center to each vertex. Default 100. */ size?: number; /** Fill color applied to all four faces. */ color?: string; } declare function tetrahedronPolygons(options?: TetrahedronPolygonsOptions): Polygon[]; /** * Regular icosahedron geometry — 20 equilateral triangular faces. * * Vertices are placed on a sphere of radius `size` centered at the origin. * Faces wind CCW from the outside. * * PolyCSS world space: +X right, +Y forward, +Z up. */ interface IcosahedronPolygonsOptions { /** Circumradius: distance from center to each vertex. Default 100. */ size?: number; /** Fill color applied to all 20 faces. */ color?: string; } declare function icosahedronPolygons(options?: IcosahedronPolygonsOptions): Polygon[]; /** * Regular dodecahedron geometry — 12 regular pentagonal faces. * * Vertices are placed on a sphere of radius `size` centered at the origin. * Each face is a pentagon (5 vertices) wound CCW from the outside. * * All 12 pentagonal faces of a regular dodecahedron are truly planar — each * lies on a single tangent plane. No triangulation is needed. The renderer * uses (border-shape) on Chromium and elsewhere for non-quad polygons. * * PolyCSS world space: +X right, +Y forward, +Z up. */ interface DodecahedronPolygonsOptions { /** Circumradius: distance from center to each vertex. Default 100. */ size?: number; /** Fill color applied to all 12 faces. */ color?: string; } declare function dodecahedronPolygons(options?: DodecahedronPolygonsOptions): Polygon[]; /** * Z-axis cylinder geometry with optional radius taper. * * Geometry: * - `radialSegments` side faces (quads for cylinders/frustums, triangles * when one radius collapses to a cone tip). * - `radialSegments` bottom-cap triangles (fan from center). * - `radialSegments` top-cap triangles (fan from center), omitted when * radiusTop ≈ 0 (i.e. cone tip). * * The cylinder sits centered at the origin, spanning Z = −height/2 to * Z = +height/2. Side quads are axis-aligned in the cylinder's own local * frame, which maximises the chance of hitting the quad fast-path. * * PolyCSS world space: +X right, +Y forward, +Z up. The cylinder axis is * the Z axis so a typical upright pillar stands without any extra rotation. */ interface CylinderPolygonsOptions { /** Bottom-cap radius. Default 50. */ radius?: number; /** Top-cap radius. Defaults to `radius` (straight cylinder). * Set to 0 (or near 0) for a cone. */ radiusTop?: number; /** Height along the Z axis. Default 100. */ height?: number; /** Number of radial segments. Default 12. */ radialSegments?: number; /** Fill color applied to all polygons. */ color?: string; } declare function cylinderPolygons(options?: CylinderPolygonsOptions): Polygon[]; /** * Z-axis cone geometry — a cylinder with `radiusTop: 0`. * * This is a thin wrapper around `cylinderPolygons` with `radiusTop` forced * to zero. The top cap is omitted (no area at the tip), and side faces are * emitted as triangles. * * PolyCSS world space: +X right, +Y forward, +Z up. Cone axis is Z; the apex * is at Z = +height/2 and the base at Z = -height/2. */ interface ConePolygonsOptions { /** Base radius. Default 50. */ radius?: number; /** Height along the Z axis. Default 100. */ height?: number; /** Number of radial segments. Default 12. */ radialSegments?: number; /** Fill color applied to all polygons. */ color?: string; } declare function conePolygons(options?: ConePolygonsOptions): Polygon[]; /** * Torus geometry — Z-axis ring plane. * * The torus is centered at the origin. The ring lies in the XY plane (the * ground plane in PolyCSS world space, where Z is up). The donut hole points * along the Z axis. * * Geometry: `radialSegments × tubularSegments` quads on the surface. * * DOM cost note: at default settings (12 × 16 = 192 quads) this is the * heaviest of the built-in primitives. Reduce radialSegments / tubularSegments * if render budget is tight. * * PolyCSS world space: +X right, +Y forward, +Z up. */ interface TorusPolygonsOptions { /** Distance from center of tube to center of torus. Default 50. */ radius?: number; /** Radius of the tube. Default 15. */ tube?: number; /** Number of segments around the main ring. Default 12. */ radialSegments?: number; /** Number of segments around the tube cross-section. Default 16. */ tubularSegments?: number; /** Fill color applied to all polygons. */ color?: string; } declare function torusPolygons(options?: TorusPolygonsOptions): Polygon[]; /** Tiny non-zero scale collapsed into the projection's Z column to keep * the matrix invertible. Chromium skips elements whose composed * transform is singular (m22 = 0 would make this a true projection * matrix, but Chromium would refuse to paint it), so we crush Z to 1% * of its input instead of exactly zero. The result still looks flat * to the eye — sub-pixel drift on any realistic scene size. */ declare const BAKED_SHADOW_Z_SQUASH = 0.01; /** Minimum absolute value of the up-axis light component before the * projection blows up (we divide by it). Matches the --clz clamp in * the dynamic-mode applyDynamicLightVars helper so baked + dynamic * behave identically when the light is near-horizontal. */ declare const BAKED_SHADOW_MIN_UP = 0.01; /** * Build the CSS-space shadow projection matrix for a fixed light + ground * plane. The 16-element output mirrors the retained `--shadow-proj` CSS * custom property, but with literal numbers — ready to be formatted into a * single `matrix3d(...)`. * * `lightDir` is the direction the light TRAVELS (e.g. `[0, 0, -1]` is * straight down). PolyCSS world Z is up, and the world→CSS axis swap * leaves Z alone — see styles.ts for the full convention. * * `groundCssZ` is the receiver plane in CSS-Z (= world-Z) coordinates, * already in unit-less form (matrix3d entries must be dimensionless). */ declare function buildBakedShadowProjectionMatrix(lightDir: Vec3, groundCssZ: number): number[]; /** * Radial variant of `projectCssVertexToGround` for a point light at a fixed * CSS-frame position. The shadow ray travels FROM the light THROUGH the * vertex and onto the ground plane `z = groundCssZ`, so each vertex projects * along its own direction (a true perspective projection) rather than the * single parallel direction a directional light uses. * * Returns `null` when no valid forward intersection exists — the vertex sits * on the light's side of the ground plane, or the ray runs parallel to it. * The caller drops that vertex (and any polygon that loses ≥1 vertex falls * back to a degenerate projection it can skip). * * `lightPos` and `cssVertex` are both in the dimensionless CSS frame (after * the world→CSS axis swap + tile scale), matching `projectCssVertexToGround`. */ declare function projectCssVertexToGroundFromPoint(cssVertex: Vec3, lightPos: Vec3, groundCssZ: number): [number, number] | null; /** * Point-light variant of `isBakedShadowCaster`. A polygon casts shadow when * its outward normal points away from the light — i.e. the ray from the * light to the polygon centroid runs into the back of the face. `centroid` * and `lightPos` are CSS-frame; `normal` is the CSS-frame outward normal. */ declare function isPointShadowCaster(centroid: Vec3, normal: Vec3, lightPos: Vec3): boolean; /** * Decides whether a polygon should cast a shadow given its outward * normal and the light's travel direction. * * True for polygons whose normals point in the same direction as the * light travels — i.e., on the far/dark side of the mesh from the * light's POV. Those define the silhouette of the cast shadow. * * False for front-facing polygons whose projection would land inside * the silhouette and only add overdraw. Dynamic mode hides these with * a Lambert opacity gate; baked mode skips the DOM emission entirely. */ declare function isBakedShadowCaster(normal: Vec3, lightDir: Vec3): boolean; /** * 2D convex hull (Andrew's monotone chain, O(n log n)). Returns the * hull vertices in CCW order. Used to compute a receiver mesh's XY * footprint when subtracting it from the global ground shadow. */ declare function convexHull2D(points: ReadonlyArray): Array<[number, number]>; /** * Signed area of a 2D polygon (positive for CCW vertex order, negative * for CW). Used by `ensureCcw2D` to normalize winding before concatenating * polygons into a compound SVG path under `fill-rule="nonzero"`: mixed * CCW/CW subpaths would cancel each other's winding in the overlap * region and paint an unintended hole. */ declare function polygonSignedArea2D(vertices: ReadonlyArray): number; /** * Returns the polygon's vertices in CCW order, reversing if necessary. * Operates on a copy — input is left unmodified. */ declare function ensureCcw2D(vertices: ReadonlyArray): Array<[number, number]>; /** * Projects a single CSS-3D vertex onto the shadow ground plane, returning * the resulting 2D point in CSS coordinates. Mirrors the retained * `--shadow-proj` matrix, but evaluated on the CPU for a fixed light + ground * so many projected vertices can be merged into one SVG shadow path. * * `cssVertex` is a 3D point that has already been through the world→CSS * axis swap and unit scale (so its components are dimensionless CSS-space * coordinates). `lightDir` follows the same `--clx/--cly/--clz` convention * as `buildBakedShadowProjectionMatrix`. */ declare function projectCssVertexToGround(cssVertex: Vec3, lightDir: Vec3, groundCssZ: number): [number, number]; /** * Build the parametric silhouette loop for a caster lit by a directional light. * * @param worldVerts Every caster vertex in the same world-CSS frame the * receiver projector works in (e.g. `CasterPolyItem.wv`). * @param lightDir Directional light vector (to-source) in that frame. * @param definition Max loop points. The convex hull is decimated down to this * by repeatedly dropping the lowest-area vertex (shape- * preserving). `<= 2` is treated as 3. * @returns A closed 3D loop (the silhouette vertices), or `null` if degenerate. */ declare function computeParametricShadowSilhouette(worldVerts: ReadonlyArray, lightDir: Vec3, definition: number): Vec3[] | null; /** * Build concave coverage-contour silhouette loops for a caster lit by a * directional light. * * @param polysWorldVerts Each caster polygon's vertices in the world-CSS frame * (e.g. `CasterPolyItem.wv`). * @param lightDir Directional light vector (to-source) in that frame. * @param definition Detail knob → coverage mask resolution. * @param layers Depth bands along the light. 1 (default) = one flat * outline (cross-mesh casting). >1 = depth-stratified * outlines for correct self-shadow. * @returns Closed 3D loops, or `null`. */ declare function computeCoverageShadowSilhouette(polysWorldVerts: ReadonlyArray>, lightDir: Vec3, definition: number, layers?: number, mode?: "contour" | "pixel"): Vec3[][] | null; /** True when every caster vertex lies in a single plane (a ground quad, a * billboard, etc.). Such casters have no coverage volume for the parametric * proxy — their tilted proxy would project garbage onto a coplanar receiver — * so the caller routes them through the exact path instead. */ declare function isFlatCaster(polysWv: ReadonlyArray>): boolean; /** Rubric: a CONVEX caster self-shadows nothing. The depth-band proxy still * leaks a little false self-shadow on convex meshes, so detect convexity and * skip self-shadow for them. Capped at `maxPolys` (O(faces × verts), and large * meshes are essentially never convex — they early-exit on the first concave * face anyway). */ declare function isConvexCaster(polysWv: ReadonlyArray>, maxPolys?: number): boolean; interface ParametricOverrideInput { /** Caster polygons, each a vertex list in the world-CSS frame. */ polysWorldVerts: ReadonlyArray>; /** Directional light (to-source) in the world-CSS frame. */ lightDir: Vec3; /** Effective definition (caller folds in per-mesh override + drag cap). */ definition: number; /** True when the caster is also the receiver (self-shadow). */ isSelf: boolean; /** `"pixel"` greedy-meshes the coverage into voxel rects; default contour. */ style?: "vector" | "pixel"; /** Shadow-casting point lights (CSS position + index in `allPointLights`). */ pointLights?: ReadonlyArray<{ position: Vec3; index: number; }>; } interface ParametricOverrideResult { /** Directional override loops, or undefined to use the exact path. */ overrideSilhouette?: Vec3[][]; /** Per-point-light radial override loops (indexed by point light index). */ overridePointSilhouettes?: Array; } /** Build the parametric override(s) for one caster — directional plus one * radial silhouette per shadow-casting point light. Returns empty overrides * (use the exact path) for flat casters and convex self-shadow. */ declare function buildParametricCasterOverride(input: ParametricOverrideInput): ParametricOverrideResult; type Pt = readonly [number, number]; /** * Clips `subject` against the convex polygon `clip`. Both polygons are * 2D, given in CCW vertex order. Returns the clipped polygon as a new * array of points; an empty array means `subject` lies entirely outside. */ declare function clipPolygonToConvex2D(subject: ReadonlyArray, clip: ReadonlyArray): Array<[number, number]>; /** * One coplanar group of receiver polygons projected into a 2D (u, v) basis on * the shared plane. Sutherland-Hodgman clips caster-projected shadows to this * group's outline; the per-member polygons let the renderer post-filter sub- * shadows that fall outside the actual surface union (concave-bridging air * gaps inside the convex hull). */ type ReceiverPlaneGroup = { O: Vec3; n: Vec3; u: Vec3; v: Vec3; outlineUv: Array<[number, number]>; memberPolysUv: Array>; memberPolyIndices: number[]; }; /** Convert a world-space scalar to CSS pixels. The default matches PolyCSS's * current renderer scale: one world unit = BASE_TILE CSS px. */ declare function worldDistanceToCss(value: number, worldUnitPx?: number): number; /** Convert a CSS-pixel scalar back to PolyCSS world units. */ declare function cssDistanceToWorld(value: number, worldUnitPx?: number): number; /** World→CSS axis swap. World is `+X right, +Y forward, +Z up`; the renderer's * internal frame swaps X↔Y and scales by BASE_TILE (one world unit = * BASE_TILE CSS px). Same conversion every renderer applies at the boundary * for mesh positions, polygon vertices, and light directions. */ declare function worldPositionToCss(p: Vec3, worldUnitPx?: number): Vec3; /** Inverse of {@link worldPositionToCss}: CSS-pixel frame → world XYZ. */ declare function cssPositionToWorld(p: Vec3, worldUnitPx?: number): Vec3; /** World→CSS axis swap for directions (no scale; directions stay unit). The * polygon basis stores normals in the swapped CSS frame, so light vectors * must match before any dot product. */ declare function worldDirectionToCss(d: Vec3): Vec3; /** Apply {@link worldDirectionToCss} to a directional-light object, * preserving the other fields. Used by atlas plan + buildBasisHints + * receiver-shadow callers so the light vector is in the same CSS-axis * frame as the polygon normals. Public package wrappers delegate here so * directional-light conversion stays single-source. */ declare function worldDirectionalLightToCss(light: T): T; declare const worldDistanceToPolyCss: typeof worldDistanceToCss; declare const polyCssDistanceToWorld: typeof cssDistanceToWorld; declare const worldPositionToPolyCss: typeof worldPositionToCss; declare const polyCssPositionToWorld: typeof cssPositionToWorld; declare const worldDirectionToPolyCss: typeof worldDirectionToCss; declare const worldDirectionalLightToPolyCss: typeof worldDirectionalLightToCss; /** Normalize a mesh `scale` value into a Vec3 (undefined → [1,1,1], number → * uniform, Vec3 → as-is with `?? 1` per axis). */ declare function meshScaleVec3(scale: number | Vec3 | undefined | null): Vec3; /** * Build a `vert → CSS-frame world position` function for a mesh with the * given scale + position. Pivots scale from the mesh ORIGIN. Rotation is * intentionally not applied here — shadow geometry is computed once per * mesh-transform change and already lives in world coords after this * per-vertex transform; rotation lives on the wrapper. */ declare function worldCssForMesh(scale: number | Vec3 | undefined | null): (vert: Vec3, pos: Vec3) => Vec3; /** * Minkowski expansion of a convex CCW polygon outward by `expand` units. Each * vertex moves along the bisector of its two adjacent edge outward- * perpendiculars, scaled so the edge offset distance equals `expand` (true * Minkowski sum with a disk of radius `expand`, evaluated at the vertex). For * convex inputs the result is a larger convex polygon with every edge offset * outward by exactly `expand`. */ declare function expandConvexHullOutward(hullCcw: Array<[number, number]>, expand: number): Array<[number, number]>; /** Outward extension applied to each receiver face's convex outline (CSS px). * Adjacent receiver faces sharing an edge each expand by this amount, so the * two shadows overlap by ~2×EXPAND at the corner — eliminating the sub-pixel * light strip that used to appear where two wall faces meet. 0.5 CSS px stays * sub-pixel at typical zoom. */ declare const RECEIVER_OUTLINE_EXPAND = 0.5; /** Plane-grouping tolerances. dot-product > 0.999 (~2.5° angular) catches * tessellation artifacts on flat surfaces without merging adjacent faces of * a low-poly curved mesh. Plane-offset tolerance is 0.5 CSS px — sub-pixel * coplanarity drift in glTF imports doesn't separate what should be a single * surface. */ declare const RECEIVER_NORMAL_TOL = 0.001; declare const RECEIVER_OFFSET_TOL = 0.5; /** * Groups a receiver's polygons into shadow-receiving surfaces. Two passes: * * 1. Plane bucket — group by matching normal + plane offset within tolerance * (catches tessellated flat regions). * 2. Connected component — within each plane bucket, union-find on shared- * edge adjacency (faces sharing >= 2 vertices). Catches disjoint coplanar * walls where a convex hull of everything would bridge an air gap. * * Per group, output a convex hull in the group's (u, v) coords (Minkowski- * expanded by `RECEIVER_OUTLINE_EXPAND`). * * `worldCss(vert, pos)` is the per-vertex world→CSS conversion (built via * `worldCssForMesh` for the receiver's scale + position). `dedupDrop` is the * set of receiver polygon indices to skip. */ declare function groupReceiverFaceGroups(polygons: readonly Polygon[], rpos: Vec3, worldCss: (vert: Vec3, pos: Vec3) => Vec3, dedupDrop: ReadonlySet): ReceiverPlaneGroup[]; interface PolyMeshTransformInput { position?: Vec3; scale?: number | Vec3; rotation?: Vec3; } /** * Build the mesh wrapper transform used by every renderer for PolyCSS's * world-frame mesh transform contract. */ declare function buildPolyMeshTransform(t: PolyMeshTransformInput): string | undefined; interface PolySceneTransformInput { /** World point that should appear at the viewport center. */ target?: Vec3; /** Scene orbit tilt in degrees. */ rotX?: number; /** Scene orbit rotation around world up in degrees. */ rotY?: number; /** User-facing zoom in CSS pixels per world unit. */ zoom?: number; /** Camera pull-back from target in CSS pixels. */ distance?: number; /** Auto-center offset added to target before world→CSS conversion. */ autoCenterOffset?: Vec3; /** Extra scale folded into zoom and distance for CSS zoom compensation. */ layoutScale?: number; /** CSS pixels per PolyCSS world unit. Defaults to the renderer base tile. */ worldUnitPx?: number; } /** * Build the scene-root transform used by PolyCSS renderers: * * `translateZ(-distance) scale(zoom / worldUnitPx) rotateX(rotX) rotate(rotY) translate3d(-targetCss)` */ declare function buildPolySceneTransform(input?: PolySceneTransformInput): string; /** * Caster-mesh silhouette extraction for the receiver-shadow path. * * Given a closed (or near-closed) caster mesh and a directional light, * computes the silhouette LOOPS — the closed cycles of edges where * adjacent polygons disagree on whether they face the light. For a * closed manifold convex mesh that's one loop; for concave or * higher-genus meshes it may be several. * * The receiver-shadow algorithm normally projects every caster * polygon's outline independently, which collapses to a silhouette at * paint time via SVG fill-rule:nonzero. That makes the browser do the * union work AFTER we've already paid the DOM cost for hundreds or * thousands of triangle sub-paths. Extracting the silhouette here lets * us emit ONE outline per mesh per receiver, dropping path-d size by * 100× on heavy meshes like the teapot. See H9 in * `bench/notes/SHADOW_PERF_LOG.md` and `bench/notes/H9_SILHOUETTE_DESIGN.md`. * * Pure data; no DOM access. Used by `computeReceiverShadowFaces` once * H9 lands. */ /** Per-edge ownership record. `polyB === null` for boundary edges (open * meshes). Vertex coords are the canonical "from" vertices of the edge — * silhouette walking uses them to reconstruct loop geometry. */ interface EdgeOwners { polyA: number; polyB: number | null; vertA: Vec3; vertB: Vec3; } /** * Build a map from canonical edge key → owning polygon(s). Mirrors the * vertex-quantization used by `buildSharedEdgeMap` so we agree on what * "same edge" means. * * Edge keys are orientation-independent (vertex-pair sorted by string), * so two polygons sharing an edge will report the same key. Polygons * are listed in encounter order; for manifold meshes each edge has * exactly 2 owners. Non-manifold edges (3+ owners) get truncated to * `polyB = null` so the caller treats them as boundaries — safer than * picking an arbitrary "second" polygon. */ declare function buildEdgeOwners(polygons: readonly Polygon[]): Map; /** Classify each polygon as facing the light. Convention: `n · L < 0` * means the polygon's outward face is toward the light source (light * TRAVELS in direction L, polygons are CCW-from-outside). Matches the * light-backface cull's sign convention. */ declare function classifyFacing(planeNormals: Array, lightDir: Vec3): boolean[]; declare function extractSilhouetteLoops(edgeOwners: ReadonlyMap, facing: boolean[]): Vec3[][]; /** * Per-receiver cached face geometry. One record per coplanar face group: * plane (O, n, u, v), outline polygon (Sutherland-Hodgman clip), bbox in * (u, v) for SVG sizing, and the pre-stringified matrix3d transform that * places an SVG on that face plane. * * All of this is invariant under light/caster changes. Per light tick the * caller just re-runs the per-tri SH and builds the path `d` — never * recompute groups or basis. Cache invalidated when the receiver's polygon * count or position changes. */ interface ReceiverFacePlane { O: Vec3; n: Vec3; u: Vec3; v: Vec3; outlineUv: Array<[number, number]>; memberPolysUv: Array>; memberPolyIndices: number[]; minU: number; minV: number; width: number; height: number; matrixCss: string; faceIndex: number; /** World-frame lift (already × BASE_TILE) along +n. Re-applied per-frame * when building a tight shadow SVG matrix so the SVG hovers over the face. */ lift: number; } /** * Per-caster cached per-polygon data: world-space vertices + 3D AABB * corners + caster-polygon plane normal/offset. Invariant under light * direction; depends only on the caster mesh's geometry and position. */ interface CasterPolyItem { wv: Vec3[]; bboxCorners: Vec3[]; planeN: Vec3 | null; planeOffset: number; polygonIndex: number; } /** A caster mesh's prepared items paired with a stable identifier the caller * can use for per-path attribution. */ interface ReceiverCasterInput { /** Caller-defined identifier (e.g. mesh ref, mesh shadow id). Echoed back * on each emitted path so the renderer can map a subpath to its source * caster mesh. */ id: T; items: CasterPolyItem[]; /** Self-shadow edge adjacency. When this caster is the same mesh as the * receiver, the renderer pre-computes a map polygonIndex → * set-of-other-polygonIndex that share at least one edge (within * EDGE_MATCH_EPS). The shadow algorithm skips projecting `polygonIndex` * onto any receiver face whose member set intersects the shared-edge * set — those projections are sliver shadows along seams (smooth-shaded * GLB meshes, subdivided spheres) that the user never wants to see. */ selfShadowEdgeMap?: ReadonlyMap>; /** Per-mesh edge ownership map for silhouette extraction. Cached by the * caller (WeakMap) and shared across receivers within a frame. * When present AND the caster is NOT the receiver AND items.length ≥ * SILHOUETTE_MIN_POLYS, the shadow algorithm projects per-mesh * silhouette loops instead of every front-facing triangle — collapses * the N-triangle path to 1 outline per caster per receiver. See H9 in * `bench/notes/SHADOW_PERF_LOG.md`. */ edgeOwners?: ReadonlyMap; /** Total polygon count on the source caster mesh (NOT the filtered * `items` count). Needed by silhouette extraction so the `facing` * array is sized correctly even when atlas-plan / dedup filters drop * some polygons from `items`. */ casterPolygonCount?: number; /** Parametric-shadow override: a precomputed world-frame silhouette loop set * (see `computeParametricShadowSilhouette`). When present it is projected * directly — skipping per-poly and silhouette extraction — so a cheap, * low-resolution outline casts onto every receiver via the normal pipeline. */ overrideSilhouette?: Vec3[][]; /** Per-point-light parametric override (indexed by the point light's index in * `allPointLights`). A point pass uses this RADIAL silhouette instead of the * directional `overrideSilhouette`; an undefined entry → exact point path. */ overridePointSilhouettes?: Array; } /** * Build a polygon-adjacency map: polygonIndex → set of polygonIndex that * share at least one edge (vertex pair, orientation-independent). Used by * the receiver-shadow algorithm to cull sliver shadows along mesh seams. * * Edge match tolerance is small enough to dedupe vertex coordinates that * went through `optimizeMeshPolygons` snap-to-plane but not so loose it * connects geometrically distinct polygons. */ declare function buildSharedEdgeMap(polygons: readonly Polygon[]): Map>; /** One contributing caster's shadow subpath on a single receiver face. */ interface ReceiverShadowPath { /** Caster id echoed from the input. */ casterId: T; /** Path data string: `M…L…Z` subpaths in face-local (u, v) coordinates * pre-translated so the SVG's `viewBox` is `0 0 width height`. */ d: string; /** Source polygon indices on the caster mesh in subpath order (one per * M…L…Z block). Used for DevTools attribution. */ casterPolygonIndices: number[]; } /** Per-receiver-face shadow spec. Renderer mounts one SVG per spec. */ interface ReceiverShadowFaceSpec { /** Index into the prepared `ReceiverFacePlane[]` list. Stable across * frames; used as the `data-poly-shadow-receiver-face` attr. */ faceIndex: number; /** Receiver polygon indices that make up this coplanar group. */ memberPolyIndices: number[]; /** matrix3d(...) transform that places the SVG on the face plane. */ matrixCss: string; width: number; height: number; /** Fill (and stroke) color resolved per-face: textured receivers get the * user's `shadow.color`; solid receivers get their own ambient-only * shadePolygon for byte-exact Three.js parity. */ fill: string; /** Per-face opacity (already accounts for textured-darken Lambert ratio * if applicable). */ opacity: number; paths: Array>; /** Full-lit face color C (all lights), for the multi-light merge: callers * multiply C by each pass's `fill/C` factor so overlaps composite to the * both-blocked color. Empty string for textured receivers (per-pixel base, * multiply can't be uniform — those fall back to cumulative-alpha). */ fullLitFill: string; /** Every clipped caster polygon for this pass in absolute face-(u,v) space * (NOT offset by the tight bbox). The multi-light merge re-bases these to a * shared per-face bbox so all lights' shadows live in one SVG. */ facePolysUv: Array>; } /** * Build silhouette `edgeOwners` for a caster mesh in world-CSS frame. * Used by the silhouette path inside `computeReceiverShadowFaces` (H9). * Polygons are transformed through the same world-CSS pipeline as * `prepareCasterPolyItems` (worldCssForMesh + optional rotation around * the CSS-pivot) so the silhouette loop vertices land in the same world * frame as the receiver face plane and the light direction. * * Caller caches by (mesh, polygon-list-identity + position + scale + * rotation) — invalidates only when the caster's geometry or transform * actually changes. Light direction is NOT a bust key (silhouette * adjacency is per-mesh, facing is per-frame). */ declare function prepareCasterEdgeOwners(polygons: readonly Polygon[], position: Vec3, scale: number | Vec3 | undefined | null, rotation?: Vec3 | null): ReadonlyMap; /** * Build CasterPolyItem[] for a caster mesh. Pure: same inputs → same output. * The caller memoizes by mesh ref + bust key. `includePolygonIndex(idx)` * decides which polygons participate (e.g. dedup drop + atlas-plan filter * in vanilla; just dedup drop in React/Vue without an atlas-plan concept). */ declare function prepareCasterPolyItems(polygons: readonly Polygon[], position: Vec3, scale: number | Vec3 | undefined | null, includePolygonIndex: (polygonIndex: number) => boolean, rotation?: Vec3 | null): CasterPolyItem[]; /** * Build ReceiverFacePlane[] for a receiver mesh. Pure: groups coplanar * polygons, computes (u,v) basis + outline, applies interior occlusion cull * (drops face planes hidden behind a parallel face plane within wall- * thickness range). * * `shadowLift` is the world-unit lift applied along each face normal so the * shadow SVG composites above the surface without z-fighting (matches the * ground-shadow `shadow.lift` option). */ declare function prepareReceiverFacePlanes(polygons: readonly Polygon[], position: Vec3, scale: number | Vec3 | undefined | null, dedupDrop: ReadonlySet, shadowLift: number, rotation?: Vec3 | null): ReceiverFacePlane[]; /** Input for `computeReceiverShadowFaces`. */ interface ComputeReceiverShadowFacesInput { /** Precomputed face planes from `prepareReceiverFacePlanes`. */ receiverPlanes: ReceiverFacePlane[]; /** Receiver's polygon list, used to look up per-face fill color. */ receiverPolygons: readonly Polygon[]; /** Whether the receiver mesh has any textured polygons. Drives the * textured-darken opacity calc vs solid-replace fill color. */ receiverHasTexture: boolean; /** Per-caster items + caller id, in caller-defined order. */ casters: ReceiverCasterInput[]; /** Light direction in CSS frame, pointing TOWARD the light (to-source). * For a point light this is a representative direction (used only for the * textured-receiver opacity ratio); per-vertex directions come from * `lightPos`. */ lightDir: Vec3; /** When set, the light is a point light at this CSS-frame position. The * shadow projection becomes radial (per-vertex direction from the light) * and the silhouette fast path is disabled (facing is per-polygon). */ lightPos?: Vec3; /** Every point light in the scene (CSS-frame absolute positions), used to * compute the SHADED shadow color — a shadow shows the receiver lit by all * lights EXCEPT the blocked one, so a spot shadowed from one colored light * still shows the others' color (Three.js parity). Includes non-shadow- * casting lights, since they still illuminate the shadowed region. */ allPointLights?: ReadonlyArray<{ position: Vec3; color?: string; intensity?: number; }>; /** For a point-light pass, the index into `allPointLights` of the light * being shadowed (excluded from the remaining-light fill). Undefined for * the directional pass (which instead excludes the directional light). */ thisPointIndex?: number; /** Camera cull rotation (rotX/rotY + receiver mesh rotation) so back- * facing receiver faces can be skipped. */ cameraRot: CameraCullRotation; /** Ambient light (used for solid-receiver shadow tint via shadePolygon). */ ambientLight?: PolyAmbientLight; /** Directional light (used for textured-darken opacity calc). */ directionalLight?: PolyDirectionalLight; /** Scene shadow options. */ shadow?: { color?: string; opacity?: number; maxExtend?: number; }; } /** * The pure per-frame algorithm. Returns one ReceiverShadowFaceSpec for each * visible receiver face that catches at least one caster's shadow. Skips * back-facing faces. Caller mounts SVGs per spec. */ declare function computeReceiverShadowFaces(input: ComputeReceiverShadowFacesInput): ReceiverShadowFaceSpec[]; /** One path inside a merged face SVG. */ interface MergedShadowLayer { /** Path data (`M…L…Z`, already offset to the SVG's tight bbox). */ d: string; /** Fill color (remaining-light color for a single layer, multiply factor for * a merged solid layer, dark shadow color for textured). */ fill: string; /** Apply `mix-blend-mode: multiply` (merged solid layers only). */ multiply: boolean; /** Per-path opacity (1 for merged solid layers, which carry strength on the * SVG; the pass's own opacity otherwise). */ opacity: number; } /** One receiver FACE's merged shadow, ready to mount as a single SVG. */ interface MergedShadowFace { faceIndex: number; memberPolyIndices: number[]; matrixCss: string; width: number; height: number; /** SVG-level opacity (shadow strength for merged solid faces, else 1). */ svgOpacity: number; /** Full-lit base path (merged solid faces only); null otherwise. */ baseFill: string | null; baseD: string | null; layers: MergedShadowLayer[]; } /** Inputs for `computeMergedReceiverShadows` — the full light set for one * receiver, plus its prepared face planes and casters. */ interface MergedReceiverShadowInput { receiverPlanes: ReceiverFacePlane[]; receiverPolygons: readonly Polygon[]; receiverHasTexture: boolean; casters: ReceiverCasterInput[]; /** Directional light vector in CSS frame (to-source). */ lightDir: Vec3; /** Run the directional pass (caller gates on a real, nonzero-intensity * directional light). */ runDirectional: boolean; /** One pass per shadow-casting point light: CSS position + index into * `allPointLights`. Empty in dynamic mode (point lights are baked-only). */ pointPasses: ReadonlyArray<{ lightPos: Vec3; index: number; }>; /** All point lights (CSS positions) for the shaded shadow color; empty in * dynamic mode. */ allPointLights?: ReadonlyArray<{ position: Vec3; color?: string; intensity?: number; }>; cameraRot: CameraCullRotation; ambientLight?: PolyAmbientLight; directionalLight?: PolyDirectionalLight; shadow?: { color?: string; opacity?: number; maxExtend?: number; }; } /** * Run every light pass for one receiver and merge each face's passes into a * single SVG descriptor. Shared by all three renderers so multi-light shadow * overlap is identical everywhere. */ declare function computeMergedReceiverShadows(input: MergedReceiverShadowInput): MergedShadowFace[]; /** * PolyAnimationMixer — three.js-shaped animation API for polycss. * * Mirrors three.js's AnimationMixer + AnimationAction surface closely enough * that users familiar with drei's `useAnimations` can migrate without friction. * * Loop mode constants match three.js numeric values exactly: * LoopOnce = 2200, LoopRepeat = 2201, LoopPingPong = 2202 */ declare const LoopOnce: 2200; declare const LoopRepeat: 2201; declare const LoopPingPong: 2202; type LoopMode = typeof LoopOnce | typeof LoopRepeat | typeof LoopPingPong; /** * Minimal target interface the mixer requires. `PolyMeshHandle` from both * the polycss vanilla API and the React/Vue frameworks satisfies this * structurally — no import needed. */ interface PolyAnimationTarget { setPolygons(polygons: Polygon[]): void; } /** * Per-clip playback action. Mirrors three.js `AnimationAction` method surface. * All mutating methods return `this` for chaining. */ interface PolyAnimationAction { /** Start playing (sets weight=1, resets time if not already playing). */ play(): PolyAnimationAction; /** Stop playing and reset time to 0. */ stop(): PolyAnimationAction; /** Reset time to 0 without stopping. */ reset(): PolyAnimationAction; /** Fade weight from 0 to 1 over `durationSeconds`. */ fadeIn(durationSeconds: number): PolyAnimationAction; /** Fade weight from current to 0 over `durationSeconds`. */ fadeOut(durationSeconds: number): PolyAnimationAction; /** * Cross-fade from this action to `target` over `durationSeconds`. * Fades this out and target in simultaneously. */ crossFadeTo(target: PolyAnimationAction, durationSeconds: number): PolyAnimationAction; /** * Cross-fade from `from` into this action over `durationSeconds`. * Sugar for `from.fadeOut(d); this.fadeIn(d)`. */ crossFadeFrom(from: PolyAnimationAction, durationSeconds: number): PolyAnimationAction; /** Set loop mode and repetition count. */ setLoop(mode: LoopMode, repetitions: number): PolyAnimationAction; /** Override the effective time scale. */ setEffectiveTimeScale(scale: number): PolyAnimationAction; /** Override the effective weight. */ setEffectiveWeight(weight: number): PolyAnimationAction; /** When true, the action freezes on the last frame after finishing. */ clampWhenFinished: boolean; /** Playback speed multiplier. Default 1. */ timeScale: number; /** Blend weight [0, 1]. Default 1. */ weight: number; /** Current playback position in seconds. */ time: number; /** * When false, the action contributes 0 weight to the blend even if * `weight > 0`. Time still advances. Default true. */ enabled: boolean; /** * When true, time does NOT advance on `mixer.update()` but the action * remains active and contributes its current weight to the blend. Default false. */ paused: boolean; /** Whether the action is currently playing. */ readonly isRunning: boolean; } /** * Drives one or more `PolyAnimationAction`s against a single mesh target. * Mirrors the three.js `AnimationMixer` API. */ interface PolyAnimationMixer { /** * Return the action for a clip (by index or name). Creates the action if it * doesn't exist yet (lazy instantiation, same as three.js). */ clipAction(clip: number | string): PolyAnimationAction; /** * Return an existing action without creating one. Returns null if the * action hasn't been instantiated yet. */ existingAction(clip: number | string): PolyAnimationAction | null; /** * Advance all active actions by `deltaSeconds` and apply the resulting * polygon frame to the root target. Call this once per animation frame. */ update(deltaSeconds: number): void; /** Stop all active actions. */ stopAllAction(): void; /** Remove a cached action for `clip`. */ uncacheClip(clip: number | string): void; /** Remove all cached actions for this mixer's root. */ uncacheRoot(): void; } declare function createPolyAnimationMixer(root: PolyAnimationTarget, controller: ParseAnimationController): PolyAnimationMixer; interface OptimizeAnimatedMeshPolygonsOptions { /** Public quality/resolution intent. Defaults to "lossy". */ meshResolution?: MeshResolution; } declare function optimizeAnimatedMeshPolygons(result: ParseResult, options?: OptimizeAnimatedMeshPolygonsOptions): ParseResult; interface ObjParseOptions { /** * Largest mesh extent (in scene-space units). The mesh is uniformly * scaled so its longest bbox dimension equals this. Default: 60. */ targetSize?: number; /** * Where to place the mesh-local origin relative to the parsed geometry. * * - `"min"` (default): bbox-min sits at local (0,0,0); geometry lives in * the +X+Y+Z quadrant. This is PolyCSS's historical behavior. * - `true` (or `"center"`): bbox-center sits at local (0,0,0); geometry * is centered around the origin. Pair with `scene.add(parse, {position, * rotation:[...]})` to get three.js-style rotate-in-place around the * centroid. * * Three.js's `GLTFLoader`/`OBJLoader` don't reposition vertices at all; * for byte-parity loading set this to a separate explicit `false` once * the no-offset option lands. */ center?: boolean | "min" | "center"; /** * Color used for faces that have no `usemtl` in scope, or whose material * name doesn't resolve via `materialColors`. Default: "#888888". */ defaultColor?: string; /** * Override map: material name → CSS color string. Falls back to: * 1. The material name interpreted as a 6-char hex (e.g. "FF9800" → "#FF9800"), * 2. Otherwise a slot from `palette` indexed by first-seen material order, * 3. Otherwise `defaultColor`. */ materialColors?: Record; /** * Optional map: material name → texture URL. When set, every triangle * emitted under that material gets `texture` populated. The renderer * stamps the image across the triangle's local 2D plane. */ materialTextures?: Record; /** * Palette used to assign colors to materials whose names aren't hex. * Each new non-hex material name takes the next palette slot. */ palette?: string[]; /** * Names of `o ` objects to KEEP. When set, faces outside these * objects are dropped. */ includeObjects?: string[]; /** * Names of `o ` objects to DROP. Applied after `includeObjects`. * Faces with no enclosing `o` line are kept unless `includeObjects` is set. */ excludeObjects?: string[]; } declare function parseObj(text: string, options?: ObjParseOptions): ParseResult; /** * Wavefront `.mtl` material file parser. Companion to parseObj — reads the * material library that ships next to a `.obj` and returns per-material * diffuse color (`Kd`) and optional diffuse texture map path (`map_Kd`). * * Usage: * const mtl = await fetch("/foo.mtl").then(r => r.text()); * const { colors, textures } = parseMtl(mtl); * const obj = await fetch("/foo.obj").then(r => r.text()); * const { polygons } = parseObj(obj, { materialColors: colors, materialTextures: textures }); * * Texture paths are returned exactly as written in the .mtl — relative paths, * Windows backslashes etc. are not normalized. Callers are expected to * resolve them against the .mtl's base URL. * * NOTE: parseMtl intentionally returns its own `MtlParseResult` shape * (NOT the unified `ParseResult`). It's an asymmetric helper — it emits * materials, not polygons — and forcing it into ParseResult would mean * an empty `polygons[]` and a misleading `dispose()`. */ interface MtlParseResult { /** Material name → CSS hex color (from `Kd r g b`). */ colors: Record; /** Material name → texture path (from `map_Kd `). Path is unresolved. */ textures: Record; } declare function parseMtl(text: string): MtlParseResult; interface GltfParseOptions { /** Largest mesh extent (units). Mesh is uniformly scaled to fit. Default 60. */ targetSize?: number; /** Color used when a primitive has no material or no baseColorFactor. */ defaultColor?: string; /** * Override map: glTF material name → CSS color string. Falls back to the * material's `pbrMetallicRoughness.baseColorFactor` if not in this map. */ materialColors?: Record; /** * Override map: glTF material name → texture image URL. Takes priority over * `pbrMetallicRoughness.baseColorTexture`; useful for GLB/GLTF exports that * preserved UVs but dropped external image references. */ materialTextures?: Record; /** * Which axis is "up" in the source mesh. * - "y" (default, glTF spec): cyclic permutation (x,y,z) → (z,x,y) so * +Y ends up on PolyCSS's +Z (elevation). * - "z" (Blender-style, FBX2glTF often emits this): identity, no swap. * Pick "z" if the model lands on its side / lies down instead of * standing. */ upAxis?: "y" | "z"; /** * Where to place the mesh-local origin relative to the parsed geometry. * * - `"min"` (default): bbox-min sits at local (0,0,0); geometry lives in * the +X+Y+Z quadrant. This is PolyCSS's historical behavior. * - `true` (or `"center"`): bbox-center sits at local (0,0,0); geometry * is centered around the origin. Pair with `scene.add(parse, {position, * rotation:[...]})` to get three.js-style rotate-in-place around the * centroid. */ center?: boolean | "min" | "center"; /** * For .gltf (non-binary) — resolve a glTF buffer URI to its bytes. The * built-in parser handles GLB binary chunks natively; .gltf files with * external .bin files need this. */ resolveBuffer?: (uri: string) => Promise | Uint8Array; /** * Base URL the source file lives at. Used to resolve external image URIs * (`doc.images[i].uri = "Textures/foo.png"`) against the GLB/glTF's * location. Without this, relative URIs would resolve against the page, * which 404s. Pass the same URL you fetched the file from. */ baseUrl?: string; } declare function parseGltf(input: ArrayBuffer | Uint8Array, options?: GltfParseOptions): ParseResult; interface SolidTextureSampleOptions { /** * Set false to keep every textured polygon texture-backed. Defaults to true * when a browser-like Image + canvas environment is available. */ enabled?: boolean; /** Per-channel tolerance for declaring sampled texels uniform. Default 2. */ colorTolerance?: number; /** Skip decoding very large textures for this optimization. Default 16 MP. */ maxTexturePixels?: number; } declare function bakeSolidTextureSampledPolygons(polygons: Polygon[], options?: SolidTextureSampleOptions): Promise; declare function bakeSolidTextureSamples(result: ParseResult, options?: SolidTextureSampleOptions): Promise; interface VoxParseOptions { /** * Largest mesh extent (in scene-space units). For `.vox`, the requested * extent is snapped to the nearest integer CSS cell size to keep voxel * fast-path coordinates integral. Default: 60. */ targetSize?: number; /** * Optional lossy palette simplification. When > 0, opaque, hue-compatible * palette colors within this RGB distance are folded into the most-used * nearby color before greedy voxel meshing. Default: disabled. */ paletteMergeDistance?: number; /** * Optional lossy local cleanup. When > 0, small face-plane color islands * and thin streaks are recolored to a neighboring dominant hue-compatible * color within this RGB distance before greedy voxel meshing. Default: * disabled. */ colorRegionMergeDistance?: number; } declare function parseVox(buffer: ArrayBuffer, options?: VoxParseOptions): ParseResult; interface StlParseOptions { /** Largest mesh extent (units). Mesh is uniformly scaled to fit. Default: 60. */ targetSize?: number; /** Padding offset added after scaling. Default: 1. */ gridShift?: number; /** Solid color assigned to every STL triangle. Default: "#888888". */ defaultColor?: string; /** * Which axis is "up" in the source mesh. * - "z" (default): identity axes, matching common CAD/3D-print STL exports. * - "y": cyclic permutation (x,y,z) → (z,x,y), matching OBJ/glTF's +Y-up path. */ upAxis?: "z" | "y"; } declare function parseStl(source: ArrayBuffer | Uint8Array | string, options?: StlParseOptions): ParseResult; /** * loadMesh — high-level fetch+parse dispatcher. Picks the parser by file * extension, fetches the URL, runs the parser, returns the unified * `ParseResult`. * * Supported: * - `.obj` → text fetch + `parseObj` * - `.glb` → ArrayBuffer fetch + `parseGltf` * - `.gltf` → ArrayBuffer fetch + `parseGltf` (caller may pass `baseUrl`) * - `.vox` → ArrayBuffer fetch + `parseVox` * - `.stl` → ArrayBuffer fetch + `parseStl` * * `.mtl` is rejected — it's a material file, not a mesh. Use `parseMtl` * directly if you want to read materials. * * Other extensions throw. Future formats (PLY, 3MF) plug in here. */ interface LoadMeshOptions { /** * Base URL for resolving relative texture/buffer URIs inside the mesh * (passed through to `parseGltf` for embedded image extraction). When * omitted, the URL passed to `loadMesh` is used as the base. */ baseUrl?: string; /** * Companion `.mtl` URL for OBJ files. When set, loadMesh fetches the * mtl, runs `parseMtl`, and threads `materialColors` + `materialTextures` * into `parseObj` — so the OBJ renders with its authored materials. * Texture paths inside the mtl are resolved against the mtl URL. * Ignored for `.glb` / `.gltf` (they carry materials inline). */ mtlUrl?: string; /** Forwarded to `parseObj` (merged with materials derived from `mtlUrl`). */ objOptions?: ObjParseOptions; /** Forwarded to `parseGltf`. */ gltfOptions?: GltfParseOptions; /** Forwarded to `parseVox`. */ voxOptions?: VoxParseOptions; /** Forwarded to `parseStl`. */ stlOptions?: StlParseOptions; /** * Converts texture-backed faces whose UV samples are a uniform color into * solid-color polygons before culling/merging. This avoids atlas sprites for * low-poly models that use texture atlases as color swatches. */ solidTextureSamples?: boolean | SolidTextureSampleOptions; /** * Mesh optimization intent. Defaults to "lossy"; set "lossless" to keep * exact planar candidates only. STL imports use the conservative lossless * optimizer path in both modes. */ meshResolution?: MeshResolution; } declare function loadMesh(url: string, options?: LoadMeshOptions): Promise; declare const DEFAULT_TILE = 50; declare const DEFAULT_LIGHT_DIR: Vec3; declare const DEFAULT_LIGHT_COLOR = "#ffffff"; declare const DEFAULT_LIGHT_INTENSITY = 1; declare const DEFAULT_AMBIENT_COLOR = "#ffffff"; declare const DEFAULT_AMBIENT_INTENSITY = 0.4; declare const ATLAS_MAX_SIZE = 4096; declare const ATLAS_PADDING = 1; declare const MIN_ATLAS_SCALE = 0.1; declare const MAX_ATLAS_SCALE = 1; declare const AUTO_ATLAS_LOW_AREA: number; declare const AUTO_ATLAS_MEDIUM_AREA: number; declare const AUTO_ATLAS_MAX_BITMAP_SIDE = 2048; declare const AUTO_ATLAS_MAX_DECODED_BYTES_MOBILE: number; declare const AUTO_ATLAS_MAX_DECODED_BYTES_DESKTOP: number; declare const AUTO_ATLAS_SCALE_GUARD = 0.995; declare const COLOR_PARSE_CACHE_MAX = 512; declare const ASYNC_RENDER_BUDGET_MS = 12; declare const RECT_EPS = 0.001; declare const BASIS_EPS = 1e-9; declare const SURFACE_NORMAL_EPS = 0.0001; declare const SURFACE_DISTANCE_EPS = 0.1; declare const SEAM_LIGHT_EPS = 0.01; declare const TEXTURE_TRIANGLE_BLEED = 0.75; declare const TEXTURE_EDGE_REPAIR_ALPHA_MIN = 1; declare const TEXTURE_EDGE_REPAIR_SOURCE_ALPHA_MIN = 250; declare const TEXTURE_EDGE_REPAIR_RADIUS = 1.5; declare const SOLID_TRIANGLE_BLEED = 0.75; declare const DEFAULT_MATRIX_DECIMALS = 3; declare const DEFAULT_BORDER_SHAPE_DECIMALS = 2; declare const DEFAULT_ATLAS_CSS_DECIMALS = 4; declare const DECIMAL_SCALES: number[]; declare const SOLID_QUAD_CANONICAL_SIZE = 64; declare const SOLID_TRIANGLE_CANONICAL_SIZE = 32; declare const SOLID_TRIANGLE_LARGE_BORDER_CANONICAL_SIZE = 96; declare const ATLAS_CANONICAL_SIZE_EXPLICIT = 64; declare const ATLAS_CANONICAL_SIZE_AUTO_DESKTOP = 128; declare const BORDER_SHAPE_CENTER_PERCENT = 50; declare const BORDER_SHAPE_POINT_EPS = 1e-7; declare const BORDER_SHAPE_CANONICAL_SIZE = 16; declare const BORDER_SHAPE_BLEED = 0.9; declare const CORNER_SHAPE_POINT_EPS = 0.75; declare const CORNER_SHAPE_DUPLICATE_EPS = 0.2; declare const PROJECTIVE_QUAD_DENOM_EPS = 0.05; declare const PROJECTIVE_QUAD_MAX_WEIGHT_RATIO = 256; declare const PROJECTIVE_QUAD_BLEED = 0.6; declare const DEFAULT_SEAM_BLEED = 1.5; /** Clamp the `seamBleed` ratio. `undefined` → 1 (full default), 0 → no * bleed, values outside [0,1] are clamped. Single source of truth for * how the public ratio maps to per-strategy bleed multipliers. */ declare function resolveBleedRatio(seamBleed: number | "auto" | undefined): number; interface RGB { r: number; g: number; b: number; } interface RGBFactors { r: number; g: number; b: number; } interface UvAffine { a: number; b: number; c: number; d: number; e: number; f: number; } interface UvSampleRect { minU: number; minV: number; maxU: number; maxV: number; } interface TextureTrianglePlan { screenPts: number[]; uvAffine: UvAffine | null; uvSampleRect: UvSampleRect | null; } interface TextureAtlasPlan { index: number; polygon: Polygon; texture?: string; tileSize: number; layerElevation: number; matrix: string; canonicalMatrix: string; atlasMatrix: string; atlasCanonicalSize?: number; atlasLeafSizing?: PolyTextureLeafSizing; atlasLeafWidth?: number; atlasLeafHeight?: number; projectiveMatrix: string | null; canvasW: number; canvasH: number; screenPts: number[]; uvAffine: UvAffine | null; uvSampleRect: UvSampleRect | null; textureTriangles: TextureTrianglePlan[] | null; textureEdgeRepairEdges: Set | null; textureEdgeRepair: boolean; seamBleed?: number; seamBleedEdges?: Set; seamBleedEdgeAmounts?: Map; seamBleedInsets?: SeamBleedInsets; /** Resolved per-strategy bleed multiplier (0..1, default 1). Populated * at plan construction from `options.seamBleed` via `resolveBleedRatio`. * Downstream emitters (borderShapeGeometryForPlan, projective-quad * rasteriser, etc.) read this and multiply their hardcoded per-strategy * bleed constants by it. Single knob for "scale down all my bleeds". */ bleedRatio?: number; /** World-space surface normal — stable across light changes, used by dynamic mode. */ normal: Vec3; textureTint: RGBFactors; shadedColor: string; } interface PolyTextureLeafSourceRect { x: number; y: number; width: number; height: number; } interface PolyTextureLeafGeometry { source: PolyTextureImageSource; url: string; sourceRect: PolyTextureLeafSourceRect; leafWidth: number; leafHeight: number; matrix: string; backgroundPosition: [number, number]; backgroundSize: [number, number]; imageRendering: PolyTextureImageRendering; lighting: PolyTextureImageLighting; projection: PolyTextureProjection; } interface PolyTextureLeafResolverOptions { imageRendering?: PolyTextureImageRendering; backend?: PolyTextureBackend; lighting?: PolyTextureImageLighting; projection?: PolyTextureProjection; allowProjective?: boolean; projectiveQuadGuards?: ProjectiveQuadGuardSettings; } interface BorderShapeBounds { minX: number; minY: number; width: number; height: number; } interface BorderShapeGeometry { bounds: BorderShapeBounds; points: Array<[number, number]>; } type CornerShapeCorner = "topLeft" | "topRight" | "bottomRight" | "bottomLeft"; type CornerShapeSide = "left" | "right" | "top" | "bottom"; interface CornerShapeRadius { x: number; y: number; } interface CornerShapeGeometry { bounds: BorderShapeBounds; radii: Partial>; } type TextureQuality = number | "auto"; type PolySeamBleed = number | "auto"; type PolySeamBleedEdgeValue = ReadonlySet | ReadonlyMap; type PolySeamBleedEdges = ReadonlyMap | readonly (PolySeamBleedEdgeValue | undefined)[]; type PolyRenderStrategy = "b" | "i" | "u"; type SolidTrianglePrimitive = "border" | "border-large" | "corner-bevel"; interface PolyRenderStrategiesOption { /** Strategies to skip; polygons that would normally use them fall through * the chain (b → i → s, u → i → s, i → s). `` is the universal * fallback and cannot be disabled — textured polys have no other path. */ disable?: readonly PolyRenderStrategy[]; } interface SeamBleedInsets { left: number; right: number; top: number; bottom: number; } interface PackedTextureAtlasEntry extends TextureAtlasPlan { pageIndex: number; x: number; y: number; } interface PackedPage { width: number; height: number; entries: PackedTextureAtlasEntry[]; } interface PackingShelf { x: number; y: number; height: number; } interface PackingPage extends PackedPage { shelves: PackingShelf[]; sealed?: boolean; } interface PackedAtlas { entries: Array; pages: PackedPage[]; } interface SolidTriangleBasis { a: number; b: number; c: number; } interface SolidTriangleColorPlan { index: number; polygon: Polygon; colorComputed: boolean; bakedColor?: string; bakedRgb?: RGB; bakedAlpha?: number; dynamicVars?: string; } interface SolidTrianglePlan extends SolidTriangleColorPlan { styleText: string; transformText: string; basis: SolidTriangleBasis; primitive: SolidTrianglePrimitive; } interface SolidTriangleComputeOptions { basis?: SolidTriangleBasis; includeColor?: boolean; matrixDecimals?: number; color?: string; primitive?: SolidTrianglePrimitive; /** Pre-resolved primitive used when `primitive` is not set — replaces the * browser-global resolution that formerly happened inside the function. */ resolvedPrimitive?: SolidTrianglePrimitive | null; } interface StableTriangleColorState { updatesDisabled: boolean; freezeFrames: number; colorFrame: number; maxStep: number; } interface SolidTriangleFrame { polygonCount: number; vertices: ArrayLike; colors?: readonly (string | undefined)[]; } interface SolidPaintDefaults { paintColor?: string; dynamicColor?: { r: number; g: number; b: number; }; dynamicColorKey?: string; } interface TextureAtlasPage { width: number; height: number; url: string | null; } interface RectBrush { left: number; top: number; width: number; height: number; } interface LocalBasis { xAxis: Vec3; yAxis: Vec3; local2D: Vec2[]; shiftX: number; shiftY: number; canvasW: number; canvasH: number; pixelArea: number; rawArea: number; } interface BasisOptions { optimize: boolean; fixedXAxis?: Vec3; boundsOrigin?: Vec3; snapBounds?: boolean; seamEdges?: Set; } interface BasisHint { xAxis?: Vec3; boundsOrigin?: Vec3; seamEdges: Set; textureEdgeRepairEdges?: Set; } interface PolygonBasisInfo { pts: Vec3[]; normal: Vec3; planeD: number; optimizable: boolean; } interface ProjectiveQuadGuardSettings { denomEps: number; maxWeightRatio: number; bleed: number; disableGuards: boolean; } interface ProjectiveQuadGuardOverrides { denomEps?: number; maxWeightRatio?: number; bleed?: number; disableGuards?: boolean; } interface ProjectiveQuadGuardGlobal { __polycssProjectiveQuadGuards?: ProjectiveQuadGuardOverrides; } interface ProjectiveQuadCoefficients { g: number; h: number; w1: number; w3: number; } interface StablePlanBasis { normal: Vec3; xAxis: Vec3; yAxis: Vec3; tx: number; ty: number; tz: number; } /** Options for solidTrianglePlan computation — the pure-math subset of * RenderTextureAtlasOptions with no DOM reference. */ interface SolidTrianglePlanOptions { tileSize?: number; layerElevation?: number; directionalLight?: PolyDirectionalLight; /** Point lights in MESH-LOCAL frame (renderer pre-transforms each scene * point light by inverse-rotate(worldPos - meshPos) so positions match * the local cssPoints frame). Direction-only, per-face Lambert. */ pointLights?: PolyPointLight[]; ambientLight?: PolyAmbientLight; textureLighting?: PolyTextureLightingMode; solidPaintDefaults?: SolidPaintDefaults; strategies?: PolyRenderStrategiesOption; seamBleed?: PolySeamBleed; seamEdges?: Set; /** Per-strategy bleed multiplier (0..1, default 1). Scales the * hardcoded SOLID_TRIANGLE_BLEED used as the seamBleed fallback when * no shared-edge bleed is present. Populated upstream from * `resolveBleedRatio(publicOptions.seamBleed)`. */ bleedRatio?: number; /** * Indices (into the polygon array being planned) of polygons that the * directional light cannot physically reach because another polygon of * the same mesh is between them and the light source. Per-polygon * directScale is forced to 0 for indices in this set, so they receive * ambient lighting only — matching what a shadow-map-equivalent pass * would produce. */ lightOccludedPolyIndices?: ReadonlySet; } /** Internal solid-triangle plan options (extends SolidTrianglePlanOptions). */ interface InternalSolidTrianglePlanOptions extends SolidTrianglePlanOptions { optimizeStableTriangleStyle?: boolean; stableTriangleColorSteps?: number; stableTriangleMatrixDecimals?: number; } /** Options accepted by the public {@link computeTextureAtlasPlanPublic} wrapper. */ interface ComputeTextureAtlasPlanOptions { tileSize?: number; layerElevation?: number; directionalLight?: PolyDirectionalLight; /** Point lights in MESH-LOCAL frame (renderer pre-transforms each scene * point light by inverse-rotate(worldPos - meshPos) so positions match * the local cssPoints frame). Direction-only, per-face Lambert. */ pointLights?: PolyPointLight[]; ambientLight?: PolyAmbientLight; /** Shared-edge set returned by {@link buildTextureEdgeRepairSets}. */ textureEdgeRepairEdges?: Set; seamBleed?: PolySeamBleed; seamEdges?: Set; /** Indices of polygons that the directional light cannot reach because * another polygon of the same mesh occludes them (precomputed via * {@link import("../cull/lightVisibility").computeLightVisibility}). When * `index ∈ lightOccludedPolyIndices`, the polygon's direct lighting term * is forced to zero and only ambient remains. Matches the vanilla * renderer's self-shadow path. */ lightOccludedPolyIndices?: ReadonlySet; } declare function roundDecimal(value: number, decimals: number): string; declare function formatCssLength(value: number, decimals?: number): string; declare function formatMatrix3dValues(values: readonly number[], decimals?: number): string; declare function formatAffineMatrix3dColumns(xCol: Vec3, yCol: Vec3, zCol: Vec3, txCol: Vec3, decimals?: number): string; declare function formatAffineMatrix3dScalars(x0: number, x1: number, x2: number, y0: number, y1: number, y2: number, z0: number, z1: number, z2: number, tx0: number, tx1: number, tx2: number, decimals?: number): string; declare function formatAffineMatrix3dTransformScalars(x0: number, x1: number, x2: number, y0: number, y1: number, y2: number, z0: number, z1: number, z2: number, tx0: number, tx1: number, tx2: number, decimals?: number): string; declare function formatScaledMatrixFromPlan(entry: TextureAtlasPlan, scaleX: number, scaleY: number, offsetX?: number, offsetY?: number): string; declare function formatBorderShapeMatrix(entry: TextureAtlasPlan, bounds: BorderShapeBounds): string; declare function formatSolidQuadMatrix(entry: TextureAtlasPlan): string; declare function formatAtlasMatrix(entry: TextureAtlasPlan, atlasLeafWidth: number, atlasLeafHeight?: number): string; declare function formatPercent(value: number, decimals?: number): string; /** Format a raw comma-separated matrix3d value string with rounded decimals. */ declare function formatMatrix3d(matrix: string, decimals?: number): string; /** Format a pixel CSS length value. */ declare function formatCssLengthPx(value: number, decimals?: number): string; /** * Produce the CSS matrix3d transform for a solid-quad (``) leaf, including * the canonical primitive scale. */ declare function formatSolidQuadEntryMatrix(entry: TextureAtlasPlan): string; declare function buildTextureEdgeRepairSets(polygons: Polygon[]): Array | undefined>; declare function resolveSeamBleed(value: unknown, fallback: number): number; declare function normalizedSeamBleed(value: unknown): number | undefined; declare function safePlanSeamBleedAmount(screenPts: number[], edgeIndex: number, requested: number): number; declare function computePlanSeamBleedEdgeAmounts(screenPts: number[], seamEdges: ReadonlySet | undefined, seamBleed: number | undefined): Map | undefined; declare function seamBleedAmountArray(vertexCount: number, edgeAmounts: ReadonlyMap | undefined): number[] | null; declare function computeSeamBleedInsets(screenPts: number[], edgeAmounts: ReadonlyMap | undefined): SeamBleedInsets | undefined; interface SeamBleedDetectionOptions { tileSize?: number; layerElevation?: number; directionalLight?: unknown; ambientLight?: unknown; } declare function buildSeamBleedPolygonSet(polygons: Polygon[], options?: SeamBleedDetectionOptions): Set; declare function buildSeamBleedPolygonEdges(polygons: Polygon[], options?: SeamBleedDetectionOptions): Map>; type PureColorParseResult = ReturnType; declare function cachedParsePureColor(input: string): PureColorParseResult; declare function parseHex(hex: string): RGB; declare function rgbKey({ r, g, b }: RGB): string; /** Returns the parsed alpha for a color string (1.0 default). */ declare function parseAlpha(input: string): number; declare function rgbToHex({ r, g, b }: RGB): string; /** * Tint factors for a textured polygon, in LINEAR light space. * * Returns the per-channel multiplier that the rasterizer should apply to the * texture pixels' LINEAR values — matching Three.js MeshLambertMaterial: * lit_linear = albedo_linear × tint * tint = (lightColor × lambert × I + ambientColor × I_amb) / π * * Light + ambient colors are interpreted as sRGB and converted to linear. * The rasterizer is responsible for decoding the texture sample from sRGB * to linear before multiplying by these factors, then re-encoding for paint * (see applyTextureTint in the renderer). `directScale` is already * `intensity × max(n·L, 0)` (computed by the caller). */ /** * One point light's per-face contribution to a polygon: its color and the * scalar `intensity × max(0, n·L̂)` already computed by the caller against the * face normal + centroid (point lights are direction-only — no distance * falloff). Multiple lights of different colours can't fold into one scalar, * so the shading functions accumulate these per channel in linear space. */ interface PointLightContrib { color: string; scale: number; } declare function textureTintFactors(directScale: number, lightColor: string, ambientColor: string, ambientIntensity: number, pointContribs?: readonly PointLightContrib[]): RGBFactors; declare function tintToCss({ r, g, b }: RGBFactors): string; declare function shadePolygon(baseColor: string, directScale: number, lightColor: string, ambientColor: string, ambientIntensity: number, pointContribs?: readonly PointLightContrib[]): string; declare function quantizeCssColor(input: string, steps: number): string; declare function rgbEqual(a: RGB | undefined, b: RGB | undefined): boolean; declare function stepRgbToward(current: RGB, target: RGB, maxStep: number): RGB; declare function rgbToCss(rgb: RGB, alpha?: number): string; declare function colorErrorScore(current: string | undefined, next: string): number; declare function fullRectBounds(entry: TextureAtlasPlan): { left: number; top: number; width: number; height: number; } | null; declare function isFullRectSolid(entry: TextureAtlasPlan): boolean; declare function isSolidTrianglePlan(entry: TextureAtlasPlan): boolean; declare function isProjectiveQuadPlan(entry: TextureAtlasPlan): entry is TextureAtlasPlan & { projectiveMatrix: string; }; declare function safariCssProjectiveUnsupported(userAgent: string): boolean; declare function incrementCount(map: Map, key: string): void; declare function dominantCountKey(map: Map): string | undefined; interface FilterAtlasPlansEnv { solidTriangleSupported: boolean; projectiveQuadSupported: boolean; borderShapeSupported: boolean; textureBackend?: PolyTextureBackend; textureImageRendering?: PolyTextureImageRendering; textureImageLighting?: PolyTextureImageLighting; textureProjection?: PolyTextureProjection; /** When true, non-triangle non-rect non-projective polys whose plan has * cornerShapeGeometryForPlan != null are excluded from the atlas (they * render as via corner-*-shape: bevel CSS — matches vanilla's * createCornerShapeSolidElement path). Falsy / undefined preserves the * earlier core behaviour (those polys stay in atlas as fallback). */ cornerShapeSupported?: boolean; } /** * Filter a plan array to the subset that needs atlas packing, given the active * render strategies and texture-lighting mode. Plans excluded from the atlas * will be rendered via ``, ``, or `` by the framework components. */ declare function filterAtlasPlans(plans: Array, textureLighting: PolyTextureLightingMode, disabled: ReadonlySet, env: FilterAtlasPlansEnv): Array; interface GetSolidPaintDefaultsEnv { solidTriangleSupported: boolean; projectiveQuadSupported: boolean; cornerShapeSupported: boolean; borderShapeSupported: boolean; } declare function getSolidPaintDefaultsForPlansCore(plans: Array, textureLighting: PolyTextureLightingMode, disabled: ReadonlySet, env: GetSolidPaintDefaultsEnv, parseHexFn: (color: string) => RGB, rgbKeyFn: (rgb: RGB) => string, cornerShapeGeometryForPlanFn?: (plan: TextureAtlasPlan) => unknown): { paintColor?: string; dynamicColorKey?: string; dynamicColor?: RGB; }; declare function cssPoints(vertices: Vec3[], tile: number, elev: number): Vec3[]; declare function computeSurfaceNormal(pts: Vec3[]): Vec3 | null; declare function isConvexPolygonPoints(points: Array<[number, number]>): boolean; declare function signedArea2D(points: Array<[number, number]>): number; declare function intersect2DLines(a0: [number, number], a1: [number, number], b0: [number, number], b1: [number, number]): [number, number] | null; declare function intersect2DLinesRaw(a0x: number, a0y: number, a1x: number, a1y: number, b0x: number, b0y: number, b1x: number, b1y: number): Vec2 | null; declare function expandClipPoints(points: number[], amount: number): number[]; declare function offsetConvexPolygonPoints(points: number[], amount: number): number[]; declare function offsetConvexPolygonPointsByEdgeAmounts(points: number[], amounts: readonly number[]): number[]; declare function offsetTrianglePoints(x0: number, y0: number, x1: number, y1: number, x2: number, y2: number, amount: number): number[]; declare function offsetStableTrianglePoints(left: number, right: number, height: number, amount: number): number[]; declare function stableBasisFromPlan(source: TextureAtlasPlan, polygon: Polygon): StablePlanBasis | null; declare function stableTriangleMatrixDecimals(matrixDecimals: number | undefined): number; declare function polygonContainsPoint(points: Array<[number, number]>, px?: number, py?: number): boolean; declare function borderShapeBoundsFromPoints(points: number[], fallbackWidth: number, fallbackHeight: number): BorderShapeBounds; /** Reads `entry.bleedRatio` (defaulted to 1) and scales BORDER_SHAPE_BLEED * accordingly. Plans are tagged with the ratio at construction time * (see computeTextureAtlasPlan) so every consumer gets the same value. */ declare function borderShapeGeometryForPlan(entry: TextureAtlasPlan): BorderShapeGeometry; declare function simplifyCornerShapePoints(points: Array<[number, number]>): Array<[number, number]>; declare function cornerShapePointSides([x, y]: [number, number]): Set | null; declare function sharedCornerShapeSide(a: Set, b: Set): boolean; declare function cornerShapeDiagonal(aPoint: [number, number], aSides: Set, bPoint: [number, number], bSides: Set): [CornerShapeCorner, CornerShapeRadius] | null; declare function cornerShapeGeometryForPlan(entry: TextureAtlasPlan): CornerShapeGeometry | null; declare function cssBorderShapeForGeometry(points: Array<[number, number]>): string; declare function cssBorderShapeForPlan(entry: TextureAtlasPlan): string; declare function formatBorderShapeEntryMatrix(entry: TextureAtlasPlan): string; declare function formatBorderShapeElementStyle(entry: TextureAtlasPlan): string; declare function formatCornerShapeElementStyle(entry: TextureAtlasPlan, geometry: CornerShapeGeometry): string; declare function computeSolidTriangleColorPlanFromNormal(polygon: Polygon, index: number, nx: number, ny: number, nz: number, options: SolidTrianglePlanOptions, includeColor: boolean, colorOverride?: string): SolidTriangleColorPlan; declare function computeSolidTriangleColorPlan(polygon: Polygon, index: number, options: SolidTrianglePlanOptions): SolidTriangleColorPlan | null; declare function computeSolidTrianglePlan(polygon: Polygon, index: number, options: SolidTrianglePlanOptions, computeOptions?: SolidTriangleComputeOptions): SolidTrianglePlan | null; declare function computeSolidTrianglePlanFromCssPoints(polygon: Polygon, index: number, options: SolidTrianglePlanOptions, computeOptions: SolidTriangleComputeOptions, p0x: number, p0y: number, p0z: number, p1x: number, p1y: number, p1z: number, p2x: number, p2y: number, p2z: number): SolidTrianglePlan | null; declare function resolveProjectiveQuadGuards(overrides: ProjectiveQuadGuardOverrides | undefined): ProjectiveQuadGuardSettings; declare function computeProjectiveQuadCoefficients(q: Array<[number, number]>, guards: ProjectiveQuadGuardSettings): ProjectiveQuadCoefficients | null; declare function computeProjectiveQuadMatrix(screenPts: number[], xAxis: Vec3, yAxis: Vec3, normal: Vec3, tx: number, ty: number, tz: number, guards: ProjectiveQuadGuardSettings, seamBleedEdgeAmounts?: ReadonlyMap): string | null; declare function dotVec(a: Vec3, b: Vec3): number; declare function crossVec(a: Vec3, b: Vec3): Vec3; declare function isBasisOptimizable(polygon: Polygon): boolean; declare function getPolygonBasisInfo(polygon: Polygon, tile: number, elev: number): PolygonBasisInfo | null; declare function compatibleSurface(a: PolygonBasisInfo | null, b: PolygonBasisInfo | null): boolean; declare function compatibleBleedSurface(a: PolygonBasisInfo | null, b: PolygonBasisInfo | null): boolean; declare function seamLightBrightness(info: PolygonBasisInfo | null, options: SolidTrianglePlanOptions): number | null; declare function basisAxisKey(axis: Vec3): string; declare function makeLocalBasis(pts: Vec3[], origin: Vec3, normal: Vec3, rawXAxis: Vec3, options?: { boundsOrigin?: Vec3; snapBounds?: boolean; }): LocalBasis | null; declare function evaluateIslandAxis(component: number[], infos: Array, axis: Vec3, boundsOrigin: Vec3): { pixelArea: number; rawArea: number; } | null; declare function chooseIslandXAxis(component: number[], infos: Array): BasisHint | null; declare function buildBasisHints(polygons: Polygon[], options: SolidTrianglePlanOptions): Array; declare function chooseLocalBasis(pts: Vec3[], origin: Vec3, normal: Vec3, options: BasisOptions): LocalBasis | null; declare function isFullRectBasis(basis: LocalBasis): boolean; declare function computeUvAffine(points: Vec2[], uvs: Vec2[]): UvAffine | null; declare function computeUvSampleRect(uvs: Vec2[]): UvSampleRect | null; declare function projectTextureTriangle(triangle: TextureTriangle, tile: number, elev: number, origin: Vec3, xAxis: Vec3, yAxis: Vec3, shiftX: number, shiftY: number): TextureTrianglePlan | null; declare function computeTextureAtlasPlan(polygon: Polygon, index: number, options: SolidTrianglePlanOptions, projectiveQuadGuards: ProjectiveQuadGuardSettings, basisHint?: BasisHint): TextureAtlasPlan | null; /** * Compute the per-polygon layout plan for one polygon in isolation. * * This is the public single-polygon variant used by React and Vue components. * It does not run the cross-polygon basis-optimisation or seam-detection that * the full `renderPolygonsWithTextureAtlas` pipeline performs, but the * strategy selection (projective-quad, rect, etc.) is identical to the * canonical renderer. * * The `projectiveQuadOverrides` parameter is the pre-resolved override bag * formerly obtained from `doc.defaultView.__polycssProjectiveQuadGuards`. * Callers that have a Document should extract it before calling; callers in * browser-free environments can pass `undefined` for the default guards. */ declare function computeTextureAtlasPlanPublic(polygon: Polygon, index: number, options?: ComputeTextureAtlasPlanOptions, projectiveQuadOverrides?: ProjectiveQuadGuardOverrides, /** Cross-polygon basis hint pre-computed via {@link buildBasisHints} on * the full polygon array. When supplied, it overrides the per-polygon * textureEdgeRepairEdges fallback below. Vanilla's renderer always passes * this from {@link buildBasisHints}; React/Vue mirror that path. */ basisHintOverride?: BasisHint): TextureAtlasPlan | null; declare function resolvePolyTextureLeafGeometry(plan: TextureAtlasPlan, options?: PolyTextureLeafResolverOptions): PolyTextureLeafGeometry | null; declare function resolvePolyTextureImageSource(polygon: Polygon): PolyTextureImageSource | undefined; declare function resolvePolyTextureUrl(polygon: Polygon): string | undefined; declare function resolvePolyTexturePresentation(polygon: Polygon, defaults?: PolyTexturePresentation): PolyTexturePresentation; declare function resolvePolyTextureImageRendering(polygon: Polygon, defaultImageRendering: PolyTextureImageRendering | undefined): PolyTextureImageRendering; declare function normalizeAtlasScale(scale: number | string | undefined): number; declare function atlasArea(pages: PackedPage[]): number; declare function autoAtlasScaleCap(pages: PackedPage[], maxDecodedBytes: number): number; declare function autoAtlasScale(pages: PackedPage[], maxDecodedBytes: number): number; declare function atlasBitmapMaxSide(pages: PackedPage[], atlasScale: number): number; declare function atlasDecodedBytes(pages: PackedPage[], atlasScale: number): number; declare function autoAtlasBudgetFactor(pages: PackedPage[], atlasScale: number, maxDecodedBytes: number): number; /** Returns the max decoded-bytes budget for the given device class. */ declare function autoAtlasMaxDecodedBytes(isMobile: boolean): number; /** Returns the atlas canonical size for the given texture quality and device class. */ declare function atlasCanonicalSizeForTextureQuality(textureQualityInput: TextureQuality | undefined, isMobile: boolean): number; declare function applyPackedAtlasCanonicalSize(packed: PackedAtlas, atlasCanonicalSize: number): PackedAtlas; declare function resolveAtlasLeafBox(entry: TextureAtlasPlan, atlasScale: number, textureLeafSizing: PolyTextureLeafSizing | undefined, atlasCanonicalSize?: number): { width: number; height: number; sizing: PolyTextureLeafSizing; }; declare function applyPackedAtlasLeafSizing(packed: PackedAtlas, atlasCanonicalSize: number, atlasScale: number, textureLeafSizing?: PolyTextureLeafSizing | undefined): PackedAtlas; declare function atlasCanonicalSizeForEntry(entry: TextureAtlasPlan): number; declare function atlasPadding(atlasScale: number): number; declare function packTextureAtlasPlans(plans: Array, atlasScale?: number): PackedAtlas; /** * Pack atlas plans and resolve atlas scale, accepting a pre-resolved isMobile * boolean instead of a Document reference. */ declare function packTextureAtlasPlansWithScaleCore(plans: Array, textureQualityInput: TextureQuality | undefined, isMobile: boolean, textureLeafSizing?: PolyTextureLeafSizing | undefined): { packed: PackedAtlas; atlasScale: number; atlasCanonicalSize: number; }; export { ASYNC_RENDER_BUDGET_MS, ATLAS_CANONICAL_SIZE_AUTO_DESKTOP, ATLAS_CANONICAL_SIZE_EXPLICIT, ATLAS_MAX_SIZE, ATLAS_PADDING, AUTO_ATLAS_LOW_AREA, AUTO_ATLAS_MAX_BITMAP_SIDE, AUTO_ATLAS_MAX_DECODED_BYTES_DESKTOP, AUTO_ATLAS_MAX_DECODED_BYTES_MOBILE, AUTO_ATLAS_MEDIUM_AREA, AUTO_ATLAS_SCALE_GUARD, type ArrowPolygonsOptions, type AxesHelperOptions, BAKED_SHADOW_MIN_UP, BAKED_SHADOW_Z_SQUASH, BASIS_EPS, BORDER_SHAPE_BLEED, BORDER_SHAPE_CANONICAL_SIZE, BORDER_SHAPE_CENTER_PERCENT, BORDER_SHAPE_POINT_EPS, type BasisHint, type BasisOptions, type BorderShapeBounds, type BorderShapeGeometry, type BoxFace, type BoxFaceOptions, type BoxPolygonsOptions, CAMERA_BACKFACE_CULL_EPS, COLOR_PARSE_CACHE_MAX, CORNER_SHAPE_DUPLICATE_EPS, CORNER_SHAPE_POINT_EPS, type CameraCullNormalGroup, type CameraCullRotation, CameraState, type CasterPolyItem, type ComputeReceiverShadowFacesInput, type ComputeTextureAtlasPlanOptions, type ConePolygonsOptions, type CornerShapeCorner, type CornerShapeGeometry, type CornerShapeRadius, type CornerShapeSide, type CoverPlanarPolygonsOptions, type CullInteriorOptions, type CylinderPolygonsOptions, DECIMAL_SCALES, DEFAULT_AMBIENT_COLOR, DEFAULT_AMBIENT_INTENSITY, DEFAULT_ATLAS_CSS_DECIMALS, DEFAULT_BORDER_SHAPE_DECIMALS, DEFAULT_LIGHT_COLOR, DEFAULT_LIGHT_DIR, DEFAULT_LIGHT_INTENSITY, DEFAULT_MATRIX_DECIMALS, DEFAULT_SEAM_BLEED, DEFAULT_SEAM_FACET_SPLIT_OPTIONS, DEFAULT_SEAM_OVERLAP_OPTIONS, DEFAULT_TILE, type DedupeOverlappingPolygonsOptions, type DodecahedronPolygonsOptions, type EdgeOwners, type FilterAtlasPlansEnv, type GetSolidPaintDefaultsEnv, type GltfParseOptions, type IcosahedronPolygonsOptions, type InternalSolidTrianglePlanOptions, type LoadMeshOptions, type LocalBasis, type LoopMode, LoopOnce, LoopPingPong, LoopRepeat, MAX_ATLAS_SCALE, MIN_ATLAS_SCALE, type MergedReceiverShadowInput, type MergedShadowFace, type MergedShadowLayer, MeshResolution, type MtlParseResult, type NormalizeResult, type ObjParseOptions, type OctahedronPolygonsOptions, type OptimizeAnimatedMeshPolygonsOptions, type OptimizeMeshParseResultOptions, type OptimizeMeshPolygonsOptions, PROJECTIVE_QUAD_BLEED, PROJECTIVE_QUAD_DENOM_EPS, PROJECTIVE_QUAD_MAX_WEIGHT_RATIO, type PackedAtlas, type PackedPage, type PackedTextureAtlasEntry, type PackingPage, type PackingShelf, type ParametricOverrideInput, type ParametricOverrideResult, type ParseAnimationClip, type ParseAnimationController, type ParseResult, type ParseStlColor, type ParseStlSolid, type ParseStlTopology, type ParsedColor, type PlanePolygonsOptions, PolyAmbientLight, type PolyAnimationAction, type ParseAnimationClip as PolyAnimationClip, type PolyAnimationMixer, type PolyAnimationTarget, PolyDirectionalLight, type PolyMeshTransformInput, PolyPointLight, type PolyRenderStrategiesOption, type PolyRenderStrategy, type PolySceneTransformInput, type PolySeamBleed, type PolySeamBleedEdgeValue, type PolySeamBleedEdges, PolyTextureBackend, PolyTextureImageLighting, PolyTextureImageRendering, PolyTextureImageSource, type PolyTextureLeafGeometry, type PolyTextureLeafResolverOptions, PolyTextureLeafSizing, type PolyTextureLeafSourceRect, PolyTextureLightingMode, PolyTexturePresentation, PolyTextureProjection, type PolyVoxelCell, type PolyVoxelSource, Polygon, type PolygonBasisInfo, type PolygonFace, type ProjectiveQuadCoefficients, type ProjectiveQuadGuardGlobal, type ProjectiveQuadGuardOverrides, type ProjectiveQuadGuardSettings, QUAT_IDENTITY, type Quat, RECEIVER_NORMAL_TOL, RECEIVER_OFFSET_TOL, RECEIVER_OUTLINE_EXPAND, RECT_EPS, type RGB, type RGBFactors, type ReceiverCasterInput, type ReceiverFacePlane, type ReceiverPlaneGroup, type ReceiverShadowFaceSpec, type ReceiverShadowPath, type RectBrush, type RingPolygonsOptions, type RingQuadPolygonsOptions, SEAM_LIGHT_EPS, SOLID_QUAD_CANONICAL_SIZE, SOLID_TRIANGLE_BLEED, SOLID_TRIANGLE_CANONICAL_SIZE, SOLID_TRIANGLE_LARGE_BORDER_CANONICAL_SIZE, SURFACE_DISTANCE_EPS, SURFACE_NORMAL_EPS, type SceneBbox, type SceneContext, type SceneContextBuildArgs, type SceneContextBuildResult, type ScreenToWorldOptions, type SeamBleedInsets, type SeamFacetSplitCandidate, type SeamFacetSplitCandidateReason, type SeamFacetSplitOptions, type SeamFacetSplitReport, type SeamOverlapCandidate, type SeamOverlapCandidateKind, type SeamOverlapDiagnostics, type SeamOverlapOptions, type SimplifyTriangleMeshPolygonsOptions, type SolidPaintDefaults, type SolidTextureSampleOptions, type SolidTriangleBasis, type SolidTriangleColorPlan, type SolidTriangleComputeOptions, type SolidTriangleFrame, type SolidTrianglePlan, type SolidTrianglePlanOptions, type SolidTrianglePrimitive, type SpherePolygonsOptions, type StablePlanBasis, type StableTriangleColorState, type StlParseOptions, TEXTURE_EDGE_REPAIR_ALPHA_MIN, TEXTURE_EDGE_REPAIR_RADIUS, TEXTURE_EDGE_REPAIR_SOURCE_ALPHA_MIN, TEXTURE_TRIANGLE_BLEED, type TetrahedronPolygonsOptions, type TextureAtlasPage, type TextureAtlasPlan, type TexturePaintMetrics, type TexturePaintMetricsOptions, type TextureQuality, TextureTriangle, type TextureTrianglePlan, type TorusPolygonsOptions, type UvAffine, type UvSampleRect, VOXEL_CAMERA_CULL_AXIS_EPS, VOXEL_CAMERA_CULL_NORMAL_LIMIT, Vec2, Vec3, type VoxParseOptions, applyPackedAtlasCanonicalSize, applyPackedAtlasLeafSizing, arrowPolygons, atlasArea, atlasBitmapMaxSide, atlasCanonicalSizeForEntry, atlasCanonicalSizeForTextureQuality, atlasDecodedBytes, atlasPadding, autoAtlasBudgetFactor, autoAtlasMaxDecodedBytes, autoAtlasScale, autoAtlasScaleCap, axesHelperPolygons, bakeSolidTextureSampledPolygons, bakeSolidTextureSamples, basisAxisKey, borderShapeBoundsFromPoints, borderShapeGeometryForPlan, boxPolygons, buildBakedShadowProjectionMatrix, buildBasisHints, buildEdgeOwners, buildParametricCasterOverride, buildPolyMeshTransform, buildPolySceneTransform, buildSceneContext, buildSeamBleedPolygonEdges, buildSeamBleedPolygonSet, buildSharedEdgeMap, buildTextureEdgeRepairSets, cachedParsePureColor, cameraCullNormalGroups, cameraCullNormalGroupsFromPolygons, cameraCullNormalKey, cameraCullVisibleSignature, cameraFacingDepth, chooseIslandXAxis, chooseLocalBasis, clampChannel, classifyFacing, clipPolygonToConvex2D, colorErrorScore, compatibleBleedSurface, compatibleSurface, computeCoverageShadowSilhouette, computeLightVisibility, computeMergedReceiverShadows, computeParametricShadowSilhouette, computePlanSeamBleedEdgeAmounts, computeProjectiveQuadCoefficients, computeProjectiveQuadMatrix, computeReceiverShadowFaces, computeSceneBbox, computeSeamBleedInsets, computeShapeLighting, computeSolidTriangleColorPlan, computeSolidTriangleColorPlanFromNormal, computeSolidTrianglePlan, computeSolidTrianglePlanFromCssPoints, computeSurfaceNormal, computeTextureAtlasPlan, computeTextureAtlasPlanPublic, computeTexturePaintMetrics, computeUvAffine, computeUvSampleRect, conePolygons, convexHull2D, cornerShapeDiagonal, cornerShapeGeometryForPlan, cornerShapePointSides, coverPlanarPolygons, createPolyAnimationMixer, crossVec, cssBorderShapeForGeometry, cssBorderShapeForPlan, cssDistanceToWorld, cssPoints, cssPositionToWorld, cullInteriorPolygons, cylinderPolygons, dedupeOverlappingPolygons, dodecahedronPolygons, dominantCountKey, dotVec, ensureCcw2D, eulerXYZFromQuat, evaluateIslandAxis, expandClipPoints, expandConvexHullOutward, extractSilhouetteLoops, filterAtlasPlans, findOverlappingPolygonDuplicates, formatAffineMatrix3dColumns, formatAffineMatrix3dScalars, formatAffineMatrix3dTransformScalars, formatAtlasMatrix, formatBorderShapeElementStyle, formatBorderShapeEntryMatrix, formatBorderShapeMatrix, formatColor, formatCornerShapeElementStyle, formatCssLength, formatCssLengthPx, formatMatrix3d, formatMatrix3dValues, formatPercent, formatScaledMatrixFromPlan, formatSolidQuadEntryMatrix, formatSolidQuadMatrix, fullRectBounds, getPolygonBasisInfo, getSolidPaintDefaultsForPlansCore, groupReceiverFaceGroups, icosahedronPolygons, incrementCount, intersect2DLines, intersect2DLinesRaw, inverseRotateVec3, isAxisAlignedSurfaceNormal, isBakedShadowCaster, isBasisOptimizable, isConvexCaster, isConvexPolygonPoints, isFlatCaster, isFullRectBasis, isFullRectSolid, isPointShadowCaster, isProjectiveQuadPlan, isSolidTrianglePlan, isVoxelCameraCullableNormalGroups, loadMesh, makeLocalBasis, mergePolygons, meshScaleVec3, normalFacesCamera, normalizeAtlasScale, normalizePolygons, normalizedSeamBleed, octahedronPolygons, offsetConvexPolygonPoints, offsetConvexPolygonPointsByEdgeAmounts, offsetStableTrianglePoints, offsetTrianglePoints, optimizeAnimatedMeshPolygons, optimizeMeshParseResult, optimizeMeshPolygons, packTextureAtlasPlans, packTextureAtlasPlansWithScaleCore, parseAlpha, parseColor, parseGltf, parseHex, parseHexColor, parseMtl, parseObj, parsePureColor, parseRgbColor, parseStl, parseVox, planePolygons, polyCssDistanceToWorld, polyCssPositionToWorld, polygonContainsPoint, polygonCssSurfaceNormal, polygonFaces, polygonFacesCamera, polygonSignedArea2D, prepareCasterEdgeOwners, prepareCasterPolyItems, prepareReceiverFacePlanes, projectCssVertexToGround, projectCssVertexToGroundFromPoint, projectTextureTriangle, quantizeCssColor, quatFromAxisAngle, quatFromEulerXYZ, quatMultiply, repairMeshSeams, resolveAtlasLeafBox, resolveBleedRatio, resolvePolyTextureImageRendering, resolvePolyTextureImageSource, resolvePolyTextureLeafGeometry, resolvePolyTexturePresentation, resolvePolyTextureUrl, resolveProjectiveQuadGuards, resolveSeamBleed, rgbEqual, rgbKey, rgbToCss, rgbToHex, ringPolygons, ringQuadPolygons, rotateVec3, rotateVec3InWrapperCssFrame, roundDecimal, safariCssProjectiveUnsupported, safePlanSeamBleedAmount, screenToWorldOnSphere, screenToWorldRay, seamBleedAmountArray, seamFacetSplitPolygons, seamFacetSplitReport, seamLightBrightness, seamOverlapDiagnostics, seamOverlapPolygons, seamOverlapReport, shadeColor, shadePolygon, sharedCornerShapeSide, signedArea2D, simplifyCornerShapePoints, simplifyTriangleMeshPolygons, spherePolygons, stableBasisFromPlan, stableTriangleMatrixDecimals, stepRgbToward, tetrahedronPolygons, textureTintFactors, tintToCss, torusPolygons, worldCssForMesh, worldDirectionToCss, worldDirectionToPolyCss, worldDirectionalLightToCss, worldDirectionalLightToPolyCss, worldDistanceToCss, worldDistanceToPolyCss, worldPositionToCss, worldPositionToPolyCss };