@layoutit/polycss-react 0.2.1 → 0.2.2

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.cjs CHANGED
@@ -20,15 +20,15 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var src_exports = {};
22
22
  __export(src_exports, {
23
- BASE_TILE: () => import_polycss_core24.BASE_TILE,
24
- CAMERA_BACKFACE_CULL_EPS: () => import_polycss_core24.CAMERA_BACKFACE_CULL_EPS,
25
- DEFAULT_CAMERA_STATE: () => import_polycss_core24.DEFAULT_CAMERA_STATE,
26
- DEFAULT_PROJECTION: () => import_polycss_core24.DEFAULT_PROJECTION,
27
- DEFAULT_SEAM_FACET_SPLIT_OPTIONS: () => import_polycss_core24.DEFAULT_SEAM_FACET_SPLIT_OPTIONS,
28
- DEFAULT_SEAM_OVERLAP_OPTIONS: () => import_polycss_core24.DEFAULT_SEAM_OVERLAP_OPTIONS,
29
- LoopOnce: () => import_polycss_core24.LoopOnce,
30
- LoopPingPong: () => import_polycss_core24.LoopPingPong,
31
- LoopRepeat: () => import_polycss_core24.LoopRepeat,
23
+ BASE_TILE: () => import_polycss_core27.BASE_TILE,
24
+ CAMERA_BACKFACE_CULL_EPS: () => import_polycss_core27.CAMERA_BACKFACE_CULL_EPS,
25
+ DEFAULT_CAMERA_STATE: () => import_polycss_core27.DEFAULT_CAMERA_STATE,
26
+ DEFAULT_PROJECTION: () => import_polycss_core27.DEFAULT_PROJECTION,
27
+ DEFAULT_SEAM_FACET_SPLIT_OPTIONS: () => import_polycss_core27.DEFAULT_SEAM_FACET_SPLIT_OPTIONS,
28
+ DEFAULT_SEAM_OVERLAP_OPTIONS: () => import_polycss_core27.DEFAULT_SEAM_OVERLAP_OPTIONS,
29
+ LoopOnce: () => import_polycss_core27.LoopOnce,
30
+ LoopPingPong: () => import_polycss_core27.LoopPingPong,
31
+ LoopRepeat: () => import_polycss_core27.LoopRepeat,
32
32
  Poly: () => Poly,
33
33
  PolyAxesHelper: () => PolyAxesHelper,
34
34
  PolyBox: () => PolyBox,
@@ -41,6 +41,7 @@ __export(src_exports, {
41
41
  PolyFirstPersonControls: () => PolyFirstPersonControls,
42
42
  PolyGround: () => PolyGround,
43
43
  PolyIcosahedron: () => PolyIcosahedron,
44
+ PolyIframe: () => PolyIframe,
44
45
  PolyMapControls: () => PolyMapControls,
45
46
  PolyMesh: () => PolyMesh,
46
47
  PolyOctahedron: () => PolyOctahedron,
@@ -55,71 +56,74 @@ __export(src_exports, {
55
56
  PolyTetrahedron: () => PolyTetrahedron,
56
57
  PolyTorus: () => PolyTorus,
57
58
  PolyTransformControls: () => PolyTransformControls,
58
- VOXEL_CAMERA_CULL_AXIS_EPS: () => import_polycss_core24.VOXEL_CAMERA_CULL_AXIS_EPS,
59
- VOXEL_CAMERA_CULL_NORMAL_LIMIT: () => import_polycss_core24.VOXEL_CAMERA_CULL_NORMAL_LIMIT,
60
- arrowPolygons: () => import_polycss_core24.arrowPolygons,
61
- axesHelperPolygons: () => import_polycss_core24.axesHelperPolygons,
62
- bakeSolidTextureSampledPolygons: () => import_polycss_core24.bakeSolidTextureSampledPolygons,
63
- bakeSolidTextureSamples: () => import_polycss_core24.bakeSolidTextureSamples,
64
- boxPolygons: () => import_polycss_core24.boxPolygons,
65
- buildSceneContext: () => import_polycss_core24.buildSceneContext,
66
- cameraCullNormalGroups: () => import_polycss_core24.cameraCullNormalGroups,
67
- cameraCullNormalGroupsFromPolygons: () => import_polycss_core24.cameraCullNormalGroupsFromPolygons,
68
- cameraCullNormalKey: () => import_polycss_core24.cameraCullNormalKey,
69
- cameraCullVisibleSignature: () => import_polycss_core24.cameraCullVisibleSignature,
70
- cameraFacingDepth: () => import_polycss_core24.cameraFacingDepth,
71
- clampChannel: () => import_polycss_core24.clampChannel,
59
+ VOXEL_CAMERA_CULL_AXIS_EPS: () => import_polycss_core27.VOXEL_CAMERA_CULL_AXIS_EPS,
60
+ VOXEL_CAMERA_CULL_NORMAL_LIMIT: () => import_polycss_core27.VOXEL_CAMERA_CULL_NORMAL_LIMIT,
61
+ arrowPolygons: () => import_polycss_core27.arrowPolygons,
62
+ axesHelperPolygons: () => import_polycss_core27.axesHelperPolygons,
63
+ bakeSolidTextureSampledPolygons: () => import_polycss_core27.bakeSolidTextureSampledPolygons,
64
+ bakeSolidTextureSamples: () => import_polycss_core27.bakeSolidTextureSamples,
65
+ boxPolygons: () => import_polycss_core27.boxPolygons,
66
+ buildSceneContext: () => import_polycss_core27.buildSceneContext,
67
+ cameraCullNormalGroups: () => import_polycss_core27.cameraCullNormalGroups,
68
+ cameraCullNormalGroupsFromPolygons: () => import_polycss_core27.cameraCullNormalGroupsFromPolygons,
69
+ cameraCullNormalKey: () => import_polycss_core27.cameraCullNormalKey,
70
+ cameraCullVisibleSignature: () => import_polycss_core27.cameraCullVisibleSignature,
71
+ cameraFacingDepth: () => import_polycss_core27.cameraFacingDepth,
72
+ clampChannel: () => import_polycss_core27.clampChannel,
72
73
  collectPolyRenderStats: () => collectPolyRenderStats,
73
- computeSceneBbox: () => import_polycss_core24.computeSceneBbox,
74
- computeShapeLighting: () => import_polycss_core24.computeShapeLighting,
75
- computeTexturePaintMetrics: () => import_polycss_core24.computeTexturePaintMetrics,
76
- conePolygons: () => import_polycss_core24.conePolygons,
77
- coverPlanarPolygons: () => import_polycss_core24.coverPlanarPolygons,
78
- createIsometricCamera: () => import_polycss_core24.createIsometricCamera,
79
- createPolyAnimationMixer: () => import_polycss_core24.createPolyAnimationMixer,
80
- cullInteriorPolygons: () => import_polycss_core24.cullInteriorPolygons,
81
- cylinderPolygons: () => import_polycss_core24.cylinderPolygons,
82
- dodecahedronPolygons: () => import_polycss_core24.dodecahedronPolygons,
74
+ computeSceneBbox: () => import_polycss_core27.computeSceneBbox,
75
+ computeShapeLighting: () => import_polycss_core27.computeShapeLighting,
76
+ computeTexturePaintMetrics: () => import_polycss_core27.computeTexturePaintMetrics,
77
+ conePolygons: () => import_polycss_core27.conePolygons,
78
+ coverPlanarPolygons: () => import_polycss_core27.coverPlanarPolygons,
79
+ createIsometricCamera: () => import_polycss_core27.createIsometricCamera,
80
+ createPolyAnimationMixer: () => import_polycss_core27.createPolyAnimationMixer,
81
+ cullInteriorPolygons: () => import_polycss_core27.cullInteriorPolygons,
82
+ cylinderPolygons: () => import_polycss_core27.cylinderPolygons,
83
+ dodecahedronPolygons: () => import_polycss_core27.dodecahedronPolygons,
83
84
  findMeshUnderPoint: () => findMeshUnderPoint,
84
85
  findPolyMeshHandle: () => findPolyMeshHandle,
85
- formatColor: () => import_polycss_core24.formatColor,
86
- icosahedronPolygons: () => import_polycss_core24.icosahedronPolygons,
86
+ formatColor: () => import_polycss_core27.formatColor,
87
+ icosahedronPolygons: () => import_polycss_core27.icosahedronPolygons,
87
88
  injectPolyBaseStyles: () => injectPolyBaseStyles,
88
- inverseRotateVec3: () => import_polycss_core24.inverseRotateVec3,
89
- isAxisAlignedSurfaceNormal: () => import_polycss_core24.isAxisAlignedSurfaceNormal,
90
- isVoxelCameraCullableNormalGroups: () => import_polycss_core24.isVoxelCameraCullableNormalGroups,
91
- loadMesh: () => import_polycss_core24.loadMesh,
92
- mergePolygons: () => import_polycss_core24.mergePolygons,
93
- normalFacesCamera: () => import_polycss_core24.normalFacesCamera,
94
- normalizeInvertMultiplier: () => import_polycss_core24.normalizeInvertMultiplier,
95
- normalizePolygons: () => import_polycss_core24.normalizePolygons,
96
- octahedronPolygons: () => import_polycss_core24.octahedronPolygons,
97
- optimizeAnimatedMeshPolygons: () => import_polycss_core24.optimizeAnimatedMeshPolygons,
98
- optimizeMeshPolygons: () => import_polycss_core24.optimizeMeshPolygons,
99
- parseColor: () => import_polycss_core24.parseColor,
100
- parseGltf: () => import_polycss_core24.parseGltf,
101
- parseHexColor: () => import_polycss_core24.parseHexColor,
102
- parseMtl: () => import_polycss_core24.parseMtl,
103
- parseObj: () => import_polycss_core24.parseObj,
104
- parsePureColor: () => import_polycss_core24.parsePureColor,
105
- parseRgbColor: () => import_polycss_core24.parseRgbColor,
106
- parseVox: () => import_polycss_core24.parseVox,
107
- planePolygons: () => import_polycss_core24.planePolygons,
89
+ inverseRotateVec3: () => import_polycss_core27.inverseRotateVec3,
90
+ isAxisAlignedSurfaceNormal: () => import_polycss_core27.isAxisAlignedSurfaceNormal,
91
+ isVoxelCameraCullableNormalGroups: () => import_polycss_core27.isVoxelCameraCullableNormalGroups,
92
+ loadMesh: () => import_polycss_core27.loadMesh,
93
+ mergePolygons: () => import_polycss_core27.mergePolygons,
94
+ normalFacesCamera: () => import_polycss_core27.normalFacesCamera,
95
+ normalizeInvertMultiplier: () => import_polycss_core27.normalizeInvertMultiplier,
96
+ normalizePolygons: () => import_polycss_core27.normalizePolygons,
97
+ octahedronPolygons: () => import_polycss_core27.octahedronPolygons,
98
+ optimizeAnimatedMeshPolygons: () => import_polycss_core27.optimizeAnimatedMeshPolygons,
99
+ optimizeMeshParseResult: () => import_polycss_core27.optimizeMeshParseResult,
100
+ optimizeMeshPolygons: () => import_polycss_core27.optimizeMeshPolygons,
101
+ parseColor: () => import_polycss_core27.parseColor,
102
+ parseGltf: () => import_polycss_core27.parseGltf,
103
+ parseHexColor: () => import_polycss_core27.parseHexColor,
104
+ parseMtl: () => import_polycss_core27.parseMtl,
105
+ parseObj: () => import_polycss_core27.parseObj,
106
+ parsePureColor: () => import_polycss_core27.parsePureColor,
107
+ parseRgbColor: () => import_polycss_core27.parseRgbColor,
108
+ parseStl: () => import_polycss_core27.parseStl,
109
+ parseVox: () => import_polycss_core27.parseVox,
110
+ planePolygons: () => import_polycss_core27.planePolygons,
108
111
  pointInMeshElement: () => pointInMeshElement,
109
- polygonCssSurfaceNormal: () => import_polycss_core24.polygonCssSurfaceNormal,
110
- polygonFaces: () => import_polycss_core24.polygonFaces,
111
- polygonFacesCamera: () => import_polycss_core24.polygonFacesCamera,
112
- repairMeshSeams: () => import_polycss_core24.repairMeshSeams,
113
- ringPolygons: () => import_polycss_core24.ringPolygons,
114
- rotateVec3: () => import_polycss_core24.rotateVec3,
115
- seamFacetSplitPolygons: () => import_polycss_core24.seamFacetSplitPolygons,
116
- seamFacetSplitReport: () => import_polycss_core24.seamFacetSplitReport,
117
- seamOverlapDiagnostics: () => import_polycss_core24.seamOverlapDiagnostics,
118
- seamOverlapPolygons: () => import_polycss_core24.seamOverlapPolygons,
119
- seamOverlapReport: () => import_polycss_core24.seamOverlapReport,
120
- shadeColor: () => import_polycss_core24.shadeColor,
121
- tetrahedronPolygons: () => import_polycss_core24.tetrahedronPolygons,
122
- torusPolygons: () => import_polycss_core24.torusPolygons,
112
+ polygonCssSurfaceNormal: () => import_polycss_core27.polygonCssSurfaceNormal,
113
+ polygonFaces: () => import_polycss_core27.polygonFaces,
114
+ polygonFacesCamera: () => import_polycss_core27.polygonFacesCamera,
115
+ repairMeshSeams: () => import_polycss_core27.repairMeshSeams,
116
+ ringPolygons: () => import_polycss_core27.ringPolygons,
117
+ rotateVec3: () => import_polycss_core27.rotateVec3,
118
+ seamFacetSplitPolygons: () => import_polycss_core27.seamFacetSplitPolygons,
119
+ seamFacetSplitReport: () => import_polycss_core27.seamFacetSplitReport,
120
+ seamOverlapDiagnostics: () => import_polycss_core27.seamOverlapDiagnostics,
121
+ seamOverlapPolygons: () => import_polycss_core27.seamOverlapPolygons,
122
+ seamOverlapReport: () => import_polycss_core27.seamOverlapReport,
123
+ shadeColor: () => import_polycss_core27.shadeColor,
124
+ simplifyTriangleMeshPolygons: () => import_polycss_core27.simplifyTriangleMeshPolygons,
125
+ tetrahedronPolygons: () => import_polycss_core27.tetrahedronPolygons,
126
+ torusPolygons: () => import_polycss_core27.torusPolygons,
123
127
  useCameraContext: () => useCameraContext,
124
128
  usePolyAnimation: () => usePolyAnimation,
125
129
  usePolyCamera: () => usePolyCamera,
@@ -231,8 +235,9 @@ function usePolyCamera(options) {
231
235
  const cssX = wy * tileSize;
232
236
  const cssY = wx * tileSize;
233
237
  const cssZ = wz * tileSize;
238
+ const cssZoom = s.zoom / tileSize;
234
239
  const distancePart = s.distance !== 0 ? `translateZ(${-s.distance}px) ` : "";
235
- el.style.transform = `${distancePart}scale(${s.zoom}) rotateX(${s.rotX}deg) rotate(${s.rotY}deg) translate3d(${-cssX}px, ${-cssY}px, ${-cssZ}px)`;
240
+ el.style.transform = `${distancePart}scale(${cssZoom}) rotateX(${s.rotX}deg) rotate(${s.rotY}deg) translate3d(${-cssX}px, ${-cssY}px, ${-cssZ}px)`;
236
241
  }
237
242
  store.updateCameraFromRef(handle);
238
243
  store.notifyAll();
@@ -251,8 +256,9 @@ function usePolyCamera(options) {
251
256
  const cssX = wy * tileSize;
252
257
  const cssY = wx * tileSize;
253
258
  const cssZ = wz * tileSize;
259
+ const cssZoom = s.zoom / tileSize;
254
260
  const distancePart = s.distance !== 0 ? `translateZ(${-s.distance}px) ` : "";
255
- el.style.transform = `${distancePart}scale(${s.zoom}) rotateX(${s.rotX}deg) rotate(${s.rotY}deg) translate3d(${-cssX}px, ${-cssY}px, ${-cssZ}px)`;
261
+ el.style.transform = `${distancePart}scale(${cssZoom}) rotateX(${s.rotX}deg) rotate(${s.rotY}deg) translate3d(${-cssX}px, ${-cssY}px, ${-cssZ}px)`;
256
262
  }, [store]);
257
263
  return {
258
264
  store,
@@ -265,7 +271,7 @@ function usePolyCamera(options) {
265
271
 
266
272
  // src/camera/PolyPerspectiveCamera.tsx
267
273
  var import_jsx_runtime = require("react/jsx-runtime");
268
- var DEFAULT_PERSPECTIVE = 8e3;
274
+ var DEFAULT_PERSPECTIVE = 32e3;
269
275
  function PolyPerspectiveCameraInner({
270
276
  zoom,
271
277
  target,
@@ -331,7 +337,12 @@ function PolyOrthographicCameraInner({
331
337
  );
332
338
  const cameraStyle = {
333
339
  ...style,
334
- perspective: "none"
340
+ // Vanilla emits 1000000px instead of "none" because true `perspective: none`
341
+ // sends Chrome down a compositor fast path that mis-rasterizes <u>
342
+ // border-triangle leaves. A very large finite value is visually orthographic
343
+ // but routes through the normal compositor path. Mirror that here so React
344
+ // produces byte-identical output to vanilla.
345
+ perspective: "1000000px"
335
346
  };
336
347
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(PolyCameraContext.Provider, { value: contextValue, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
337
348
  "div",
@@ -346,8 +357,8 @@ function PolyOrthographicCameraInner({
346
357
  var PolyOrthographicCamera = (0, import_react5.memo)(PolyOrthographicCameraInner);
347
358
 
348
359
  // src/scene/PolyScene.tsx
349
- var import_react13 = require("react");
350
- var import_polycss_core12 = require("@layoutit/polycss-core");
360
+ var import_react14 = require("react");
361
+ var import_polycss_core13 = require("@layoutit/polycss-core");
351
362
 
352
363
  // src/scene/useSceneContext.ts
353
364
  var import_react6 = require("react");
@@ -400,21 +411,24 @@ var CORE_BASE_STYLES = `
400
411
  will-change: transform;
401
412
  }
402
413
 
403
- /* \u2500\u2500 Camera wrapper (perspective + interactive drag) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
414
+ /* \u2500\u2500 Camera wrapper \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
404
415
 
416
+ /* Matches vanilla .polycss-camera: a simple positioned block that fills the
417
+ host. PolyPerspectiveCamera / PolyOrthographicCamera apply perspective
418
+ inline (driven by their perspective prop), so the CSS file does not bake a
419
+ default. Earlier React/Vue copies added flex centering, overflow:hidden,
420
+ contain:paint, isolation:isolate which shifted layout vs vanilla; removed
421
+ so cross-renderer iframes lay out identically. */
405
422
  .polycss-camera {
406
- display: flex;
423
+ position: relative;
424
+ display: block;
407
425
  width: 100%;
408
- justify-content: center;
409
- align-items: center;
410
- perspective: 8000px;
411
- min-height: inherit;
412
426
  height: 100%;
413
- position: relative;
414
- overflow: hidden;
415
- contain: paint;
416
- isolation: isolate;
417
427
  }
428
+ /* React-only descendant default: applies preserve-3d to controls + helpers
429
+ that ride on the camera; vanilla does not need this because every internal
430
+ element sets the property directly. Kept here so user-supplied wrapper
431
+ divs inside PolyCamera still participate in 3D layout. */
418
432
  .polycss-camera * {
419
433
  transform-style: preserve-3d;
420
434
  position: absolute;
@@ -436,6 +450,17 @@ var CORE_BASE_STYLES = `
436
450
  transform-style: preserve-3d !important;
437
451
  }
438
452
 
453
+ /* \u2500\u2500 Mesh wrapper \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
454
+
455
+ .polycss-mesh {
456
+ position: absolute;
457
+ transform-style: preserve-3d;
458
+ /* Pivot at wrapper local (0,0,0) for three.js mesh.position/rotation/scale
459
+ parity. Geometry is positioned by the parser (bbox-min-at-origin by
460
+ default, or pre-centered via loadMesh { center: true }). */
461
+ transform-origin: 0 0 0;
462
+ }
463
+
439
464
  /* \u2500\u2500 Polygon leaf element \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
440
465
 
441
466
  /*
@@ -521,19 +546,34 @@ var CORE_BASE_STYLES = `
521
546
  border-width: 0 16px 32px 16px;
522
547
  }
523
548
 
524
- .polycss-scene u.polycss-large-border-triangle {
525
- border-width: 0 48px 96px 48px;
526
- }
549
+ @supports (corner-top-left-shape: bevel) and (corner-top-right-shape: bevel) {
550
+ .polycss-scene > u,
551
+ .polycss-mesh > u,
552
+ .polycss-bucket > u {
553
+ border-width: 0;
554
+ width: 32px;
555
+ height: 32px;
556
+ background-color: currentColor;
557
+ border-top-left-radius: 50% 100%;
558
+ border-top-right-radius: 50% 100%;
559
+ corner-top-left-shape: bevel;
560
+ corner-top-right-shape: bevel;
561
+ }
527
562
 
528
- .polycss-scene u.polycss-corner-triangle {
529
- width: 32px;
530
- height: 32px;
531
- background: currentColor;
532
- border: 0;
533
- border-top-left-radius: 50% 100%;
534
- border-top-right-radius: 50% 100%;
535
- corner-top-left-shape: bevel;
536
- corner-top-right-shape: bevel;
563
+ .polycss-scene > u.polycss-corner-shape-solid,
564
+ .polycss-mesh > u.polycss-corner-shape-solid,
565
+ .polycss-bucket > u.polycss-corner-shape-solid {
566
+ width: 16px;
567
+ height: 16px;
568
+ box-sizing: border-box;
569
+ border: 0;
570
+ background: currentColor;
571
+ border-radius: 0;
572
+ corner-top-left-shape: initial;
573
+ corner-top-right-shape: initial;
574
+ corner-bottom-right-shape: initial;
575
+ corner-bottom-left-shape: initial;
576
+ }
537
577
  }
538
578
 
539
579
  /* \u2500\u2500 Gizmo override \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
@@ -638,22 +678,33 @@ var CORE_BASE_STYLES = `
638
678
  here (not inline per polygon) so each leaf only carries its tiny normal
639
679
  declarations \u2014 ~12\xD7 smaller per-polygon style payload on big meshes. */
640
680
  .polycss-scene[data-polycss-lighting="dynamic"] s {
681
+ /*
682
+ * Three.js MeshLambertMaterial parity for textured surfaces. See
683
+ * packages/polycss/src/styles/styles.ts for derivation and probe data.
684
+ * Kept in lockstep across renderer copies per cross-package discipline.
685
+ */
641
686
  background-color: rgb(
642
- calc(255 * (var(--par) * var(--pai)
643
- + var(--plr) * var(--pli) * max(0,
644
- var(--pnx) * var(--plx) +
645
- var(--pny) * var(--ply) +
646
- var(--pnz) * var(--plz))))
647
- calc(255 * (var(--pag) * var(--pai)
648
- + var(--plg) * var(--pli) * max(0,
649
- var(--pnx) * var(--plx) +
650
- var(--pny) * var(--ply) +
651
- var(--pnz) * var(--plz))))
652
- calc(255 * (var(--pab) * var(--pai)
653
- + var(--plb) * var(--pli) * max(0,
654
- var(--pnx) * var(--plx) +
655
- var(--pny) * var(--ply) +
656
- var(--pnz) * var(--plz))))
687
+ calc(255 * max(0, 1.055 * pow(min(1, (
688
+ pow((var(--par) + 0.055) / 1.055, 2.4) * var(--pai) +
689
+ pow((var(--plr) + 0.055) / 1.055, 2.4) * var(--pli) * max(0,
690
+ var(--pnx) * var(--plx) +
691
+ var(--pny) * var(--ply) +
692
+ var(--pnz) * var(--plz))
693
+ ) / 3.14159265), 0.4167) - 0.055))
694
+ calc(255 * max(0, 1.055 * pow(min(1, (
695
+ pow((var(--pag) + 0.055) / 1.055, 2.4) * var(--pai) +
696
+ pow((var(--plg) + 0.055) / 1.055, 2.4) * var(--pli) * max(0,
697
+ var(--pnx) * var(--plx) +
698
+ var(--pny) * var(--ply) +
699
+ var(--pnz) * var(--plz))
700
+ ) / 3.14159265), 0.4167) - 0.055))
701
+ calc(255 * max(0, 1.055 * pow(min(1, (
702
+ pow((var(--pab) + 0.055) / 1.055, 2.4) * var(--pai) +
703
+ pow((var(--plb) + 0.055) / 1.055, 2.4) * var(--pli) * max(0,
704
+ var(--pnx) * var(--plx) +
705
+ var(--pny) * var(--ply) +
706
+ var(--pnz) * var(--plz))
707
+ ) / 3.14159265), 0.4167) - 0.055))
657
708
  );
658
709
  background-blend-mode: multiply;
659
710
  background-image: var(--polycss-atlas-url);
@@ -672,35 +723,48 @@ var CORE_BASE_STYLES = `
672
723
  }
673
724
 
674
725
  .polycss-scene[data-polycss-lighting="dynamic"] b,
726
+ .polycss-scene[data-polycss-lighting="dynamic"] i,
675
727
  .polycss-scene[data-polycss-lighting="dynamic"] u {
728
+ /*
729
+ * Three.js MeshLambertMaterial parity (default useLegacyLights=false,
730
+ * physically-correct pipeline). See packages/polycss/src/styles/styles.ts
731
+ * for the derivation and probe data \u2014 kept in lockstep across the three
732
+ * renderer copies per cross-package discipline.
733
+ */
676
734
  color: rgb(
677
- calc(255 * var(--psr) * (var(--par) * var(--pai)
678
- + var(--plr) * var(--pli) * max(0,
679
- var(--pnx) * var(--plx) +
680
- var(--pny) * var(--ply) +
681
- var(--pnz) * var(--plz))))
682
- calc(255 * var(--psg) * (var(--pag) * var(--pai)
683
- + var(--plg) * var(--pli) * max(0,
684
- var(--pnx) * var(--plx) +
685
- var(--pny) * var(--ply) +
686
- var(--pnz) * var(--plz))))
687
- calc(255 * var(--psb) * (var(--pab) * var(--pai)
688
- + var(--plb) * var(--pli) * max(0,
689
- var(--pnx) * var(--plx) +
690
- var(--pny) * var(--ply) +
691
- var(--pnz) * var(--plz))))
735
+ calc(255 * max(0, 1.055 * pow(min(1,
736
+ pow((var(--psr) + 0.055) / 1.055, 2.4) * (
737
+ pow((var(--par) + 0.055) / 1.055, 2.4) * var(--pai) +
738
+ pow((var(--plr) + 0.055) / 1.055, 2.4) * var(--pli) * max(0,
739
+ var(--pnx) * var(--plx) +
740
+ var(--pny) * var(--ply) +
741
+ var(--pnz) * var(--plz))
742
+ ) / 3.14159265
743
+ ), 0.4167) - 0.055))
744
+ calc(255 * max(0, 1.055 * pow(min(1,
745
+ pow((var(--psg) + 0.055) / 1.055, 2.4) * (
746
+ pow((var(--pag) + 0.055) / 1.055, 2.4) * var(--pai) +
747
+ pow((var(--plg) + 0.055) / 1.055, 2.4) * var(--pli) * max(0,
748
+ var(--pnx) * var(--plx) +
749
+ var(--pny) * var(--ply) +
750
+ var(--pnz) * var(--plz))
751
+ ) / 3.14159265
752
+ ), 0.4167) - 0.055))
753
+ calc(255 * max(0, 1.055 * pow(min(1,
754
+ pow((var(--psb) + 0.055) / 1.055, 2.4) * (
755
+ pow((var(--pab) + 0.055) / 1.055, 2.4) * var(--pai) +
756
+ pow((var(--plb) + 0.055) / 1.055, 2.4) * var(--pli) * max(0,
757
+ var(--pnx) * var(--plx) +
758
+ var(--pny) * var(--ply) +
759
+ var(--pnz) * var(--plz))
760
+ ) / 3.14159265
761
+ ), 0.4167) - 0.055))
692
762
  );
693
763
  }
694
764
 
695
- /* <q> \u2014 dedicated shadow leaf. Same border-shape rendering trick as <i>
696
- (border-color: currentColor fills the polygon outline) but with its
697
- own tag so we don't have to thread :not(.polycss-shadow) exclusions
698
- through every dynamic-mode color rule. backface-visibility must be
699
- visible because the projection matrix is near-rank-deficient and the
700
- resulting plane's normal can read as back-facing under some camera
701
- angles; the leaf is intentionally always painted. Strip the UA's
702
- default ::before/::after open-/close-quote so the element is just a
703
- styled box. */
765
+ /* Reserved internal <q> shadow element rules. Current shadow emission uses SVG
766
+ surfaces; these rules keep any retained <q> markup styled as a plain
767
+ border-shape leaf instead of inheriting UA quote styling. */
704
768
  .polycss-scene q {
705
769
  position: absolute;
706
770
  display: block;
@@ -723,11 +787,12 @@ var CORE_BASE_STYLES = `
723
787
  content: none;
724
788
  }
725
789
 
726
- /* \u2500\u2500 Cast shadow projection (dynamic-mode CSS path) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
790
+ /* \u2500\u2500 Retained <q> shadow projection (dynamic-mode CSS path) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
727
791
 
728
792
  /*
729
- * Shadow projection matrix. Projects any 3D point P onto the horizontal
730
- * ground plane (cssZ \u2248 G) along the CSS-space light direction (--clx/y/z).
793
+ * Projection matrix for retained internal <q> shadow leaves. Projects any
794
+ * 3D point P onto the horizontal ground plane (cssZ \u2248 G) along the CSS-space
795
+ * light direction (--clx/y/z).
731
796
  *
732
797
  * In PolyCSS's world convention world Z is up (red-green plane is the
733
798
  * floor in the axes helper). After the world\u2192CSS swap (Y\u2194X), world Z stays
@@ -740,7 +805,7 @@ var CORE_BASE_STYLES = `
740
805
  * has a valid layout box. The fix: collapse along z by a near-zero
741
806
  * scale (Z_SQUASH = 0.01) instead of exactly zero \u2014 output.z is then
742
807
  * approximately G with ~1% drift from the input, full-rank and renderable.
743
- * The shadow still looks flat to the eye (the drift is sub-pixel for
808
+ * The result still looks flat to the eye (the drift is sub-pixel for
744
809
  * any realistic scene size).
745
810
  *
746
811
  * out.cssX = P.cssX - (--clx/--clz) * (P.cssZ - G)
@@ -768,24 +833,16 @@ var CORE_BASE_STYLES = `
768
833
  );
769
834
  }
770
835
 
771
- /* <q> shadow leaf \u2014 Lambert-gated opacity. Polygons facing the light cast
772
- full shadow; polygons facing away cast zero shadow (their projection
773
- would stack inside the silhouette and produce ugly overdraw). The
774
- * 10 multiplier sharpens the cutoff so small positive Lambert values
775
- jump quickly to 1, giving a near-binary visibility decision with a
776
- smooth edge transition. Pure CSS calc \u2014 no JS at light-change time.
777
-
778
- Scoped to dynamic mode: baked-mode shadow leaves are dropped up-front
779
- by isBakedShadowCaster() and don't carry --pnx/--pny/--pnz, so an
780
- unscoped gate would silently zero them via the @property initial
781
- values. */
836
+ /* Retained <q> opacity gate. Polygons facing the light cast full shadow;
837
+ polygons facing away cast zero shadow. The * 10 multiplier sharpens the
838
+ cutoff so small positive Lambert values jump quickly to 1. */
782
839
  .polycss-scene[data-polycss-lighting="dynamic"] q {
783
840
  opacity: clamp(0, calc((var(--pnx) * var(--clx) + var(--pny) * var(--cly) + var(--pnz) * var(--clz)) * 10), 1);
784
841
  }
785
842
  `;
786
843
 
787
844
  // src/scene/atlas/index.tsx
788
- var import_polycss_core11 = require("@layoutit/polycss-core");
845
+ var import_polycss_core12 = require("@layoutit/polycss-core");
789
846
 
790
847
  // src/scene/atlas/detection.ts
791
848
  var import_polycss_core3 = require("@layoutit/polycss-core");
@@ -802,6 +859,7 @@ function borderShapeSupported(doc) {
802
859
  return media("(pointer: fine)").matches && media("(hover: hover)").matches;
803
860
  }
804
861
  function solidTriangleSupported(doc) {
862
+ if (cornerTriangleSupported(doc)) return true;
805
863
  const win = doc.defaultView ?? (typeof window !== "undefined" ? window : void 0);
806
864
  const userAgent = win?.navigator?.userAgent ?? "";
807
865
  if (!userAgent) return true;
@@ -811,6 +869,10 @@ function cornerShapeSupported(doc) {
811
869
  const css = doc.defaultView?.CSS ?? (typeof CSS !== "undefined" ? CSS : void 0);
812
870
  return !!css?.supports?.("corner-top-left-shape", "bevel") && !!css.supports("corner-top-right-shape", "bevel") && !!css.supports("corner-bottom-right-shape", "bevel") && !!css.supports("corner-bottom-left-shape", "bevel");
813
871
  }
872
+ function cornerTriangleSupported(doc) {
873
+ const css = doc.defaultView?.CSS ?? (typeof CSS !== "undefined" ? CSS : void 0);
874
+ return !!css?.supports?.("corner-top-left-shape", "bevel") && !!css.supports("corner-top-right-shape", "bevel");
875
+ }
814
876
  function projectiveQuadSupported(doc) {
815
877
  const win = doc.defaultView ?? (typeof window !== "undefined" ? window : void 0);
816
878
  const userAgent = win?.navigator?.userAgent ?? "";
@@ -855,6 +917,8 @@ function isBorderShapeSupported(doc) {
855
917
  function isSolidTriangleSupported(doc) {
856
918
  const d = doc ?? (typeof document !== "undefined" ? document : null);
857
919
  if (!d) {
920
+ const css = typeof CSS !== "undefined" ? CSS : void 0;
921
+ if (!!css?.supports?.("corner-top-left-shape", "bevel") && !!css.supports("corner-top-right-shape", "bevel")) return true;
858
922
  const userAgent = (typeof navigator !== "undefined" ? navigator : globalThis.navigator)?.userAgent ?? "";
859
923
  if (!userAgent) return true;
860
924
  const isChromiumFamily = /\b(?:Chrome|HeadlessChrome|Chromium|Edg|OPR)\//.test(userAgent);
@@ -867,9 +931,12 @@ function isSolidTriangleSupported(doc) {
867
931
  // src/scene/atlas/filterPlans.ts
868
932
  var import_polycss_core4 = require("@layoutit/polycss-core");
869
933
  function filterAtlasPlans(plans, textureLighting, disabled, doc) {
934
+ const d = doc ?? (typeof document !== "undefined" ? document : null);
870
935
  return (0, import_polycss_core4.filterAtlasPlans)(plans, textureLighting, disabled, {
871
936
  solidTriangleSupported: isSolidTriangleSupported(doc),
872
- borderShapeSupported: isBorderShapeSupported(doc)
937
+ projectiveQuadSupported: doc ? projectiveQuadSupported(doc) : true,
938
+ borderShapeSupported: isBorderShapeSupported(doc),
939
+ cornerShapeSupported: d ? cornerShapeSupported(d) : false
873
940
  });
874
941
  }
875
942
 
@@ -922,16 +989,29 @@ function setCssTransform(ctx, atlasScale, a = 1, b = 0, c = 0, d = 1, e = 0, f =
922
989
  f * atlasScale
923
990
  );
924
991
  }
992
+ function srgbByteToLinear(c) {
993
+ const u = c / 255;
994
+ return u <= 0.04045 ? u / 12.92 : Math.pow((u + 0.055) / 1.055, 2.4);
995
+ }
996
+ function linearToSrgbByte(c) {
997
+ const s = c <= 31308e-7 ? c * 12.92 : 1.055 * Math.pow(c, 1 / 2.4) - 0.055;
998
+ return Math.max(0, Math.min(255, Math.round(s * 255)));
999
+ }
925
1000
  function applyTextureTint(ctx, x, y, width, height, tint, atlasScale) {
926
- if (Math.abs(tint.r - 1) < 1e-3 && Math.abs(tint.g - 1) < 1e-3 && Math.abs(tint.b - 1) < 1e-3) {
927
- return;
1001
+ const px = Math.round(x * atlasScale);
1002
+ const py = Math.round(y * atlasScale);
1003
+ const pw = Math.max(1, Math.round(width * atlasScale));
1004
+ const ph = Math.max(1, Math.round(height * atlasScale));
1005
+ const img = ctx.getImageData(px, py, pw, ph);
1006
+ const data = img.data;
1007
+ const tr = tint.r, tg = tint.g, tb = tint.b;
1008
+ for (let i = 0; i < data.length; i += 4) {
1009
+ if (data[i + 3] === 0) continue;
1010
+ data[i] = linearToSrgbByte(srgbByteToLinear(data[i]) * tr);
1011
+ data[i + 1] = linearToSrgbByte(srgbByteToLinear(data[i + 1]) * tg);
1012
+ data[i + 2] = linearToSrgbByte(srgbByteToLinear(data[i + 2]) * tb);
928
1013
  }
929
- ctx.save();
930
- setCssTransform(ctx, atlasScale);
931
- ctx.globalCompositeOperation = "multiply";
932
- ctx.fillStyle = (0, import_polycss_core6.tintToCss)(tint);
933
- ctx.fillRect(x, y, width, height);
934
- ctx.restore();
1014
+ ctx.putImageData(img, px, py);
935
1015
  }
936
1016
  function drawImageCover(ctx, img, x, y, width, height, atlasScale) {
937
1017
  const srcW = img.naturalWidth || img.width || 1;
@@ -1339,6 +1419,15 @@ var SOLID_TRIANGLE_LARGE_BORDER_CANONICAL_SIZE = 96;
1339
1419
  var SOLID_TRIANGLE_LARGE_BORDER_WIDTH = "0 48px 96px 48px";
1340
1420
  var cachedSolidTriangleUserAgent;
1341
1421
  var cachedSolidTriangleCanonicalSize = SOLID_TRIANGLE_CANONICAL_SIZE;
1422
+ function cornerTriangleSupported2() {
1423
+ const css = typeof CSS !== "undefined" ? CSS : void 0;
1424
+ return !!css?.supports?.("corner-top-left-shape", "bevel") && !!css.supports("corner-top-right-shape", "bevel");
1425
+ }
1426
+ function solidTrianglePrimitive() {
1427
+ if (cornerTriangleSupported2()) return "corner-bevel";
1428
+ const ua = typeof navigator !== "undefined" ? navigator.userAgent : "";
1429
+ return /\bFirefox\//.test(ua) ? "border-large" : "border";
1430
+ }
1342
1431
  function solidTriangleCanonicalSize() {
1343
1432
  const ua = typeof navigator !== "undefined" ? navigator.userAgent : "";
1344
1433
  if (ua !== cachedSolidTriangleUserAgent) {
@@ -1348,7 +1437,35 @@ function solidTriangleCanonicalSize() {
1348
1437
  return cachedSolidTriangleCanonicalSize;
1349
1438
  }
1350
1439
  function solidTriangleBorderWidth() {
1351
- return solidTriangleCanonicalSize() === SOLID_TRIANGLE_LARGE_BORDER_CANONICAL_SIZE ? SOLID_TRIANGLE_LARGE_BORDER_WIDTH : void 0;
1440
+ return solidTrianglePrimitive() === "border-large" ? SOLID_TRIANGLE_LARGE_BORDER_WIDTH : void 0;
1441
+ }
1442
+ function solidTrianglePaintStyle() {
1443
+ const primitive = solidTrianglePrimitive();
1444
+ if (primitive === "corner-bevel") return void 0;
1445
+ const borderWidth = primitive === "border-large" ? SOLID_TRIANGLE_LARGE_BORDER_WIDTH : void 0;
1446
+ return borderWidth ? { borderWidth } : void 0;
1447
+ }
1448
+ function applySolidTrianglePaintStyle(el) {
1449
+ const primitive = solidTrianglePrimitive();
1450
+ if (primitive === "corner-bevel") {
1451
+ el.style.width = "";
1452
+ el.style.height = "";
1453
+ el.style.backgroundColor = "";
1454
+ el.style.borderWidth = "";
1455
+ el.style.borderTopLeftRadius = "";
1456
+ el.style.borderTopRightRadius = "";
1457
+ el.style.removeProperty("corner-top-left-shape");
1458
+ el.style.removeProperty("corner-top-right-shape");
1459
+ } else {
1460
+ el.style.width = "";
1461
+ el.style.height = "";
1462
+ el.style.backgroundColor = "";
1463
+ el.style.borderTopLeftRadius = "";
1464
+ el.style.borderTopRightRadius = "";
1465
+ el.style.removeProperty("corner-top-left-shape");
1466
+ el.style.removeProperty("corner-top-right-shape");
1467
+ el.style.borderWidth = primitive === "border-large" ? SOLID_TRIANGLE_LARGE_BORDER_WIDTH : "";
1468
+ }
1352
1469
  }
1353
1470
  function parseHex2(hex) {
1354
1471
  const parsed = (0, import_polycss_core7.parsePureColor)(hex);
@@ -1763,9 +1880,10 @@ function solidTriangleStyle(entry, textureLighting, pointerEvents, solidPaintDef
1763
1880
  txCol[2],
1764
1881
  1
1765
1882
  ].map((v) => (Math.round(v * 1e3) / 1e3 || 0).toString()).join(",");
1883
+ const primitiveStyle = solidTrianglePaintStyle();
1766
1884
  return {
1767
1885
  transform: `matrix3d(${canonicalMatrix})`,
1768
- borderWidth: solidTriangleBorderWidth(),
1886
+ ...primitiveStyle ?? {},
1769
1887
  ...sharedStyle
1770
1888
  };
1771
1889
  }
@@ -1994,6 +2112,7 @@ function updateStableTriangleDom(root, polygons, options = {}) {
1994
2112
  const el = leaves[i];
1995
2113
  if (el.style.visibility) el.style.visibility = "";
1996
2114
  el.__polycssStableTriangleBasis = style.basis;
2115
+ applySolidTrianglePaintStyle(el);
1997
2116
  el.style.transform = style.transform;
1998
2117
  if (style.borderWidth !== void 0) el.style.borderWidth = style.borderWidth;
1999
2118
  if (style.color !== void 0) applyStableTriangleColor(el, i, style.color, options);
@@ -2003,8 +2122,8 @@ function updateStableTriangleDom(root, polygons, options = {}) {
2003
2122
 
2004
2123
  // src/scene/atlas/paintDefaults.ts
2005
2124
  var import_polycss_core8 = require("@layoutit/polycss-core");
2006
- function computeTextureAtlasPlan(polygon, index, options = {}) {
2007
- return (0, import_polycss_core8.computeTextureAtlasPlanPublic)(polygon, index, options);
2125
+ function computeTextureAtlasPlan(polygon, index, options = {}, basisHint) {
2126
+ return (0, import_polycss_core8.computeTextureAtlasPlanPublic)(polygon, index, options, void 0, basisHint);
2008
2127
  }
2009
2128
  function getSolidPaintDefaults(plans, textureLighting, strategies) {
2010
2129
  const disabled = new Set(strategies?.disable ?? []);
@@ -2016,20 +2135,40 @@ var import_react7 = require("react");
2016
2135
  function pageShells(pages) {
2017
2136
  return pages.map((page) => ({ width: page.width, height: page.height, url: null }));
2018
2137
  }
2019
- function textureAtlasPagesEqual(a, b) {
2020
- return a.length === b.length && a.every((page, index) => {
2021
- const other = b[index];
2022
- return page.width === other.width && page.height === other.height && page.url === other.url;
2023
- });
2138
+ function blobUrlsOf(pages) {
2139
+ return pages.flatMap((page) => page.url?.startsWith("blob:") ? [page.url] : []);
2140
+ }
2141
+ function decodeBlobUrls(urls) {
2142
+ if (urls.length === 0 || typeof Image === "undefined") return Promise.resolve();
2143
+ return Promise.all(urls.map((url) => {
2144
+ const img = new Image();
2145
+ img.src = url;
2146
+ const decoded = img.decode?.();
2147
+ return decoded ? decoded.catch(() => {
2148
+ }) : Promise.resolve();
2149
+ })).then(() => void 0);
2150
+ }
2151
+ function deferRevoke(urls) {
2152
+ if (urls.length === 0) return;
2153
+ const run = () => {
2154
+ for (const url of urls) URL.revokeObjectURL(url);
2155
+ };
2156
+ if (typeof requestAnimationFrame === "function") requestAnimationFrame(run);
2157
+ else setTimeout(run, 0);
2024
2158
  }
2025
- function useTextureAtlas(plans, textureLighting, textureQualityInput, strategies) {
2159
+ function useTextureAtlas(plans, textureLighting, textureQualityInput, strategies, atomic = false) {
2026
2160
  const disabled = (0, import_react7.useMemo)(
2027
2161
  () => new Set(strategies?.disable ?? []),
2028
2162
  // eslint-disable-next-line react-hooks/exhaustive-deps
2029
2163
  [strategies?.disable?.join(",")]
2030
2164
  );
2031
2165
  const atlasPlans = (0, import_react7.useMemo)(
2032
- () => filterAtlasPlans(plans, textureLighting, disabled),
2166
+ () => filterAtlasPlans(
2167
+ plans,
2168
+ textureLighting,
2169
+ disabled,
2170
+ typeof document !== "undefined" ? document : null
2171
+ ),
2033
2172
  [plans, textureLighting, disabled]
2034
2173
  );
2035
2174
  const { packed, atlasScale } = (0, import_react7.useMemo)(
@@ -2040,38 +2179,93 @@ function useTextureAtlas(plans, textureLighting, textureQualityInput, strategies
2040
2179
  ),
2041
2180
  [atlasPlans, textureQualityInput]
2042
2181
  );
2043
- const [pages, setPages] = (0, import_react7.useState)(
2044
- () => pageShells(packed.pages)
2045
- );
2182
+ const [pages, setPages] = (0, import_react7.useState)(() => pageShells(packed.pages));
2183
+ const [frame, setFrame] = (0, import_react7.useState)(() => ({
2184
+ plans,
2185
+ entries: packed.entries,
2186
+ pages: pageShells(packed.pages)
2187
+ }));
2188
+ const shownUrls = (0, import_react7.useRef)([]);
2189
+ const seqRef = (0, import_react7.useRef)(0);
2190
+ const mountedRef = (0, import_react7.useRef)(true);
2046
2191
  (0, import_react7.useEffect)(() => {
2192
+ mountedRef.current = true;
2193
+ return () => {
2194
+ mountedRef.current = false;
2195
+ for (const url of shownUrls.current) URL.revokeObjectURL(url);
2196
+ shownUrls.current = [];
2197
+ };
2198
+ }, []);
2199
+ (0, import_react7.useEffect)(() => {
2200
+ if (atomic) {
2201
+ const seq = ++seqRef.current;
2202
+ const snapPlans = plans;
2203
+ const snapEntries = packed.entries;
2204
+ if (packed.pages.length === 0 || typeof document === "undefined") {
2205
+ deferRevoke(shownUrls.current);
2206
+ shownUrls.current = [];
2207
+ setFrame({ plans: snapPlans, entries: snapEntries, pages: pageShells(packed.pages) });
2208
+ return;
2209
+ }
2210
+ const stale = () => seq !== seqRef.current;
2211
+ let built2 = [];
2212
+ buildAtlasPages(packed.pages, textureLighting, document, atlasScale, stale).then(async (nextPages) => {
2213
+ built2 = blobUrlsOf(nextPages);
2214
+ await decodeBlobUrls(built2);
2215
+ if (!mountedRef.current || stale()) {
2216
+ deferRevoke(built2);
2217
+ return;
2218
+ }
2219
+ const prev = shownUrls.current;
2220
+ shownUrls.current = built2;
2221
+ built2 = [];
2222
+ deferRevoke(prev);
2223
+ setFrame({ plans: snapPlans, entries: snapEntries, pages: nextPages });
2224
+ }).catch(() => {
2225
+ });
2226
+ return;
2227
+ }
2047
2228
  let cancelled = false;
2048
- let urls = [];
2049
- const nextPageShells = pageShells(packed.pages);
2050
- setPages((prev) => textureAtlasPagesEqual(prev, nextPageShells) ? prev : nextPageShells);
2051
- if (packed.pages.length === 0 || typeof document === "undefined") {
2229
+ if (packed.pages.length === 0) {
2230
+ deferRevoke(shownUrls.current);
2231
+ shownUrls.current = [];
2232
+ setPages((prev) => prev.length === 0 ? prev : []);
2052
2233
  return () => {
2053
2234
  };
2054
2235
  }
2055
- buildAtlasPages(packed.pages, textureLighting, document, atlasScale, () => cancelled).then((nextPages) => {
2236
+ if (typeof document === "undefined") return () => {
2237
+ };
2238
+ setPages((prev) => prev.some((page) => page.url) ? prev : pageShells(packed.pages));
2239
+ let built = [];
2240
+ buildAtlasPages(packed.pages, textureLighting, document, atlasScale, () => cancelled).then(async (nextPages) => {
2241
+ built = blobUrlsOf(nextPages);
2242
+ await decodeBlobUrls(built);
2056
2243
  if (cancelled) {
2057
- for (const page of nextPages) {
2058
- if (page.url?.startsWith("blob:")) URL.revokeObjectURL(page.url);
2059
- }
2244
+ deferRevoke(built);
2060
2245
  return;
2061
2246
  }
2062
- urls = nextPages.flatMap((page) => page.url?.startsWith("blob:") ? [page.url] : []);
2063
- setPages((prev) => textureAtlasPagesEqual(prev, nextPages) ? prev : nextPages);
2247
+ const stale = shownUrls.current;
2248
+ shownUrls.current = built;
2249
+ built = [];
2250
+ deferRevoke(stale);
2251
+ setPages(nextPages);
2064
2252
  }).catch(() => {
2065
- if (!cancelled) {
2066
- setPages((prev) => textureAtlasPagesEqual(prev, nextPageShells) ? prev : nextPageShells);
2067
- }
2068
2253
  });
2069
2254
  return () => {
2070
2255
  cancelled = true;
2071
- for (const url of urls) URL.revokeObjectURL(url);
2256
+ deferRevoke(built);
2072
2257
  };
2073
- }, [packed, textureLighting, atlasScale]);
2258
+ }, [packed, textureLighting, atlasScale, atomic]);
2259
+ if (atomic) {
2260
+ return {
2261
+ plans: frame.plans,
2262
+ entries: frame.entries,
2263
+ pages: frame.pages,
2264
+ ready: frame.pages.length === 0 || frame.pages.every((page) => !!page.url)
2265
+ };
2266
+ }
2074
2267
  return {
2268
+ plans,
2075
2269
  entries: packed.entries,
2076
2270
  pages,
2077
2271
  ready: pages.length === 0 || pages.every((page) => !!page.url)
@@ -2102,6 +2296,7 @@ var TextureTrianglePoly = (0, import_react8.memo)(function TextureTrianglePoly2(
2102
2296
  {
2103
2297
  className: elementClassName,
2104
2298
  style: { ...triangleStyle, ...styleProp },
2299
+ "data-poly-index": entry.index,
2105
2300
  ...domEventHandlers,
2106
2301
  ...dataAttrs,
2107
2302
  ...domAttrs
@@ -2133,6 +2328,7 @@ function orderBrushInlineStyle(el) {
2133
2328
  }
2134
2329
  var TextureBorderShapePoly = (0, import_react9.memo)(function TextureBorderShapePoly2({
2135
2330
  entry,
2331
+ textureLighting,
2136
2332
  solidPaintDefaults,
2137
2333
  className,
2138
2334
  style: styleProp,
@@ -2145,7 +2341,9 @@ var TextureBorderShapePoly = (0, import_react9.memo)(function TextureBorderShape
2145
2341
  const bDisabled = disabledStrategies?.has("b") ?? false;
2146
2342
  const useIForFullRect = bDisabled && isBorderShapeSupported();
2147
2343
  const borderShape = !fullRect || useIForFullRect ? (0, import_polycss_core9.cssBorderShapeForPlan)(entry) : null;
2148
- const useDefaultPaint = entry.shadedColor === solidPaintDefaults?.paintColor;
2344
+ const dynamic = textureLighting === "dynamic";
2345
+ const base = parseHex2(entry.polygon.color ?? "#cccccc");
2346
+ const useDefaultDynamicColor = dynamic && rgbKey2(base) === solidPaintDefaults?.dynamicColorKey;
2149
2347
  const setElementRef = (0, import_react9.useCallback)((el) => {
2150
2348
  if (!el) return;
2151
2349
  if (borderShape) el.style.setProperty("border-shape", borderShape);
@@ -2155,8 +2353,25 @@ var TextureBorderShapePoly = (0, import_react9.memo)(function TextureBorderShape
2155
2353
  const transform = borderShape ? (0, import_polycss_core9.formatBorderShapeEntryMatrix)(entry) : (0, import_polycss_core9.formatSolidQuadEntryMatrix)(entry);
2156
2354
  const style = {
2157
2355
  transform,
2158
- color: useDefaultPaint ? void 0 : entry.shadedColor,
2356
+ // Baked mode: always emit per-leaf shaded color when known so the
2357
+ // first render matches the post-light-nudge commit path. Vanilla
2358
+ // dropped the `=== solidPaintDefaults.paintColor` shortcut in
2359
+ // commit 0423777 — leaves with undefined shadedColor would inherit
2360
+ // the (often-wrong) wrapper --polycss-paint default, producing
2361
+ // facets that look dimmer than they should until a light change
2362
+ // forced an explicit re-bake.
2363
+ color: dynamic ? void 0 : entry.shadedColor,
2159
2364
  pointerEvents: pointerEvents === "none" ? "none" : void 0,
2365
+ ...dynamic ? {
2366
+ ["--pnx"]: entry.normal[0].toFixed(4),
2367
+ ["--pny"]: entry.normal[1].toFixed(4),
2368
+ ["--pnz"]: entry.normal[2].toFixed(4),
2369
+ ...useDefaultDynamicColor ? null : {
2370
+ ["--psr"]: (base.r / 255).toFixed(4),
2371
+ ["--psg"]: (base.g / 255).toFixed(4),
2372
+ ["--psb"]: (base.b / 255).toFixed(4)
2373
+ }
2374
+ } : null,
2160
2375
  ...styleProp
2161
2376
  };
2162
2377
  const dataAttrs = entry.polygon.data ? Object.fromEntries(
@@ -2169,6 +2384,7 @@ var TextureBorderShapePoly = (0, import_react9.memo)(function TextureBorderShape
2169
2384
  {
2170
2385
  className: elementClassName,
2171
2386
  style,
2387
+ "data-poly-index": entry.index,
2172
2388
  ...domEventHandlers,
2173
2389
  ...dataAttrs,
2174
2390
  ...domAttrs
@@ -2181,6 +2397,7 @@ var TextureBorderShapePoly = (0, import_react9.memo)(function TextureBorderShape
2181
2397
  ref: setElementRef,
2182
2398
  className: elementClassName,
2183
2399
  style,
2400
+ "data-poly-index": entry.index,
2184
2401
  ...domEventHandlers,
2185
2402
  ...dataAttrs,
2186
2403
  ...domAttrs
@@ -2210,7 +2427,8 @@ var TextureProjectiveSolidPoly = (0, import_react10.memo)(function TextureProjec
2210
2427
  // would drop it to 3 decimals and leave visible seam gaps between
2211
2428
  // adjacent projective quads at zoom-out (matches vanilla scene.add).
2212
2429
  transform: `matrix3d(${entry.projectiveMatrix})`,
2213
- color: dynamic || entry.shadedColor === solidPaintDefaults?.paintColor ? void 0 : entry.shadedColor,
2430
+ // Baked: always emit per-leaf shaded color (vanilla commit 0423777).
2431
+ color: dynamic ? void 0 : entry.shadedColor,
2214
2432
  pointerEvents: pointerEvents === "none" ? "none" : void 0,
2215
2433
  ...dynamic && !useDefaultDynamicColor ? {
2216
2434
  ["--pnx"]: entry.normal[0].toFixed(4),
@@ -2235,6 +2453,7 @@ var TextureProjectiveSolidPoly = (0, import_react10.memo)(function TextureProjec
2235
2453
  {
2236
2454
  className: elementClassName,
2237
2455
  style,
2456
+ "data-poly-index": entry.index,
2238
2457
  ...domEventHandlers,
2239
2458
  ...dataAttrs,
2240
2459
  ...domAttrs
@@ -2242,11 +2461,69 @@ var TextureProjectiveSolidPoly = (0, import_react10.memo)(function TextureProjec
2242
2461
  );
2243
2462
  });
2244
2463
 
2245
- // src/scene/atlas/atlasPoly.tsx
2464
+ // src/scene/atlas/cornerShapeSolid.tsx
2246
2465
  var import_react11 = require("react");
2247
2466
  var import_polycss_core10 = require("@layoutit/polycss-core");
2248
2467
  var import_jsx_runtime6 = require("react/jsx-runtime");
2249
- var TextureAtlasPoly = (0, import_react11.memo)(function TextureAtlasPoly2({
2468
+ function formatPaintCss(entry, textureLighting, solidPaintDefaults) {
2469
+ if (textureLighting === "dynamic") {
2470
+ const base = (0, import_polycss_core10.parseHex)(entry.polygon.color ?? "#cccccc");
2471
+ let style = `;--pnx:${entry.normal[0].toFixed(4)};--pny:${entry.normal[1].toFixed(4)};--pnz:${entry.normal[2].toFixed(4)}`;
2472
+ if ((0, import_polycss_core10.rgbKey)(base) !== solidPaintDefaults?.dynamicColorKey) {
2473
+ style += `;--psr:${(base.r / 255).toFixed(4)};--psg:${(base.g / 255).toFixed(4)};--psb:${(base.b / 255).toFixed(4)}`;
2474
+ }
2475
+ return style;
2476
+ }
2477
+ return entry.shadedColor ? `;color:${entry.shadedColor}` : "";
2478
+ }
2479
+ var TextureCornerShapeSolidPoly = (0, import_react11.memo)(function TextureCornerShapeSolidPoly2({
2480
+ entry,
2481
+ geometry,
2482
+ textureLighting,
2483
+ solidPaintDefaults,
2484
+ className,
2485
+ style: styleProp,
2486
+ domAttrs,
2487
+ domEventHandlers,
2488
+ pointerEvents = "auto"
2489
+ }) {
2490
+ const cornerShapeCss = (0, import_polycss_core10.formatCornerShapeElementStyle)(entry, geometry);
2491
+ const paintCss = formatPaintCss(entry, textureLighting, solidPaintDefaults);
2492
+ const pointerCss = pointerEvents === "none" ? ";pointer-events:none" : "";
2493
+ const fullCss = cornerShapeCss + paintCss + pointerCss;
2494
+ const setRef = (0, import_react11.useCallback)((el) => {
2495
+ if (!el) return;
2496
+ el.setAttribute("style", fullCss);
2497
+ if (styleProp) {
2498
+ for (const [k, v] of Object.entries(styleProp)) {
2499
+ if (v === void 0 || v === null) continue;
2500
+ if (k.startsWith("--")) el.style.setProperty(k, String(v));
2501
+ else el.style[k] = String(v);
2502
+ }
2503
+ }
2504
+ }, [fullCss, styleProp]);
2505
+ const dataAttrs = entry.polygon.data ? Object.fromEntries(
2506
+ Object.entries(entry.polygon.data).map(([k, v]) => [`data-${k}`, String(v)])
2507
+ ) : {};
2508
+ const elementClassName = className?.trim() || void 0;
2509
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2510
+ "u",
2511
+ {
2512
+ ref: setRef,
2513
+ className: elementClassName,
2514
+ "data-poly-index": entry.index,
2515
+ ...domEventHandlers,
2516
+ ...dataAttrs,
2517
+ ...domAttrs
2518
+ }
2519
+ );
2520
+ });
2521
+
2522
+ // src/scene/atlas/atlasPoly.tsx
2523
+ var import_react12 = require("react");
2524
+ var import_polycss_core11 = require("@layoutit/polycss-core");
2525
+ var import_jsx_runtime7 = require("react/jsx-runtime");
2526
+ var TextureAtlasPoly = (0, import_react12.memo)(function TextureAtlasPoly2({
2250
2527
  entry,
2251
2528
  page,
2252
2529
  textureLighting,
@@ -2262,17 +2539,23 @@ var TextureAtlasPoly = (0, import_react11.memo)(function TextureAtlasPoly2({
2262
2539
  const atlasCanonicalSize = entry.atlasCanonicalSize ?? ATLAS_CANONICAL_SIZE_EXPLICIT;
2263
2540
  const atlasWidth = entry.canvasW || 1;
2264
2541
  const atlasHeight = entry.canvasH || 1;
2265
- const atlasPosition = page ? `${(0, import_polycss_core10.formatCssLengthPx)(-entry.x / atlasWidth * atlasCanonicalSize)} ${(0, import_polycss_core10.formatCssLengthPx)(-entry.y / atlasHeight * atlasCanonicalSize)}` : void 0;
2266
- const atlasSize = page ? `${(0, import_polycss_core10.formatCssLengthPx)(page.width / atlasWidth * atlasCanonicalSize)} ${(0, import_polycss_core10.formatCssLengthPx)(page.height / atlasHeight * atlasCanonicalSize)}` : void 0;
2542
+ const atlasPosition = page ? `${(0, import_polycss_core11.formatCssLengthPx)(-entry.x / atlasWidth * atlasCanonicalSize)} ${(0, import_polycss_core11.formatCssLengthPx)(-entry.y / atlasHeight * atlasCanonicalSize)}` : void 0;
2543
+ const atlasSize = page ? `${(0, import_polycss_core11.formatCssLengthPx)(page.width / atlasWidth * atlasCanonicalSize)} ${(0, import_polycss_core11.formatCssLengthPx)(page.height / atlasHeight * atlasCanonicalSize)}` : void 0;
2267
2544
  const dynamicMask = dynamic && page?.url ? `url(${page.url})` : void 0;
2268
2545
  const background = !dynamic && page?.url ? `url(${page.url}) ${atlasPosition} / ${atlasSize} no-repeat` : void 0;
2269
2546
  const style = {
2270
- transform: (0, import_polycss_core10.formatMatrix3d)(entry.atlasMatrix),
2547
+ transform: (0, import_polycss_core11.formatMatrix3d)(entry.atlasMatrix),
2271
2548
  ["--polycss-atlas-size"]: `${atlasCanonicalSize}px`,
2272
- background,
2273
- backgroundImage: dynamic && page?.url ? `url(${page.url})` : void 0,
2274
- backgroundPosition: dynamic ? atlasPosition : void 0,
2275
- backgroundSize: dynamic ? atlasSize : void 0,
2549
+ // Listing the `background` shorthand alongside the `background-*` longhands
2550
+ // in one inline style object makes React warn on every update (mixing
2551
+ // shorthand and non-shorthand for the same property). Branch so only the
2552
+ // current mode's keys are assigned — baked gets `background`, dynamic gets
2553
+ // the longhands.
2554
+ ...dynamic ? {
2555
+ backgroundImage: page?.url ? `url(${page.url})` : void 0,
2556
+ backgroundPosition: atlasPosition,
2557
+ backgroundSize: atlasSize
2558
+ } : { background },
2276
2559
  ...dynamic ? {
2277
2560
  ["--pnx"]: entry.normal[0].toFixed(4),
2278
2561
  ["--pny"]: entry.normal[1].toFixed(4),
@@ -2297,11 +2580,12 @@ var TextureAtlasPoly = (0, import_react11.memo)(function TextureAtlasPoly2({
2297
2580
  Object.entries(entry.polygon.data).map(([k, v]) => [`data-${k}`, String(v)])
2298
2581
  ) : {};
2299
2582
  const elementClassName = className?.trim() || void 0;
2300
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2583
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2301
2584
  "s",
2302
2585
  {
2303
2586
  className: elementClassName,
2304
2587
  style,
2588
+ "data-poly-index": entry.index,
2305
2589
  ...domEventHandlers,
2306
2590
  ...dataAttrs,
2307
2591
  ...domAttrs
@@ -2310,14 +2594,14 @@ var TextureAtlasPoly = (0, import_react11.memo)(function TextureAtlasPoly2({
2310
2594
  });
2311
2595
 
2312
2596
  // src/scene/sceneContext.ts
2313
- var import_react12 = require("react");
2314
- var PolySceneContext = (0, import_react12.createContext)(null);
2597
+ var import_react13 = require("react");
2598
+ var PolySceneContext = (0, import_react13.createContext)(null);
2315
2599
  function usePolySceneContext2() {
2316
- return (0, import_react12.useContext)(PolySceneContext);
2600
+ return (0, import_react13.useContext)(PolySceneContext);
2317
2601
  }
2318
2602
 
2319
2603
  // src/scene/PolyScene.tsx
2320
- var import_jsx_runtime7 = require("react/jsx-runtime");
2604
+ var import_jsx_runtime8 = require("react/jsx-runtime");
2321
2605
  function PolySceneInner({
2322
2606
  polygons: polygonsProp,
2323
2607
  centerPolygons: centerPolygonsProp,
@@ -2329,7 +2613,7 @@ function PolySceneInner({
2329
2613
  ambientLight,
2330
2614
  textureLighting = "baked",
2331
2615
  textureQuality,
2332
- seamBleed = import_polycss_core12.DEFAULT_SEAM_BLEED,
2616
+ seamBleed = import_polycss_core13.DEFAULT_SEAM_BLEED,
2333
2617
  strategies,
2334
2618
  autoCenter = false,
2335
2619
  shadow,
@@ -2343,27 +2627,29 @@ function PolySceneInner({
2343
2627
  debugShowBackfaces
2344
2628
  }) {
2345
2629
  const { store, sceneElRef, applyTransformDirect } = useCameraContext();
2346
- const localSceneRef = (0, import_react13.useCallback)(
2630
+ const [sceneEl, setSceneEl] = (0, import_react14.useState)(null);
2631
+ const localSceneRef = (0, import_react14.useCallback)(
2347
2632
  (el) => {
2348
2633
  sceneElRef.current = el;
2634
+ setSceneEl(el);
2349
2635
  },
2350
2636
  [sceneElRef]
2351
2637
  );
2352
- (0, import_react13.useEffect)(() => {
2638
+ (0, import_react14.useEffect)(() => {
2353
2639
  const el = sceneElRef.current;
2354
2640
  if (!el) return;
2355
2641
  el.classList.toggle("polycss-debug-show-backfaces", !!debugShowBackfaces);
2356
2642
  }, [debugShowBackfaces, sceneElRef]);
2357
- const injectedRef = (0, import_react13.useRef)(false);
2358
- (0, import_react13.useEffect)(() => {
2643
+ const injectedRef = (0, import_react14.useRef)(false);
2644
+ (0, import_react14.useEffect)(() => {
2359
2645
  if (injectedRef.current) return;
2360
2646
  if (typeof document !== "undefined") {
2361
2647
  injectPolyBaseStyles(document);
2362
2648
  injectedRef.current = true;
2363
2649
  }
2364
2650
  }, []);
2365
- const inputPolygons = (0, import_react13.useMemo)(() => polygonsProp ?? [], [polygonsProp]);
2366
- const centerInputPolygons = (0, import_react13.useMemo)(
2651
+ const inputPolygons = (0, import_react14.useMemo)(() => polygonsProp ?? [], [polygonsProp]);
2652
+ const centerInputPolygons = (0, import_react14.useMemo)(
2367
2653
  () => centerPolygonsProp ?? null,
2368
2654
  [centerPolygonsProp]
2369
2655
  );
@@ -2377,7 +2663,7 @@ function PolySceneInner({
2377
2663
  const sceneBbox = centerInputPolygons ? centerSceneBbox : renderSceneBbox;
2378
2664
  const directionalForAtlas = textureLighting === "dynamic" ? void 0 : directionalLight;
2379
2665
  const ambientForAtlas = textureLighting === "dynamic" ? void 0 : ambientLight;
2380
- const polyContext = (0, import_react13.useMemo)(() => {
2666
+ const polyContext = (0, import_react14.useMemo)(() => {
2381
2667
  const tileSize = 50;
2382
2668
  return {
2383
2669
  tileSize,
@@ -2388,7 +2674,7 @@ function PolySceneInner({
2388
2674
  seamBleed
2389
2675
  };
2390
2676
  }, [directionalForAtlas, ambientForAtlas, textureLighting, seamBleed]);
2391
- const autoCenterOffset = (0, import_react13.useMemo)(() => {
2677
+ const autoCenterOffset = (0, import_react14.useMemo)(() => {
2392
2678
  if (!autoCenter) return [0, 0, 0];
2393
2679
  return [
2394
2680
  (sceneBbox.min[0] + sceneBbox.max[0]) / 2,
@@ -2396,18 +2682,18 @@ function PolySceneInner({
2396
2682
  (sceneBbox.min[2] + sceneBbox.max[2]) / 2
2397
2683
  ];
2398
2684
  }, [autoCenter, sceneBbox]);
2399
- (0, import_react13.useLayoutEffect)(() => {
2685
+ (0, import_react14.useLayoutEffect)(() => {
2400
2686
  store.setAutoCenterOffset(autoCenterOffset);
2401
2687
  applyTransformDirect();
2402
2688
  }, [store, autoCenterOffset, applyTransformDirect]);
2403
- (0, import_react13.useLayoutEffect)(() => {
2689
+ (0, import_react14.useLayoutEffect)(() => {
2404
2690
  applyTransformDirect();
2405
2691
  });
2406
2692
  const computedClassName = `polycss-scene${className ? ` ${className}` : ""}`;
2407
- const textureAtlasPlans = (0, import_react13.useMemo)(
2693
+ const textureAtlasPlans = (0, import_react14.useMemo)(
2408
2694
  () => {
2409
- const repairEdges = (0, import_polycss_core11.buildTextureEdgeRepairSets)(polygons);
2410
- const seamBleedEdges = seamBleed === "auto" || typeof seamBleed === "number" && Number.isFinite(seamBleed) && seamBleed > 0 ? (0, import_polycss_core11.buildSeamBleedPolygonEdges)(polygons, {
2695
+ const repairEdges = (0, import_polycss_core12.buildTextureEdgeRepairSets)(polygons);
2696
+ const seamBleedEdges = seamBleed === "auto" || typeof seamBleed === "number" && Number.isFinite(seamBleed) && seamBleed > 0 ? (0, import_polycss_core12.buildSeamBleedPolygonEdges)(polygons, {
2411
2697
  tileSize: polyContext.tileSize,
2412
2698
  layerElevation: polyContext.layerElevation,
2413
2699
  directionalLight: directionalForAtlas,
@@ -2423,22 +2709,23 @@ function PolySceneInner({
2423
2709
  [polygons, polyContext, seamBleed, directionalForAtlas, ambientForAtlas]
2424
2710
  );
2425
2711
  const textureAtlas = useTextureAtlas(textureAtlasPlans, textureLighting, textureQuality, strategies);
2426
- const dynamicLightVars = (0, import_react13.useMemo)(() => {
2712
+ const dynamicLightVars = (0, import_react14.useMemo)(() => {
2427
2713
  if (textureLighting !== "dynamic") return null;
2428
- const dir = directionalLight?.direction ?? [0.4, -0.7, 0.59];
2714
+ const userDir = directionalLight?.direction ?? [0.4, -0.7, 0.59];
2715
+ const dir = (0, import_polycss_core13.worldDirectionToCss)(userDir);
2429
2716
  const len = Math.hypot(dir[0], dir[1], dir[2]) || 1;
2430
2717
  const lx = dir[0] / len, ly = dir[1] / len, lz = dir[2] / len;
2431
- const lightRgb = (0, import_polycss_core12.parseHexColor)(directionalLight?.color ?? "#ffffff")?.rgb ?? [255, 255, 255];
2432
- const ambRgb = (0, import_polycss_core12.parseHexColor)(ambientLight?.color ?? "#ffffff")?.rgb ?? [255, 255, 255];
2718
+ const lightRgb = (0, import_polycss_core13.parseHexColor)(directionalLight?.color ?? "#ffffff")?.rgb ?? [255, 255, 255];
2719
+ const ambRgb = (0, import_polycss_core13.parseHexColor)(ambientLight?.color ?? "#ffffff")?.rgb ?? [255, 255, 255];
2433
2720
  const lightIntensity = directionalLight?.intensity ?? 1;
2434
2721
  const ambientIntensity = ambientLight?.intensity ?? 0.4;
2435
2722
  const ch = (n) => (n / 255).toFixed(4);
2436
2723
  const rawClz = lz;
2437
2724
  const clz = Math.sign(rawClz || 1) * Math.max(Math.abs(rawClz), 0.01);
2438
2725
  return {
2439
- ["--plx"]: lx.toFixed(4),
2440
- ["--ply"]: ly.toFixed(4),
2441
- ["--plz"]: lz.toFixed(4),
2726
+ ["--plx"]: lx.toFixed(2),
2727
+ ["--ply"]: ly.toFixed(2),
2728
+ ["--plz"]: lz.toFixed(2),
2442
2729
  ["--plr"]: ch(lightRgb[0]),
2443
2730
  ["--plg"]: ch(lightRgb[1]),
2444
2731
  ["--plb"]: ch(lightRgb[2]),
@@ -2447,32 +2734,37 @@ function PolySceneInner({
2447
2734
  ["--pag"]: ch(ambRgb[1]),
2448
2735
  ["--pab"]: ch(ambRgb[2]),
2449
2736
  ["--pai"]: ambientIntensity.toFixed(4),
2450
- ["--clx"]: lx.toFixed(4),
2451
- ["--cly"]: ly.toFixed(4),
2452
- ["--clz"]: clz.toFixed(4)
2737
+ ["--clx"]: lx.toFixed(2),
2738
+ ["--cly"]: ly.toFixed(2),
2739
+ ["--clz"]: clz.toFixed(2)
2453
2740
  };
2454
2741
  }, [textureLighting, directionalLight, ambientLight]);
2455
- const shadowCastersRef = (0, import_react13.useRef)(/* @__PURE__ */ new Map());
2456
- const [groundCssZ, setGroundCssZ] = (0, import_react13.useState)(null);
2457
- const recomputeGroundCssZ = (0, import_react13.useCallback)(() => {
2742
+ const shadowCastersRef = (0, import_react14.useRef)(/* @__PURE__ */ new Map());
2743
+ const shadowReceiversRef = (0, import_react14.useRef)(/* @__PURE__ */ new Set());
2744
+ const [groundCssZ, setGroundCssZ] = (0, import_react14.useState)(null);
2745
+ const [shadowCastersVersion, setShadowCastersVersion] = (0, import_react14.useState)(0);
2746
+ const [hasShadowReceiver, setHasShadowReceiver] = (0, import_react14.useState)(false);
2747
+ const recomputeGroundCssZ = (0, import_react14.useCallback)(() => {
2458
2748
  let minWorldZ = Infinity;
2459
- for (const polys of shadowCastersRef.current.values()) {
2460
- for (const poly of polys) {
2749
+ for (const data of shadowCastersRef.current.values()) {
2750
+ for (const poly of data.polygons) {
2461
2751
  for (const v of poly.vertices) {
2462
- if (v[2] < minWorldZ) minWorldZ = v[2];
2752
+ const z = v[2] + (data.position[2] ?? 0);
2753
+ if (z < minWorldZ) minWorldZ = z;
2463
2754
  }
2464
2755
  }
2465
2756
  }
2466
2757
  if (!Number.isFinite(minWorldZ)) return null;
2467
2758
  const lift = shadow?.lift ?? 0.05;
2468
- return (minWorldZ + lift) * import_polycss_core12.BASE_TILE;
2759
+ return (minWorldZ + lift) * import_polycss_core13.BASE_TILE;
2469
2760
  }, [shadow]);
2470
- const registerShadowCaster = (0, import_react13.useCallback)((meshId, meshPolygons) => {
2471
- if (meshPolygons === null) {
2761
+ const registerShadowCaster = (0, import_react14.useCallback)((meshId, data) => {
2762
+ if (data === null) {
2472
2763
  shadowCastersRef.current.delete(meshId);
2473
2764
  } else {
2474
- shadowCastersRef.current.set(meshId, meshPolygons);
2765
+ shadowCastersRef.current.set(meshId, data);
2475
2766
  }
2767
+ setShadowCastersVersion((v) => v + 1);
2476
2768
  const next = recomputeGroundCssZ();
2477
2769
  setGroundCssZ((prev) => prev === next ? prev : next);
2478
2770
  const el = sceneElRef.current;
@@ -2483,7 +2775,12 @@ function PolySceneInner({
2483
2775
  el.style.removeProperty("--shadow-ground-cssz");
2484
2776
  }
2485
2777
  }, [sceneElRef, textureLighting, recomputeGroundCssZ]);
2486
- (0, import_react13.useEffect)(() => {
2778
+ const registerShadowReceiver = (0, import_react14.useCallback)((meshId, registered) => {
2779
+ if (registered) shadowReceiversRef.current.add(meshId);
2780
+ else shadowReceiversRef.current.delete(meshId);
2781
+ setHasShadowReceiver(shadowReceiversRef.current.size > 0);
2782
+ }, []);
2783
+ (0, import_react14.useEffect)(() => {
2487
2784
  const el = sceneElRef.current;
2488
2785
  if (!el) return;
2489
2786
  if (textureLighting === "dynamic" && groundCssZ !== null) {
@@ -2492,19 +2789,19 @@ function PolySceneInner({
2492
2789
  el.style.removeProperty("--shadow-ground-cssz");
2493
2790
  }
2494
2791
  }, [textureLighting, sceneElRef, groundCssZ]);
2495
- (0, import_react13.useEffect)(() => {
2792
+ (0, import_react14.useEffect)(() => {
2496
2793
  setGroundCssZ((prev) => {
2497
2794
  const next = recomputeGroundCssZ();
2498
2795
  return prev === next ? prev : next;
2499
2796
  });
2500
2797
  }, [recomputeGroundCssZ]);
2501
- const disabledStrategies = (0, import_react13.useMemo)(
2798
+ const disabledStrategies = (0, import_react14.useMemo)(
2502
2799
  () => strategies?.disable?.length ? new Set(strategies.disable) : void 0,
2503
2800
  [strategies]
2504
2801
  );
2505
2802
  const polyChildren = textureAtlas.entries.map((entry, index) => {
2506
2803
  if (entry) {
2507
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2804
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2508
2805
  TextureAtlasPoly,
2509
2806
  {
2510
2807
  entry,
@@ -2518,8 +2815,8 @@ function PolySceneInner({
2518
2815
  if (!plan || plan.texture) return null;
2519
2816
  const useU = !disabledStrategies?.has("u");
2520
2817
  const useProjectiveSolid = !disabledStrategies?.has("b");
2521
- if (useU && (0, import_polycss_core11.isSolidTrianglePlan)(plan)) {
2522
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2818
+ if (useU && (0, import_polycss_core12.isSolidTrianglePlan)(plan)) {
2819
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2523
2820
  TextureTrianglePoly,
2524
2821
  {
2525
2822
  entry: plan,
@@ -2528,12 +2825,12 @@ function PolySceneInner({
2528
2825
  plan.index
2529
2826
  );
2530
2827
  }
2531
- if (useProjectiveSolid && (0, import_polycss_core11.isProjectiveQuadPlan)(plan)) {
2532
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TextureProjectiveSolidPoly, { entry: plan, textureLighting }, plan.index);
2828
+ if (useProjectiveSolid && (0, import_polycss_core12.isProjectiveQuadPlan)(plan)) {
2829
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(TextureProjectiveSolidPoly, { entry: plan, textureLighting }, plan.index);
2533
2830
  }
2534
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TextureBorderShapePoly, { entry: plan, disabledStrategies }, plan.index);
2831
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(TextureBorderShapePoly, { entry: plan, textureLighting, disabledStrategies }, plan.index);
2535
2832
  });
2536
- const sceneCtxValue = (0, import_react13.useMemo)(
2833
+ const sceneCtxValue = (0, import_react14.useMemo)(
2537
2834
  () => ({
2538
2835
  textureLighting,
2539
2836
  directionalLight,
@@ -2542,11 +2839,16 @@ function PolySceneInner({
2542
2839
  seamBleed,
2543
2840
  shadow,
2544
2841
  registerShadowCaster,
2545
- groundCssZ
2842
+ registerShadowReceiver,
2843
+ shadowCasters: shadowCastersRef.current,
2844
+ shadowCastersVersion,
2845
+ hasShadowReceiver,
2846
+ groundCssZ,
2847
+ sceneEl
2546
2848
  }),
2547
- [textureLighting, directionalLight, ambientLight, strategies, seamBleed, shadow, registerShadowCaster, groundCssZ]
2849
+ [textureLighting, directionalLight, ambientLight, strategies, seamBleed, shadow, registerShadowCaster, registerShadowReceiver, shadowCastersVersion, hasShadowReceiver, groundCssZ, sceneEl]
2548
2850
  );
2549
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(PolySceneContext.Provider, { value: sceneCtxValue, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
2851
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(PolySceneContext.Provider, { value: sceneCtxValue, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
2550
2852
  "div",
2551
2853
  {
2552
2854
  ref: localSceneRef,
@@ -2564,27 +2866,27 @@ function PolySceneInner({
2564
2866
  }
2565
2867
  ) });
2566
2868
  }
2567
- var PolyScene = (0, import_react13.memo)(PolySceneInner);
2869
+ var PolyScene = (0, import_react14.memo)(PolySceneInner);
2568
2870
 
2569
2871
  // src/scene/PolyMesh.tsx
2570
- var import_react15 = require("react");
2571
- var import_polycss_core15 = require("@layoutit/polycss-core");
2872
+ var import_react16 = require("react");
2873
+ var import_polycss_core16 = require("@layoutit/polycss-core");
2572
2874
 
2573
2875
  // src/scene/useMesh.ts
2574
- var import_react14 = require("react");
2575
- var import_polycss_core13 = require("@layoutit/polycss-core");
2876
+ var import_react15 = require("react");
2877
+ var import_polycss_core14 = require("@layoutit/polycss-core");
2576
2878
  var EMPTY_POLYGONS = [];
2577
2879
  var EMPTY_WARNINGS = [];
2578
2880
  function usePolyMesh(src, options) {
2579
- const [state, setState] = (0, import_react14.useState)({
2881
+ const [state, setState] = (0, import_react15.useState)({
2580
2882
  polygons: EMPTY_POLYGONS,
2581
2883
  voxelSource: void 0,
2582
2884
  loading: !!src,
2583
2885
  error: null,
2584
2886
  warnings: EMPTY_WARNINGS
2585
2887
  });
2586
- const activeResultRef = (0, import_react14.useRef)(null);
2587
- const dispose = (0, import_react14.useCallback)(() => {
2888
+ const activeResultRef = (0, import_react15.useRef)(null);
2889
+ const dispose = (0, import_react15.useCallback)(() => {
2588
2890
  const r = activeResultRef.current;
2589
2891
  if (r) {
2590
2892
  try {
@@ -2594,7 +2896,7 @@ function usePolyMesh(src, options) {
2594
2896
  activeResultRef.current = null;
2595
2897
  }
2596
2898
  }, []);
2597
- (0, import_react14.useEffect)(() => {
2899
+ (0, import_react15.useEffect)(() => {
2598
2900
  if (!src) {
2599
2901
  dispose();
2600
2902
  setState({
@@ -2609,7 +2911,7 @@ function usePolyMesh(src, options) {
2609
2911
  let cancelled = false;
2610
2912
  setState((prev) => ({ ...prev, loading: true, error: null }));
2611
2913
  const prevResult = activeResultRef.current;
2612
- (0, import_polycss_core13.loadMesh)(src, options).then((result) => {
2914
+ (0, import_polycss_core14.loadMesh)(src, options).then((result) => {
2613
2915
  if (cancelled) {
2614
2916
  try {
2615
2917
  result.dispose();
@@ -2646,7 +2948,7 @@ function usePolyMesh(src, options) {
2646
2948
  cancelled = true;
2647
2949
  };
2648
2950
  }, [src, dispose]);
2649
- (0, import_react14.useEffect)(() => {
2951
+ (0, import_react15.useEffect)(() => {
2650
2952
  return () => dispose();
2651
2953
  }, [dispose]);
2652
2954
  return {
@@ -2659,8 +2961,12 @@ function usePolyMesh(src, options) {
2659
2961
  };
2660
2962
  }
2661
2963
 
2964
+ // src/scene/PolyMesh.tsx
2965
+ var import_polycss_core17 = require("@layoutit/polycss-core");
2966
+ var import_react_dom = require("react-dom");
2967
+
2662
2968
  // src/scene/voxelRenderer.ts
2663
- var import_polycss_core14 = require("@layoutit/polycss-core");
2969
+ var import_polycss_core15 = require("@layoutit/polycss-core");
2664
2970
  var DEFAULT_LIGHT_DIR2 = [0.4, -0.7, 0.59];
2665
2971
  var DEFAULT_LIGHT_COLOR2 = "#ffffff";
2666
2972
  var DEFAULT_LIGHT_INTENSITY2 = 1;
@@ -2668,9 +2974,9 @@ var DEFAULT_AMBIENT_COLOR2 = "#ffffff";
2668
2974
  var DEFAULT_AMBIENT_INTENSITY2 = 0.4;
2669
2975
  var DESKTOP_PRIMITIVE_SIZE = 1;
2670
2976
  var MOBILE_PRIMITIVE_SIZE = 8;
2671
- var VOXEL_SEAM_BLEED = import_polycss_core14.PROJECTIVE_QUAD_BLEED;
2977
+ var VOXEL_SEAM_BLEED = import_polycss_core15.PROJECTIVE_QUAD_BLEED;
2672
2978
  var VOXEL_SEAM_EPS = 1e-6;
2673
- var VOXEL_PROJECTIVE_QUAD_GUARDS = (0, import_polycss_core14.resolveProjectiveQuadGuards)({ bleed: 0 });
2979
+ var VOXEL_PROJECTIVE_QUAD_GUARDS = (0, import_polycss_core15.resolveProjectiveQuadGuards)({ bleed: 0 });
2674
2980
  var FACE_NORMALS = {
2675
2981
  t: [0, 0, 1],
2676
2982
  b: [0, 0, -1],
@@ -2691,7 +2997,7 @@ var FACE_BY_NORMAL = /* @__PURE__ */ new Map([
2691
2997
  function visibleFaceSignature(rotation) {
2692
2998
  const visible = [];
2693
2999
  for (const face of FACE_ORDER) {
2694
- if ((0, import_polycss_core14.normalFacesCamera)(FACE_NORMALS[face], rotation)) visible.push(face);
3000
+ if ((0, import_polycss_core15.normalFacesCamera)(FACE_NORMALS[face], rotation)) visible.push(face);
2695
3001
  }
2696
3002
  return visible.join("|");
2697
3003
  }
@@ -2760,11 +3066,11 @@ function polygonBrush(polygon) {
2760
3066
  return {
2761
3067
  axis: "z",
2762
3068
  face,
2763
- left: minY * import_polycss_core14.BASE_TILE,
2764
- top: minX * import_polycss_core14.BASE_TILE,
2765
- width: Math.max(0, (maxY - minY) * import_polycss_core14.BASE_TILE),
2766
- height: Math.max(0, (maxX - minX) * import_polycss_core14.BASE_TILE),
2767
- z: minZ * import_polycss_core14.BASE_TILE,
3069
+ left: minY * import_polycss_core15.BASE_TILE,
3070
+ top: minX * import_polycss_core15.BASE_TILE,
3071
+ width: Math.max(0, (maxY - minY) * import_polycss_core15.BASE_TILE),
3072
+ height: Math.max(0, (maxX - minX) * import_polycss_core15.BASE_TILE),
3073
+ z: minZ * import_polycss_core15.BASE_TILE,
2768
3074
  baseColor,
2769
3075
  bleed: zeroVoxelSeamBleed()
2770
3076
  };
@@ -2773,11 +3079,11 @@ function polygonBrush(polygon) {
2773
3079
  return {
2774
3080
  axis: "x",
2775
3081
  face,
2776
- left: minY * import_polycss_core14.BASE_TILE,
2777
- top: minZ * import_polycss_core14.BASE_TILE,
2778
- width: Math.max(0, (maxY - minY) * import_polycss_core14.BASE_TILE),
2779
- height: Math.max(0, (maxZ - minZ) * import_polycss_core14.BASE_TILE),
2780
- z: -minX * import_polycss_core14.BASE_TILE,
3082
+ left: minY * import_polycss_core15.BASE_TILE,
3083
+ top: minZ * import_polycss_core15.BASE_TILE,
3084
+ width: Math.max(0, (maxY - minY) * import_polycss_core15.BASE_TILE),
3085
+ height: Math.max(0, (maxZ - minZ) * import_polycss_core15.BASE_TILE),
3086
+ z: -minX * import_polycss_core15.BASE_TILE,
2781
3087
  baseColor,
2782
3088
  bleed: zeroVoxelSeamBleed()
2783
3089
  };
@@ -2786,11 +3092,11 @@ function polygonBrush(polygon) {
2786
3092
  return {
2787
3093
  axis: "y",
2788
3094
  face,
2789
- left: minZ * import_polycss_core14.BASE_TILE,
2790
- top: minX * import_polycss_core14.BASE_TILE,
2791
- width: Math.max(0, (maxZ - minZ) * import_polycss_core14.BASE_TILE),
2792
- height: Math.max(0, (maxX - minX) * import_polycss_core14.BASE_TILE),
2793
- z: -minY * import_polycss_core14.BASE_TILE,
3095
+ left: minZ * import_polycss_core15.BASE_TILE,
3096
+ top: minX * import_polycss_core15.BASE_TILE,
3097
+ width: Math.max(0, (maxZ - minZ) * import_polycss_core15.BASE_TILE),
3098
+ height: Math.max(0, (maxX - minX) * import_polycss_core15.BASE_TILE),
3099
+ z: -minY * import_polycss_core15.BASE_TILE,
2794
3100
  baseColor,
2795
3101
  bleed: zeroVoxelSeamBleed()
2796
3102
  };
@@ -2810,7 +3116,7 @@ function worldLineKey(segment) {
2810
3116
  return key;
2811
3117
  }
2812
3118
  function cssPointForVertex(v) {
2813
- return [v[1] * import_polycss_core14.BASE_TILE, v[0] * import_polycss_core14.BASE_TILE, v[2] * import_polycss_core14.BASE_TILE];
3119
+ return [v[1] * import_polycss_core15.BASE_TILE, v[0] * import_polycss_core15.BASE_TILE, v[2] * import_polycss_core15.BASE_TILE];
2814
3120
  }
2815
3121
  function localPointForItem(item, p) {
2816
3122
  if (item.axis === "x") return [p[0], p[2]];
@@ -2886,7 +3192,7 @@ function applyVoxelSeamBleed(polygons, items) {
2886
3192
  }
2887
3193
  }
2888
3194
  function parseColor(input) {
2889
- const parsed = (0, import_polycss_core14.parsePureColor)(input);
3195
+ const parsed = (0, import_polycss_core15.parsePureColor)(input);
2890
3196
  if (!parsed) return { r: 255, g: 255, b: 255, alpha: 1 };
2891
3197
  return {
2892
3198
  r: parsed.rgb[0],
@@ -2897,7 +3203,7 @@ function parseColor(input) {
2897
3203
  }
2898
3204
  function canonicalBrushColor(input) {
2899
3205
  if (!input) return "#cccccc";
2900
- const parsed = (0, import_polycss_core14.parsePureColor)(input);
3206
+ const parsed = (0, import_polycss_core15.parsePureColor)(input);
2901
3207
  if (!parsed) return input;
2902
3208
  const rgb = {
2903
3209
  r: parsed.rgb[0],
@@ -2919,9 +3225,6 @@ function clampChannel(value) {
2919
3225
  return Math.round(Math.max(0, Math.min(255, value)));
2920
3226
  }
2921
3227
  function shadeBrushColor(normal, baseColor, directionalLight, ambientLight) {
2922
- const base = parseColor(baseColor);
2923
- const light = parseColor(directionalLight?.color ?? DEFAULT_LIGHT_COLOR2);
2924
- const ambient = parseColor(ambientLight?.color ?? DEFAULT_AMBIENT_COLOR2);
2925
3228
  const lightDir = directionalLight?.direction ?? DEFAULT_LIGHT_DIR2;
2926
3229
  const lightLen = Math.hypot(lightDir[0], lightDir[1], lightDir[2]) || 1;
2927
3230
  const lx = lightDir[0] / lightLen;
@@ -2929,16 +3232,15 @@ function shadeBrushColor(normal, baseColor, directionalLight, ambientLight) {
2929
3232
  const lz = lightDir[2] / lightLen;
2930
3233
  const directScale = Math.max(0, directionalLight?.intensity ?? DEFAULT_LIGHT_INTENSITY2) * Math.max(0, normal[0] * lx + normal[1] * ly + normal[2] * lz);
2931
3234
  const ambientIntensity = Math.max(0, ambientLight?.intensity ?? DEFAULT_AMBIENT_INTENSITY2);
2932
- const tintR = ambient.r / 255 * ambientIntensity + light.r / 255 * directScale;
2933
- const tintG = ambient.g / 255 * ambientIntensity + light.g / 255 * directScale;
2934
- const tintB = ambient.b / 255 * ambientIntensity + light.b / 255 * directScale;
2935
- const shaded = {
2936
- r: base.r * tintR,
2937
- g: base.g * tintG,
2938
- b: base.b * tintB,
2939
- alpha: base.alpha
2940
- };
2941
- return shaded.alpha < 1 ? `rgba(${clampChannel(shaded.r)}, ${clampChannel(shaded.g)}, ${clampChannel(shaded.b)}, ${shaded.alpha})` : rgbToHex2(shaded);
3235
+ const lightColor = directionalLight?.color ?? DEFAULT_LIGHT_COLOR2;
3236
+ const ambientColor = ambientLight?.color ?? DEFAULT_AMBIENT_COLOR2;
3237
+ const shaded = (0, import_polycss_core15.shadePolygon)(baseColor, directScale, lightColor, ambientColor, ambientIntensity);
3238
+ const base = parseColor(baseColor);
3239
+ if (base.alpha >= 1) return shaded;
3240
+ const r = parseInt(shaded.slice(1, 3), 16);
3241
+ const g = parseInt(shaded.slice(3, 5), 16);
3242
+ const b = parseInt(shaded.slice(5, 7), 16);
3243
+ return `rgba(${r}, ${g}, ${b}, ${base.alpha})`;
2942
3244
  }
2943
3245
  function buildDirectMatrixItems(polygons) {
2944
3246
  if (!polygons?.length) return [];
@@ -3012,7 +3314,7 @@ function voxelSeamEdgeAmounts(item) {
3012
3314
  function rescaleProjectiveMatrix(matrix, primitiveSize) {
3013
3315
  const values = matrix.split(",").map(Number);
3014
3316
  if (values.length !== 16 || values.some((value) => !Number.isFinite(value))) return null;
3015
- const scale = import_polycss_core14.SOLID_QUAD_CANONICAL_SIZE / primitiveSize;
3317
+ const scale = import_polycss_core15.SOLID_QUAD_CANONICAL_SIZE / primitiveSize;
3016
3318
  for (let i = 0; i < 8; i += 1) values[i] *= scale;
3017
3319
  return `matrix3d(${values.map((value) => Number(value.toFixed(6))).join(",")})`;
3018
3320
  }
@@ -3080,7 +3382,7 @@ function affineVoxelMatrix(item, primitiveSize) {
3080
3382
  }
3081
3383
  function directMatrix(item, primitiveSize) {
3082
3384
  const { xAxis, yAxis, normal, tx, ty, tz } = voxelProjectiveBasis(item);
3083
- const projective = (0, import_polycss_core14.computeProjectiveQuadMatrix)(
3385
+ const projective = (0, import_polycss_core15.computeProjectiveQuadMatrix)(
3084
3386
  voxelScreenPts(item),
3085
3387
  xAxis,
3086
3388
  yAxis,
@@ -3114,9 +3416,9 @@ function projectedPoint(item, rotation) {
3114
3416
  let center = itemCenter(item);
3115
3417
  const meshRotation = rotation.meshRotation;
3116
3418
  if (meshRotation) {
3117
- center = (0, import_polycss_core14.rotateVec3)(center, meshRotation[0] ?? 0, meshRotation[1] ?? 0, meshRotation[2] ?? 0);
3419
+ center = (0, import_polycss_core15.rotateVec3)(center, meshRotation[0] ?? 0, meshRotation[1] ?? 0, meshRotation[2] ?? 0);
3118
3420
  }
3119
- const [x, y] = (0, import_polycss_core14.rotateVec3)(center, rotation.rotX, 0, rotation.rotY);
3421
+ const [x, y] = (0, import_polycss_core15.rotateVec3)(center, rotation.rotX, 0, rotation.rotY);
3120
3422
  return { x, y };
3121
3423
  }
3122
3424
  function orderDirectMatrixItems(items, visibleFaces, rotation) {
@@ -3384,7 +3686,9 @@ function findMeshUnderPoint(clientX, clientY, filter) {
3384
3686
  }
3385
3687
 
3386
3688
  // src/scene/PolyMesh.tsx
3387
- var import_jsx_runtime8 = require("react/jsx-runtime");
3689
+ var import_jsx_runtime9 = require("react/jsx-runtime");
3690
+ var reactEdgeOwnersCache = /* @__PURE__ */ new WeakMap();
3691
+ var reactEdgeOwnersCacheKey = /* @__PURE__ */ new WeakMap();
3388
3692
  function solidPaintVars(defaults) {
3389
3693
  const out = {};
3390
3694
  if (defaults.paintColor) out["--polycss-paint"] = defaults.paintColor;
@@ -3396,27 +3700,29 @@ function solidPaintVars(defaults) {
3396
3700
  return Object.keys(out).length > 0 ? out : null;
3397
3701
  }
3398
3702
  function buildTransform(position, scale, rotation) {
3703
+ const sx = typeof scale === "number" ? scale : scale?.[0] ?? 1;
3704
+ const sy = typeof scale === "number" ? scale : scale?.[1] ?? 1;
3705
+ const sz = typeof scale === "number" ? scale : scale?.[2] ?? 1;
3706
+ const hasScale = sx !== 1 || sy !== 1 || sz !== 1;
3707
+ const hasRotation = !!rotation && (!!rotation[0] || !!rotation[1] || !!rotation[2]);
3708
+ const cssPos = position ? [position[1] * import_polycss_core16.BASE_TILE, position[0] * import_polycss_core16.BASE_TILE, position[2] * import_polycss_core16.BASE_TILE] : [0, 0, 0];
3399
3709
  const parts = [];
3400
- if (position) {
3401
- parts.push(`translate3d(${position[0]}px, ${position[1]}px, ${position[2]}px)`);
3710
+ if (cssPos[0] !== 0 || cssPos[1] !== 0 || cssPos[2] !== 0) {
3711
+ parts.push(`translate3d(${cssPos[0]}px, ${cssPos[1]}px, ${cssPos[2]}px)`);
3402
3712
  }
3403
- if (scale !== void 0) {
3404
- if (typeof scale === "number") {
3405
- if (scale !== 1) parts.push(`scale3d(${scale}, ${scale}, ${scale})`);
3406
- } else {
3407
- parts.push(`scale3d(${scale[0]}, ${scale[1]}, ${scale[2]})`);
3408
- }
3713
+ if (hasRotation) {
3714
+ if (rotation[0]) parts.push(`rotateY(${-rotation[0]}deg)`);
3715
+ if (rotation[1]) parts.push(`rotateX(${-rotation[1]}deg)`);
3716
+ if (rotation[2]) parts.push(`rotateZ(${-rotation[2]}deg)`);
3409
3717
  }
3410
- if (rotation) {
3411
- if (rotation[0]) parts.push(`rotateX(${rotation[0]}deg)`);
3412
- if (rotation[1]) parts.push(`rotateY(${rotation[1]}deg)`);
3413
- if (rotation[2]) parts.push(`rotateZ(${rotation[2]}deg)`);
3718
+ if (hasScale) {
3719
+ parts.push(`scale3d(${sx}, ${sy}, ${sz})`);
3414
3720
  }
3415
3721
  return parts.length > 0 ? parts.join(" ") : void 0;
3416
3722
  }
3417
3723
  function recenterPolygons(polygons) {
3418
3724
  if (polygons.length === 0) return polygons;
3419
- const bbox = (0, import_polycss_core15.computeSceneBbox)(polygons);
3725
+ const bbox = (0, import_polycss_core16.computeSceneBbox)(polygons);
3420
3726
  const cx = (bbox.min[0] + bbox.max[0]) / 2;
3421
3727
  const cy = (bbox.min[1] + bbox.max[1]) / 2;
3422
3728
  const cz = (bbox.min[2] + bbox.max[2]) / 2;
@@ -3433,16 +3739,21 @@ function recenterPolygons(polygons) {
3433
3739
  } : null
3434
3740
  }));
3435
3741
  }
3436
- var PolyMesh = (0, import_react15.forwardRef)(function PolyMesh2({
3742
+ var PolyMesh = (0, import_react16.forwardRef)(function PolyMesh2({
3437
3743
  id,
3438
3744
  src,
3439
3745
  mtl,
3440
3746
  polygons: polygonsProp,
3747
+ voxelSource: voxelSourceProp,
3441
3748
  autoCenter,
3442
3749
  textureLighting,
3443
3750
  textureQuality,
3444
3751
  seamBleed,
3752
+ atomicAtlas,
3753
+ onFrameReady,
3445
3754
  castShadow,
3755
+ receiveShadow,
3756
+ merge = true,
3446
3757
  children,
3447
3758
  fallback,
3448
3759
  errorFallback,
@@ -3466,7 +3777,7 @@ var PolyMesh = (0, import_react15.forwardRef)(function PolyMesh2({
3466
3777
  onPointerLeave,
3467
3778
  onPointerCancel
3468
3779
  }, forwardedRef) {
3469
- const mergedOptions = (0, import_react15.useMemo)(() => {
3780
+ const mergedOptions = (0, import_react16.useMemo)(() => {
3470
3781
  if (!mtl && !parseOptions && meshResolution === void 0) return void 0;
3471
3782
  return {
3472
3783
  ...parseOptions ?? {},
@@ -3476,54 +3787,37 @@ var PolyMesh = (0, import_react15.forwardRef)(function PolyMesh2({
3476
3787
  }, [mtl, parseOptions, meshResolution]);
3477
3788
  const fetched = usePolyMesh(src ?? "", mergedOptions);
3478
3789
  const externalPolygons = src ? fetched.polygons : polygonsProp ?? [];
3479
- const externalVoxelSource = src ? fetched.voxelSource : void 0;
3480
- const [localPolygons, setLocalPolygons] = (0, import_react15.useState)(null);
3481
- const prevExternalRef = (0, import_react15.useRef)(externalPolygons);
3790
+ const externalVoxelSource = src ? fetched.voxelSource : voxelSourceProp;
3791
+ const [localPolygons, setLocalPolygons] = (0, import_react16.useState)(null);
3792
+ const prevExternalRef = (0, import_react16.useRef)(externalPolygons);
3482
3793
  if (prevExternalRef.current !== externalPolygons) {
3483
3794
  prevExternalRef.current = externalPolygons;
3484
3795
  if (localPolygons !== null) setLocalPolygons(null);
3485
3796
  }
3486
- const sourcePolygons = localPolygons ?? externalPolygons;
3797
+ const rawSourcePolygons = localPolygons ?? externalPolygons;
3798
+ const sourcePolygons = (0, import_react16.useMemo)(
3799
+ () => merge ? (0, import_polycss_core16.optimizeMeshPolygons)(rawSourcePolygons, meshResolution !== void 0 ? { meshResolution } : void 0) : rawSourcePolygons,
3800
+ [rawSourcePolygons, merge, meshResolution]
3801
+ );
3487
3802
  const hasRenderProp = typeof children === "function";
3488
3803
  const renderPolygon = hasRenderProp ? children : null;
3489
3804
  const staticChildren = hasRenderProp ? null : children;
3490
3805
  const hasStaticChildren = staticChildren !== null && staticChildren !== void 0 && staticChildren !== false;
3491
- const polygons = (0, import_react15.useMemo)(
3806
+ const polygons = (0, import_react16.useMemo)(
3492
3807
  () => autoCenter ? recenterPolygons(sourcePolygons) : sourcePolygons,
3493
3808
  [sourcePolygons, autoCenter]
3494
3809
  );
3495
3810
  const transform = buildTransform(position, scale, rotation);
3496
- const transformOrigin = (0, import_react15.useMemo)(() => {
3497
- if (polygons.length === 0) return void 0;
3498
- let minX = Infinity, minY = Infinity, minZ = Infinity;
3499
- let maxX = -Infinity, maxY = -Infinity, maxZ = -Infinity;
3500
- for (const poly of polygons) {
3501
- for (const v of poly.vertices) {
3502
- if (v[0] < minX) minX = v[0];
3503
- if (v[0] > maxX) maxX = v[0];
3504
- if (v[1] < minY) minY = v[1];
3505
- if (v[1] > maxY) maxY = v[1];
3506
- if (v[2] < minZ) minZ = v[2];
3507
- if (v[2] > maxZ) maxZ = v[2];
3508
- }
3509
- }
3510
- if (!Number.isFinite(minX)) return void 0;
3511
- const tile = 50;
3512
- const x = (minY + maxY) / 2 * tile;
3513
- const y = (minX + maxX) / 2 * tile;
3514
- const z = (minZ + maxZ) / 2 * tile;
3515
- return `${x}px ${y}px ${z}px`;
3516
- }, [polygons]);
3517
- const wrapperRef = (0, import_react15.useRef)(null);
3518
- const propsRef = (0, import_react15.useRef)({ position, scale, rotation });
3811
+ const wrapperRef = (0, import_react16.useRef)(null);
3812
+ const propsRef = (0, import_react16.useRef)({ position, scale, rotation });
3519
3813
  propsRef.current = { position, scale, rotation };
3520
- const polygonsRef = (0, import_react15.useRef)(polygons);
3814
+ const polygonsRef = (0, import_react16.useRef)(polygons);
3521
3815
  polygonsRef.current = polygons;
3522
- const [bakedRotation, setBakedRotation] = (0, import_react15.useState)(rotation);
3523
- const stableTriangleColorFrameRef = (0, import_react15.useRef)(0);
3524
- const setPolygonsImplRef = (0, import_react15.useRef)(() => {
3816
+ const [bakedRotation, setBakedRotation] = (0, import_react16.useState)(rotation);
3817
+ const stableTriangleColorFrameRef = (0, import_react16.useRef)(0);
3818
+ const setPolygonsImplRef = (0, import_react16.useRef)(() => {
3525
3819
  });
3526
- const handle = (0, import_react15.useMemo)(() => ({
3820
+ const handle = (0, import_react16.useMemo)(() => ({
3527
3821
  get element() {
3528
3822
  return wrapperRef.current;
3529
3823
  },
@@ -3544,17 +3838,17 @@ var PolyMesh = (0, import_react15.forwardRef)(function PolyMesh2({
3544
3838
  setLocalPolygons([...current]);
3545
3839
  }
3546
3840
  }), [id]);
3547
- (0, import_react15.useImperativeHandle)(forwardedRef, () => handle, [handle]);
3548
- (0, import_react15.useEffect)(() => {
3841
+ (0, import_react16.useImperativeHandle)(forwardedRef, () => handle, [handle]);
3842
+ (0, import_react16.useEffect)(() => {
3549
3843
  const el = wrapperRef.current;
3550
3844
  if (!el) return;
3551
3845
  registerMeshElement(el, handle);
3552
3846
  return () => unregisterMeshElement(el);
3553
3847
  }, [handle]);
3554
- const cameraCtx = (0, import_react15.useContext)(PolyCameraContext);
3848
+ const cameraCtx = (0, import_react16.useContext)(PolyCameraContext);
3555
3849
  const cameraElRef = cameraCtx?.cameraElRef ?? null;
3556
- const pointerDownAtRef = (0, import_react15.useRef)(null);
3557
- const makeEvent = (0, import_react15.useCallback)(
3850
+ const pointerDownAtRef = (0, import_react16.useRef)(null);
3851
+ const makeEvent = (0, import_react16.useCallback)(
3558
3852
  function makeEvent2(nativeEvent, clientX, clientY) {
3559
3853
  const intersections = [];
3560
3854
  if (typeof document !== "undefined" && typeof document.elementsFromPoint === "function") {
@@ -3593,7 +3887,7 @@ var PolyMesh = (0, import_react15.forwardRef)(function PolyMesh2({
3593
3887
  },
3594
3888
  [cameraElRef, handle]
3595
3889
  );
3596
- const wrapperHandlers = (0, import_react15.useMemo)(() => {
3890
+ const wrapperHandlers = (0, import_react16.useMemo)(() => {
3597
3891
  const dispatch = (polyHandler, reactEvent, nativeEvent, clientX, clientY) => {
3598
3892
  if (!polyHandler) return;
3599
3893
  const polyEvent = makeEvent(nativeEvent, clientX, clientY);
@@ -3677,90 +3971,144 @@ var PolyMesh = (0, import_react15.forwardRef)(function PolyMesh2({
3677
3971
  const sceneCtx = usePolySceneContext2();
3678
3972
  const effectiveTextureLighting = textureLighting ?? sceneCtx?.textureLighting ?? "baked";
3679
3973
  const effectiveStrategies = sceneCtx?.strategies;
3680
- const effectiveSeamBleed = seamBleed ?? sceneCtx?.seamBleed ?? import_polycss_core15.DEFAULT_SEAM_BLEED;
3681
- const effectiveDirectional = effectiveTextureLighting === "dynamic" ? void 0 : sceneCtx?.directionalLight;
3682
- const effectiveAmbient = effectiveTextureLighting === "dynamic" ? void 0 : sceneCtx?.ambientLight;
3974
+ const disabledStrategies = (0, import_react16.useMemo)(
3975
+ () => effectiveStrategies?.disable?.length ? new Set(effectiveStrategies.disable) : void 0,
3976
+ [effectiveStrategies]
3977
+ );
3978
+ const effectiveSeamBleed = seamBleed ?? sceneCtx?.seamBleed ?? import_polycss_core16.DEFAULT_SEAM_BLEED;
3979
+ const effectiveDirectional = sceneCtx?.directionalLight;
3980
+ const effectiveAmbient = sceneCtx?.ambientLight;
3683
3981
  const directVoxelEnabled = Boolean(
3684
3982
  externalVoxelSource && localPolygons === null && !renderPolygon && !hasStaticChildren && effectiveTextureLighting === "baked" && !castShadow
3685
3983
  );
3686
3984
  const sceneDirectionalLight = sceneCtx?.directionalLight;
3687
- const dynamicLightOverride = (0, import_react15.useMemo)(() => {
3985
+ const dynamicLightOverride = (0, import_react16.useMemo)(() => {
3688
3986
  if (effectiveTextureLighting !== "dynamic") return null;
3689
3987
  if (!rotation || rotation[0] === 0 && rotation[1] === 0 && rotation[2] === 0) return null;
3690
3988
  if (!sceneDirectionalLight) return null;
3691
3989
  const dir = sceneDirectionalLight.direction;
3692
- const localDir = (0, import_polycss_core15.inverseRotateVec3)(dir, rotation);
3990
+ const localDir = (0, import_polycss_core16.inverseRotateVec3)(dir, rotation);
3693
3991
  const len = Math.hypot(localDir[0], localDir[1], localDir[2]) || 1;
3694
3992
  return {
3695
- ["--plx"]: (localDir[0] / len).toFixed(4),
3696
- ["--ply"]: (localDir[1] / len).toFixed(4),
3697
- ["--plz"]: (localDir[2] / len).toFixed(4)
3993
+ ["--plx"]: (localDir[0] / len).toFixed(2),
3994
+ ["--ply"]: (localDir[1] / len).toFixed(2),
3995
+ ["--plz"]: (localDir[2] / len).toFixed(2)
3698
3996
  };
3699
3997
  }, [effectiveTextureLighting, rotation, sceneDirectionalLight]);
3700
- const bakedDirectional = (0, import_react15.useMemo)(() => {
3998
+ const bakedDirectional = (0, import_react16.useMemo)(() => {
3701
3999
  if (!effectiveDirectional) return effectiveDirectional;
3702
4000
  const rot = bakedRotation ?? [0, 0, 0];
3703
- if (rot[0] === 0 && rot[1] === 0 && rot[2] === 0) return effectiveDirectional;
4001
+ const cssLight = (0, import_polycss_core17.worldDirectionalLightToCss)(effectiveDirectional);
4002
+ if (rot[0] === 0 && rot[1] === 0 && rot[2] === 0) return cssLight;
3704
4003
  return {
3705
- ...effectiveDirectional,
3706
- direction: (0, import_polycss_core15.inverseRotateVec3)(effectiveDirectional.direction, rot)
4004
+ ...cssLight,
4005
+ direction: (0, import_polycss_core16.inverseRotateVec3)(cssLight.direction, rot)
3707
4006
  };
3708
4007
  }, [effectiveDirectional, bakedRotation]);
3709
- const atlasPlans = (0, import_react15.useMemo)(
4008
+ const lightOccludedPolyIndices = void 0;
4009
+ const atlasPlans = (0, import_react16.useMemo)(
3710
4010
  () => {
3711
4011
  if (renderPolygon || directVoxelEnabled) return [];
3712
- const repairEdges = (0, import_polycss_core11.buildTextureEdgeRepairSets)(polygons);
3713
- const seamBleedEdges = effectiveSeamBleed === "auto" || typeof effectiveSeamBleed === "number" && Number.isFinite(effectiveSeamBleed) && effectiveSeamBleed > 0 ? (0, import_polycss_core11.buildSeamBleedPolygonEdges)(polygons, {
4012
+ const repairEdges = (0, import_polycss_core12.buildTextureEdgeRepairSets)(polygons);
4013
+ const seamBleedEdges = effectiveSeamBleed === "auto" || typeof effectiveSeamBleed === "number" && Number.isFinite(effectiveSeamBleed) && effectiveSeamBleed > 0 ? (0, import_polycss_core12.buildSeamBleedPolygonEdges)(polygons, {
3714
4014
  directionalLight: bakedDirectional,
3715
4015
  ambientLight: effectiveAmbient
3716
4016
  }) : null;
3717
- return polygons.map((p, i) => computeTextureAtlasPlan(p, i, {
4017
+ const basisHints = (0, import_polycss_core17.buildBasisHints)(polygons, {
3718
4018
  directionalLight: bakedDirectional,
3719
- ambientLight: effectiveAmbient,
3720
- seamBleed: seamBleedEdges?.has(i) ? effectiveSeamBleed : void 0,
3721
- seamEdges: seamBleedEdges?.get(i),
3722
- textureEdgeRepairEdges: repairEdges[i]
3723
- }));
4019
+ ambientLight: effectiveAmbient
4020
+ });
4021
+ return polygons.map((p, i) => computeTextureAtlasPlan(
4022
+ p,
4023
+ i,
4024
+ {
4025
+ directionalLight: bakedDirectional,
4026
+ ambientLight: effectiveAmbient,
4027
+ seamBleed: seamBleedEdges?.has(i) ? effectiveSeamBleed : void 0,
4028
+ seamEdges: seamBleedEdges?.get(i),
4029
+ textureEdgeRepairEdges: repairEdges[i],
4030
+ lightOccludedPolyIndices
4031
+ },
4032
+ basisHints[i]
4033
+ ));
3724
4034
  },
3725
- [renderPolygon, directVoxelEnabled, polygons, bakedDirectional, effectiveAmbient, effectiveSeamBleed]
4035
+ [renderPolygon, directVoxelEnabled, polygons, bakedDirectional, effectiveAmbient, effectiveSeamBleed, lightOccludedPolyIndices]
3726
4036
  );
3727
4037
  const textureAtlas = useTextureAtlas(
3728
4038
  atlasPlans,
3729
4039
  effectiveTextureLighting,
3730
4040
  textureQuality,
3731
- effectiveStrategies
4041
+ effectiveStrategies,
4042
+ atomicAtlas
3732
4043
  );
3733
- const solidPaintDefaults = (0, import_react15.useMemo)(
3734
- () => !renderPolygon ? getSolidPaintDefaults(atlasPlans, effectiveTextureLighting, effectiveStrategies) : {},
3735
- [renderPolygon, atlasPlans, effectiveTextureLighting, effectiveStrategies]
4044
+ const solidPaintDefaults = (0, import_react16.useMemo)(
4045
+ () => !renderPolygon ? getSolidPaintDefaults(textureAtlas.plans, effectiveTextureLighting, effectiveStrategies) : {},
4046
+ [renderPolygon, textureAtlas.plans, effectiveTextureLighting, effectiveStrategies]
3736
4047
  );
3737
- const defaultPaintVars = (0, import_react15.useMemo)(
4048
+ const onFrameReadyRef = (0, import_react16.useRef)(onFrameReady);
4049
+ onFrameReadyRef.current = onFrameReady;
4050
+ (0, import_react16.useLayoutEffect)(() => {
4051
+ if (atomicAtlas && textureAtlas.ready) onFrameReadyRef.current?.();
4052
+ }, [textureAtlas.entries]);
4053
+ const defaultPaintVars = (0, import_react16.useMemo)(
3738
4054
  () => solidPaintVars(solidPaintDefaults),
3739
4055
  [solidPaintDefaults]
3740
4056
  );
3741
- const meshIdRef = (0, import_react15.useRef)(/* @__PURE__ */ Symbol());
4057
+ const meshIdRef = (0, import_react16.useRef)(/* @__PURE__ */ Symbol());
3742
4058
  const sceneRegisterShadowCaster = sceneCtx?.registerShadowCaster;
3743
- (0, import_react15.useEffect)(() => {
4059
+ const renderedPolygonIndices = (0, import_react16.useMemo)(() => {
4060
+ const dedupDrop = (0, import_polycss_core16.findOverlappingPolygonDuplicates)(polygons, {
4061
+ normalTolerance: 0.1,
4062
+ distanceTolerance: 0.5,
4063
+ overlapFraction: 0.95,
4064
+ preserveDoubleSidedBackfaces: false
4065
+ });
4066
+ const s = /* @__PURE__ */ new Set();
4067
+ for (let i = 0; i < atlasPlans.length; i++) {
4068
+ if (atlasPlans[i] && !dedupDrop.has(i)) s.add(i);
4069
+ }
4070
+ return s;
4071
+ }, [atlasPlans, polygons]);
4072
+ (0, import_react16.useEffect)(() => {
3744
4073
  if (!sceneRegisterShadowCaster) return;
3745
4074
  if (castShadow) {
3746
- sceneRegisterShadowCaster(meshIdRef.current, polygons);
4075
+ sceneRegisterShadowCaster(meshIdRef.current, {
4076
+ polygons,
4077
+ position: position ?? [0, 0, 0],
4078
+ scale,
4079
+ rotation,
4080
+ renderedPolygonIndices
4081
+ });
3747
4082
  } else {
3748
4083
  sceneRegisterShadowCaster(meshIdRef.current, null);
3749
4084
  }
3750
4085
  return () => {
3751
4086
  sceneRegisterShadowCaster(meshIdRef.current, null);
3752
4087
  };
3753
- }, [sceneRegisterShadowCaster, castShadow, polygons]);
4088
+ }, [sceneRegisterShadowCaster, castShadow, polygons, position, scale, rotation, renderedPolygonIndices]);
4089
+ const sceneRegisterShadowReceiver = sceneCtx?.registerShadowReceiver;
4090
+ (0, import_react16.useEffect)(() => {
4091
+ if (!sceneRegisterShadowReceiver) return;
4092
+ sceneRegisterShadowReceiver(meshIdRef.current, !!receiveShadow);
4093
+ return () => {
4094
+ sceneRegisterShadowReceiver(meshIdRef.current, false);
4095
+ };
4096
+ }, [sceneRegisterShadowReceiver, receiveShadow]);
3754
4097
  const bakedShadowGroundCssZ = sceneCtx?.groundCssZ ?? null;
3755
4098
  const sceneShadow = sceneCtx?.shadow;
3756
- const shadowSvgNode = (0, import_react15.useMemo)(() => {
4099
+ const sceneHasReceiver = sceneCtx?.hasShadowReceiver ?? false;
4100
+ const shadowSvgNode = (0, import_react16.useMemo)(() => {
3757
4101
  if (!castShadow || renderPolygon) return null;
4102
+ if (sceneHasReceiver) return null;
3758
4103
  if (bakedShadowGroundCssZ === null) return null;
3759
- const lightDir = sceneDirectionalLight?.direction ?? [0.4, -0.7, 0.59];
3760
- const shadowDedupDrop = (0, import_polycss_core15.findOverlappingPolygonDuplicates)(polygons, {
4104
+ const userGroundLightDir = sceneDirectionalLight?.direction ?? [0.4, -0.7, 0.59];
4105
+ const lightDir = (0, import_polycss_core16.worldDirectionToCss)(userGroundLightDir);
4106
+ const meshPosZ = position?.[2] ?? 0;
4107
+ const localGroundCssZ = bakedShadowGroundCssZ - meshPosZ * import_polycss_core16.BASE_TILE;
4108
+ const shadowDedupDrop = (0, import_polycss_core16.findOverlappingPolygonDuplicates)(polygons, {
3761
4109
  normalTolerance: 0.1,
3762
4110
  distanceTolerance: 0.5,
3763
- overlapFraction: 0.4,
4111
+ overlapFraction: 0.95,
3764
4112
  preserveDoubleSidedBackfaces: false
3765
4113
  });
3766
4114
  const projections = [];
@@ -3774,15 +4122,15 @@ var PolyMesh = (0, import_react15.forwardRef)(function PolyMesh2({
3774
4122
  const projected = [];
3775
4123
  for (const v of polygon.vertices) {
3776
4124
  const cssVertex = [
3777
- v[1] * import_polycss_core15.BASE_TILE,
3778
- v[0] * import_polycss_core15.BASE_TILE,
3779
- v[2] * import_polycss_core15.BASE_TILE
4125
+ v[1] * import_polycss_core16.BASE_TILE,
4126
+ v[0] * import_polycss_core16.BASE_TILE,
4127
+ v[2] * import_polycss_core16.BASE_TILE
3780
4128
  ];
3781
4129
  if (cssVertex[0] < fpMinX) fpMinX = cssVertex[0];
3782
4130
  if (cssVertex[1] < fpMinY) fpMinY = cssVertex[1];
3783
4131
  if (cssVertex[0] > fpMaxX) fpMaxX = cssVertex[0];
3784
4132
  if (cssVertex[1] > fpMaxY) fpMaxY = cssVertex[1];
3785
- const p = (0, import_polycss_core15.projectCssVertexToGround)(cssVertex, lightDir, bakedShadowGroundCssZ);
4133
+ const p = (0, import_polycss_core16.projectCssVertexToGround)(cssVertex, lightDir, localGroundCssZ);
3786
4134
  projected.push(p);
3787
4135
  if (p[0] < minX) minX = p[0];
3788
4136
  if (p[1] < minY) minY = p[1];
@@ -3802,17 +4150,17 @@ var PolyMesh = (0, import_react15.forwardRef)(function PolyMesh2({
3802
4150
  if (!(width > 0) || !(height > 0)) return null;
3803
4151
  const shadowColor = sceneShadow?.color ?? "#000000";
3804
4152
  const shadowOpacity = sceneShadow?.opacity ?? 0.25;
3805
- const parsed = (0, import_polycss_core15.parseHexColor)(shadowColor)?.rgb ?? [0, 0, 0];
4153
+ const parsed = (0, import_polycss_core16.parseHexColor)(shadowColor)?.rgb ?? [0, 0, 0];
3806
4154
  let d = "";
3807
4155
  for (const verts of projections) {
3808
- const ccw = (0, import_polycss_core15.ensureCcw2D)(verts);
4156
+ const ccw = (0, import_polycss_core16.ensureCcw2D)(verts);
3809
4157
  d += `M${(ccw[0][0] - bx0).toFixed(3)},${(ccw[0][1] - by0).toFixed(3)}`;
3810
4158
  for (let j = 1; j < ccw.length; j++) {
3811
4159
  d += `L${(ccw[j][0] - bx0).toFixed(3)},${(ccw[j][1] - by0).toFixed(3)}`;
3812
4160
  }
3813
4161
  d += "Z";
3814
4162
  }
3815
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4163
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
3816
4164
  "svg",
3817
4165
  {
3818
4166
  className: "polycss-shadow polycss-shadow-svg",
@@ -3828,16 +4176,16 @@ var PolyMesh = (0, import_react15.forwardRef)(function PolyMesh2({
3828
4176
  transformOrigin: "0 0",
3829
4177
  pointerEvents: "none",
3830
4178
  willChange: "transform",
3831
- transform: `translate3d(${bx0.toFixed(3)}px,${by0.toFixed(3)}px,${bakedShadowGroundCssZ.toFixed(3)}px)`
4179
+ transform: `translate3d(${bx0.toFixed(3)}px,${by0.toFixed(3)}px,${localGroundCssZ.toFixed(3)}px)`
3832
4180
  },
3833
- children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4181
+ children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
3834
4182
  "path",
3835
4183
  {
3836
4184
  d,
3837
4185
  fill: `rgb(${parsed[0]},${parsed[1]},${parsed[2]})`,
3838
4186
  fillRule: "nonzero",
3839
4187
  stroke: `rgb(${parsed[0]},${parsed[1]},${parsed[2]})`,
3840
- strokeWidth: "2",
4188
+ strokeWidth: "3",
3841
4189
  strokeLinejoin: "round",
3842
4190
  opacity: shadowOpacity.toFixed(4)
3843
4191
  }
@@ -3845,7 +4193,125 @@ var PolyMesh = (0, import_react15.forwardRef)(function PolyMesh2({
3845
4193
  },
3846
4194
  "shadow-svg"
3847
4195
  );
3848
- }, [castShadow, renderPolygon, polygons, atlasPlans, sceneDirectionalLight, bakedShadowGroundCssZ, sceneShadow]);
4196
+ }, [castShadow, renderPolygon, polygons, atlasPlans, sceneDirectionalLight, bakedShadowGroundCssZ, sceneShadow, sceneHasReceiver]);
4197
+ const shadowCasters = sceneCtx?.shadowCasters;
4198
+ const shadowCastersVersion = sceneCtx?.shadowCastersVersion ?? 0;
4199
+ const [cameraTick, setCameraTick] = (0, import_react16.useState)(0);
4200
+ (0, import_react16.useEffect)(() => {
4201
+ if (!receiveShadow) return;
4202
+ return cameraCtx?.store.subscribe(() => setCameraTick((n) => n + 1));
4203
+ }, [receiveShadow, cameraCtx?.store]);
4204
+ void cameraTick;
4205
+ const selfShadowEdgeMap = (0, import_react16.useMemo)(
4206
+ () => receiveShadow ? (0, import_polycss_core16.buildSharedEdgeMap)(polygons) : void 0,
4207
+ [polygons, receiveShadow]
4208
+ );
4209
+ const receiverShadowSvgs = (0, import_react16.useMemo)(() => {
4210
+ if (!receiveShadow) return null;
4211
+ if (!shadowCasters || shadowCasters.size === 0) return null;
4212
+ const userLightDir = sceneDirectionalLight?.direction ?? [0.4, -0.7, 0.59];
4213
+ const lightDir = (0, import_polycss_core16.worldDirectionToCss)(userLightDir);
4214
+ const shadowLift = sceneShadow?.lift ?? 1e-3;
4215
+ const planes = (0, import_polycss_core16.prepareReceiverFacePlanes)(
4216
+ polygons,
4217
+ position ?? [0, 0, 0],
4218
+ scale,
4219
+ /* @__PURE__ */ new Set(),
4220
+ shadowLift,
4221
+ rotation
4222
+ );
4223
+ if (planes.length === 0) return null;
4224
+ const casterInputs = [];
4225
+ for (const [casterId, data] of shadowCasters) {
4226
+ const rendered = data.renderedPolygonIndices;
4227
+ const items = (0, import_polycss_core16.prepareCasterPolyItems)(
4228
+ data.polygons,
4229
+ data.position,
4230
+ data.scale,
4231
+ rendered ? (idx) => rendered.has(idx) : () => true,
4232
+ data.rotation ?? null
4233
+ );
4234
+ const isSelf = data.polygons === polygons;
4235
+ const selfMap = isSelf ? selfShadowEdgeMap : void 0;
4236
+ let edgeOwners;
4237
+ if (!isSelf && data.polygons.length >= 40) {
4238
+ const dposArr = data.position;
4239
+ const drot = data.rotation ?? null;
4240
+ const dsKey = JSON.stringify(data.scale ?? null);
4241
+ const eoKey = `${dposArr[0]},${dposArr[1]},${dposArr[2]}|${drot ? drot.join(",") : "n"}|${dsKey}`;
4242
+ let cachedOwners = reactEdgeOwnersCache.get(data.polygons);
4243
+ if (cachedOwners === void 0 || reactEdgeOwnersCacheKey.get(data.polygons) !== eoKey) {
4244
+ cachedOwners = (0, import_polycss_core16.prepareCasterEdgeOwners)(data.polygons, dposArr, data.scale, drot);
4245
+ reactEdgeOwnersCache.set(data.polygons, cachedOwners);
4246
+ reactEdgeOwnersCacheKey.set(data.polygons, eoKey);
4247
+ }
4248
+ edgeOwners = cachedOwners;
4249
+ }
4250
+ casterInputs.push({
4251
+ id: casterId,
4252
+ items,
4253
+ selfShadowEdgeMap: selfMap,
4254
+ edgeOwners,
4255
+ casterPolygonCount: data.polygons.length
4256
+ });
4257
+ }
4258
+ const cameraState = cameraCtx?.store.getState().cameraState;
4259
+ const cameraRot = {
4260
+ rotX: cameraState?.rotX ?? 65,
4261
+ rotY: cameraState?.rotY ?? 45,
4262
+ meshRotation: rotation
4263
+ };
4264
+ const specs = (0, import_polycss_core16.computeReceiverShadowFaces)({
4265
+ receiverPlanes: planes,
4266
+ receiverPolygons: polygons,
4267
+ receiverHasTexture: polygons.some((p) => p.texture !== void 0),
4268
+ casters: casterInputs,
4269
+ lightDir,
4270
+ cameraRot,
4271
+ ambientLight: sceneCtx?.ambientLight,
4272
+ directionalLight: sceneDirectionalLight,
4273
+ shadow: { color: sceneShadow?.color, opacity: sceneShadow?.opacity ?? 0.25, maxExtend: sceneShadow?.maxExtend }
4274
+ });
4275
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_jsx_runtime9.Fragment, { children: specs.map((spec) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4276
+ "svg",
4277
+ {
4278
+ className: "polycss-shadow polycss-shadow-svg polycss-shadow-receiver",
4279
+ "data-poly-shadow-type": "receiver",
4280
+ "data-poly-shadow-receiver-face": spec.faceIndex,
4281
+ "data-poly-shadow-receiver-polys": JSON.stringify(spec.memberPolyIndices),
4282
+ width: spec.width,
4283
+ height: spec.height,
4284
+ viewBox: `0 0 ${spec.width} ${spec.height}`,
4285
+ style: {
4286
+ position: "absolute",
4287
+ top: 0,
4288
+ left: 0,
4289
+ display: "block",
4290
+ overflow: "hidden",
4291
+ transformOrigin: "0 0",
4292
+ pointerEvents: "none",
4293
+ willChange: "transform",
4294
+ transform: spec.matrixCss
4295
+ },
4296
+ children: spec.paths.map((p, i) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4297
+ "path",
4298
+ {
4299
+ d: p.d,
4300
+ fill: spec.fill,
4301
+ stroke: spec.fill,
4302
+ strokeWidth: "3",
4303
+ strokeLinejoin: "round",
4304
+ opacity: spec.opacity.toFixed(4),
4305
+ "data-poly-shadow-caster-polys": JSON.stringify(p.casterPolygonIndices)
4306
+ },
4307
+ i
4308
+ ))
4309
+ },
4310
+ `receiver-${spec.faceIndex}`
4311
+ )) });
4312
+ }, [receiveShadow, shadowCasters, shadowCastersVersion, polygons, position, scale, rotation, sceneDirectionalLight, sceneShadow, sceneCtx?.ambientLight, cameraCtx?.store, cameraTick, selfShadowEdgeMap]);
4313
+ const portalSceneEl = sceneCtx?.sceneEl ?? null;
4314
+ const portaledReceiverShadowSvgs = portalSceneEl && receiverShadowSvgs ? (0, import_react_dom.createPortal)(receiverShadowSvgs, portalSceneEl) : null;
3849
4315
  setPolygonsImplRef.current = (nextPolygons) => {
3850
4316
  const nextRenderedPolygons = autoCenter ? recenterPolygons(nextPolygons) : nextPolygons;
3851
4317
  polygonsRef.current = nextRenderedPolygons;
@@ -3865,8 +4331,8 @@ var PolyMesh = (0, import_react15.forwardRef)(function PolyMesh2({
3865
4331
  }
3866
4332
  setLocalPolygons([...nextPolygons]);
3867
4333
  };
3868
- const voxelRendererRef = (0, import_react15.useRef)(null);
3869
- (0, import_react15.useLayoutEffect)(() => {
4334
+ const voxelRendererRef = (0, import_react16.useRef)(null);
4335
+ (0, import_react16.useLayoutEffect)(() => {
3870
4336
  const root = wrapperRef.current;
3871
4337
  voxelRendererRef.current?.dispose();
3872
4338
  voxelRendererRef.current = null;
@@ -3907,7 +4373,6 @@ var PolyMesh = (0, import_react15.forwardRef)(function PolyMesh2({
3907
4373
  ]);
3908
4374
  const wrapperStyle = {
3909
4375
  transform,
3910
- ...transformOrigin ? { transformOrigin } : null,
3911
4376
  ...dynamicLightOverride,
3912
4377
  ...style,
3913
4378
  ...defaultPaintVars
@@ -3915,10 +4380,10 @@ var PolyMesh = (0, import_react15.forwardRef)(function PolyMesh2({
3915
4380
  const renderedPolygons = renderPolygon ? polygons.map((p, i) => (
3916
4381
  // Render-prop: caller controls how each polygon renders. We still
3917
4382
  // wrap in a fragment with key so React reconciliation works.
3918
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(RenderPropPolygon, { polygon: p, index: i, children: renderPolygon }, i)
4383
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(RenderPropPolygon, { polygon: p, index: i, children: renderPolygon }, i)
3919
4384
  )) : textureAtlas.entries.map((entry, index) => {
3920
4385
  if (entry) {
3921
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4386
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
3922
4387
  TextureAtlasPoly,
3923
4388
  {
3924
4389
  entry,
@@ -3929,10 +4394,10 @@ var PolyMesh = (0, import_react15.forwardRef)(function PolyMesh2({
3929
4394
  entry.index
3930
4395
  );
3931
4396
  }
3932
- const plan = atlasPlans[index];
4397
+ const plan = textureAtlas.plans[index];
3933
4398
  if (!plan || plan.texture) return null;
3934
- if ((0, import_polycss_core11.isProjectiveQuadPlan)(plan)) {
3935
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4399
+ if ((0, import_polycss_core12.isProjectiveQuadPlan)(plan)) {
4400
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
3936
4401
  TextureProjectiveSolidPoly,
3937
4402
  {
3938
4403
  entry: plan,
@@ -3942,26 +4407,44 @@ var PolyMesh = (0, import_react15.forwardRef)(function PolyMesh2({
3942
4407
  plan.index
3943
4408
  );
3944
4409
  }
3945
- return (0, import_polycss_core11.isSolidTrianglePlan)(plan) ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3946
- TextureTrianglePoly,
3947
- {
3948
- entry: plan,
3949
- textureLighting: effectiveTextureLighting,
3950
- solidPaintDefaults
3951
- },
3952
- plan.index
3953
- ) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4410
+ if ((0, import_polycss_core12.isSolidTrianglePlan)(plan)) {
4411
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4412
+ TextureTrianglePoly,
4413
+ {
4414
+ entry: plan,
4415
+ textureLighting: effectiveTextureLighting,
4416
+ solidPaintDefaults
4417
+ },
4418
+ plan.index
4419
+ );
4420
+ }
4421
+ const cornerGeo = !disabledStrategies?.has("i") ? (0, import_polycss_core17.cornerShapeGeometryForPlan)(plan) : null;
4422
+ if (cornerGeo) {
4423
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4424
+ TextureCornerShapeSolidPoly,
4425
+ {
4426
+ entry: plan,
4427
+ geometry: cornerGeo,
4428
+ textureLighting: effectiveTextureLighting,
4429
+ solidPaintDefaults
4430
+ },
4431
+ plan.index
4432
+ );
4433
+ }
4434
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
3954
4435
  TextureBorderShapePoly,
3955
4436
  {
3956
4437
  entry: plan,
3957
- solidPaintDefaults
4438
+ textureLighting: effectiveTextureLighting,
4439
+ solidPaintDefaults,
4440
+ disabledStrategies
3958
4441
  },
3959
4442
  plan.index
3960
4443
  );
3961
4444
  });
3962
4445
  if (src) {
3963
4446
  if (fetched.loading && fetched.polygons.length === 0) {
3964
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4447
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
3965
4448
  "div",
3966
4449
  {
3967
4450
  ref: wrapperRef,
@@ -3974,7 +4457,7 @@ var PolyMesh = (0, import_react15.forwardRef)(function PolyMesh2({
3974
4457
  );
3975
4458
  }
3976
4459
  if (fetched.error && fetched.polygons.length === 0) {
3977
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4460
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
3978
4461
  "div",
3979
4462
  {
3980
4463
  ref: wrapperRef,
@@ -3987,7 +4470,7 @@ var PolyMesh = (0, import_react15.forwardRef)(function PolyMesh2({
3987
4470
  );
3988
4471
  }
3989
4472
  }
3990
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
4473
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
3991
4474
  "div",
3992
4475
  {
3993
4476
  ref: wrapperRef,
@@ -3997,6 +4480,7 @@ var PolyMesh = (0, import_react15.forwardRef)(function PolyMesh2({
3997
4480
  ...wrapperHandlers,
3998
4481
  children: [
3999
4482
  shadowSvgNode,
4483
+ portaledReceiverShadowSvgs,
4000
4484
  renderedPolygons,
4001
4485
  staticChildren
4002
4486
  ]
@@ -4008,12 +4492,89 @@ function RenderPropPolygon({
4008
4492
  index,
4009
4493
  children
4010
4494
  }) {
4011
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_jsx_runtime8.Fragment, { children: children(polygon, index) });
4495
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_jsx_runtime9.Fragment, { children: children(polygon, index) });
4496
+ }
4497
+
4498
+ // src/scene/PolyIframe.tsx
4499
+ var import_polycss_core18 = require("@layoutit/polycss-core");
4500
+ var import_jsx_runtime10 = require("react/jsx-runtime");
4501
+ function buildIframeTransform(position, rotation, scale, cssWidth, cssHeight) {
4502
+ const sx = typeof scale === "number" ? scale : scale?.[0] ?? 1;
4503
+ const sy = typeof scale === "number" ? scale : scale?.[1] ?? 1;
4504
+ const sz = typeof scale === "number" ? scale : scale?.[2] ?? 1;
4505
+ const hasScale = sx !== 1 || sy !== 1 || sz !== 1;
4506
+ const hasRotation = !!rotation && (!!rotation[0] || !!rotation[1] || !!rotation[2]);
4507
+ const cssX = (position?.[1] ?? 0) * import_polycss_core18.BASE_TILE;
4508
+ const cssY = (position?.[0] ?? 0) * import_polycss_core18.BASE_TILE;
4509
+ const cssZ = (position?.[2] ?? 0) * import_polycss_core18.BASE_TILE;
4510
+ const parts = [];
4511
+ parts.push(`translate3d(${cssX}px, ${cssY}px, ${cssZ}px)`);
4512
+ if (hasRotation) {
4513
+ if (rotation[0]) parts.push(`rotateY(${-rotation[0]}deg)`);
4514
+ if (rotation[1]) parts.push(`rotateX(${-rotation[1]}deg)`);
4515
+ if (rotation[2]) parts.push(`rotateZ(${-rotation[2]}deg)`);
4516
+ }
4517
+ if (hasScale) parts.push(`scale3d(${sx}, ${sy}, ${sz})`);
4518
+ parts.push(`translate(${-cssWidth / 2}px, ${-cssHeight / 2}px)`);
4519
+ return parts.join(" ");
4520
+ }
4521
+ function PolyIframe({
4522
+ src,
4523
+ width,
4524
+ height,
4525
+ position,
4526
+ rotation,
4527
+ scale,
4528
+ allow,
4529
+ sandbox,
4530
+ loading,
4531
+ referrerPolicy,
4532
+ title,
4533
+ className,
4534
+ style
4535
+ }) {
4536
+ const cssW = width * import_polycss_core18.BASE_TILE;
4537
+ const cssH = height * import_polycss_core18.BASE_TILE;
4538
+ const transform = buildIframeTransform(position, rotation, scale, cssW, cssH);
4539
+ const wrapperStyle = {
4540
+ position: "absolute",
4541
+ left: 0,
4542
+ top: 0,
4543
+ transformOrigin: "0 0",
4544
+ transformStyle: "preserve-3d",
4545
+ transform,
4546
+ ...style
4547
+ };
4548
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
4549
+ "div",
4550
+ {
4551
+ className: `polycss-iframe${className ? ` ${className}` : ""}`,
4552
+ style: wrapperStyle,
4553
+ children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
4554
+ "iframe",
4555
+ {
4556
+ src,
4557
+ allow,
4558
+ sandbox,
4559
+ loading,
4560
+ referrerPolicy,
4561
+ title,
4562
+ style: {
4563
+ width: cssW,
4564
+ height: cssH,
4565
+ border: 0,
4566
+ display: "block",
4567
+ background: "#000"
4568
+ }
4569
+ }
4570
+ )
4571
+ }
4572
+ );
4012
4573
  }
4013
4574
 
4014
4575
  // src/scene/PolyGround.tsx
4015
- var import_react16 = require("react");
4016
- var import_jsx_runtime9 = require("react/jsx-runtime");
4576
+ var import_react17 = require("react");
4577
+ var import_jsx_runtime11 = require("react/jsx-runtime");
4017
4578
  function PolyGround({
4018
4579
  size = 6,
4019
4580
  z = 0,
@@ -4021,7 +4582,7 @@ function PolyGround({
4021
4582
  color = "#7d848e",
4022
4583
  className
4023
4584
  }) {
4024
- const polygons = (0, import_react16.useMemo)(() => {
4585
+ const polygons = (0, import_react17.useMemo)(() => {
4025
4586
  const half = size / 2;
4026
4587
  const [cx, cy] = center;
4027
4588
  const vertices = [
@@ -4032,7 +4593,7 @@ function PolyGround({
4032
4593
  ];
4033
4594
  return [{ vertices, color }];
4034
4595
  }, [size, z, center, color]);
4035
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4596
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
4036
4597
  PolyMesh,
4037
4598
  {
4038
4599
  polygons,
@@ -4043,9 +4604,9 @@ function PolyGround({
4043
4604
  }
4044
4605
 
4045
4606
  // src/scene/usePolyMaterial.ts
4046
- var import_react17 = require("react");
4607
+ var import_react18 = require("react");
4047
4608
  function usePolyMaterial(options) {
4048
- return (0, import_react17.useMemo)(
4609
+ return (0, import_react18.useMemo)(
4049
4610
  () => ({ texture: options.texture, key: options.key }),
4050
4611
  // eslint-disable-next-line react-hooks/exhaustive-deps
4051
4612
  [options.texture, options.key]
@@ -4053,8 +4614,8 @@ function usePolyMaterial(options) {
4053
4614
  }
4054
4615
 
4055
4616
  // src/shapes/Poly.tsx
4056
- var import_react18 = require("react");
4057
- var import_jsx_runtime10 = require("react/jsx-runtime");
4617
+ var import_react19 = require("react");
4618
+ var import_jsx_runtime12 = require("react/jsx-runtime");
4058
4619
  var DIRECT_TEXTURE_CSS_DECIMALS = 4;
4059
4620
  function formatCssLength(value, decimals = DIRECT_TEXTURE_CSS_DECIMALS) {
4060
4621
  const next = value.toFixed(decimals).replace(/\.?0+$/, "");
@@ -4106,7 +4667,7 @@ function MaterialDirectPoly({
4106
4667
  Object.entries(plan.polygon.data).map(([k, v]) => [`data-${k}`, String(v)])
4107
4668
  ) : {};
4108
4669
  const elementClassName = className?.trim() || void 0;
4109
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
4670
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
4110
4671
  "i",
4111
4672
  {
4112
4673
  className: elementClassName,
@@ -4159,7 +4720,7 @@ function PolyInner({
4159
4720
  const textureQuality = textureQualityProp ?? context?.textureQuality;
4160
4721
  const polygonColor = baseColorProp ?? color;
4161
4722
  const effectiveTexture = material?.texture ?? texture;
4162
- const atlasPlan = (0, import_react18.useMemo)(
4723
+ const atlasPlan = (0, import_react19.useMemo)(
4163
4724
  () => computeTextureAtlasPlan(
4164
4725
  { vertices, color: polygonColor, texture: effectiveTexture, uvs, data },
4165
4726
  0,
@@ -4180,11 +4741,11 @@ function PolyInner({
4180
4741
  context?.directionalLight
4181
4742
  ]
4182
4743
  );
4183
- const materialUvRect = (0, import_react18.useMemo)(
4744
+ const materialUvRect = (0, import_react19.useMemo)(
4184
4745
  () => material && uvs ? isAxisAlignedRectUVs(uvs) : null,
4185
4746
  [material, uvs]
4186
4747
  );
4187
- const atlasPlans = (0, import_react18.useMemo)(
4748
+ const atlasPlans = (0, import_react19.useMemo)(
4188
4749
  () => materialUvRect ? [] : [atlasPlan],
4189
4750
  [materialUvRect, atlasPlan]
4190
4751
  );
@@ -4237,7 +4798,7 @@ function PolyInner({
4237
4798
  const wrapperTransform = transformParts.length > 0 ? transformParts.join(" ") : void 0;
4238
4799
  let front = null;
4239
4800
  if (materialUvRect && material && atlasPlan) {
4240
- front = /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
4801
+ front = /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
4241
4802
  MaterialDirectPoly,
4242
4803
  {
4243
4804
  plan: atlasPlan,
@@ -4253,7 +4814,7 @@ function PolyInner({
4253
4814
  } else {
4254
4815
  const atlasEntry = textureAtlas.entries[0];
4255
4816
  if (atlasEntry) {
4256
- front = /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
4817
+ front = /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
4257
4818
  TextureAtlasPoly,
4258
4819
  {
4259
4820
  entry: atlasEntry,
@@ -4267,7 +4828,7 @@ function PolyInner({
4267
4828
  }
4268
4829
  );
4269
4830
  } else if (atlasPlan && !atlasPlan.texture) {
4270
- front = (0, import_polycss_core11.isSolidTrianglePlan)(atlasPlan) ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
4831
+ front = (0, import_polycss_core12.isSolidTrianglePlan)(atlasPlan) ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
4271
4832
  TextureTrianglePoly,
4272
4833
  {
4273
4834
  entry: atlasPlan,
@@ -4278,7 +4839,7 @@ function PolyInner({
4278
4839
  domEventHandlers,
4279
4840
  pointerEvents: pointerEventsProp ?? "auto"
4280
4841
  }
4281
- ) : (0, import_polycss_core11.isProjectiveQuadPlan)(atlasPlan) ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
4842
+ ) : (0, import_polycss_core12.isProjectiveQuadPlan)(atlasPlan) ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
4282
4843
  TextureProjectiveSolidPoly,
4283
4844
  {
4284
4845
  entry: atlasPlan,
@@ -4289,10 +4850,11 @@ function PolyInner({
4289
4850
  domEventHandlers,
4290
4851
  pointerEvents: pointerEventsProp ?? "auto"
4291
4852
  }
4292
- ) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
4853
+ ) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
4293
4854
  TextureBorderShapePoly,
4294
4855
  {
4295
4856
  entry: atlasPlan,
4857
+ textureLighting,
4296
4858
  className,
4297
4859
  style: styleProp,
4298
4860
  domAttrs,
@@ -4304,7 +4866,7 @@ function PolyInner({
4304
4866
  }
4305
4867
  if (!front) return null;
4306
4868
  if (!wrapperTransform) return front;
4307
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
4869
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
4308
4870
  "div",
4309
4871
  {
4310
4872
  className: "polycss-poly-wrapper",
@@ -4317,12 +4879,12 @@ function PolyInner({
4317
4879
  }
4318
4880
  );
4319
4881
  }
4320
- var Poly = (0, import_react18.memo)(PolyInner);
4882
+ var Poly = (0, import_react19.memo)(PolyInner);
4321
4883
 
4322
4884
  // src/shapes/PolyShapes.tsx
4323
- var import_react19 = require("react");
4324
- var import_polycss_core16 = require("@layoutit/polycss-core");
4325
- var import_jsx_runtime11 = require("react/jsx-runtime");
4885
+ var import_react20 = require("react");
4886
+ var import_polycss_core19 = require("@layoutit/polycss-core");
4887
+ var import_jsx_runtime13 = require("react/jsx-runtime");
4326
4888
  function PolyBox({
4327
4889
  size,
4328
4890
  center,
@@ -4336,12 +4898,12 @@ function PolyBox({
4336
4898
  faces,
4337
4899
  ...meshProps
4338
4900
  }) {
4339
- const polygons = (0, import_react19.useMemo)(
4340
- () => (0, import_polycss_core16.boxPolygons)({ size, center, min, max, color, texture, material, uvs, data, faces }),
4901
+ const polygons = (0, import_react20.useMemo)(
4902
+ () => (0, import_polycss_core19.boxPolygons)({ size, center, min, max, color, texture, material, uvs, data, faces }),
4341
4903
  // eslint-disable-next-line react-hooks/exhaustive-deps
4342
4904
  [size, center, min, max, color, texture, material, uvs, data, faces]
4343
4905
  );
4344
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(PolyMesh, { polygons, ...meshProps });
4906
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PolyMesh, { polygons, ...meshProps });
4345
4907
  }
4346
4908
  function PolyPlane({
4347
4909
  axis,
@@ -4351,12 +4913,12 @@ function PolyPlane({
4351
4913
  color,
4352
4914
  ...meshProps
4353
4915
  }) {
4354
- const polygons = (0, import_react19.useMemo)(
4355
- () => (0, import_polycss_core16.planePolygons)({ axis, size, offset, along, color }),
4916
+ const polygons = (0, import_react20.useMemo)(
4917
+ () => (0, import_polycss_core19.planePolygons)({ axis, size, offset, along, color }),
4356
4918
  // eslint-disable-next-line react-hooks/exhaustive-deps
4357
4919
  [axis, size, offset, along, color]
4358
4920
  );
4359
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(PolyMesh, { polygons, ...meshProps });
4921
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PolyMesh, { polygons, ...meshProps });
4360
4922
  }
4361
4923
  function PolyRing({
4362
4924
  axis,
@@ -4366,12 +4928,12 @@ function PolyRing({
4366
4928
  color,
4367
4929
  ...meshProps
4368
4930
  }) {
4369
- const polygons = (0, import_react19.useMemo)(
4370
- () => (0, import_polycss_core16.ringPolygons)({ axis, radius, halfThickness, segments, color }),
4931
+ const polygons = (0, import_react20.useMemo)(
4932
+ () => (0, import_polycss_core19.ringPolygons)({ axis, radius, halfThickness, segments, color }),
4371
4933
  // eslint-disable-next-line react-hooks/exhaustive-deps
4372
4934
  [axis, radius, halfThickness, segments, color]
4373
4935
  );
4374
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(PolyMesh, { polygons, ...meshProps });
4936
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PolyMesh, { polygons, ...meshProps });
4375
4937
  }
4376
4938
  function PolyOctahedron({
4377
4939
  center,
@@ -4379,48 +4941,48 @@ function PolyOctahedron({
4379
4941
  color,
4380
4942
  ...meshProps
4381
4943
  }) {
4382
- const polygons = (0, import_react19.useMemo)(
4383
- () => (0, import_polycss_core16.octahedronPolygons)({ center, size, color }),
4944
+ const polygons = (0, import_react20.useMemo)(
4945
+ () => (0, import_polycss_core19.octahedronPolygons)({ center, size, color }),
4384
4946
  // eslint-disable-next-line react-hooks/exhaustive-deps
4385
4947
  [center, size, color]
4386
4948
  );
4387
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(PolyMesh, { polygons, ...meshProps });
4949
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PolyMesh, { polygons, ...meshProps });
4388
4950
  }
4389
4951
  function PolyTetrahedron({
4390
4952
  size,
4391
4953
  color,
4392
4954
  ...meshProps
4393
4955
  }) {
4394
- const polygons = (0, import_react19.useMemo)(
4395
- () => (0, import_polycss_core16.tetrahedronPolygons)({ size, color }),
4956
+ const polygons = (0, import_react20.useMemo)(
4957
+ () => (0, import_polycss_core19.tetrahedronPolygons)({ size, color }),
4396
4958
  // eslint-disable-next-line react-hooks/exhaustive-deps
4397
4959
  [size, color]
4398
4960
  );
4399
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(PolyMesh, { polygons, ...meshProps });
4961
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PolyMesh, { polygons, ...meshProps });
4400
4962
  }
4401
4963
  function PolyIcosahedron({
4402
4964
  size,
4403
4965
  color,
4404
4966
  ...meshProps
4405
4967
  }) {
4406
- const polygons = (0, import_react19.useMemo)(
4407
- () => (0, import_polycss_core16.icosahedronPolygons)({ size, color }),
4968
+ const polygons = (0, import_react20.useMemo)(
4969
+ () => (0, import_polycss_core19.icosahedronPolygons)({ size, color }),
4408
4970
  // eslint-disable-next-line react-hooks/exhaustive-deps
4409
4971
  [size, color]
4410
4972
  );
4411
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(PolyMesh, { polygons, ...meshProps });
4973
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PolyMesh, { polygons, ...meshProps });
4412
4974
  }
4413
4975
  function PolyDodecahedron({
4414
4976
  size,
4415
4977
  color,
4416
4978
  ...meshProps
4417
4979
  }) {
4418
- const polygons = (0, import_react19.useMemo)(
4419
- () => (0, import_polycss_core16.dodecahedronPolygons)({ size, color }),
4980
+ const polygons = (0, import_react20.useMemo)(
4981
+ () => (0, import_polycss_core19.dodecahedronPolygons)({ size, color }),
4420
4982
  // eslint-disable-next-line react-hooks/exhaustive-deps
4421
4983
  [size, color]
4422
4984
  );
4423
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(PolyMesh, { polygons, ...meshProps });
4985
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PolyMesh, { polygons, ...meshProps });
4424
4986
  }
4425
4987
  function PolySphere({
4426
4988
  radius,
@@ -4428,12 +4990,12 @@ function PolySphere({
4428
4990
  color,
4429
4991
  ...meshProps
4430
4992
  }) {
4431
- const polygons = (0, import_react19.useMemo)(
4432
- () => (0, import_polycss_core16.spherePolygons)({ radius, subdivisions, color }),
4993
+ const polygons = (0, import_react20.useMemo)(
4994
+ () => (0, import_polycss_core19.spherePolygons)({ radius, subdivisions, color }),
4433
4995
  // eslint-disable-next-line react-hooks/exhaustive-deps
4434
4996
  [radius, subdivisions, color]
4435
4997
  );
4436
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(PolyMesh, { polygons, ...meshProps });
4998
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PolyMesh, { polygons, ...meshProps });
4437
4999
  }
4438
5000
  function PolyCylinder({
4439
5001
  radius,
@@ -4443,12 +5005,12 @@ function PolyCylinder({
4443
5005
  color,
4444
5006
  ...meshProps
4445
5007
  }) {
4446
- const polygons = (0, import_react19.useMemo)(
4447
- () => (0, import_polycss_core16.cylinderPolygons)({ radius, radiusTop, height, radialSegments, color }),
5008
+ const polygons = (0, import_react20.useMemo)(
5009
+ () => (0, import_polycss_core19.cylinderPolygons)({ radius, radiusTop, height, radialSegments, color }),
4448
5010
  // eslint-disable-next-line react-hooks/exhaustive-deps
4449
5011
  [radius, radiusTop, height, radialSegments, color]
4450
5012
  );
4451
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(PolyMesh, { polygons, ...meshProps });
5013
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PolyMesh, { polygons, ...meshProps });
4452
5014
  }
4453
5015
  function PolyCone({
4454
5016
  radius,
@@ -4457,12 +5019,12 @@ function PolyCone({
4457
5019
  color,
4458
5020
  ...meshProps
4459
5021
  }) {
4460
- const polygons = (0, import_react19.useMemo)(
4461
- () => (0, import_polycss_core16.conePolygons)({ radius, height, radialSegments, color }),
5022
+ const polygons = (0, import_react20.useMemo)(
5023
+ () => (0, import_polycss_core19.conePolygons)({ radius, height, radialSegments, color }),
4462
5024
  // eslint-disable-next-line react-hooks/exhaustive-deps
4463
5025
  [radius, height, radialSegments, color]
4464
5026
  );
4465
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(PolyMesh, { polygons, ...meshProps });
5027
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PolyMesh, { polygons, ...meshProps });
4466
5028
  }
4467
5029
  function PolyTorus({
4468
5030
  radius,
@@ -4472,17 +5034,17 @@ function PolyTorus({
4472
5034
  color,
4473
5035
  ...meshProps
4474
5036
  }) {
4475
- const polygons = (0, import_react19.useMemo)(
4476
- () => (0, import_polycss_core16.torusPolygons)({ radius, tube, radialSegments, tubularSegments, color }),
5037
+ const polygons = (0, import_react20.useMemo)(
5038
+ () => (0, import_polycss_core19.torusPolygons)({ radius, tube, radialSegments, tubularSegments, color }),
4477
5039
  // eslint-disable-next-line react-hooks/exhaustive-deps
4478
5040
  [radius, tube, radialSegments, tubularSegments, color]
4479
5041
  );
4480
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(PolyMesh, { polygons, ...meshProps });
5042
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PolyMesh, { polygons, ...meshProps });
4481
5043
  }
4482
5044
 
4483
5045
  // src/controls/PolyFirstPersonControls.tsx
4484
- var import_react20 = require("react");
4485
- var import_polycss_core17 = require("@layoutit/polycss-core");
5046
+ var import_react21 = require("react");
5047
+ var import_polycss_core20 = require("@layoutit/polycss-core");
4486
5048
  var DEFAULTS = {
4487
5049
  enabled: true,
4488
5050
  lookEnabled: true,
@@ -4540,28 +5102,28 @@ function emitEvent(registry, type) {
4540
5102
  }
4541
5103
  }
4542
5104
  }
4543
- var PolyFirstPersonControls = (0, import_react20.forwardRef)(function PolyFirstPersonControls2(props, ref) {
5105
+ var PolyFirstPersonControls = (0, import_react21.forwardRef)(function PolyFirstPersonControls2(props, ref) {
4544
5106
  const { store, cameraRef, cameraElRef, applyTransformDirect } = useCameraContext();
4545
- const onChangeRef = (0, import_react20.useRef)(props.onChange);
4546
- const onInteractionStartRef = (0, import_react20.useRef)(props.onInteractionStart);
4547
- const onInteractionEndRef = (0, import_react20.useRef)(props.onInteractionEnd);
4548
- (0, import_react20.useEffect)(() => {
5107
+ const onChangeRef = (0, import_react21.useRef)(props.onChange);
5108
+ const onInteractionStartRef = (0, import_react21.useRef)(props.onInteractionStart);
5109
+ const onInteractionEndRef = (0, import_react21.useRef)(props.onInteractionEnd);
5110
+ (0, import_react21.useEffect)(() => {
4549
5111
  onChangeRef.current = props.onChange;
4550
5112
  onInteractionStartRef.current = props.onInteractionStart;
4551
5113
  onInteractionEndRef.current = props.onInteractionEnd;
4552
5114
  });
4553
- const optsRef = (0, import_react20.useRef)(resolveOptions(DEFAULTS, props));
4554
- const cameraOriginRef = (0, import_react20.useRef)([0, 0, 0]);
4555
- const rafIdRef = (0, import_react20.useRef)(null);
4556
- const lastTimeRef = (0, import_react20.useRef)(0);
4557
- const stoppedRef = (0, import_react20.useRef)(false);
4558
- const pointerLockedRef = (0, import_react20.useRef)(false);
4559
- const interactingRef = (0, import_react20.useRef)(false);
4560
- const keysHeldRef = (0, import_react20.useRef)(/* @__PURE__ */ new Set());
4561
- const verticalVelRef = (0, import_react20.useRef)(0);
4562
- const jumpOffsetRef = (0, import_react20.useRef)(0);
4563
- const registryRef = (0, import_react20.useRef)(makeRegistry());
4564
- (0, import_react20.useImperativeHandle)(ref, () => ({
5115
+ const optsRef = (0, import_react21.useRef)(resolveOptions(DEFAULTS, props));
5116
+ const cameraOriginRef = (0, import_react21.useRef)([0, 0, 0]);
5117
+ const rafIdRef = (0, import_react21.useRef)(null);
5118
+ const lastTimeRef = (0, import_react21.useRef)(0);
5119
+ const stoppedRef = (0, import_react21.useRef)(false);
5120
+ const pointerLockedRef = (0, import_react21.useRef)(false);
5121
+ const interactingRef = (0, import_react21.useRef)(false);
5122
+ const keysHeldRef = (0, import_react21.useRef)(/* @__PURE__ */ new Set());
5123
+ const verticalVelRef = (0, import_react21.useRef)(0);
5124
+ const jumpOffsetRef = (0, import_react21.useRef)(0);
5125
+ const registryRef = (0, import_react21.useRef)(makeRegistry());
5126
+ (0, import_react21.useImperativeHandle)(ref, () => ({
4565
5127
  update(partial) {
4566
5128
  const prev = optsRef.current;
4567
5129
  optsRef.current = resolveOptions(prev, partial);
@@ -4658,7 +5220,7 @@ var PolyFirstPersonControls = (0, import_react20.forwardRef)(function PolyFirstP
4658
5220
  const host = cameraElRef.current;
4659
5221
  const perspStr = host ? getComputedStyle(host).perspective : "";
4660
5222
  const n = parseFloat(perspStr);
4661
- return (Number.isFinite(n) && n > 0 ? n : 8e3) / import_polycss_core17.BASE_TILE;
5223
+ return (Number.isFinite(n) && n > 0 ? n : 32e3) / import_polycss_core20.BASE_TILE;
4662
5224
  }
4663
5225
  function deriveTarget() {
4664
5226
  const s = cameraRef.current.state;
@@ -4747,7 +5309,7 @@ var PolyFirstPersonControls = (0, import_react20.forwardRef)(function PolyFirstP
4747
5309
  cancelAnimationFrame(rafIdRef.current);
4748
5310
  rafIdRef.current = null;
4749
5311
  }
4750
- (0, import_react20.useEffect)(() => {
5312
+ (0, import_react21.useEffect)(() => {
4751
5313
  const host = cameraElRef.current;
4752
5314
  if (!host) return;
4753
5315
  const doc = host.ownerDocument ?? document;
@@ -4885,7 +5447,7 @@ var PolyFirstPersonControls = (0, import_react20.forwardRef)(function PolyFirstP
4885
5447
  }
4886
5448
  };
4887
5449
  }, [cameraElRef, cameraRef, applyTransformDirect, store]);
4888
- (0, import_react20.useEffect)(() => {
5450
+ (0, import_react21.useEffect)(() => {
4889
5451
  optsRef.current = resolveOptions(optsRef.current, props);
4890
5452
  const host = cameraElRef.current;
4891
5453
  if (host && !stoppedRef.current) {
@@ -4896,10 +5458,10 @@ var PolyFirstPersonControls = (0, import_react20.forwardRef)(function PolyFirstP
4896
5458
  });
4897
5459
 
4898
5460
  // src/controls/PolyMapControls.tsx
4899
- var import_react21 = require("react");
5461
+ var import_react22 = require("react");
4900
5462
 
4901
5463
  // src/controls/sharedControls.ts
4902
- var import_polycss_core18 = require("@layoutit/polycss-core");
5464
+ var import_polycss_core21 = require("@layoutit/polycss-core");
4903
5465
  var POINTER_DRAG_SPEED = 4;
4904
5466
  function invertFactor(invert) {
4905
5467
  if (invert === true) return -1;
@@ -4910,7 +5472,7 @@ function applyOrbit(dx, dy, s, handle, invert) {
4910
5472
  const f = invertFactor(invert);
4911
5473
  const dX = dx / POINTER_DRAG_SPEED * f;
4912
5474
  const dY = dy / POINTER_DRAG_SPEED * f;
4913
- const rotX = Math.max(0, Math.min(100, s.rotX - dY));
5475
+ const rotX = s.rotX - dY;
4914
5476
  const rotY = ((s.rotY - dX) % 360 + 360) % 360;
4915
5477
  handle.update({ rotX, rotY });
4916
5478
  }
@@ -4920,7 +5482,7 @@ function applyPan(dx, dy, s, handle, _invert) {
4920
5482
  const cosRotX = cosRotXRaw >= 0 ? Math.max(0.1, cosRotXRaw) : Math.min(-0.1, cosRotXRaw);
4921
5483
  const cZ = Math.cos(s.rotY * Math.PI / 180);
4922
5484
  const sZ = Math.sin(s.rotY * Math.PI / 180);
4923
- const k = z * import_polycss_core18.BASE_TILE;
5485
+ const k = z * import_polycss_core21.BASE_TILE;
4924
5486
  const targetD0 = (dx * sZ - dy * cZ / cosRotX) / k;
4925
5487
  const targetD1 = -(dx * cZ + dy * sZ / cosRotX) / k;
4926
5488
  const t = s.target;
@@ -5069,19 +5631,19 @@ function PolyMapControls({
5069
5631
  onInteractionEnd
5070
5632
  }) {
5071
5633
  const { store, cameraRef, cameraElRef, applyTransformDirect } = useCameraContext();
5072
- const dragRef = (0, import_react21.useRef)(drag);
5073
- const wheelRef = (0, import_react21.useRef)(wheel);
5074
- const dollyRef = (0, import_react21.useRef)(dolly);
5075
- const invertRef = (0, import_react21.useRef)(invert);
5076
- const zoomMinRef = (0, import_react21.useRef)(minZoom);
5077
- const zoomMaxRef = (0, import_react21.useRef)(maxZoom);
5078
- const distanceMinRef = (0, import_react21.useRef)(minDistance);
5079
- const distanceMaxRef = (0, import_react21.useRef)(maxDistance);
5080
- const animateRef = (0, import_react21.useRef)(animate);
5081
- const onChangeRef = (0, import_react21.useRef)(onChange);
5082
- const onInteractionStartRef = (0, import_react21.useRef)(onInteractionStart);
5083
- const onInteractionEndRef = (0, import_react21.useRef)(onInteractionEnd);
5084
- (0, import_react21.useEffect)(() => {
5634
+ const dragRef = (0, import_react22.useRef)(drag);
5635
+ const wheelRef = (0, import_react22.useRef)(wheel);
5636
+ const dollyRef = (0, import_react22.useRef)(dolly);
5637
+ const invertRef = (0, import_react22.useRef)(invert);
5638
+ const zoomMinRef = (0, import_react22.useRef)(minZoom);
5639
+ const zoomMaxRef = (0, import_react22.useRef)(maxZoom);
5640
+ const distanceMinRef = (0, import_react22.useRef)(minDistance);
5641
+ const distanceMaxRef = (0, import_react22.useRef)(maxDistance);
5642
+ const animateRef = (0, import_react22.useRef)(animate);
5643
+ const onChangeRef = (0, import_react22.useRef)(onChange);
5644
+ const onInteractionStartRef = (0, import_react22.useRef)(onInteractionStart);
5645
+ const onInteractionEndRef = (0, import_react22.useRef)(onInteractionEnd);
5646
+ (0, import_react22.useEffect)(() => {
5085
5647
  dragRef.current = drag;
5086
5648
  wheelRef.current = wheel;
5087
5649
  dollyRef.current = dolly;
@@ -5126,8 +5688,8 @@ function PolyMapControls({
5126
5688
  console.error("[polycss/react] PolyMapControls onInteractionEnd threw:", err);
5127
5689
  }
5128
5690
  };
5129
- const animationPausedShared = (0, import_react21.useRef)({ value: false }).current;
5130
- (0, import_react21.useEffect)(() => {
5691
+ const animationPausedShared = (0, import_react22.useRef)({ value: false }).current;
5692
+ (0, import_react22.useEffect)(() => {
5131
5693
  if (!drag) return;
5132
5694
  const el = cameraElRef.current;
5133
5695
  if (!el) return;
@@ -5237,12 +5799,12 @@ function PolyMapControls({
5237
5799
  el.style.userSelect = "";
5238
5800
  };
5239
5801
  }, [drag, applyTransformDirect, cameraElRef, cameraRef, store]);
5240
- (0, import_react21.useEffect)(
5802
+ (0, import_react22.useEffect)(
5241
5803
  () => makeWheelEffect({ wheel, dollyRef, wheelRef, zoomMinRef, zoomMaxRef, distanceMinRef, distanceMaxRef, cameraElRef, cameraRef, applyTransformDirect, store, fireStart, fireChange, fireEnd }),
5242
5804
  // eslint-disable-next-line react-hooks/exhaustive-deps
5243
5805
  [wheel, applyTransformDirect, cameraElRef, cameraRef, store]
5244
5806
  );
5245
- (0, import_react21.useEffect)(
5807
+ (0, import_react22.useEffect)(
5246
5808
  () => makeAnimateEffect({ animateOn: !!animate, animateRef, animationPausedShared, applyTransformDirect, cameraRef, store, fireChange }),
5247
5809
  // eslint-disable-next-line react-hooks/exhaustive-deps
5248
5810
  [!!animate, animationPausedShared, applyTransformDirect, cameraRef, store]
@@ -5251,7 +5813,7 @@ function PolyMapControls({
5251
5813
  }
5252
5814
 
5253
5815
  // src/controls/PolyOrbitControls.tsx
5254
- var import_react22 = require("react");
5816
+ var import_react23 = require("react");
5255
5817
  function PolyOrbitControls({
5256
5818
  drag = true,
5257
5819
  wheel = true,
@@ -5267,19 +5829,19 @@ function PolyOrbitControls({
5267
5829
  onInteractionEnd
5268
5830
  }) {
5269
5831
  const { store, cameraRef, cameraElRef, applyTransformDirect } = useCameraContext();
5270
- const dragRef = (0, import_react22.useRef)(drag);
5271
- const wheelRef = (0, import_react22.useRef)(wheel);
5272
- const dollyRef = (0, import_react22.useRef)(dolly);
5273
- const invertRef = (0, import_react22.useRef)(invert);
5274
- const zoomMinRef = (0, import_react22.useRef)(minZoom);
5275
- const zoomMaxRef = (0, import_react22.useRef)(maxZoom);
5276
- const distanceMinRef = (0, import_react22.useRef)(minDistance);
5277
- const distanceMaxRef = (0, import_react22.useRef)(maxDistance);
5278
- const animateRef = (0, import_react22.useRef)(animate);
5279
- const onChangeRef = (0, import_react22.useRef)(onChange);
5280
- const onInteractionStartRef = (0, import_react22.useRef)(onInteractionStart);
5281
- const onInteractionEndRef = (0, import_react22.useRef)(onInteractionEnd);
5282
- (0, import_react22.useEffect)(() => {
5832
+ const dragRef = (0, import_react23.useRef)(drag);
5833
+ const wheelRef = (0, import_react23.useRef)(wheel);
5834
+ const dollyRef = (0, import_react23.useRef)(dolly);
5835
+ const invertRef = (0, import_react23.useRef)(invert);
5836
+ const zoomMinRef = (0, import_react23.useRef)(minZoom);
5837
+ const zoomMaxRef = (0, import_react23.useRef)(maxZoom);
5838
+ const distanceMinRef = (0, import_react23.useRef)(minDistance);
5839
+ const distanceMaxRef = (0, import_react23.useRef)(maxDistance);
5840
+ const animateRef = (0, import_react23.useRef)(animate);
5841
+ const onChangeRef = (0, import_react23.useRef)(onChange);
5842
+ const onInteractionStartRef = (0, import_react23.useRef)(onInteractionStart);
5843
+ const onInteractionEndRef = (0, import_react23.useRef)(onInteractionEnd);
5844
+ (0, import_react23.useEffect)(() => {
5283
5845
  dragRef.current = drag;
5284
5846
  wheelRef.current = wheel;
5285
5847
  dollyRef.current = dolly;
@@ -5324,8 +5886,8 @@ function PolyOrbitControls({
5324
5886
  console.error("[polycss/react] PolyOrbitControls onInteractionEnd threw:", err);
5325
5887
  }
5326
5888
  };
5327
- const animationPausedShared = (0, import_react22.useRef)({ value: false }).current;
5328
- (0, import_react22.useEffect)(() => {
5889
+ const animationPausedShared = (0, import_react23.useRef)({ value: false }).current;
5890
+ (0, import_react23.useEffect)(() => {
5329
5891
  if (!drag) return;
5330
5892
  const el = cameraElRef.current;
5331
5893
  if (!el) return;
@@ -5435,12 +5997,12 @@ function PolyOrbitControls({
5435
5997
  el.style.userSelect = "";
5436
5998
  };
5437
5999
  }, [drag, applyTransformDirect, cameraElRef, cameraRef, store]);
5438
- (0, import_react22.useEffect)(
6000
+ (0, import_react23.useEffect)(
5439
6001
  () => makeWheelEffect({ wheel, dollyRef, wheelRef, zoomMinRef, zoomMaxRef, distanceMinRef, distanceMaxRef, cameraElRef, cameraRef, applyTransformDirect, store, fireStart, fireChange, fireEnd }),
5440
6002
  // eslint-disable-next-line react-hooks/exhaustive-deps
5441
6003
  [wheel, applyTransformDirect, cameraElRef, cameraRef, store]
5442
6004
  );
5443
- (0, import_react22.useEffect)(
6005
+ (0, import_react23.useEffect)(
5444
6006
  () => makeAnimateEffect({ animateOn: !!animate, animateRef, animationPausedShared, applyTransformDirect, cameraRef, store, fireChange }),
5445
6007
  // eslint-disable-next-line react-hooks/exhaustive-deps
5446
6008
  [!!animate, animationPausedShared, applyTransformDirect, cameraRef, store]
@@ -5449,10 +6011,10 @@ function PolyOrbitControls({
5449
6011
  }
5450
6012
 
5451
6013
  // src/controls/TransformControls.tsx
5452
- var import_react23 = require("react");
5453
- var import_polycss_core19 = require("@layoutit/polycss-core");
5454
- var import_jsx_runtime12 = require("react/jsx-runtime");
5455
- var FALLBACK_CAMERA_STORE = createSceneStore(import_polycss_core19.DEFAULT_CAMERA_STATE);
6014
+ var import_react24 = require("react");
6015
+ var import_polycss_core22 = require("@layoutit/polycss-core");
6016
+ var import_jsx_runtime14 = require("react/jsx-runtime");
6017
+ var FALLBACK_CAMERA_STORE = createSceneStore(import_polycss_core22.DEFAULT_CAMERA_STATE);
5456
6018
  var COLOR_X = "#ff3653";
5457
6019
  var COLOR_Y = "#8adb00";
5458
6020
  var COLOR_Z = "#2c8fff";
@@ -5729,7 +6291,7 @@ function startRingDrag(opts) {
5729
6291
  const startRotation = target.getRotation() ?? [0, 0, 0];
5730
6292
  onMouseDown?.();
5731
6293
  onDraggingChanged?.(true);
5732
- const qStart = (0, import_polycss_core19.quatFromEulerXYZ)(startRotation);
6294
+ const qStart = (0, import_polycss_core22.quatFromEulerXYZ)(startRotation);
5733
6295
  const handleMove = (ev) => {
5734
6296
  const a = Math.atan2(ev.clientY - centerY, ev.clientX - centerX);
5735
6297
  let d = a - lastAngle;
@@ -5741,8 +6303,8 @@ function startRingDrag(opts) {
5741
6303
  degrees = snap(degrees, rotationSnap);
5742
6304
  const axisVec = [0, 0, 0];
5743
6305
  axisVec[cssAxis] = 1;
5744
- const qDelta = (0, import_polycss_core19.quatFromAxisAngle)(axisVec, degrees * Math.PI / 180);
5745
- const newRotation = (0, import_polycss_core19.eulerXYZFromQuat)((0, import_polycss_core19.quatMultiply)(qDelta, qStart));
6306
+ const qDelta = (0, import_polycss_core22.quatFromAxisAngle)(axisVec, degrees * Math.PI / 180);
6307
+ const newRotation = (0, import_polycss_core22.eulerXYZFromQuat)((0, import_polycss_core22.quatMultiply)(qDelta, qStart));
5746
6308
  onObjectChange?.({ object: target, rotation: newRotation });
5747
6309
  onChange?.();
5748
6310
  };
@@ -5781,11 +6343,11 @@ function PolyTransformControls({
5781
6343
  onMouseUp,
5782
6344
  onDraggingChanged
5783
6345
  }) {
5784
- const [, forceRender] = (0, import_react23.useState)(0);
5785
- (0, import_react23.useEffect)(() => {
6346
+ const [, forceRender] = (0, import_react24.useState)(0);
6347
+ (0, import_react24.useEffect)(() => {
5786
6348
  forceRender((n) => n + 1);
5787
6349
  }, [object]);
5788
- const cameraCtxForRot = (0, import_react23.useContext)(PolyCameraContext);
6350
+ const cameraCtxForRot = (0, import_react24.useContext)(PolyCameraContext);
5789
6351
  const rotX = useStoreSelector(
5790
6352
  cameraCtxForRot?.store ?? FALLBACK_CAMERA_STORE,
5791
6353
  (s) => s.cameraState.rotX
@@ -5794,11 +6356,11 @@ function PolyTransformControls({
5794
6356
  cameraCtxForRot?.store ?? FALLBACK_CAMERA_STORE,
5795
6357
  (s) => s.cameraState.rotY
5796
6358
  );
5797
- const [hoveredKey, setHoveredKey] = (0, import_react23.useState)(null);
5798
- const [draggingKey, setDraggingKey] = (0, import_react23.useState)(null);
5799
- const cameraCtx = (0, import_react23.useContext)(PolyCameraContext);
6359
+ const [hoveredKey, setHoveredKey] = (0, import_react24.useState)(null);
6360
+ const [draggingKey, setDraggingKey] = (0, import_react24.useState)(null);
6361
+ const cameraCtx = (0, import_react24.useContext)(PolyCameraContext);
5800
6362
  const cameraElRef = cameraCtx?.cameraElRef;
5801
- const dragRef = (0, import_react23.useRef)({
6363
+ const dragRef = (0, import_react24.useRef)({
5802
6364
  target: null,
5803
6365
  mode: "translate",
5804
6366
  shaftLengthCss: 0,
@@ -5807,7 +6369,7 @@ function PolyTransformControls({
5807
6369
  translationSnap: null,
5808
6370
  rotationSnap: null
5809
6371
  });
5810
- (0, import_react23.useEffect)(() => {
6372
+ (0, import_react24.useEffect)(() => {
5811
6373
  const cameraEl = cameraElRef?.current;
5812
6374
  if (!cameraEl) return;
5813
6375
  const onPointerDown = (event) => {
@@ -5929,10 +6491,13 @@ function PolyTransformControls({
5929
6491
  const position = target.getPosition() ?? [0, 0, 0];
5930
6492
  const polygons = target.getPolygons();
5931
6493
  const bboxCenter = gizmoCenterForMesh(polygons);
6494
+ const cssPosX = position[1] * SCENE_TILE_SIZE;
6495
+ const cssPosY = position[0] * SCENE_TILE_SIZE;
6496
+ const cssPosZ = position[2] * SCENE_TILE_SIZE;
5932
6497
  const wrapperPos = [
5933
- position[0] + bboxCenter[0],
5934
- position[1] + bboxCenter[1],
5935
- position[2] + bboxCenter[2]
6498
+ cssPosX + bboxCenter[0],
6499
+ cssPosY + bboxCenter[1],
6500
+ cssPosZ + bboxCenter[2]
5936
6501
  ];
5937
6502
  const baseLength = gizmoLengthForMesh(polygons);
5938
6503
  const shaftLengthCss = baseLength * size;
@@ -5961,7 +6526,7 @@ function PolyTransformControls({
5961
6526
  // own; descendants opt in via the default `auto`.
5962
6527
  zIndex: 1e3
5963
6528
  };
5964
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
6529
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
5965
6530
  "div",
5966
6531
  {
5967
6532
  className: "polycss-transform-controls",
@@ -5977,7 +6542,7 @@ function PolyTransformControls({
5977
6542
  const dragging = draggingKey === spec.key;
5978
6543
  const alpha = dragging ? ALPHA_DRAGGING : hovered ? ALPHA_HOVER : ALPHA_IDLE;
5979
6544
  const backFacing = isAxisBackFacing(spec.cssAxis, spec.sign, rotX, rotY);
5980
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
6545
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
5981
6546
  TranslateArrow,
5982
6547
  {
5983
6548
  cssAxis: spec.cssAxis,
@@ -6009,7 +6574,7 @@ function PolyTransformControls({
6009
6574
  const hovered = hoveredKey === spec.key;
6010
6575
  const dragging = draggingKey === spec.key;
6011
6576
  const alpha = dragging ? ALPHA_DRAGGING : hovered ? ALPHA_HOVER : ALPHA_IDLE;
6012
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
6577
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
6013
6578
  TranslatePlane,
6014
6579
  {
6015
6580
  axisA: spec.axisA,
@@ -6041,7 +6606,7 @@ function PolyTransformControls({
6041
6606
  const hovered = hoveredKey === spec.key;
6042
6607
  const dragging = draggingKey === spec.key;
6043
6608
  const alpha = dragging ? ALPHA_DRAGGING : hovered ? ALPHA_HOVER : ALPHA_IDLE;
6044
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
6609
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
6045
6610
  RotateRing,
6046
6611
  {
6047
6612
  cssAxis: spec.cssAxis,
@@ -6086,7 +6651,7 @@ function TranslateArrow({
6086
6651
  onDraggingStart,
6087
6652
  onDraggingStop
6088
6653
  }) {
6089
- const cbRef = (0, import_react23.useRef)({
6654
+ const cbRef = (0, import_react24.useRef)({
6090
6655
  onChange,
6091
6656
  onObjectChange,
6092
6657
  onMouseDown,
@@ -6108,9 +6673,9 @@ function TranslateArrow({
6108
6673
  enabled,
6109
6674
  translationSnap
6110
6675
  };
6111
- const polygons = (0, import_react23.useMemo)(() => {
6676
+ const polygons = (0, import_react24.useMemo)(() => {
6112
6677
  const lengthWorld = shaftLengthCss / SCENE_TILE_SIZE;
6113
- return (0, import_polycss_core19.arrowPolygons)({
6678
+ return (0, import_polycss_core22.arrowPolygons)({
6114
6679
  axis: WORLD_AXIS_FOR_CSS[cssAxis],
6115
6680
  sign,
6116
6681
  shaftLength: lengthWorld,
@@ -6121,7 +6686,7 @@ function TranslateArrow({
6121
6686
  shaft: includeShaft
6122
6687
  });
6123
6688
  }, [cssAxis, sign, color, shaftLengthCss, includeShaft]);
6124
- const onPointerDown = (0, import_react23.useCallback)(
6689
+ const onPointerDown = (0, import_react24.useCallback)(
6125
6690
  (e) => {
6126
6691
  if (!cbRef.current.enabled) return;
6127
6692
  e.stopPropagation();
@@ -6150,7 +6715,7 @@ function TranslateArrow({
6150
6715
  },
6151
6716
  [cssAxis, sign, target, shaftLengthCss]
6152
6717
  );
6153
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
6718
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
6154
6719
  PolyMesh,
6155
6720
  {
6156
6721
  polygons,
@@ -6183,7 +6748,7 @@ function TranslatePlane({
6183
6748
  onDraggingStart,
6184
6749
  onDraggingStop
6185
6750
  }) {
6186
- const cbRef = (0, import_react23.useRef)({
6751
+ const cbRef = (0, import_react24.useRef)({
6187
6752
  onChange,
6188
6753
  onObjectChange,
6189
6754
  onMouseDown,
@@ -6205,7 +6770,7 @@ function TranslatePlane({
6205
6770
  enabled,
6206
6771
  translationSnap
6207
6772
  };
6208
- const polygons = (0, import_react23.useMemo)(() => {
6773
+ const polygons = (0, import_react24.useMemo)(() => {
6209
6774
  const lengthWorld = shaftLengthCss / SCENE_TILE_SIZE;
6210
6775
  const worldPerp = WORLD_AXIS_FOR_CSS[perpAxis];
6211
6776
  const worldA = (worldPerp + 1) % 3;
@@ -6215,14 +6780,14 @@ function TranslatePlane({
6215
6780
  const signA = isAxisBackFacing(cssAForOffset, 1, rotX, rotY) ? -1 : 1;
6216
6781
  const signB = isAxisBackFacing(cssBForOffset, 1, rotX, rotY) ? -1 : 1;
6217
6782
  const mag = lengthWorld * PLANE_OFFSET_RATIO;
6218
- return (0, import_polycss_core19.planePolygons)({
6783
+ return (0, import_polycss_core22.planePolygons)({
6219
6784
  axis: worldPerp,
6220
6785
  size: lengthWorld * PLANE_HALF_SIZE_RATIO,
6221
6786
  offset: [signA * mag, signB * mag],
6222
6787
  color
6223
6788
  });
6224
6789
  }, [perpAxis, color, shaftLengthCss, rotX, rotY]);
6225
- const onPointerDown = (0, import_react23.useCallback)(
6790
+ const onPointerDown = (0, import_react24.useCallback)(
6226
6791
  (e) => {
6227
6792
  if (!cbRef.current.enabled) return;
6228
6793
  e.stopPropagation();
@@ -6251,7 +6816,7 @@ function TranslatePlane({
6251
6816
  },
6252
6817
  [axisA, axisB, target, shaftLengthCss]
6253
6818
  );
6254
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
6819
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
6255
6820
  PolyMesh,
6256
6821
  {
6257
6822
  polygons,
@@ -6280,7 +6845,7 @@ function RotateRing({
6280
6845
  onDraggingStart,
6281
6846
  onDraggingStop
6282
6847
  }) {
6283
- const cbRef = (0, import_react23.useRef)({
6848
+ const cbRef = (0, import_react24.useRef)({
6284
6849
  onChange,
6285
6850
  onObjectChange,
6286
6851
  onMouseDown,
@@ -6302,10 +6867,10 @@ function RotateRing({
6302
6867
  enabled,
6303
6868
  rotationSnap
6304
6869
  };
6305
- const polygons = (0, import_react23.useMemo)(() => {
6870
+ const polygons = (0, import_react24.useMemo)(() => {
6306
6871
  const radiusWorld = radiusCss / SCENE_TILE_SIZE;
6307
6872
  const outerWorld = radiusWorld * RING_QUAD_OUTER_RATIO;
6308
- return (0, import_polycss_core19.ringQuadPolygons)({
6873
+ return (0, import_polycss_core22.ringQuadPolygons)({
6309
6874
  axis: WORLD_AXIS_FOR_CSS[cssAxis],
6310
6875
  outerRadius: outerWorld,
6311
6876
  color
@@ -6313,7 +6878,7 @@ function RotateRing({
6313
6878
  }, [cssAxis, color, radiusCss]);
6314
6879
  const ringInnerRatio = (1 - RING_HALF_THICKNESS_RATIO) / RING_QUAD_OUTER_RATIO;
6315
6880
  const ringOuterRatio = (1 + RING_HALF_THICKNESS_RATIO) / RING_QUAD_OUTER_RATIO;
6316
- const onPointerDown = (0, import_react23.useCallback)(
6881
+ const onPointerDown = (0, import_react24.useCallback)(
6317
6882
  (e) => {
6318
6883
  if (!cbRef.current.enabled) return;
6319
6884
  e.stopPropagation();
@@ -6340,7 +6905,7 @@ function RotateRing({
6340
6905
  },
6341
6906
  [cssAxis, target]
6342
6907
  );
6343
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
6908
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
6344
6909
  PolyMesh,
6345
6910
  {
6346
6911
  polygons,
@@ -6358,9 +6923,9 @@ function RotateRing({
6358
6923
  }
6359
6924
 
6360
6925
  // src/select/Select.tsx
6361
- var import_react24 = require("react");
6362
- var import_jsx_runtime13 = require("react/jsx-runtime");
6363
- var SelectContext = (0, import_react24.createContext)(null);
6926
+ var import_react25 = require("react");
6927
+ var import_jsx_runtime15 = require("react/jsx-runtime");
6928
+ var SelectContext = (0, import_react25.createContext)(null);
6364
6929
  function PolySelect({
6365
6930
  multiple = false,
6366
6931
  filter,
@@ -6371,19 +6936,19 @@ function PolySelect({
6371
6936
  className,
6372
6937
  style
6373
6938
  }) {
6374
- const [selected, setSelectedState] = (0, import_react24.useState)([]);
6375
- const filterRef = (0, import_react24.useRef)(filter);
6939
+ const [selected, setSelectedState] = (0, import_react25.useState)([]);
6940
+ const filterRef = (0, import_react25.useRef)(filter);
6376
6941
  filterRef.current = filter;
6377
- const onChangeRef = (0, import_react24.useRef)(onChange);
6942
+ const onChangeRef = (0, import_react25.useRef)(onChange);
6378
6943
  onChangeRef.current = onChange;
6379
- const multipleRef = (0, import_react24.useRef)(multiple);
6944
+ const multipleRef = (0, import_react25.useRef)(multiple);
6380
6945
  multipleRef.current = multiple;
6381
- const apply = (0, import_react24.useCallback)((next) => {
6946
+ const apply = (0, import_react25.useCallback)((next) => {
6382
6947
  const filtered = filterRef.current ? filterRef.current(next) : next;
6383
6948
  setSelectedState(filtered);
6384
6949
  if (onChangeRef.current) onChangeRef.current(filtered);
6385
6950
  }, []);
6386
- const api = (0, import_react24.useMemo)(() => {
6951
+ const api = (0, import_react25.useMemo)(() => {
6387
6952
  return {
6388
6953
  selected,
6389
6954
  set: (next) => apply(next),
@@ -6416,14 +6981,14 @@ function PolySelect({
6416
6981
  has: (h) => selected.includes(h)
6417
6982
  };
6418
6983
  }, [selected, apply]);
6419
- const wrapperRef = (0, import_react24.useRef)(null);
6420
- const cameraCtx = (0, import_react24.useContext)(PolyCameraContext);
6984
+ const wrapperRef = (0, import_react25.useRef)(null);
6985
+ const cameraCtx = (0, import_react25.useContext)(PolyCameraContext);
6421
6986
  const cameraElRef = cameraCtx?.cameraElRef;
6422
- const clearOnMissRef = (0, import_react24.useRef)(clearOnMiss);
6987
+ const clearOnMissRef = (0, import_react25.useRef)(clearOnMiss);
6423
6988
  clearOnMissRef.current = clearOnMiss;
6424
- const onPointerMissedRef = (0, import_react24.useRef)(onPointerMissed);
6989
+ const onPointerMissedRef = (0, import_react25.useRef)(onPointerMissed);
6425
6990
  onPointerMissedRef.current = onPointerMissed;
6426
- const findMeshUnderPoint2 = (0, import_react24.useCallback)(
6991
+ const findMeshUnderPoint2 = (0, import_react25.useCallback)(
6427
6992
  (clientX, clientY) => findMeshUnderPoint(
6428
6993
  clientX,
6429
6994
  clientY,
@@ -6431,7 +6996,7 @@ function PolySelect({
6431
6996
  ),
6432
6997
  []
6433
6998
  );
6434
- (0, import_react24.useEffect)(() => {
6999
+ (0, import_react25.useEffect)(() => {
6435
7000
  const cameraEl = cameraElRef?.current;
6436
7001
  if (!cameraEl) return;
6437
7002
  const onClick = (event) => {
@@ -6478,7 +7043,7 @@ function PolySelect({
6478
7043
  }
6479
7044
  };
6480
7045
  const wrapperStyle = { display: "contents", ...style };
6481
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(SelectContext.Provider, { value: api, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
7046
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(SelectContext.Provider, { value: api, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
6482
7047
  "div",
6483
7048
  {
6484
7049
  ref: wrapperRef,
@@ -6491,10 +7056,10 @@ function PolySelect({
6491
7056
  ) });
6492
7057
  }
6493
7058
  function usePolySelect() {
6494
- return (0, import_react24.useContext)(SelectContext)?.selected ?? [];
7059
+ return (0, import_react25.useContext)(SelectContext)?.selected ?? [];
6495
7060
  }
6496
7061
  function usePolySelectionApi() {
6497
- const ctx = (0, import_react24.useContext)(SelectContext);
7062
+ const ctx = (0, import_react25.useContext)(SelectContext);
6498
7063
  if (!ctx) {
6499
7064
  throw new Error("polycss: usePolySelectionApi must be used inside <PolySelect>.");
6500
7065
  }
@@ -6502,9 +7067,9 @@ function usePolySelectionApi() {
6502
7067
  }
6503
7068
 
6504
7069
  // src/helpers/PolyAxesHelper.tsx
6505
- var import_react25 = require("react");
6506
- var import_polycss_core20 = require("@layoutit/polycss-core");
6507
- var import_jsx_runtime14 = require("react/jsx-runtime");
7070
+ var import_react26 = require("react");
7071
+ var import_polycss_core23 = require("@layoutit/polycss-core");
7072
+ var import_jsx_runtime16 = require("react/jsx-runtime");
6508
7073
  function PolyAxesHelper({
6509
7074
  size,
6510
7075
  thickness,
@@ -6513,18 +7078,17 @@ function PolyAxesHelper({
6513
7078
  yColor,
6514
7079
  zColor
6515
7080
  }) {
6516
- const polygons = (0, import_react25.useMemo)(
6517
- () => (0, import_polycss_core20.axesHelperPolygons)({ size, thickness, negative, xColor, yColor, zColor }),
7081
+ const polygons = (0, import_react26.useMemo)(
7082
+ () => (0, import_polycss_core23.axesHelperPolygons)({ size, thickness, negative, xColor, yColor, zColor }),
6518
7083
  [size, thickness, negative, xColor, yColor, zColor]
6519
7084
  );
6520
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(PolyMesh, { polygons });
7085
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(PolyMesh, { polygons, merge: false });
6521
7086
  }
6522
7087
 
6523
7088
  // src/helpers/PolyDirectionalLightHelper.tsx
6524
- var import_react26 = require("react");
6525
- var import_polycss_core21 = require("@layoutit/polycss-core");
6526
- var import_jsx_runtime15 = require("react/jsx-runtime");
6527
- var TILE = 50;
7089
+ var import_react27 = require("react");
7090
+ var import_polycss_core24 = require("@layoutit/polycss-core");
7091
+ var import_jsx_runtime17 = require("react/jsx-runtime");
6528
7092
  function PolyDirectionalLightHelper({
6529
7093
  light,
6530
7094
  target,
@@ -6533,26 +7097,27 @@ function PolyDirectionalLightHelper({
6533
7097
  color
6534
7098
  }) {
6535
7099
  const swatch = color ?? light.color ?? "#ffd54a";
6536
- const polygons = (0, import_react26.useMemo)(
6537
- () => (0, import_polycss_core21.octahedronPolygons)({ center: [0, 0, 0], size, color: swatch }),
7100
+ const polygons = (0, import_react27.useMemo)(
7101
+ () => (0, import_polycss_core24.octahedronPolygons)({ center: [0, 0, 0], size, color: swatch }),
6538
7102
  [size, swatch]
6539
7103
  );
6540
- const meshPosition = (0, import_react26.useMemo)(() => {
7104
+ const meshPosition = (0, import_react27.useMemo)(() => {
6541
7105
  const [dx, dy, dz] = light.direction;
6542
7106
  const len = Math.hypot(dx, dy, dz) || 1;
6543
7107
  const tx = target?.[0] ?? 0;
6544
7108
  const ty = target?.[1] ?? 0;
6545
7109
  const tz = target?.[2] ?? 0;
6546
- const worldX = tx + dy / len * distance;
6547
- const worldY = ty + dx / len * distance;
6548
- const worldZ = tz + dz / len * distance;
6549
- return [worldY * TILE, worldX * TILE, worldZ * TILE];
7110
+ return [
7111
+ tx + dx / len * distance,
7112
+ ty + dy / len * distance,
7113
+ tz + dz / len * distance
7114
+ ];
6550
7115
  }, [light.direction, target, distance]);
6551
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(PolyMesh, { polygons, position: meshPosition });
7116
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(PolyMesh, { polygons, position: meshPosition, merge: false });
6552
7117
  }
6553
7118
 
6554
7119
  // src/styles/colorResolver.ts
6555
- var import_polycss_core22 = require("@layoutit/polycss-core");
7120
+ var import_polycss_core25 = require("@layoutit/polycss-core");
6556
7121
 
6557
7122
  // src/renderStats.ts
6558
7123
  var ZERO_SURFACE_LEAF_COUNTS = {
@@ -6606,7 +7171,7 @@ function collectPolyRenderStats(root, optionsOrPolygonCount) {
6606
7171
  surfaceLeafCounts.clippedSolid += queryCount(scope, "i");
6607
7172
  surfaceLeafCounts.atlas += queryCount(scope, "s");
6608
7173
  surfaceLeafCounts.stableTriangle += queryCount(scope, "u");
6609
- shadowLeafCount += queryCount(scope, "q");
7174
+ shadowLeafCount += queryCount(scope, "q, .polycss-shadow-svg");
6610
7175
  bucketCount += queryCount(scope, ".polycss-bucket");
6611
7176
  }
6612
7177
  const mountedPolygonLeafCount = surfaceLeafCounts.quad + surfaceLeafCounts.clippedSolid + surfaceLeafCounts.atlas + surfaceLeafCounts.stableTriangle;
@@ -6620,17 +7185,17 @@ function collectPolyRenderStats(root, optionsOrPolygonCount) {
6620
7185
  }
6621
7186
 
6622
7187
  // src/animation/usePolyAnimation.ts
6623
- var import_react27 = require("react");
6624
- var import_polycss_core23 = require("@layoutit/polycss-core");
7188
+ var import_react28 = require("react");
7189
+ var import_polycss_core26 = require("@layoutit/polycss-core");
6625
7190
  function resolveRoot(rootArg) {
6626
7191
  if (!rootArg) return null;
6627
7192
  if ("current" in rootArg) return rootArg.current;
6628
7193
  return rootArg;
6629
7194
  }
6630
7195
  function usePolyAnimation(clips, controller, root) {
6631
- const internalRef = (0, import_react27.useRef)(null);
6632
- const mixerRef = (0, import_react27.useRef)(null);
6633
- (0, import_react27.useEffect)(() => {
7196
+ const internalRef = (0, import_react28.useRef)(null);
7197
+ const mixerRef = (0, import_react28.useRef)(null);
7198
+ (0, import_react28.useEffect)(() => {
6634
7199
  if (!clips || clips.length === 0 || !controller) {
6635
7200
  mixerRef.current = null;
6636
7201
  return;
@@ -6640,14 +7205,14 @@ function usePolyAnimation(clips, controller, root) {
6640
7205
  mixerRef.current = null;
6641
7206
  return;
6642
7207
  }
6643
- mixerRef.current = (0, import_polycss_core23.createPolyAnimationMixer)(resolvedRoot, controller);
7208
+ mixerRef.current = (0, import_polycss_core26.createPolyAnimationMixer)(resolvedRoot, controller);
6644
7209
  return () => {
6645
7210
  mixerRef.current?.stopAllAction();
6646
7211
  mixerRef.current?.uncacheRoot();
6647
7212
  mixerRef.current = null;
6648
7213
  };
6649
7214
  }, [clips, controller]);
6650
- (0, import_react27.useEffect)(() => {
7215
+ (0, import_react28.useEffect)(() => {
6651
7216
  if (!clips || clips.length === 0 || !controller) return;
6652
7217
  let rafId;
6653
7218
  let lastTime = null;
@@ -6670,7 +7235,7 @@ function usePolyAnimation(clips, controller, root) {
6670
7235
  }, [clips, controller]);
6671
7236
  const resolvedClips = clips ?? [];
6672
7237
  const resolvedNames = resolvedClips.map((c) => c.name);
6673
- const actions = (0, import_react27.useMemo)(() => {
7238
+ const actions = (0, import_react28.useMemo)(() => {
6674
7239
  const target = {};
6675
7240
  for (const clip of resolvedClips) {
6676
7241
  Object.defineProperty(target, clip.name, {
@@ -6698,7 +7263,7 @@ function usePolyAnimation(clips, controller, root) {
6698
7263
  }
6699
7264
 
6700
7265
  // src/index.ts
6701
- var import_polycss_core24 = require("@layoutit/polycss-core");
7266
+ var import_polycss_core27 = require("@layoutit/polycss-core");
6702
7267
  // Annotate the CommonJS export names for ESM import in node:
6703
7268
  0 && (module.exports = {
6704
7269
  BASE_TILE,
@@ -6722,6 +7287,7 @@ var import_polycss_core24 = require("@layoutit/polycss-core");
6722
7287
  PolyFirstPersonControls,
6723
7288
  PolyGround,
6724
7289
  PolyIcosahedron,
7290
+ PolyIframe,
6725
7291
  PolyMapControls,
6726
7292
  PolyMesh,
6727
7293
  PolyOctahedron,
@@ -6776,6 +7342,7 @@ var import_polycss_core24 = require("@layoutit/polycss-core");
6776
7342
  normalizePolygons,
6777
7343
  octahedronPolygons,
6778
7344
  optimizeAnimatedMeshPolygons,
7345
+ optimizeMeshParseResult,
6779
7346
  optimizeMeshPolygons,
6780
7347
  parseColor,
6781
7348
  parseGltf,
@@ -6784,6 +7351,7 @@ var import_polycss_core24 = require("@layoutit/polycss-core");
6784
7351
  parseObj,
6785
7352
  parsePureColor,
6786
7353
  parseRgbColor,
7354
+ parseStl,
6787
7355
  parseVox,
6788
7356
  planePolygons,
6789
7357
  pointInMeshElement,
@@ -6799,6 +7367,7 @@ var import_polycss_core24 = require("@layoutit/polycss-core");
6799
7367
  seamOverlapPolygons,
6800
7368
  seamOverlapReport,
6801
7369
  shadeColor,
7370
+ simplifyTriangleMeshPolygons,
6802
7371
  tetrahedronPolygons,
6803
7372
  torusPolygons,
6804
7373
  useCameraContext,