@layoutit/polycss-core 0.2.1 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -147,6 +147,20 @@ interface Polygon {
147
147
  * @internal
148
148
  */
149
149
  textureTriangles?: TextureTriangle[];
150
+ /**
151
+ * Import-time simplifier seam keys retained after solid texture baking.
152
+ * Renderers ignore this; mesh optimization uses it to avoid welding vertices
153
+ * that shared a position but came from different texture/attribute seams.
154
+ * @internal
155
+ */
156
+ simplifyVertexKeys?: string[];
157
+ /**
158
+ * Stricter import-time simplifier keys that preserve source vertex identity.
159
+ * Renderers ignore this; candidate simplification uses it only for fallback
160
+ * passes where relaxed seam welding loses after render-cost optimization.
161
+ * @internal
162
+ */
163
+ simplifySourceVertexKeys?: string[];
150
164
  /**
151
165
  * Source material requested two-sided rendering. Importers use this so
152
166
  * optimization passes do not collapse intentional reverse-wound faces.
@@ -183,8 +197,8 @@ declare function normalizePolygons(input: Polygon[]): NormalizeResult;
183
197
  * (already normalized) and returns the data the framework wrappers need
184
198
  * to render.
185
199
  *
186
- * No cube grid, no per-Z layer bucketing, no wall mask, no neighbor-based
187
- * occlusion. Just a polygon list and a scene bbox.
200
+ * The renderer consumes a flat polygon list plus a scene bbox; higher-level
201
+ * culling or mesh optimization happens before this context is built.
188
202
  */
189
203
 
190
204
  interface SceneBbox {
@@ -236,10 +250,8 @@ declare function buildSceneContext(args: SceneContextBuildArgs): SceneContextBui
236
250
 
237
251
  /**
238
252
  * Polygon geometry helpers — pure math operating on Polygon vertices.
239
- *
240
- * After cube removal in Phase 2, this module carries small polygon-level
241
- * helpers for downstream consumers (lighting, debug metrics, etc.). The
242
- * cube / ramp / wedge / spike face emitters lived here in voxcss; they're gone.
253
+ * These helpers feed lighting, diagnostics, and renderer metrics without
254
+ * depending on browser APIs.
243
255
  */
244
256
 
245
257
  interface PolygonFace {
@@ -278,8 +290,7 @@ interface TexturePaintMetrics {
278
290
  }
279
291
  /**
280
292
  * Surface a polygon as a single face. The returned array always has length 1;
281
- * the indirection exists so callers that historically iterated faces (e.g.
282
- * the manifold check, the canvas validator) can keep their loop shape.
293
+ * the indirection lets face-oriented consumers keep a common loop shape.
283
294
  *
284
295
  * Returns an empty array for degenerate polygons (< 3 vertices).
285
296
  */
@@ -313,6 +324,20 @@ declare function rotateVec3(v: Vec3, rxDeg: number, ryDeg: number, rzDeg: number
313
324
  * `rot` is `[rxDeg, ryDeg, rzDeg]` matching the mesh's CSS rotation prop.
314
325
  */
315
326
  declare function inverseRotateVec3(v: Vec3, rot: Vec3): Vec3;
327
+ /**
328
+ * Apply the wrapper's actual CSS rotation matrix to a CSS-frame vector,
329
+ * given the user-supplied world rotation tuple `[rx_w, ry_w, rz_w]`.
330
+ *
331
+ * The wrapper emits `rotateY(-rx_w) rotateX(-ry_w) rotateZ(-rz_w)` (the
332
+ * world↔CSS reflection conjugation of three.js XYZ Euler). That matrix
333
+ * is `Ry(-rx_w) · Rx(-ry_w) · Rz(-rz_w) · v` applied to a vector. Use
334
+ * this anywhere downstream code needs to transform a CSS-frame normal or
335
+ * point through the same rotation the wrapper applies — e.g. back-face
336
+ * culling, voxel item ordering. NOT for inverse-rotating a world light
337
+ * direction into mesh-local frame — that still uses `inverseRotateVec3`
338
+ * with the user's world rotation, since it operates in world frame.
339
+ */
340
+ declare function rotateVec3InWrapperCssFrame(v: Vec3, worldRot: Vec3): Vec3;
316
341
 
317
342
  /**
318
343
  * Minimal quaternion helpers for composing rotations.
@@ -384,7 +409,7 @@ type AutoRotateOption = boolean | number | AutoRotateConfig;
384
409
  * `distance` is the camera's pull-back from the target in CSS pixels.
385
410
  * Increasing distance moves the camera farther from the target along the
386
411
  * view axis (dolly out) — analogous to three.js's spherical radius.
387
- * Default 0 keeps the legacy behaviour unchanged.
412
+ * Default 0 keeps orthographic/isometric scenes flat.
388
413
  */
389
414
  interface CameraState {
390
415
  target: Vec3;
@@ -411,6 +436,66 @@ declare function normalizeInvertMultiplier(value: number | boolean | undefined):
411
436
  declare const DEFAULT_CAMERA_STATE: CameraState;
412
437
  declare function createIsometricCamera(initial?: Partial<CameraState>): CameraHandle;
413
438
 
439
+ /**
440
+ * Screen → world inverse projection helpers.
441
+ *
442
+ * The camera transform a CameraHandle emits is:
443
+ *
444
+ * `translateZ(-distance) scale(zoom/tile) rotateX(rotX) rotate(rotY) translate3d(-cssX, -cssY, -cssZ)`
445
+ *
446
+ * (right-to-left for point transforms). For an orthographic camera the
447
+ * depth component drops out at projection time, so a screen position
448
+ * (mx, my) unprojects to a 3D RAY in world space — every depth value
449
+ * along that ray maps to the same screen position. Callers pick a
450
+ * specific 3D point by intersecting the ray with a surface they care
451
+ * about (here: a sphere).
452
+ *
453
+ * `screenToWorldOnSphere` is the common case: given a mouse position,
454
+ * find where on a fixed sphere (e.g. the draggable light marker's
455
+ * sphere of constant radius) the cursor is pointing. Returns null
456
+ * when the ray misses the sphere.
457
+ */
458
+
459
+ interface ScreenToWorldOptions {
460
+ /** Camera state — rotX/rotY/zoom/target, same shape `CameraHandle.state` exposes. */
461
+ camera: CameraState;
462
+ /** World-units → CSS-px factor. Pass `DEFAULT_TILE` from the renderer. */
463
+ tileSize: number;
464
+ /** Viewport center in CSS pixels (screen coordinates). The mouse position
465
+ * is interpreted relative to this point. */
466
+ viewportCenterX: number;
467
+ viewportCenterY: number;
468
+ /** Mouse screen position (CSS pixels). */
469
+ mouseX: number;
470
+ mouseY: number;
471
+ }
472
+ /**
473
+ * Unproject the cursor to a ray in world coordinates.
474
+ *
475
+ * `ray(t) = origin + t · direction`
476
+ *
477
+ * `origin` is the world point that screen-coincides with the mouse at
478
+ * depth t=0; `direction` is the camera-forward axis in world frame.
479
+ * For orthographic cameras the ray is parallel — every point along it
480
+ * projects to the same screen position.
481
+ */
482
+ declare function screenToWorldRay(opts: ScreenToWorldOptions): {
483
+ origin: Vec3;
484
+ direction: Vec3;
485
+ };
486
+ /**
487
+ * Unproject the cursor and intersect with a sphere in world coords.
488
+ * Returns the FRONT intersection (the one closer to the camera) when
489
+ * the ray hits the sphere. When the ray misses (cursor outside the
490
+ * projected silhouette), returns the point on the sphere's silhouette
491
+ * closest to the cursor — that way callers like draggable helpers keep
492
+ * tracking the cursor along the rim instead of freezing in place.
493
+ */
494
+ declare function screenToWorldOnSphere(opts: ScreenToWorldOptions & {
495
+ sphereCenter: Vec3;
496
+ sphereRadius: number;
497
+ }): Vec3 | null;
498
+
414
499
  interface ParsedColor {
415
500
  rgb: [number, number, number];
416
501
  alpha: number;
@@ -447,9 +532,8 @@ declare function computeShapeLighting(normal: Vec3, baseColor: string, direction
447
532
  /**
448
533
  * Merge coplanar same-color adjacent triangles into N-vertex polygons.
449
534
  *
450
- * Each polygon is rendered as one atlas-backed DOM element — so a mesh whose
451
- * triangles came from quads or pentagons collapses back into its original
452
- * face count.
535
+ * Each visible polygon renders as one DOM leaf — so a mesh whose triangles
536
+ * came from quads or pentagons collapses back into its original face count.
453
537
  *
454
538
  * - Geodesic spheres: ~half the triangles came from quad subdivisions
455
539
  * - OBJ imports: many were quads/n-gons fan-triangulated by the importer
@@ -480,10 +564,9 @@ declare function mergePolygons(input: Polygon[]): Polygon[];
480
564
  * Why this exists: modelers (and importers) often emit redundant geometry
481
565
  * for the same visible surface — a doubled face on a wall, an inner shell
482
566
  * coincident with an outer shell, or two N-gons that fan-triangulate the
483
- * same region. Each duplicate is a real `<i>` element at render time:
484
- * it costs DOM, Lambert math, atlas budget, AND it produces stacked
485
- * shadow leaves that visibly multiply on the receiver (overlapping dark
486
- * patches on the ground).
567
+ * same region. Each duplicate is a real render leaf at render time:
568
+ * it costs DOM, Lambert math, atlas budget, and redundant shadow projection
569
+ * work.
487
570
  *
488
571
  * This is a separate concern from `cullInteriorPolygons` (which removes
489
572
  * polygons fully *enclosed* by other geometry, conservative against
@@ -547,9 +630,8 @@ interface DedupeOverlappingPolygonsOptions {
547
630
  * larger area as a tiebreaker.
548
631
  *
549
632
  * Exposed for callers that want to act on the index set directly —
550
- * e.g. shadow casting can use a looser tolerance to skip shadow leaves
551
- * for redundant casters without removing them from the renderable
552
- * polygon set. */
633
+ * e.g. shadow casting can use a looser tolerance to skip redundant caster
634
+ * projections without removing them from the renderable polygon set. */
553
635
  declare function findOverlappingPolygonDuplicates(input: Polygon[], options?: DedupeOverlappingPolygonsOptions): Set<number>;
554
636
  declare function dedupeOverlappingPolygons(input: Polygon[], options?: DedupeOverlappingPolygonsOptions): Polygon[];
555
637
 
@@ -572,9 +654,44 @@ interface CoverPlanarPolygonsOptions {
572
654
  */
573
655
  declare function coverPlanarPolygons(input: Polygon[], options?: CoverPlanarPolygonsOptions): Polygon[];
574
656
 
657
+ interface SimplifyTriangleMeshPolygonsOptions {
658
+ /** Target triangle ratio per eligible connected material group. Default 0.7. */
659
+ ratio?: number;
660
+ /** Maximum accepted local plane displacement in scene units. Default 0.18. */
661
+ maxError?: number;
662
+ /** Reject collapses that rotate any affected face beyond this angle. Default 65. */
663
+ maxNormalAngleDeg?: number;
664
+ /** Only collapse vertices onto existing endpoint positions. Default true. */
665
+ preserveVertices?: boolean;
666
+ /** Use stricter importer source-vertex keys instead of relaxed seam keys. Default "relaxed". */
667
+ vertexKeyMode?: "relaxed" | "source";
668
+ /** Keep topological/material boundary vertices fixed. Default true. */
669
+ lockBoundary?: boolean;
670
+ /** Skip small groups where decimation is unlikely to repay the mutation cost. Default 80. */
671
+ minGroupTriangles?: number;
672
+ /** Hard cap for rebuild passes. Default 12. */
673
+ maxPasses?: number;
674
+ }
675
+ /**
676
+ * Import-time triangle decimation for already-solid meshes.
677
+ *
678
+ * This is deliberately conservative: textured polygons and material/color
679
+ * boundaries are kept out of the collapse graph, endpoint-preserving collapses
680
+ * mirror meshoptimizer's index-buffer simplification shape by default, and
681
+ * callers can cheaply compare the returned candidate against their normal
682
+ * render-cost optimizer before accepting it.
683
+ */
684
+ declare function simplifyTriangleMeshPolygons(polygons: Polygon[], options?: SimplifyTriangleMeshPolygonsOptions): Polygon[];
685
+
575
686
  interface OptimizeMeshPolygonsOptions {
576
687
  /** Public quality/resolution intent. Defaults to "lossy". */
577
688
  meshResolution?: MeshResolution;
689
+ /**
690
+ * Return as soon as the optimizer finds a result with at most this many
691
+ * polygons. Useful for candidate comparisons where the caller already knows
692
+ * the maximum DOM leaf count it can accept.
693
+ */
694
+ stopAtPolygonCount?: number;
578
695
  /**
579
696
  * Run the planar cover pass as an exact candidate for untextured coplanar
580
697
  * regions. Defaults to true.
@@ -583,6 +700,137 @@ interface OptimizeMeshPolygonsOptions {
583
700
  }
584
701
  declare function optimizeMeshPolygons(polygons: Polygon[], options?: OptimizeMeshPolygonsOptions): Polygon[];
585
702
 
703
+ /**
704
+ * Unified parser return type. All polygon-emitting parsers (parseObj,
705
+ * parseGltf, parseVox, parseStl, the loadMesh dispatcher) return this exact shape.
706
+ *
707
+ * The asymmetric helper `parseMtl` returns its own `MtlParseResult` (it
708
+ * emits materials, not polygons) — see parseMtl.ts for the rationale.
709
+ *
710
+ * Lifecycle contract: callers MUST call `dispose()` when the result is no
711
+ * longer needed. Idempotent — safe to call on unmount even if `objectUrls`
712
+ * is empty (e.g. `parseObj`, where it's a no-op).
713
+ */
714
+
715
+ interface PolyVoxelCell {
716
+ x: number;
717
+ y: number;
718
+ z: number;
719
+ color: string;
720
+ }
721
+ interface PolyVoxelSource {
722
+ kind: "magica-vox";
723
+ cells: PolyVoxelCell[];
724
+ rows: number;
725
+ cols: number;
726
+ depth: number;
727
+ scale: number;
728
+ sourceBytes: number;
729
+ }
730
+ interface ParseStlTopology {
731
+ componentCount: number;
732
+ repairedTriangleCount: number;
733
+ outwardComponentCount: number;
734
+ suppliedNormalComponentCount: number;
735
+ inconsistentSharedEdgeCount: number;
736
+ nonManifoldSharedEdgeCount: number;
737
+ }
738
+ interface ParseStlColor {
739
+ format: "magics";
740
+ defaultColor: string;
741
+ alpha: number;
742
+ coloredTriangleCount: number;
743
+ defaultColorTriangleCount: number;
744
+ }
745
+ interface ParseStlSolid {
746
+ name: string;
747
+ start: number;
748
+ count: number;
749
+ }
750
+ interface ParseAnimationClip {
751
+ /** Stable numeric index in the source file's animation array. */
752
+ index: number;
753
+ /** Human-readable clip name. Falls back to `animation_N` when omitted. */
754
+ name: string;
755
+ /** Clip duration in seconds, derived from its sampler input accessors. */
756
+ duration: number;
757
+ /** Number of glTF animation channels in the clip. */
758
+ channelCount: number;
759
+ }
760
+ interface ParseAnimationController {
761
+ /** Animation clips exposed by the parsed mesh. Empty when none are usable. */
762
+ clips: ParseAnimationClip[];
763
+ /**
764
+ * Sample a clip at `timeSeconds` and return a fresh polygon list.
765
+ * `clip` accepts either the clip index or its name. Time wraps by duration.
766
+ */
767
+ sample: (clip: number | string, timeSeconds: number) => Polygon[];
768
+ }
769
+ interface ParseResult {
770
+ /** The mesh, as a flat polygon list. Already vertex-permuted to polycss space. */
771
+ polygons: Polygon[];
772
+ /** Optional raw voxel source for `.vox` fast paths; polygon fallback remains authoritative. */
773
+ voxelSource?: PolyVoxelSource;
774
+ /** Optional animation sampler for formats that carry timeline data. */
775
+ animation?: ParseAnimationController;
776
+ /**
777
+ * Blob/object URLs minted during parse (e.g. embedded GLB images). Pass-by-
778
+ * reference — the same array is exposed on the result for visibility, and
779
+ * `dispose()` revokes each one. Do NOT mutate this array externally.
780
+ */
781
+ objectUrls: string[];
782
+ /**
783
+ * Idempotent — revokes object URLs. Safe to call on unmount, safe to call
784
+ * twice. Parsers without minted URLs (parseObj, parseMtl) supply a no-op.
785
+ */
786
+ dispose: () => void;
787
+ /**
788
+ * Non-fatal warnings raised during parse. Empty for parsers that don't
789
+ * have a warning channel; populated when downstream `normalizePolygons`
790
+ * is invoked through the high-level pipeline.
791
+ */
792
+ warnings: string[];
793
+ /** Optional format-specific metadata. */
794
+ metadata?: {
795
+ /** Triangle count after fan-triangulation (parseObj) or post-triangulation (parseGltf). */
796
+ triangleCount?: number;
797
+ /** Mesh names from the file (for glTF, from doc.meshes[].name). */
798
+ meshes?: string[];
799
+ /** Material names (in first-seen order). */
800
+ materials?: string[];
801
+ /** Animation clips from the file, mirrored from `animation.clips`. */
802
+ animations?: ParseAnimationClip[];
803
+ /** Source file size in bytes (for diagnostics). */
804
+ sourceBytes?: number;
805
+ /** Voxel count for `.vox` sources. */
806
+ voxelCount?: number;
807
+ /** Printable binary STL header, trimmed. */
808
+ stlHeader?: string;
809
+ /** Binary STL color metadata when a supported color extension is present. */
810
+ stlColor?: ParseStlColor;
811
+ /** Consecutive ASCII `solid` groups after parse/filtering. */
812
+ stlSolids?: ParseStlSolid[];
813
+ /** STL winding/connectivity diagnostics. */
814
+ stlTopology?: ParseStlTopology;
815
+ };
816
+ }
817
+
818
+ interface OptimizeMeshParseResultOptions {
819
+ /** Render-cost optimization intent. Defaults to "lossy". */
820
+ meshResolution?: MeshResolution;
821
+ /** Parse result before solid texture baking, used to identify baked swatch faces. */
822
+ source?: ParseResult;
823
+ /** Merge near-identical baked swatch colors in lossy mode. Default 36. */
824
+ bakedTextureColorMergeDistance?: number;
825
+ /** Try static triangle simplification before final polygon optimization. Default true. */
826
+ simplifyTriangleMeshes?: boolean;
827
+ /** Options for the static triangle simplifier. */
828
+ simplifyTriangleMeshOptions?: SimplifyTriangleMeshPolygonsOptions;
829
+ /** Candidate optimizer early-stop drop target. Default 0.15. */
830
+ simplifyEarlyStopDropRatio?: number;
831
+ }
832
+ declare function optimizeMeshParseResult(result: ParseResult, options?: OptimizeMeshParseResultOptions): ParseResult;
833
+
586
834
  type SeamOverlapCandidateKind = "true-gap" | "connected-facet" | "material-boundary";
587
835
  interface SeamOverlapCandidate {
588
836
  kind: SeamOverlapCandidateKind;
@@ -696,9 +944,56 @@ declare function seamOverlapReport(polygons: Polygon[], options?: number | SeamO
696
944
  interface CullInteriorOptions {
697
945
  /** Hemisphere ray samples per polygon. Higher = fewer false positives, slower. Default 8. */
698
946
  samples?: number;
947
+ /** Bypass the "large open topology" safety bail-out. By default a mesh
948
+ * with >20% boundary edges is treated as open (terrain, partial geometry)
949
+ * and culling is skipped to avoid false-positive removals on exposed faces.
950
+ * Buildings imported from OBJ commonly have windows/doors that push the
951
+ * boundary ratio above the threshold while STILL having clearly enclosed
952
+ * interior walls that should be culled — set this when the caller has
953
+ * another signal (e.g. mesh is known to be a building / room) that
954
+ * interior culling is desired. Default false. */
955
+ force?: boolean;
956
+ /** Fraction of sample directions that must find at least one escaping
957
+ * origin for the polygon to count as visible. Default `1 / samples`
958
+ * (the original "any escape keeps" behaviour). Raise to ~0.5 to
959
+ * classify polygons as interior when MOST hemisphere directions are
960
+ * blocked — catches interior panels of building meshes (the cottage's
961
+ * porch trim, back-of-wall door frames) that have a sliver of clear
962
+ * sky through windows/openings but are otherwise enclosed. */
963
+ minEscapeRatio?: number;
699
964
  }
700
965
  declare function cullInteriorPolygons(polygons: Polygon[], options?: CullInteriorOptions): Polygon[];
701
966
 
967
+ /**
968
+ * computeLightVisibility — for each polygon of a mesh, determines whether
969
+ * direct light from a given direction physically reaches it (ray from the
970
+ * polygon's centroid toward the light source is unblocked) or is occluded
971
+ * by other geometry of the same mesh.
972
+ *
973
+ * This is the CPU equivalent of one shadow-map sample per polygon from the
974
+ * light's POV. Used by the baked atlas pipeline so polygons in shadow get
975
+ * lit with ambient-only color, matching Three.js's depth-pass occlusion.
976
+ *
977
+ * Algorithm: Möller-Trumbore ray-triangle intersection per candidate, with
978
+ * a flat-array BVH for any-hit traversal. Cost: O(F log F) per mesh per
979
+ * light direction. Cottage at ~240 polys: ~5-10 ms. Caller should cache by
980
+ * (mesh geometry version, lightDir hash) and only recompute on change.
981
+ */
982
+
983
+ /**
984
+ * Returns a Set of polygon indices that are OCCLUDED from the light
985
+ * (a ray from the polygon's centroid in the +lightDir direction hits
986
+ * another polygon of the same mesh before escaping to infinity).
987
+ *
988
+ * `lightDir` is the direction TO the light source (matching the
989
+ * convention used by `shadePolygon`'s caller — points from the
990
+ * surface toward the light).
991
+ *
992
+ * Caller should cache by mesh-geometry version + lightDir hash;
993
+ * recompute only when either changes.
994
+ */
995
+ declare function computeLightVisibility(polygons: readonly Polygon[], lightDir: Vec3, skipIndices?: ReadonlySet<number>): Set<number>;
996
+
702
997
  declare const CAMERA_BACKFACE_CULL_EPS = 0.00001;
703
998
  declare const VOXEL_CAMERA_CULL_AXIS_EPS = 0.001;
704
999
  declare const VOXEL_CAMERA_CULL_NORMAL_LIMIT = 6;
@@ -1097,10 +1392,9 @@ declare const BAKED_SHADOW_Z_SQUASH = 0.01;
1097
1392
  declare const BAKED_SHADOW_MIN_UP = 0.01;
1098
1393
  /**
1099
1394
  * Build the CSS-space shadow projection matrix for a fixed light + ground
1100
- * plane. The 16-element output mirrors the matrix3d expression in the
1101
- * dynamic-mode `--shadow-proj` CSS custom property, but with literal
1102
- * numbers — ready to be formatted into a single `matrix3d(...)` per
1103
- * shadow leaf.
1395
+ * plane. The 16-element output mirrors the retained `--shadow-proj` CSS
1396
+ * custom property, but with literal numbers — ready to be formatted into a
1397
+ * single `matrix3d(...)`.
1104
1398
  *
1105
1399
  * `lightDir` is the direction the light TRAVELS (e.g. `[0, 0, -1]` is
1106
1400
  * straight down). PolyCSS world Z is up, and the world→CSS axis swap
@@ -1144,11 +1438,9 @@ declare function polygonSignedArea2D(vertices: ReadonlyArray<readonly [number, n
1144
1438
  declare function ensureCcw2D(vertices: ReadonlyArray<readonly [number, number]>): Array<[number, number]>;
1145
1439
  /**
1146
1440
  * Projects a single CSS-3D vertex onto the shadow ground plane, returning
1147
- * the resulting 2D point in CSS coordinates. Mirrors the per-element
1148
- * matrix3d that the dynamic-mode `--shadow-proj` builds, but evaluated on
1149
- * the CPU for a fixed light + ground handy when many projected vertices
1150
- * are needed at once (e.g. rendering shadow outlines into a single SVG
1151
- * per mesh instead of one DOM leaf per casting polygon).
1441
+ * the resulting 2D point in CSS coordinates. Mirrors the retained
1442
+ * `--shadow-proj` matrix, but evaluated on the CPU for a fixed light + ground
1443
+ * so many projected vertices can be merged into one SVG shadow path.
1152
1444
  *
1153
1445
  * `cssVertex` is a 3D point that has already been through the world→CSS
1154
1446
  * axis swap and unit scale (so its components are dimensionless CSS-space
@@ -1166,92 +1458,340 @@ type Pt = readonly [number, number];
1166
1458
  declare function clipPolygonToConvex2D(subject: ReadonlyArray<Pt>, clip: ReadonlyArray<Pt>): Array<[number, number]>;
1167
1459
 
1168
1460
  /**
1169
- * Unified parser return type. All polygon-emitting parsers (parseObj,
1170
- * parseGltf, the loadMesh dispatcher) return this exact shape.
1461
+ * One coplanar group of receiver polygons projected into a 2D (u, v) basis on
1462
+ * the shared plane. Sutherland-Hodgman clips caster-projected shadows to this
1463
+ * group's outline; the per-member polygons let the renderer post-filter sub-
1464
+ * shadows that fall outside the actual surface union (concave-bridging air
1465
+ * gaps inside the convex hull).
1466
+ */
1467
+ type ReceiverPlaneGroup = {
1468
+ O: Vec3;
1469
+ n: Vec3;
1470
+ u: Vec3;
1471
+ v: Vec3;
1472
+ outlineUv: Array<[number, number]>;
1473
+ memberPolysUv: Array<Array<[number, number]>>;
1474
+ memberPolyIndices: number[];
1475
+ };
1476
+ /** World→CSS axis swap. World is `+X right, +Y forward, +Z up`; the renderer's
1477
+ * internal frame swaps X↔Y and scales by BASE_TILE (one world unit =
1478
+ * BASE_TILE CSS px). Same conversion every renderer applies at the boundary
1479
+ * for mesh positions, polygon vertices, and light directions. */
1480
+ declare function worldPositionToCss(p: Vec3): Vec3;
1481
+ /** World→CSS axis swap for directions (no scale; directions stay unit). The
1482
+ * polygon basis stores normals in the swapped CSS frame, so light vectors
1483
+ * must match before any dot product. */
1484
+ declare function worldDirectionToCss(d: Vec3): Vec3;
1485
+ /** Apply {@link worldDirectionToCss} to a directional-light object,
1486
+ * preserving the other fields. Used by atlas plan + buildBasisHints +
1487
+ * receiver-shadow callers so the light vector is in the same CSS-axis
1488
+ * frame as the polygon normals. Mirror of vanilla's
1489
+ * `worldDirectionalLightToCss` in `packages/polycss/src/api/scene/transforms.ts`. */
1490
+ declare function worldDirectionalLightToCss<T extends {
1491
+ direction?: Vec3;
1492
+ } | undefined>(light: T): T;
1493
+ /** Normalize a mesh `scale` value into a Vec3 (undefined → [1,1,1], number →
1494
+ * uniform, Vec3 → as-is with `?? 1` per axis). */
1495
+ declare function meshScaleVec3(scale: number | Vec3 | undefined | null): Vec3;
1496
+ /**
1497
+ * Build a `vert → CSS-frame world position` function for a mesh with the
1498
+ * given scale + position. Pivots scale from the mesh ORIGIN. Rotation is
1499
+ * intentionally not applied here — shadow geometry is computed once per
1500
+ * mesh-transform change and already lives in world coords after this
1501
+ * per-vertex transform; rotation lives on the wrapper.
1502
+ */
1503
+ declare function worldCssForMesh(scale: number | Vec3 | undefined | null): (vert: Vec3, pos: Vec3) => Vec3;
1504
+ /**
1505
+ * Minkowski expansion of a convex CCW polygon outward by `expand` units. Each
1506
+ * vertex moves along the bisector of its two adjacent edge outward-
1507
+ * perpendiculars, scaled so the edge offset distance equals `expand` (true
1508
+ * Minkowski sum with a disk of radius `expand`, evaluated at the vertex). For
1509
+ * convex inputs the result is a larger convex polygon with every edge offset
1510
+ * outward by exactly `expand`.
1511
+ */
1512
+ declare function expandConvexHullOutward(hullCcw: Array<[number, number]>, expand: number): Array<[number, number]>;
1513
+ /** Outward extension applied to each receiver face's convex outline (CSS px).
1514
+ * Adjacent receiver faces sharing an edge each expand by this amount, so the
1515
+ * two shadows overlap by ~2×EXPAND at the corner — eliminating the sub-pixel
1516
+ * light strip that used to appear where two wall faces meet. 0.5 CSS px stays
1517
+ * sub-pixel at typical zoom. */
1518
+ declare const RECEIVER_OUTLINE_EXPAND = 0.5;
1519
+ /** Plane-grouping tolerances. dot-product > 0.999 (~2.5° angular) catches
1520
+ * tessellation artifacts on flat surfaces without merging adjacent faces of
1521
+ * a low-poly curved mesh. Plane-offset tolerance is 0.5 CSS px — sub-pixel
1522
+ * coplanarity drift in glTF imports doesn't separate what should be a single
1523
+ * surface. */
1524
+ declare const RECEIVER_NORMAL_TOL = 0.001;
1525
+ declare const RECEIVER_OFFSET_TOL = 0.5;
1526
+ /**
1527
+ * Groups a receiver's polygons into shadow-receiving surfaces. Two passes:
1171
1528
  *
1172
- * The asymmetric helper `parseMtl` returns its own `MtlParseResult` (it
1173
- * emits materials, not polygons) — see parseMtl.ts for the rationale.
1529
+ * 1. Plane bucket group by matching normal + plane offset within tolerance
1530
+ * (catches tessellated flat regions).
1531
+ * 2. Connected component — within each plane bucket, union-find on shared-
1532
+ * edge adjacency (faces sharing >= 2 vertices). Catches disjoint coplanar
1533
+ * walls where a convex hull of everything would bridge an air gap.
1174
1534
  *
1175
- * Lifecycle contract: callers MUST call `dispose()` when the result is no
1176
- * longer needed. Idempotent — safe to call on unmount even if `objectUrls`
1177
- * is empty (e.g. `parseObj`, where it's a no-op).
1535
+ * Per group, output a convex hull in the group's (u, v) coords (Minkowski-
1536
+ * expanded by `RECEIVER_OUTLINE_EXPAND`).
1537
+ *
1538
+ * `worldCss(vert, pos)` is the per-vertex world→CSS conversion (built via
1539
+ * `worldCssForMesh` for the receiver's scale + position). `dedupDrop` is the
1540
+ * set of receiver polygon indices to skip.
1178
1541
  */
1542
+ declare function groupReceiverFaceGroups(polygons: readonly Polygon[], rpos: Vec3, worldCss: (vert: Vec3, pos: Vec3) => Vec3, dedupDrop: ReadonlySet<number>): ReceiverPlaneGroup[];
1179
1543
 
1180
- interface PolyVoxelCell {
1181
- x: number;
1182
- y: number;
1183
- z: number;
1184
- color: string;
1544
+ interface PolyMeshTransformInput {
1545
+ position?: Vec3;
1546
+ scale?: number | Vec3;
1547
+ rotation?: Vec3;
1185
1548
  }
1186
- interface PolyVoxelSource {
1187
- kind: "magica-vox";
1188
- cells: PolyVoxelCell[];
1189
- rows: number;
1190
- cols: number;
1191
- depth: number;
1192
- scale: number;
1193
- gridShift: number;
1194
- sourceBytes: number;
1549
+ /**
1550
+ * Build the mesh wrapper transform used by every renderer for PolyCSS's
1551
+ * world-frame mesh transform contract.
1552
+ */
1553
+ declare function buildPolyMeshTransform(t: PolyMeshTransformInput): string | undefined;
1554
+
1555
+ /**
1556
+ * Caster-mesh silhouette extraction for the receiver-shadow path.
1557
+ *
1558
+ * Given a closed (or near-closed) caster mesh and a directional light,
1559
+ * computes the silhouette LOOPS — the closed cycles of edges where
1560
+ * adjacent polygons disagree on whether they face the light. For a
1561
+ * closed manifold convex mesh that's one loop; for concave or
1562
+ * higher-genus meshes it may be several.
1563
+ *
1564
+ * The receiver-shadow algorithm normally projects every caster
1565
+ * polygon's outline independently, which collapses to a silhouette at
1566
+ * paint time via SVG fill-rule:nonzero. That makes the browser do the
1567
+ * union work AFTER we've already paid the DOM cost for hundreds or
1568
+ * thousands of triangle sub-paths. Extracting the silhouette here lets
1569
+ * us emit ONE outline per mesh per receiver, dropping path-d size by
1570
+ * 100× on heavy meshes like the teapot. See H9 in
1571
+ * `bench/notes/SHADOW_PERF_LOG.md` and `bench/notes/H9_SILHOUETTE_DESIGN.md`.
1572
+ *
1573
+ * Pure data; no DOM access. Used by `computeReceiverShadowFaces` once
1574
+ * H9 lands.
1575
+ */
1576
+
1577
+ /** Per-edge ownership record. `polyB === null` for boundary edges (open
1578
+ * meshes). Vertex coords are the canonical "from" vertices of the edge —
1579
+ * silhouette walking uses them to reconstruct loop geometry. */
1580
+ interface EdgeOwners {
1581
+ polyA: number;
1582
+ polyB: number | null;
1583
+ vertA: Vec3;
1584
+ vertB: Vec3;
1195
1585
  }
1196
- interface ParseAnimationClip {
1197
- /** Stable numeric index in the source file's animation array. */
1198
- index: number;
1199
- /** Human-readable clip name. Falls back to `animation_N` when omitted. */
1200
- name: string;
1201
- /** Clip duration in seconds, derived from its sampler input accessors. */
1202
- duration: number;
1203
- /** Number of glTF animation channels in the clip. */
1204
- channelCount: number;
1586
+ /**
1587
+ * Build a map from canonical edge key → owning polygon(s). Mirrors the
1588
+ * vertex-quantization used by `buildSharedEdgeMap` so we agree on what
1589
+ * "same edge" means.
1590
+ *
1591
+ * Edge keys are orientation-independent (vertex-pair sorted by string),
1592
+ * so two polygons sharing an edge will report the same key. Polygons
1593
+ * are listed in encounter order; for manifold meshes each edge has
1594
+ * exactly 2 owners. Non-manifold edges (3+ owners) get truncated to
1595
+ * `polyB = null` so the caller treats them as boundaries — safer than
1596
+ * picking an arbitrary "second" polygon.
1597
+ */
1598
+ declare function buildEdgeOwners(polygons: readonly Polygon[]): Map<string, EdgeOwners>;
1599
+ /** Classify each polygon as facing the light. Convention: `n · L < 0`
1600
+ * means the polygon's outward face is toward the light source (light
1601
+ * TRAVELS in direction L, polygons are CCW-from-outside). Matches the
1602
+ * light-backface cull's sign convention. */
1603
+ declare function classifyFacing(planeNormals: Array<Vec3 | null>, lightDir: Vec3): boolean[];
1604
+ /**
1605
+ * Walk silhouette edges into closed loops. An edge is silhouette iff
1606
+ * its two adjacent polygons disagree on `facing`. Boundary edges
1607
+ * (polyB === null) are silhouette iff their owner is front-facing —
1608
+ * the boundary is where the lit side ends.
1609
+ *
1610
+ * Returns one closed loop per connected silhouette cycle, each as a
1611
+ * Vec3[] sequence (last vertex equals first vertex implicitly; not
1612
+ * duplicated). For non-manifold or open meshes some edges may not form
1613
+ * a clean cycle; those edges are dropped and the remaining cycles are
1614
+ * returned best-effort.
1615
+ */
1616
+ declare function extractSilhouetteLoops(edgeOwners: ReadonlyMap<string, EdgeOwners>, facing: boolean[]): Vec3[][];
1617
+
1618
+ /**
1619
+ * Per-receiver cached face geometry. One record per coplanar face group:
1620
+ * plane (O, n, u, v), outline polygon (Sutherland-Hodgman clip), bbox in
1621
+ * (u, v) for SVG sizing, and the pre-stringified matrix3d transform that
1622
+ * places an SVG on that face plane.
1623
+ *
1624
+ * All of this is invariant under light/caster changes. Per light tick the
1625
+ * caller just re-runs the per-tri SH and builds the path `d` — never
1626
+ * recompute groups or basis. Cache invalidated when the receiver's polygon
1627
+ * count or position changes.
1628
+ */
1629
+ interface ReceiverFacePlane {
1630
+ O: Vec3;
1631
+ n: Vec3;
1632
+ u: Vec3;
1633
+ v: Vec3;
1634
+ outlineUv: Array<[number, number]>;
1635
+ memberPolysUv: Array<Array<[number, number]>>;
1636
+ memberPolyIndices: number[];
1637
+ minU: number;
1638
+ minV: number;
1639
+ width: number;
1640
+ height: number;
1641
+ matrixCss: string;
1642
+ faceIndex: number;
1643
+ /** World-frame lift (already × BASE_TILE) along +n. Re-applied per-frame
1644
+ * when building a tight shadow SVG matrix so the SVG hovers over the face. */
1645
+ lift: number;
1205
1646
  }
1206
- interface ParseAnimationController {
1207
- /** Animation clips exposed by the parsed mesh. Empty when none are usable. */
1208
- clips: ParseAnimationClip[];
1209
- /**
1210
- * Sample a clip at `timeSeconds` and return a fresh polygon list.
1211
- * `clip` accepts either the clip index or its name. Time wraps by duration.
1212
- */
1213
- sample: (clip: number | string, timeSeconds: number) => Polygon[];
1647
+ /**
1648
+ * Per-caster cached per-polygon data: world-space vertices + 3D AABB
1649
+ * corners + caster-polygon plane normal/offset. Invariant under light
1650
+ * direction; depends only on the caster mesh's geometry and position.
1651
+ */
1652
+ interface CasterPolyItem {
1653
+ wv: Vec3[];
1654
+ bboxCorners: Vec3[];
1655
+ planeN: Vec3 | null;
1656
+ planeOffset: number;
1657
+ polygonIndex: number;
1658
+ }
1659
+ /** A caster mesh's prepared items paired with a stable identifier the caller
1660
+ * can use for per-path attribution. */
1661
+ interface ReceiverCasterInput<T = unknown> {
1662
+ /** Caller-defined identifier (e.g. mesh ref, mesh shadow id). Echoed back
1663
+ * on each emitted path so the renderer can map a subpath to its source
1664
+ * caster mesh. */
1665
+ id: T;
1666
+ items: CasterPolyItem[];
1667
+ /** Self-shadow edge adjacency. When this caster is the same mesh as the
1668
+ * receiver, the renderer pre-computes a map polygonIndex →
1669
+ * set-of-other-polygonIndex that share at least one edge (within
1670
+ * EDGE_MATCH_EPS). The shadow algorithm skips projecting `polygonIndex`
1671
+ * onto any receiver face whose member set intersects the shared-edge
1672
+ * set — those projections are sliver shadows along seams (smooth-shaded
1673
+ * GLB meshes, subdivided spheres) that the user never wants to see. */
1674
+ selfShadowEdgeMap?: ReadonlyMap<number, ReadonlySet<number>>;
1675
+ /** Per-mesh edge ownership map for silhouette extraction. Cached by the
1676
+ * caller (WeakMap<Mesh, …>) and shared across receivers within a frame.
1677
+ * When present AND the caster is NOT the receiver AND items.length ≥
1678
+ * SILHOUETTE_MIN_POLYS, the shadow algorithm projects per-mesh
1679
+ * silhouette loops instead of every front-facing triangle — collapses
1680
+ * the N-triangle path to 1 outline per caster per receiver. See H9 in
1681
+ * `bench/notes/SHADOW_PERF_LOG.md`. */
1682
+ edgeOwners?: ReadonlyMap<string, EdgeOwners>;
1683
+ /** Total polygon count on the source caster mesh (NOT the filtered
1684
+ * `items` count). Needed by silhouette extraction so the `facing`
1685
+ * array is sized correctly even when atlas-plan / dedup filters drop
1686
+ * some polygons from `items`. */
1687
+ casterPolygonCount?: number;
1214
1688
  }
1215
- interface ParseResult {
1216
- /** The mesh, as a flat polygon list. Already vertex-permuted to polycss space. */
1217
- polygons: Polygon[];
1218
- /** Optional raw voxel source for `.vox` fast paths; polygon fallback remains authoritative. */
1219
- voxelSource?: PolyVoxelSource;
1220
- /** Optional animation sampler for formats that carry timeline data. */
1221
- animation?: ParseAnimationController;
1222
- /**
1223
- * Blob/object URLs minted during parse (e.g. embedded GLB images). Pass-by-
1224
- * reference the same array is exposed on the result for visibility, and
1225
- * `dispose()` revokes each one. Do NOT mutate this array externally.
1226
- */
1227
- objectUrls: string[];
1228
- /**
1229
- * Idempotent revokes object URLs. Safe to call on unmount, safe to call
1230
- * twice. Parsers without minted URLs (parseObj, parseMtl) supply a no-op.
1231
- */
1232
- dispose: () => void;
1233
- /**
1234
- * Non-fatal warnings raised during parse. Empty for parsers that don't
1235
- * have a warning channel; populated when downstream `normalizePolygons`
1236
- * is invoked through the high-level pipeline.
1237
- */
1238
- warnings: string[];
1239
- /** Optional format-specific metadata. */
1240
- metadata?: {
1241
- /** Triangle count after fan-triangulation (parseObj) or post-triangulation (parseGltf). */
1242
- triangleCount?: number;
1243
- /** Mesh names from the file (for glTF, from doc.meshes[].name). */
1244
- meshes?: string[];
1245
- /** Material names (in first-seen order). */
1246
- materials?: string[];
1247
- /** Animation clips from the file, mirrored from `animation.clips`. */
1248
- animations?: ParseAnimationClip[];
1249
- /** Source file size in bytes (for diagnostics). */
1250
- sourceBytes?: number;
1251
- /** Voxel count for `.vox` sources. */
1252
- voxelCount?: number;
1689
+ /**
1690
+ * Build a polygon-adjacency map: polygonIndex set of polygonIndex that
1691
+ * share at least one edge (vertex pair, orientation-independent). Used by
1692
+ * the receiver-shadow algorithm to cull sliver shadows along mesh seams.
1693
+ *
1694
+ * Edge match tolerance is small enough to dedupe vertex coordinates that
1695
+ * went through `optimizeMeshPolygons` snap-to-plane but not so loose it
1696
+ * connects geometrically distinct polygons.
1697
+ */
1698
+ declare function buildSharedEdgeMap(polygons: readonly Polygon[]): Map<number, Set<number>>;
1699
+ /** One contributing caster's shadow subpath on a single receiver face. */
1700
+ interface ReceiverShadowPath<T = unknown> {
1701
+ /** Caster id echoed from the input. */
1702
+ casterId: T;
1703
+ /** Path data string: `M…L…Z` subpaths in face-local (u, v) coordinates
1704
+ * pre-translated so the SVG's `viewBox` is `0 0 width height`. */
1705
+ d: string;
1706
+ /** Source polygon indices on the caster mesh in subpath order (one per
1707
+ * M…L…Z block). Used for DevTools attribution. */
1708
+ casterPolygonIndices: number[];
1709
+ }
1710
+ /** Per-receiver-face shadow spec. Renderer mounts one SVG per spec. */
1711
+ interface ReceiverShadowFaceSpec<T = unknown> {
1712
+ /** Index into the prepared `ReceiverFacePlane[]` list. Stable across
1713
+ * frames; used as the `data-poly-shadow-receiver-face` attr. */
1714
+ faceIndex: number;
1715
+ /** Receiver polygon indices that make up this coplanar group. */
1716
+ memberPolyIndices: number[];
1717
+ /** matrix3d(...) transform that places the SVG on the face plane. */
1718
+ matrixCss: string;
1719
+ width: number;
1720
+ height: number;
1721
+ /** Fill (and stroke) color resolved per-face: textured receivers get the
1722
+ * user's `shadow.color`; solid receivers get their own ambient-only
1723
+ * shadePolygon for byte-exact Three.js parity. */
1724
+ fill: string;
1725
+ /** Per-face opacity (already accounts for textured-darken Lambert ratio
1726
+ * if applicable). */
1727
+ opacity: number;
1728
+ paths: Array<ReceiverShadowPath<T>>;
1729
+ }
1730
+ /**
1731
+ * Build silhouette `edgeOwners` for a caster mesh in world-CSS frame.
1732
+ * Used by the silhouette path inside `computeReceiverShadowFaces` (H9).
1733
+ * Polygons are transformed through the same world-CSS pipeline as
1734
+ * `prepareCasterPolyItems` (worldCssForMesh + optional rotation around
1735
+ * the CSS-pivot) so the silhouette loop vertices land in the same world
1736
+ * frame as the receiver face plane and the light direction.
1737
+ *
1738
+ * Caller caches by (mesh, polygon-list-identity + position + scale +
1739
+ * rotation) — invalidates only when the caster's geometry or transform
1740
+ * actually changes. Light direction is NOT a bust key (silhouette
1741
+ * adjacency is per-mesh, facing is per-frame).
1742
+ */
1743
+ declare function prepareCasterEdgeOwners(polygons: readonly Polygon[], position: Vec3, scale: number | Vec3 | undefined | null, rotation?: Vec3 | null): ReadonlyMap<string, EdgeOwners>;
1744
+ /**
1745
+ * Build CasterPolyItem[] for a caster mesh. Pure: same inputs → same output.
1746
+ * The caller memoizes by mesh ref + bust key. `includePolygonIndex(idx)`
1747
+ * decides which polygons participate (e.g. dedup drop + atlas-plan filter
1748
+ * in vanilla; just dedup drop in React/Vue without an atlas-plan concept).
1749
+ */
1750
+ declare function prepareCasterPolyItems(polygons: readonly Polygon[], position: Vec3, scale: number | Vec3 | undefined | null, includePolygonIndex: (polygonIndex: number) => boolean, rotation?: Vec3 | null): CasterPolyItem[];
1751
+ /**
1752
+ * Build ReceiverFacePlane[] for a receiver mesh. Pure: groups coplanar
1753
+ * polygons, computes (u,v) basis + outline, applies interior occlusion cull
1754
+ * (drops face planes hidden behind a parallel face plane within wall-
1755
+ * thickness range).
1756
+ *
1757
+ * `shadowLift` is the world-unit lift applied along each face normal so the
1758
+ * shadow SVG composites above the surface without z-fighting (matches the
1759
+ * ground-shadow `shadow.lift` option).
1760
+ */
1761
+ declare function prepareReceiverFacePlanes(polygons: readonly Polygon[], position: Vec3, scale: number | Vec3 | undefined | null, dedupDrop: ReadonlySet<number>, shadowLift: number, rotation?: Vec3 | null): ReceiverFacePlane[];
1762
+ /** Input for `computeReceiverShadowFaces`. */
1763
+ interface ComputeReceiverShadowFacesInput<T = unknown> {
1764
+ /** Precomputed face planes from `prepareReceiverFacePlanes`. */
1765
+ receiverPlanes: ReceiverFacePlane[];
1766
+ /** Receiver's polygon list, used to look up per-face fill color. */
1767
+ receiverPolygons: readonly Polygon[];
1768
+ /** Whether the receiver mesh has any textured polygons. Drives the
1769
+ * textured-darken opacity calc vs solid-replace fill color. */
1770
+ receiverHasTexture: boolean;
1771
+ /** Per-caster items + caller id, in caller-defined order. */
1772
+ casters: ReceiverCasterInput<T>[];
1773
+ /** Directional light vector in CSS frame. */
1774
+ lightDir: Vec3;
1775
+ /** Camera cull rotation (rotX/rotY + receiver mesh rotation) so back-
1776
+ * facing receiver faces can be skipped. */
1777
+ cameraRot: CameraCullRotation;
1778
+ /** Ambient light (used for solid-receiver shadow tint via shadePolygon). */
1779
+ ambientLight?: PolyAmbientLight;
1780
+ /** Directional light (used for textured-darken opacity calc). */
1781
+ directionalLight?: PolyDirectionalLight;
1782
+ /** Scene shadow options. */
1783
+ shadow?: {
1784
+ color?: string;
1785
+ opacity?: number;
1786
+ maxExtend?: number;
1253
1787
  };
1254
1788
  }
1789
+ /**
1790
+ * The pure per-frame algorithm. Returns one ReceiverShadowFaceSpec for each
1791
+ * visible receiver face that catches at least one caster's shadow. Skips
1792
+ * back-facing faces. Caller mounts SVGs per spec.
1793
+ */
1794
+ declare function computeReceiverShadowFaces<T = unknown>(input: ComputeReceiverShadowFacesInput<T>): ReceiverShadowFaceSpec<T>[];
1255
1795
 
1256
1796
  /**
1257
1797
  * PolyAnimationMixer — three.js-shaped animation API for polycss.
@@ -1370,10 +1910,20 @@ interface ObjParseOptions {
1370
1910
  */
1371
1911
  targetSize?: number;
1372
1912
  /**
1373
- * Padding added to the bbox of every emitted polygon so they don't land
1374
- * at coordinate "0". Default: 1.
1913
+ * Where to place the mesh-local origin relative to the parsed geometry.
1914
+ *
1915
+ * - `"min"` (default): bbox-min sits at local (0,0,0); geometry lives in
1916
+ * the +X+Y+Z quadrant. This is PolyCSS's historical behavior.
1917
+ * - `true` (or `"center"`): bbox-center sits at local (0,0,0); geometry
1918
+ * is centered around the origin. Pair with `scene.add(parse, {position,
1919
+ * rotation:[...]})` to get three.js-style rotate-in-place around the
1920
+ * centroid.
1921
+ *
1922
+ * Three.js's `GLTFLoader`/`OBJLoader` don't reposition vertices at all;
1923
+ * for byte-parity loading set this to a separate explicit `false` once
1924
+ * the no-offset option lands.
1375
1925
  */
1376
- gridShift?: number;
1926
+ center?: boolean | "min" | "center";
1377
1927
  /**
1378
1928
  * Color used for faces that have no `usemtl` in scope, or whose material
1379
1929
  * name doesn't resolve via `materialColors`. Default: "#888888".
@@ -1441,8 +1991,6 @@ declare function parseMtl(text: string): MtlParseResult;
1441
1991
  interface GltfParseOptions {
1442
1992
  /** Largest mesh extent (units). Mesh is uniformly scaled to fit. Default 60. */
1443
1993
  targetSize?: number;
1444
- /** Padding offset (avoids coordinate "0"). Default 1. */
1445
- gridShift?: number;
1446
1994
  /** Color used when a primitive has no material or no baseColorFactor. */
1447
1995
  defaultColor?: string;
1448
1996
  /**
@@ -1465,6 +2013,17 @@ interface GltfParseOptions {
1465
2013
  * standing.
1466
2014
  */
1467
2015
  upAxis?: "y" | "z";
2016
+ /**
2017
+ * Where to place the mesh-local origin relative to the parsed geometry.
2018
+ *
2019
+ * - `"min"` (default): bbox-min sits at local (0,0,0); geometry lives in
2020
+ * the +X+Y+Z quadrant. This is PolyCSS's historical behavior.
2021
+ * - `true` (or `"center"`): bbox-center sits at local (0,0,0); geometry
2022
+ * is centered around the origin. Pair with `scene.add(parse, {position,
2023
+ * rotation:[...]})` to get three.js-style rotate-in-place around the
2024
+ * centroid.
2025
+ */
2026
+ center?: boolean | "min" | "center";
1468
2027
  /**
1469
2028
  * For .gltf (non-binary) — resolve a glTF buffer URI to its bytes. The
1470
2029
  * built-in parser handles GLB binary chunks natively; .gltf files with
@@ -1476,7 +2035,7 @@ interface GltfParseOptions {
1476
2035
  * (`doc.images[i].uri = "Textures/foo.png"`) against the GLB/glTF's
1477
2036
  * location. Without this, relative URIs would resolve against the page,
1478
2037
  * which 404s. Pass the same URL you fetched the file from.
1479
- */
2038
+ */
1480
2039
  baseUrl?: string;
1481
2040
  }
1482
2041
  declare function parseGltf(input: ArrayBuffer | Uint8Array, options?: GltfParseOptions): ParseResult;
@@ -1502,12 +2061,6 @@ interface VoxParseOptions {
1502
2061
  * fast-path coordinates integral. Default: 60.
1503
2062
  */
1504
2063
  targetSize?: number;
1505
- /**
1506
- * Per-coordinate offset added after scaling. Keeps coordinates away from
1507
- * zero (matching OBJ/glTF parsers). Default: 0 — vox already starts at
1508
- * non-negative integers so zero makes sensible default.
1509
- */
1510
- gridShift?: number;
1511
2064
  /**
1512
2065
  * Optional lossy palette simplification. When > 0, opaque, hue-compatible
1513
2066
  * palette colors within this RGB distance are folded into the most-used
@@ -1524,6 +2077,22 @@ interface VoxParseOptions {
1524
2077
  }
1525
2078
  declare function parseVox(buffer: ArrayBuffer, options?: VoxParseOptions): ParseResult;
1526
2079
 
2080
+ interface StlParseOptions {
2081
+ /** Largest mesh extent (units). Mesh is uniformly scaled to fit. Default: 60. */
2082
+ targetSize?: number;
2083
+ /** Padding offset added after scaling. Default: 1. */
2084
+ gridShift?: number;
2085
+ /** Solid color assigned to every STL triangle. Default: "#888888". */
2086
+ defaultColor?: string;
2087
+ /**
2088
+ * Which axis is "up" in the source mesh.
2089
+ * - "z" (default): identity axes, matching common CAD/3D-print STL exports.
2090
+ * - "y": cyclic permutation (x,y,z) → (z,x,y), matching OBJ/glTF's +Y-up path.
2091
+ */
2092
+ upAxis?: "z" | "y";
2093
+ }
2094
+ declare function parseStl(source: ArrayBuffer | Uint8Array | string, options?: StlParseOptions): ParseResult;
2095
+
1527
2096
  /**
1528
2097
  * loadMesh — high-level fetch+parse dispatcher. Picks the parser by file
1529
2098
  * extension, fetches the URL, runs the parser, returns the unified
@@ -1534,11 +2103,12 @@ declare function parseVox(buffer: ArrayBuffer, options?: VoxParseOptions): Parse
1534
2103
  * - `.glb` → ArrayBuffer fetch + `parseGltf`
1535
2104
  * - `.gltf` → ArrayBuffer fetch + `parseGltf` (caller may pass `baseUrl`)
1536
2105
  * - `.vox` → ArrayBuffer fetch + `parseVox`
2106
+ * - `.stl` → ArrayBuffer fetch + `parseStl`
1537
2107
  *
1538
2108
  * `.mtl` is rejected — it's a material file, not a mesh. Use `parseMtl`
1539
2109
  * directly if you want to read materials.
1540
2110
  *
1541
- * Other extensions throw. Future formats (STL, PLY) plug in here.
2111
+ * Other extensions throw. Future formats (PLY, 3MF) plug in here.
1542
2112
  */
1543
2113
 
1544
2114
  interface LoadMeshOptions {
@@ -1562,6 +2132,8 @@ interface LoadMeshOptions {
1562
2132
  gltfOptions?: GltfParseOptions;
1563
2133
  /** Forwarded to `parseVox`. */
1564
2134
  voxOptions?: VoxParseOptions;
2135
+ /** Forwarded to `parseStl`. */
2136
+ stlOptions?: StlParseOptions;
1565
2137
  /**
1566
2138
  * Converts texture-backed faces whose UV samples are a uniform color into
1567
2139
  * solid-color polygons before culling/merging. This avoids atlas sprites for
@@ -1570,7 +2142,8 @@ interface LoadMeshOptions {
1570
2142
  solidTextureSamples?: boolean | SolidTextureSampleOptions;
1571
2143
  /**
1572
2144
  * Mesh optimization intent. Defaults to "lossy"; set "lossless" to keep
1573
- * exact planar candidates only.
2145
+ * exact planar candidates only. STL imports use the conservative lossless
2146
+ * optimizer path in both modes.
1574
2147
  */
1575
2148
  meshResolution?: MeshResolution;
1576
2149
  }
@@ -1611,8 +2184,6 @@ declare const DECIMAL_SCALES: number[];
1611
2184
  declare const SOLID_QUAD_CANONICAL_SIZE = 64;
1612
2185
  declare const SOLID_TRIANGLE_CANONICAL_SIZE = 32;
1613
2186
  declare const SOLID_TRIANGLE_LARGE_BORDER_CANONICAL_SIZE = 96;
1614
- declare const SOLID_TRIANGLE_CORNER_CLASS = "polycss-corner-triangle";
1615
- declare const SOLID_TRIANGLE_LARGE_BORDER_CLASS = "polycss-large-border-triangle";
1616
2187
  declare const ATLAS_CANONICAL_SIZE_EXPLICIT = 64;
1617
2188
  declare const ATLAS_CANONICAL_SIZE_AUTO_DESKTOP = 128;
1618
2189
  declare const BORDER_SHAPE_CENTER_PERCENT = 50;
@@ -1625,6 +2196,10 @@ declare const PROJECTIVE_QUAD_DENOM_EPS = 0.05;
1625
2196
  declare const PROJECTIVE_QUAD_MAX_WEIGHT_RATIO = 256;
1626
2197
  declare const PROJECTIVE_QUAD_BLEED = 0.6;
1627
2198
  declare const DEFAULT_SEAM_BLEED = 1.5;
2199
+ /** Clamp the `seamBleed` ratio. `undefined` → 1 (full default), 0 → no
2200
+ * bleed, values outside [0,1] are clamped. Single source of truth for
2201
+ * how the public ratio maps to per-strategy bleed multipliers. */
2202
+ declare function resolveBleedRatio(seamBleed: number | "auto" | undefined): number;
1628
2203
 
1629
2204
  interface RGB {
1630
2205
  r: number;
@@ -1678,6 +2253,12 @@ interface TextureAtlasPlan {
1678
2253
  seamBleedEdges?: Set<number>;
1679
2254
  seamBleedEdgeAmounts?: Map<number, number>;
1680
2255
  seamBleedInsets?: SeamBleedInsets;
2256
+ /** Resolved per-strategy bleed multiplier (0..1, default 1). Populated
2257
+ * at plan construction from `options.seamBleed` via `resolveBleedRatio`.
2258
+ * Downstream emitters (borderShapeGeometryForPlan, projective-quad
2259
+ * rasteriser, etc.) read this and multiply their hardcoded per-strategy
2260
+ * bleed constants by it. Single knob for "scale down all my bleeds". */
2261
+ bleedRatio?: number;
1681
2262
  /** World-space surface normal — stable across light changes, used by dynamic mode. */
1682
2263
  normal: Vec3;
1683
2264
  textureTint: RGBFactors;
@@ -1876,6 +2457,20 @@ interface SolidTrianglePlanOptions {
1876
2457
  strategies?: PolyRenderStrategiesOption;
1877
2458
  seamBleed?: PolySeamBleed;
1878
2459
  seamEdges?: Set<number>;
2460
+ /** Per-strategy bleed multiplier (0..1, default 1). Scales the
2461
+ * hardcoded SOLID_TRIANGLE_BLEED used as the seamBleed fallback when
2462
+ * no shared-edge bleed is present. Populated upstream from
2463
+ * `resolveBleedRatio(publicOptions.seamBleed)`. */
2464
+ bleedRatio?: number;
2465
+ /**
2466
+ * Indices (into the polygon array being planned) of polygons that the
2467
+ * directional light cannot physically reach because another polygon of
2468
+ * the same mesh is between them and the light source. Per-polygon
2469
+ * directScale is forced to 0 for indices in this set, so they receive
2470
+ * ambient lighting only — matching what a shadow-map-equivalent pass
2471
+ * would produce.
2472
+ */
2473
+ lightOccludedPolyIndices?: ReadonlySet<number>;
1879
2474
  }
1880
2475
  /** Internal solid-triangle plan options (extends SolidTrianglePlanOptions). */
1881
2476
  interface InternalSolidTrianglePlanOptions extends SolidTrianglePlanOptions {
@@ -1893,6 +2488,13 @@ interface ComputeTextureAtlasPlanOptions {
1893
2488
  textureEdgeRepairEdges?: Set<number>;
1894
2489
  seamBleed?: PolySeamBleed;
1895
2490
  seamEdges?: Set<number>;
2491
+ /** Indices of polygons that the directional light cannot reach because
2492
+ * another polygon of the same mesh occludes them (precomputed via
2493
+ * {@link import("../cull/lightVisibility").computeLightVisibility}). When
2494
+ * `index ∈ lightOccludedPolyIndices`, the polygon's direct lighting term
2495
+ * is forced to zero and only ambient remains. Matches the vanilla
2496
+ * renderer's self-shadow path. */
2497
+ lightOccludedPolyIndices?: ReadonlySet<number>;
1896
2498
  }
1897
2499
 
1898
2500
  declare function roundDecimal(value: number, decimals: number): string;
@@ -1912,7 +2514,7 @@ declare function formatMatrix3d(matrix: string, decimals?: number): string;
1912
2514
  declare function formatCssLengthPx(value: number, decimals?: number): string;
1913
2515
  /**
1914
2516
  * Produce the CSS matrix3d transform for a solid-quad (`<b>`) leaf, including
1915
- * the canonical 64px primitive scale.
2517
+ * the canonical primitive scale.
1916
2518
  */
1917
2519
  declare function formatSolidQuadEntryMatrix(entry: TextureAtlasPlan): string;
1918
2520
 
@@ -1939,6 +2541,20 @@ declare function rgbKey({ r, g, b }: RGB): string;
1939
2541
  /** Returns the parsed alpha for a color string (1.0 default). */
1940
2542
  declare function parseAlpha(input: string): number;
1941
2543
  declare function rgbToHex({ r, g, b }: RGB): string;
2544
+ /**
2545
+ * Tint factors for a textured polygon, in LINEAR light space.
2546
+ *
2547
+ * Returns the per-channel multiplier that the rasterizer should apply to the
2548
+ * texture pixels' LINEAR values — matching Three.js MeshLambertMaterial:
2549
+ * lit_linear = albedo_linear × tint
2550
+ * tint = (lightColor × lambert × I + ambientColor × I_amb) / π
2551
+ *
2552
+ * Light + ambient colors are interpreted as sRGB and converted to linear.
2553
+ * The rasterizer is responsible for decoding the texture sample from sRGB
2554
+ * to linear before multiplying by these factors, then re-encoding for paint
2555
+ * (see applyTextureTint in the renderer). `directScale` is already
2556
+ * `intensity × max(n·L, 0)` (computed by the caller).
2557
+ */
1942
2558
  declare function textureTintFactors(directScale: number, lightColor: string, ambientColor: string, ambientIntensity: number): RGBFactors;
1943
2559
  declare function tintToCss({ r, g, b }: RGBFactors): string;
1944
2560
  declare function shadePolygon(baseColor: string, directScale: number, lightColor: string, ambientColor: string, ambientIntensity: number): string;
@@ -1964,7 +2580,14 @@ declare function incrementCount(map: Map<string, number>, key: string): void;
1964
2580
  declare function dominantCountKey(map: Map<string, number>): string | undefined;
1965
2581
  interface FilterAtlasPlansEnv {
1966
2582
  solidTriangleSupported: boolean;
2583
+ projectiveQuadSupported: boolean;
1967
2584
  borderShapeSupported: boolean;
2585
+ /** When true, non-triangle non-rect non-projective polys whose plan has
2586
+ * cornerShapeGeometryForPlan != null are excluded from the atlas (they
2587
+ * render as <u> via corner-*-shape: bevel CSS — matches vanilla's
2588
+ * createCornerShapeSolidElement path). Falsy / undefined preserves the
2589
+ * earlier core behaviour (those polys stay in atlas as <s> fallback). */
2590
+ cornerShapeSupported?: boolean;
1968
2591
  }
1969
2592
  /**
1970
2593
  * Filter a plan array to the subset that needs atlas packing, given the active
@@ -2000,6 +2623,9 @@ declare function stableTriangleMatrixDecimals(matrixDecimals: number | undefined
2000
2623
 
2001
2624
  declare function polygonContainsPoint(points: Array<[number, number]>, px?: number, py?: number): boolean;
2002
2625
  declare function borderShapeBoundsFromPoints(points: number[], fallbackWidth: number, fallbackHeight: number): BorderShapeBounds;
2626
+ /** Reads `entry.bleedRatio` (defaulted to 1) and scales BORDER_SHAPE_BLEED
2627
+ * accordingly. Plans are tagged with the ratio at construction time
2628
+ * (see computeTextureAtlasPlan) so every consumer gets the same value. */
2003
2629
  declare function borderShapeGeometryForPlan(entry: TextureAtlasPlan): BorderShapeGeometry;
2004
2630
  declare function simplifyCornerShapePoints(points: Array<[number, number]>): Array<[number, number]>;
2005
2631
  declare function cornerShapePointSides([x, y]: [number, number]): Set<CornerShapeSide> | null;
@@ -2058,7 +2684,12 @@ declare function computeTextureAtlasPlan(polygon: Polygon, index: number, option
2058
2684
  * Callers that have a Document should extract it before calling; callers in
2059
2685
  * browser-free environments can pass `undefined` for the default guards.
2060
2686
  */
2061
- declare function computeTextureAtlasPlanPublic(polygon: Polygon, index: number, options?: ComputeTextureAtlasPlanOptions, projectiveQuadOverrides?: ProjectiveQuadGuardOverrides): TextureAtlasPlan | null;
2687
+ declare function computeTextureAtlasPlanPublic(polygon: Polygon, index: number, options?: ComputeTextureAtlasPlanOptions, projectiveQuadOverrides?: ProjectiveQuadGuardOverrides,
2688
+ /** Cross-polygon basis hint pre-computed via {@link buildBasisHints} on
2689
+ * the full polygon array. When supplied, it overrides the per-polygon
2690
+ * textureEdgeRepairEdges fallback below. Vanilla's renderer always passes
2691
+ * this from {@link buildBasisHints}; React/Vue mirror that path. */
2692
+ basisHintOverride?: BasisHint): TextureAtlasPlan | null;
2062
2693
 
2063
2694
  declare function normalizeAtlasScale(scale: number | string | undefined): number;
2064
2695
  declare function atlasArea(pages: PackedPage[]): number;
@@ -2085,4 +2716,4 @@ declare function packTextureAtlasPlansWithScaleCore(plans: Array<TextureAtlasPla
2085
2716
  atlasCanonicalSize: number;
2086
2717
  };
2087
2718
 
2088
- 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 AutoRotateConfig, type AutoRotateOption, type AxesHelperOptions, BAKED_SHADOW_MIN_UP, BAKED_SHADOW_Z_SQUASH, BASE_TILE, 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, type CameraHandle, type CameraState, type CameraStyleInput, 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_CAMERA_STATE, DEFAULT_LIGHT_COLOR, DEFAULT_LIGHT_DIR, DEFAULT_LIGHT_INTENSITY, DEFAULT_MATRIX_DECIMALS, DEFAULT_PROJECTION, DEFAULT_SEAM_BLEED, DEFAULT_SEAM_FACET_SPLIT_OPTIONS, DEFAULT_SEAM_OVERLAP_OPTIONS, DEFAULT_TILE, type DedupeOverlappingPolygonsOptions, type DodecahedronPolygonsOptions, 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 MeshResolution, type MtlParseResult, type NormalizeResult, type ObjParseOptions, type OctahedronPolygonsOptions, type OptimizeAnimatedMeshPolygonsOptions, 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 ParseAnimationClip, type ParseAnimationController, type ParseResult, type ParsedColor, type PlanePolygonsOptions, type PolyAmbientLight, type PolyAnimationAction, type ParseAnimationClip as PolyAnimationClip, type PolyAnimationMixer, type PolyAnimationTarget, type PolyDirectionalLight, type PolyMaterial, type PolyRenderStrategiesOption, type PolyRenderStrategy, type PolySeamBleed, type PolySeamBleedEdgeValue, type PolySeamBleedEdges, type PolyTextureAlphaMode, type PolyTextureLightingMode, type PolyTextureWrap, type PolyTextureWrapMode, type PolyVoxelCell, type PolyVoxelSource, type Polygon, type PolygonBasisInfo, type PolygonFace, type ProjectiveQuadCoefficients, type ProjectiveQuadGuardGlobal, type ProjectiveQuadGuardOverrides, type ProjectiveQuadGuardSettings, QUAT_IDENTITY, type Quat, RECT_EPS, type RGB, type RGBFactors, type RectBrush, type RingPolygonsOptions, type RingQuadPolygonsOptions, SEAM_LIGHT_EPS, SOLID_QUAD_CANONICAL_SIZE, SOLID_TRIANGLE_BLEED, SOLID_TRIANGLE_CANONICAL_SIZE, SOLID_TRIANGLE_CORNER_CLASS, SOLID_TRIANGLE_LARGE_BORDER_CANONICAL_SIZE, SOLID_TRIANGLE_LARGE_BORDER_CLASS, SURFACE_DISTANCE_EPS, SURFACE_NORMAL_EPS, type SceneBbox, type SceneContext, type SceneContextBuildArgs, type SceneContextBuildResult, type SeamBleedInsets, type SeamFacetSplitCandidate, type SeamFacetSplitCandidateReason, type SeamFacetSplitOptions, type SeamFacetSplitReport, type SeamOverlapCandidate, type SeamOverlapCandidateKind, type SeamOverlapDiagnostics, type SeamOverlapOptions, type SolidPaintDefaults, type SolidTextureSampleOptions, type SolidTriangleBasis, type SolidTriangleColorPlan, type SolidTriangleComputeOptions, type SolidTriangleFrame, type SolidTrianglePlan, type SolidTrianglePlanOptions, type SolidTrianglePrimitive, type SpherePolygonsOptions, type StablePlanBasis, type StableTriangleColorState, 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, type TextureTriangle, type TextureTrianglePlan, type TorusPolygonsOptions, type UvAffine, type UvSampleRect, VOXEL_CAMERA_CULL_AXIS_EPS, VOXEL_CAMERA_CULL_NORMAL_LIMIT, type Vec2, type Vec3, type VoxParseOptions, applyPackedAtlasCanonicalSize, arrowPolygons, atlasArea, atlasBitmapMaxSide, atlasCanonicalSizeForEntry, atlasCanonicalSizeForTextureQuality, atlasDecodedBytes, atlasPadding, autoAtlasBudgetFactor, autoAtlasMaxDecodedBytes, autoAtlasScale, autoAtlasScaleCap, axesHelperPolygons, bakeSolidTextureSampledPolygons, bakeSolidTextureSamples, basisAxisKey, borderShapeBoundsFromPoints, borderShapeGeometryForPlan, boxPolygons, buildBakedShadowProjectionMatrix, buildBasisHints, buildSceneContext, buildSeamBleedPolygonEdges, buildSeamBleedPolygonSet, buildTextureEdgeRepairSets, cachedParsePureColor, cameraCullNormalGroups, cameraCullNormalGroupsFromPolygons, cameraCullNormalKey, cameraCullVisibleSignature, cameraFacingDepth, chooseIslandXAxis, chooseLocalBasis, clampChannel, clipPolygonToConvex2D, colorErrorScore, compatibleBleedSurface, compatibleSurface, computePlanSeamBleedEdgeAmounts, computeProjectiveQuadCoefficients, computeProjectiveQuadMatrix, computeSceneBbox, computeSeamBleedInsets, computeShapeLighting, computeSolidTriangleColorPlan, computeSolidTriangleColorPlanFromNormal, computeSolidTrianglePlan, computeSolidTrianglePlanFromCssPoints, computeSurfaceNormal, computeTextureAtlasPlan, computeTextureAtlasPlanPublic, computeTexturePaintMetrics, computeUvAffine, computeUvSampleRect, conePolygons, convexHull2D, cornerShapeDiagonal, cornerShapeGeometryForPlan, cornerShapePointSides, coverPlanarPolygons, createIsometricCamera, createPolyAnimationMixer, crossVec, cssBorderShapeForGeometry, cssBorderShapeForPlan, cssPoints, cullInteriorPolygons, cylinderPolygons, dedupeOverlappingPolygons, dodecahedronPolygons, dominantCountKey, dotVec, ensureCcw2D, eulerXYZFromQuat, evaluateIslandAxis, expandClipPoints, filterAtlasPlans, findOverlappingPolygonDuplicates, formatAffineMatrix3dColumns, formatAffineMatrix3dScalars, formatAffineMatrix3dTransformScalars, formatAtlasMatrix, formatBorderShapeElementStyle, formatBorderShapeEntryMatrix, formatBorderShapeMatrix, formatColor, formatCornerShapeElementStyle, formatCssLength, formatCssLengthPx, formatMatrix3d, formatMatrix3dValues, formatPercent, formatScaledMatrixFromPlan, formatSolidQuadEntryMatrix, formatSolidQuadMatrix, fullRectBounds, getPolygonBasisInfo, getSolidPaintDefaultsForPlansCore, icosahedronPolygons, incrementCount, intersect2DLines, intersect2DLinesRaw, inverseRotateVec3, isAxisAlignedSurfaceNormal, isBakedShadowCaster, isBasisOptimizable, isConvexPolygonPoints, isFullRectBasis, isFullRectSolid, isProjectiveQuadPlan, isSolidTrianglePlan, isVoxelCameraCullableNormalGroups, loadMesh, makeLocalBasis, mergePolygons, normalFacesCamera, normalizeAtlasScale, normalizeInvertMultiplier, normalizePolygons, normalizedSeamBleed, octahedronPolygons, offsetConvexPolygonPoints, offsetConvexPolygonPointsByEdgeAmounts, offsetStableTrianglePoints, offsetTrianglePoints, optimizeAnimatedMeshPolygons, optimizeMeshPolygons, packTextureAtlasPlans, packTextureAtlasPlansWithScaleCore, parseAlpha, parseColor, parseGltf, parseHex, parseHexColor, parseMtl, parseObj, parsePureColor, parseRgbColor, parseVox, planePolygons, polygonContainsPoint, polygonCssSurfaceNormal, polygonFaces, polygonFacesCamera, polygonSignedArea2D, projectCssVertexToGround, projectTextureTriangle, quantizeCssColor, quatFromAxisAngle, quatFromEulerXYZ, quatMultiply, repairMeshSeams, resolveProjectiveQuadGuards, resolveSeamBleed, rgbEqual, rgbKey, rgbToCss, rgbToHex, ringPolygons, ringQuadPolygons, rotateVec3, roundDecimal, safariCssProjectiveUnsupported, safePlanSeamBleedAmount, seamBleedAmountArray, seamFacetSplitPolygons, seamFacetSplitReport, seamLightBrightness, seamOverlapDiagnostics, seamOverlapPolygons, seamOverlapReport, shadeColor, shadePolygon, sharedCornerShapeSide, signedArea2D, simplifyCornerShapePoints, spherePolygons, stableBasisFromPlan, stableTriangleMatrixDecimals, stepRgbToward, tetrahedronPolygons, textureTintFactors, tintToCss, torusPolygons };
2719
+ 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 AutoRotateConfig, type AutoRotateOption, type AxesHelperOptions, BAKED_SHADOW_MIN_UP, BAKED_SHADOW_Z_SQUASH, BASE_TILE, 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, type CameraHandle, type CameraState, type CameraStyleInput, 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_CAMERA_STATE, DEFAULT_LIGHT_COLOR, DEFAULT_LIGHT_DIR, DEFAULT_LIGHT_INTENSITY, DEFAULT_MATRIX_DECIMALS, DEFAULT_PROJECTION, 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 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 ParseAnimationClip, type ParseAnimationController, type ParseResult, type ParseStlColor, type ParseStlSolid, type ParseStlTopology, type ParsedColor, type PlanePolygonsOptions, type PolyAmbientLight, type PolyAnimationAction, type ParseAnimationClip as PolyAnimationClip, type PolyAnimationMixer, type PolyAnimationTarget, type PolyDirectionalLight, type PolyMaterial, type PolyMeshTransformInput, type PolyRenderStrategiesOption, type PolyRenderStrategy, type PolySeamBleed, type PolySeamBleedEdgeValue, type PolySeamBleedEdges, type PolyTextureAlphaMode, type PolyTextureLightingMode, type PolyTextureWrap, type PolyTextureWrapMode, type PolyVoxelCell, type PolyVoxelSource, type 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, type TextureTriangle, type TextureTrianglePlan, type TorusPolygonsOptions, type UvAffine, type UvSampleRect, VOXEL_CAMERA_CULL_AXIS_EPS, VOXEL_CAMERA_CULL_NORMAL_LIMIT, type Vec2, type Vec3, type VoxParseOptions, applyPackedAtlasCanonicalSize, arrowPolygons, atlasArea, atlasBitmapMaxSide, atlasCanonicalSizeForEntry, atlasCanonicalSizeForTextureQuality, atlasDecodedBytes, atlasPadding, autoAtlasBudgetFactor, autoAtlasMaxDecodedBytes, autoAtlasScale, autoAtlasScaleCap, axesHelperPolygons, bakeSolidTextureSampledPolygons, bakeSolidTextureSamples, basisAxisKey, borderShapeBoundsFromPoints, borderShapeGeometryForPlan, boxPolygons, buildBakedShadowProjectionMatrix, buildBasisHints, buildEdgeOwners, buildPolyMeshTransform, buildSceneContext, buildSeamBleedPolygonEdges, buildSeamBleedPolygonSet, buildSharedEdgeMap, buildTextureEdgeRepairSets, cachedParsePureColor, cameraCullNormalGroups, cameraCullNormalGroupsFromPolygons, cameraCullNormalKey, cameraCullVisibleSignature, cameraFacingDepth, chooseIslandXAxis, chooseLocalBasis, clampChannel, classifyFacing, clipPolygonToConvex2D, colorErrorScore, compatibleBleedSurface, compatibleSurface, computeLightVisibility, computePlanSeamBleedEdgeAmounts, computeProjectiveQuadCoefficients, computeProjectiveQuadMatrix, computeReceiverShadowFaces, computeSceneBbox, computeSeamBleedInsets, computeShapeLighting, computeSolidTriangleColorPlan, computeSolidTriangleColorPlanFromNormal, computeSolidTrianglePlan, computeSolidTrianglePlanFromCssPoints, computeSurfaceNormal, computeTextureAtlasPlan, computeTextureAtlasPlanPublic, computeTexturePaintMetrics, computeUvAffine, computeUvSampleRect, conePolygons, convexHull2D, cornerShapeDiagonal, cornerShapeGeometryForPlan, cornerShapePointSides, coverPlanarPolygons, createIsometricCamera, createPolyAnimationMixer, crossVec, cssBorderShapeForGeometry, cssBorderShapeForPlan, cssPoints, 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, isConvexPolygonPoints, isFullRectBasis, isFullRectSolid, isProjectiveQuadPlan, isSolidTrianglePlan, isVoxelCameraCullableNormalGroups, loadMesh, makeLocalBasis, mergePolygons, meshScaleVec3, normalFacesCamera, normalizeAtlasScale, normalizeInvertMultiplier, normalizePolygons, normalizedSeamBleed, octahedronPolygons, offsetConvexPolygonPoints, offsetConvexPolygonPointsByEdgeAmounts, offsetStableTrianglePoints, offsetTrianglePoints, optimizeAnimatedMeshPolygons, optimizeMeshParseResult, optimizeMeshPolygons, packTextureAtlasPlans, packTextureAtlasPlansWithScaleCore, parseAlpha, parseColor, parseGltf, parseHex, parseHexColor, parseMtl, parseObj, parsePureColor, parseRgbColor, parseStl, parseVox, planePolygons, polygonContainsPoint, polygonCssSurfaceNormal, polygonFaces, polygonFacesCamera, polygonSignedArea2D, prepareCasterEdgeOwners, prepareCasterPolyItems, prepareReceiverFacePlanes, projectCssVertexToGround, projectTextureTriangle, quantizeCssColor, quatFromAxisAngle, quatFromEulerXYZ, quatMultiply, repairMeshSeams, resolveBleedRatio, 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, worldDirectionToCss as worldDirectionToPolyCss, worldDirectionalLightToCss, worldPositionToCss, worldPositionToCss as worldPositionToPolyCss };