@layoutit/polycss-react 0.2.1 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.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,75 @@ __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
+ buildPolyMeshTransform: () => import_polycss_core27.buildPolyMeshTransform,
67
+ buildSceneContext: () => import_polycss_core27.buildSceneContext,
68
+ cameraCullNormalGroups: () => import_polycss_core27.cameraCullNormalGroups,
69
+ cameraCullNormalGroupsFromPolygons: () => import_polycss_core27.cameraCullNormalGroupsFromPolygons,
70
+ cameraCullNormalKey: () => import_polycss_core27.cameraCullNormalKey,
71
+ cameraCullVisibleSignature: () => import_polycss_core27.cameraCullVisibleSignature,
72
+ cameraFacingDepth: () => import_polycss_core27.cameraFacingDepth,
73
+ clampChannel: () => import_polycss_core27.clampChannel,
72
74
  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,
75
+ computeSceneBbox: () => import_polycss_core27.computeSceneBbox,
76
+ computeShapeLighting: () => import_polycss_core27.computeShapeLighting,
77
+ computeTexturePaintMetrics: () => import_polycss_core27.computeTexturePaintMetrics,
78
+ conePolygons: () => import_polycss_core27.conePolygons,
79
+ coverPlanarPolygons: () => import_polycss_core27.coverPlanarPolygons,
80
+ createIsometricCamera: () => import_polycss_core27.createIsometricCamera,
81
+ createPolyAnimationMixer: () => import_polycss_core27.createPolyAnimationMixer,
82
+ cullInteriorPolygons: () => import_polycss_core27.cullInteriorPolygons,
83
+ cylinderPolygons: () => import_polycss_core27.cylinderPolygons,
84
+ dodecahedronPolygons: () => import_polycss_core27.dodecahedronPolygons,
83
85
  findMeshUnderPoint: () => findMeshUnderPoint,
84
86
  findPolyMeshHandle: () => findPolyMeshHandle,
85
- formatColor: () => import_polycss_core24.formatColor,
86
- icosahedronPolygons: () => import_polycss_core24.icosahedronPolygons,
87
+ formatColor: () => import_polycss_core27.formatColor,
88
+ icosahedronPolygons: () => import_polycss_core27.icosahedronPolygons,
87
89
  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,
90
+ inverseRotateVec3: () => import_polycss_core27.inverseRotateVec3,
91
+ isAxisAlignedSurfaceNormal: () => import_polycss_core27.isAxisAlignedSurfaceNormal,
92
+ isVoxelCameraCullableNormalGroups: () => import_polycss_core27.isVoxelCameraCullableNormalGroups,
93
+ loadMesh: () => import_polycss_core27.loadMesh,
94
+ mergePolygons: () => import_polycss_core27.mergePolygons,
95
+ normalFacesCamera: () => import_polycss_core27.normalFacesCamera,
96
+ normalizeInvertMultiplier: () => import_polycss_core27.normalizeInvertMultiplier,
97
+ normalizePolygons: () => import_polycss_core27.normalizePolygons,
98
+ octahedronPolygons: () => import_polycss_core27.octahedronPolygons,
99
+ optimizeAnimatedMeshPolygons: () => import_polycss_core27.optimizeAnimatedMeshPolygons,
100
+ optimizeMeshParseResult: () => import_polycss_core27.optimizeMeshParseResult,
101
+ optimizeMeshPolygons: () => import_polycss_core27.optimizeMeshPolygons,
102
+ parseColor: () => import_polycss_core27.parseColor,
103
+ parseGltf: () => import_polycss_core27.parseGltf,
104
+ parseHexColor: () => import_polycss_core27.parseHexColor,
105
+ parseMtl: () => import_polycss_core27.parseMtl,
106
+ parseObj: () => import_polycss_core27.parseObj,
107
+ parsePureColor: () => import_polycss_core27.parsePureColor,
108
+ parseRgbColor: () => import_polycss_core27.parseRgbColor,
109
+ parseStl: () => import_polycss_core27.parseStl,
110
+ parseVox: () => import_polycss_core27.parseVox,
111
+ planePolygons: () => import_polycss_core27.planePolygons,
108
112
  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,
113
+ polygonCssSurfaceNormal: () => import_polycss_core27.polygonCssSurfaceNormal,
114
+ polygonFaces: () => import_polycss_core27.polygonFaces,
115
+ polygonFacesCamera: () => import_polycss_core27.polygonFacesCamera,
116
+ repairMeshSeams: () => import_polycss_core27.repairMeshSeams,
117
+ ringPolygons: () => import_polycss_core27.ringPolygons,
118
+ rotateVec3: () => import_polycss_core27.rotateVec3,
119
+ seamFacetSplitPolygons: () => import_polycss_core27.seamFacetSplitPolygons,
120
+ seamFacetSplitReport: () => import_polycss_core27.seamFacetSplitReport,
121
+ seamOverlapDiagnostics: () => import_polycss_core27.seamOverlapDiagnostics,
122
+ seamOverlapPolygons: () => import_polycss_core27.seamOverlapPolygons,
123
+ seamOverlapReport: () => import_polycss_core27.seamOverlapReport,
124
+ shadeColor: () => import_polycss_core27.shadeColor,
125
+ simplifyTriangleMeshPolygons: () => import_polycss_core27.simplifyTriangleMeshPolygons,
126
+ tetrahedronPolygons: () => import_polycss_core27.tetrahedronPolygons,
127
+ torusPolygons: () => import_polycss_core27.torusPolygons,
123
128
  useCameraContext: () => useCameraContext,
124
129
  usePolyAnimation: () => usePolyAnimation,
125
130
  usePolyCamera: () => usePolyCamera,
@@ -127,7 +132,9 @@ __export(src_exports, {
127
132
  usePolyMesh: () => usePolyMesh,
128
133
  usePolySceneContext: () => usePolySceneContext,
129
134
  usePolySelect: () => usePolySelect,
130
- usePolySelectionApi: () => usePolySelectionApi
135
+ usePolySelectionApi: () => usePolySelectionApi,
136
+ worldDirectionToPolyCss: () => import_polycss_core27.worldDirectionToCss,
137
+ worldPositionToPolyCss: () => import_polycss_core27.worldPositionToCss
131
138
  });
132
139
  module.exports = __toCommonJS(src_exports);
133
140
 
@@ -231,8 +238,9 @@ function usePolyCamera(options) {
231
238
  const cssX = wy * tileSize;
232
239
  const cssY = wx * tileSize;
233
240
  const cssZ = wz * tileSize;
241
+ const cssZoom = s.zoom / tileSize;
234
242
  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)`;
243
+ el.style.transform = `${distancePart}scale(${cssZoom}) rotateX(${s.rotX}deg) rotate(${s.rotY}deg) translate3d(${-cssX}px, ${-cssY}px, ${-cssZ}px)`;
236
244
  }
237
245
  store.updateCameraFromRef(handle);
238
246
  store.notifyAll();
@@ -251,8 +259,9 @@ function usePolyCamera(options) {
251
259
  const cssX = wy * tileSize;
252
260
  const cssY = wx * tileSize;
253
261
  const cssZ = wz * tileSize;
262
+ const cssZoom = s.zoom / tileSize;
254
263
  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)`;
264
+ el.style.transform = `${distancePart}scale(${cssZoom}) rotateX(${s.rotX}deg) rotate(${s.rotY}deg) translate3d(${-cssX}px, ${-cssY}px, ${-cssZ}px)`;
256
265
  }, [store]);
257
266
  return {
258
267
  store,
@@ -265,7 +274,7 @@ function usePolyCamera(options) {
265
274
 
266
275
  // src/camera/PolyPerspectiveCamera.tsx
267
276
  var import_jsx_runtime = require("react/jsx-runtime");
268
- var DEFAULT_PERSPECTIVE = 8e3;
277
+ var DEFAULT_PERSPECTIVE = 32e3;
269
278
  function PolyPerspectiveCameraInner({
270
279
  zoom,
271
280
  target,
@@ -331,7 +340,12 @@ function PolyOrthographicCameraInner({
331
340
  );
332
341
  const cameraStyle = {
333
342
  ...style,
334
- perspective: "none"
343
+ // Vanilla emits 1000000px instead of "none" because true `perspective: none`
344
+ // sends Chrome down a compositor fast path that mis-rasterizes <u>
345
+ // border-triangle leaves. A very large finite value is visually orthographic
346
+ // but routes through the normal compositor path. Mirror that here so React
347
+ // produces byte-identical output to vanilla.
348
+ perspective: "1000000px"
335
349
  };
336
350
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(PolyCameraContext.Provider, { value: contextValue, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
337
351
  "div",
@@ -346,8 +360,8 @@ function PolyOrthographicCameraInner({
346
360
  var PolyOrthographicCamera = (0, import_react5.memo)(PolyOrthographicCameraInner);
347
361
 
348
362
  // src/scene/PolyScene.tsx
349
- var import_react13 = require("react");
350
- var import_polycss_core12 = require("@layoutit/polycss-core");
363
+ var import_react14 = require("react");
364
+ var import_polycss_core13 = require("@layoutit/polycss-core");
351
365
 
352
366
  // src/scene/useSceneContext.ts
353
367
  var import_react6 = require("react");
@@ -400,21 +414,24 @@ var CORE_BASE_STYLES = `
400
414
  will-change: transform;
401
415
  }
402
416
 
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 */
417
+ /* \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
418
 
419
+ /* Matches vanilla .polycss-camera: a simple positioned block that fills the
420
+ host. PolyPerspectiveCamera / PolyOrthographicCamera apply perspective
421
+ inline (driven by their perspective prop), so the CSS file does not bake a
422
+ default. Earlier React/Vue copies added flex centering, overflow:hidden,
423
+ contain:paint, isolation:isolate which shifted layout vs vanilla; removed
424
+ so cross-renderer iframes lay out identically. */
405
425
  .polycss-camera {
406
- display: flex;
426
+ position: relative;
427
+ display: block;
407
428
  width: 100%;
408
- justify-content: center;
409
- align-items: center;
410
- perspective: 8000px;
411
- min-height: inherit;
412
429
  height: 100%;
413
- position: relative;
414
- overflow: hidden;
415
- contain: paint;
416
- isolation: isolate;
417
430
  }
431
+ /* React-only descendant default: applies preserve-3d to controls + helpers
432
+ that ride on the camera; vanilla does not need this because every internal
433
+ element sets the property directly. Kept here so user-supplied wrapper
434
+ divs inside PolyCamera still participate in 3D layout. */
418
435
  .polycss-camera * {
419
436
  transform-style: preserve-3d;
420
437
  position: absolute;
@@ -436,6 +453,17 @@ var CORE_BASE_STYLES = `
436
453
  transform-style: preserve-3d !important;
437
454
  }
438
455
 
456
+ /* \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 */
457
+
458
+ .polycss-mesh {
459
+ position: absolute;
460
+ transform-style: preserve-3d;
461
+ /* Pivot at wrapper local (0,0,0) for three.js mesh.position/rotation/scale
462
+ parity. Geometry is positioned by the parser (bbox-min-at-origin by
463
+ default, or pre-centered via loadMesh { center: true }). */
464
+ transform-origin: 0 0 0;
465
+ }
466
+
439
467
  /* \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
468
 
441
469
  /*
@@ -521,19 +549,34 @@ var CORE_BASE_STYLES = `
521
549
  border-width: 0 16px 32px 16px;
522
550
  }
523
551
 
524
- .polycss-scene u.polycss-large-border-triangle {
525
- border-width: 0 48px 96px 48px;
526
- }
552
+ @supports (corner-top-left-shape: bevel) and (corner-top-right-shape: bevel) {
553
+ .polycss-scene > u,
554
+ .polycss-mesh > u,
555
+ .polycss-bucket > u {
556
+ border-width: 0;
557
+ width: 32px;
558
+ height: 32px;
559
+ background-color: currentColor;
560
+ border-top-left-radius: 50% 100%;
561
+ border-top-right-radius: 50% 100%;
562
+ corner-top-left-shape: bevel;
563
+ corner-top-right-shape: bevel;
564
+ }
527
565
 
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;
566
+ .polycss-scene > u.polycss-corner-shape-solid,
567
+ .polycss-mesh > u.polycss-corner-shape-solid,
568
+ .polycss-bucket > u.polycss-corner-shape-solid {
569
+ width: 16px;
570
+ height: 16px;
571
+ box-sizing: border-box;
572
+ border: 0;
573
+ background: currentColor;
574
+ border-radius: 0;
575
+ corner-top-left-shape: initial;
576
+ corner-top-right-shape: initial;
577
+ corner-bottom-right-shape: initial;
578
+ corner-bottom-left-shape: initial;
579
+ }
537
580
  }
538
581
 
539
582
  /* \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 +681,33 @@ var CORE_BASE_STYLES = `
638
681
  here (not inline per polygon) so each leaf only carries its tiny normal
639
682
  declarations \u2014 ~12\xD7 smaller per-polygon style payload on big meshes. */
640
683
  .polycss-scene[data-polycss-lighting="dynamic"] s {
684
+ /*
685
+ * Three.js MeshLambertMaterial parity for textured surfaces. See
686
+ * packages/polycss/src/styles/styles.ts for derivation and probe data.
687
+ * Kept in lockstep across renderer copies per cross-package discipline.
688
+ */
641
689
  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))))
690
+ calc(255 * max(0, 1.055 * pow(min(1, (
691
+ pow((var(--par) + 0.055) / 1.055, 2.4) * var(--pai) +
692
+ pow((var(--plr) + 0.055) / 1.055, 2.4) * var(--pli) * max(0,
693
+ var(--pnx) * var(--plx) +
694
+ var(--pny) * var(--ply) +
695
+ var(--pnz) * var(--plz))
696
+ ) / 3.14159265), 0.4167) - 0.055))
697
+ calc(255 * max(0, 1.055 * pow(min(1, (
698
+ pow((var(--pag) + 0.055) / 1.055, 2.4) * var(--pai) +
699
+ pow((var(--plg) + 0.055) / 1.055, 2.4) * var(--pli) * max(0,
700
+ var(--pnx) * var(--plx) +
701
+ var(--pny) * var(--ply) +
702
+ var(--pnz) * var(--plz))
703
+ ) / 3.14159265), 0.4167) - 0.055))
704
+ calc(255 * max(0, 1.055 * pow(min(1, (
705
+ pow((var(--pab) + 0.055) / 1.055, 2.4) * var(--pai) +
706
+ pow((var(--plb) + 0.055) / 1.055, 2.4) * var(--pli) * max(0,
707
+ var(--pnx) * var(--plx) +
708
+ var(--pny) * var(--ply) +
709
+ var(--pnz) * var(--plz))
710
+ ) / 3.14159265), 0.4167) - 0.055))
657
711
  );
658
712
  background-blend-mode: multiply;
659
713
  background-image: var(--polycss-atlas-url);
@@ -672,35 +726,48 @@ var CORE_BASE_STYLES = `
672
726
  }
673
727
 
674
728
  .polycss-scene[data-polycss-lighting="dynamic"] b,
729
+ .polycss-scene[data-polycss-lighting="dynamic"] i,
675
730
  .polycss-scene[data-polycss-lighting="dynamic"] u {
731
+ /*
732
+ * Three.js MeshLambertMaterial parity (default useLegacyLights=false,
733
+ * physically-correct pipeline). See packages/polycss/src/styles/styles.ts
734
+ * for the derivation and probe data \u2014 kept in lockstep across the three
735
+ * renderer copies per cross-package discipline.
736
+ */
676
737
  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))))
738
+ calc(255 * max(0, 1.055 * pow(min(1,
739
+ pow((var(--psr) + 0.055) / 1.055, 2.4) * (
740
+ pow((var(--par) + 0.055) / 1.055, 2.4) * var(--pai) +
741
+ pow((var(--plr) + 0.055) / 1.055, 2.4) * var(--pli) * max(0,
742
+ var(--pnx) * var(--plx) +
743
+ var(--pny) * var(--ply) +
744
+ var(--pnz) * var(--plz))
745
+ ) / 3.14159265
746
+ ), 0.4167) - 0.055))
747
+ calc(255 * max(0, 1.055 * pow(min(1,
748
+ pow((var(--psg) + 0.055) / 1.055, 2.4) * (
749
+ pow((var(--pag) + 0.055) / 1.055, 2.4) * var(--pai) +
750
+ pow((var(--plg) + 0.055) / 1.055, 2.4) * var(--pli) * max(0,
751
+ var(--pnx) * var(--plx) +
752
+ var(--pny) * var(--ply) +
753
+ var(--pnz) * var(--plz))
754
+ ) / 3.14159265
755
+ ), 0.4167) - 0.055))
756
+ calc(255 * max(0, 1.055 * pow(min(1,
757
+ pow((var(--psb) + 0.055) / 1.055, 2.4) * (
758
+ pow((var(--pab) + 0.055) / 1.055, 2.4) * var(--pai) +
759
+ pow((var(--plb) + 0.055) / 1.055, 2.4) * var(--pli) * max(0,
760
+ var(--pnx) * var(--plx) +
761
+ var(--pny) * var(--ply) +
762
+ var(--pnz) * var(--plz))
763
+ ) / 3.14159265
764
+ ), 0.4167) - 0.055))
692
765
  );
693
766
  }
694
767
 
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. */
768
+ /* Reserved internal <q> shadow element rules. Current shadow emission uses SVG
769
+ surfaces; these rules keep any retained <q> markup styled as a plain
770
+ border-shape leaf instead of inheriting UA quote styling. */
704
771
  .polycss-scene q {
705
772
  position: absolute;
706
773
  display: block;
@@ -723,11 +790,12 @@ var CORE_BASE_STYLES = `
723
790
  content: none;
724
791
  }
725
792
 
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 */
793
+ /* \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
794
 
728
795
  /*
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).
796
+ * Projection matrix for retained internal <q> shadow leaves. Projects any
797
+ * 3D point P onto the horizontal ground plane (cssZ \u2248 G) along the CSS-space
798
+ * light direction (--clx/y/z).
731
799
  *
732
800
  * In PolyCSS's world convention world Z is up (red-green plane is the
733
801
  * floor in the axes helper). After the world\u2192CSS swap (Y\u2194X), world Z stays
@@ -740,7 +808,7 @@ var CORE_BASE_STYLES = `
740
808
  * has a valid layout box. The fix: collapse along z by a near-zero
741
809
  * scale (Z_SQUASH = 0.01) instead of exactly zero \u2014 output.z is then
742
810
  * 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
811
+ * The result still looks flat to the eye (the drift is sub-pixel for
744
812
  * any realistic scene size).
745
813
  *
746
814
  * out.cssX = P.cssX - (--clx/--clz) * (P.cssZ - G)
@@ -768,24 +836,16 @@ var CORE_BASE_STYLES = `
768
836
  );
769
837
  }
770
838
 
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. */
839
+ /* Retained <q> opacity gate. Polygons facing the light cast full shadow;
840
+ polygons facing away cast zero shadow. The * 10 multiplier sharpens the
841
+ cutoff so small positive Lambert values jump quickly to 1. */
782
842
  .polycss-scene[data-polycss-lighting="dynamic"] q {
783
843
  opacity: clamp(0, calc((var(--pnx) * var(--clx) + var(--pny) * var(--cly) + var(--pnz) * var(--clz)) * 10), 1);
784
844
  }
785
845
  `;
786
846
 
787
847
  // src/scene/atlas/index.tsx
788
- var import_polycss_core11 = require("@layoutit/polycss-core");
848
+ var import_polycss_core12 = require("@layoutit/polycss-core");
789
849
 
790
850
  // src/scene/atlas/detection.ts
791
851
  var import_polycss_core3 = require("@layoutit/polycss-core");
@@ -802,6 +862,7 @@ function borderShapeSupported(doc) {
802
862
  return media("(pointer: fine)").matches && media("(hover: hover)").matches;
803
863
  }
804
864
  function solidTriangleSupported(doc) {
865
+ if (cornerTriangleSupported(doc)) return true;
805
866
  const win = doc.defaultView ?? (typeof window !== "undefined" ? window : void 0);
806
867
  const userAgent = win?.navigator?.userAgent ?? "";
807
868
  if (!userAgent) return true;
@@ -811,6 +872,10 @@ function cornerShapeSupported(doc) {
811
872
  const css = doc.defaultView?.CSS ?? (typeof CSS !== "undefined" ? CSS : void 0);
812
873
  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
874
  }
875
+ function cornerTriangleSupported(doc) {
876
+ const css = doc.defaultView?.CSS ?? (typeof CSS !== "undefined" ? CSS : void 0);
877
+ return !!css?.supports?.("corner-top-left-shape", "bevel") && !!css.supports("corner-top-right-shape", "bevel");
878
+ }
814
879
  function projectiveQuadSupported(doc) {
815
880
  const win = doc.defaultView ?? (typeof window !== "undefined" ? window : void 0);
816
881
  const userAgent = win?.navigator?.userAgent ?? "";
@@ -855,6 +920,8 @@ function isBorderShapeSupported(doc) {
855
920
  function isSolidTriangleSupported(doc) {
856
921
  const d = doc ?? (typeof document !== "undefined" ? document : null);
857
922
  if (!d) {
923
+ const css = typeof CSS !== "undefined" ? CSS : void 0;
924
+ if (!!css?.supports?.("corner-top-left-shape", "bevel") && !!css.supports("corner-top-right-shape", "bevel")) return true;
858
925
  const userAgent = (typeof navigator !== "undefined" ? navigator : globalThis.navigator)?.userAgent ?? "";
859
926
  if (!userAgent) return true;
860
927
  const isChromiumFamily = /\b(?:Chrome|HeadlessChrome|Chromium|Edg|OPR)\//.test(userAgent);
@@ -867,9 +934,12 @@ function isSolidTriangleSupported(doc) {
867
934
  // src/scene/atlas/filterPlans.ts
868
935
  var import_polycss_core4 = require("@layoutit/polycss-core");
869
936
  function filterAtlasPlans(plans, textureLighting, disabled, doc) {
937
+ const d = doc ?? (typeof document !== "undefined" ? document : null);
870
938
  return (0, import_polycss_core4.filterAtlasPlans)(plans, textureLighting, disabled, {
871
939
  solidTriangleSupported: isSolidTriangleSupported(doc),
872
- borderShapeSupported: isBorderShapeSupported(doc)
940
+ projectiveQuadSupported: doc ? projectiveQuadSupported(doc) : true,
941
+ borderShapeSupported: isBorderShapeSupported(doc),
942
+ cornerShapeSupported: d ? cornerShapeSupported(d) : false
873
943
  });
874
944
  }
875
945
 
@@ -922,16 +992,29 @@ function setCssTransform(ctx, atlasScale, a = 1, b = 0, c = 0, d = 1, e = 0, f =
922
992
  f * atlasScale
923
993
  );
924
994
  }
995
+ function srgbByteToLinear(c) {
996
+ const u = c / 255;
997
+ return u <= 0.04045 ? u / 12.92 : Math.pow((u + 0.055) / 1.055, 2.4);
998
+ }
999
+ function linearToSrgbByte(c) {
1000
+ const s = c <= 31308e-7 ? c * 12.92 : 1.055 * Math.pow(c, 1 / 2.4) - 0.055;
1001
+ return Math.max(0, Math.min(255, Math.round(s * 255)));
1002
+ }
925
1003
  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;
1004
+ const px = Math.round(x * atlasScale);
1005
+ const py = Math.round(y * atlasScale);
1006
+ const pw = Math.max(1, Math.round(width * atlasScale));
1007
+ const ph = Math.max(1, Math.round(height * atlasScale));
1008
+ const img = ctx.getImageData(px, py, pw, ph);
1009
+ const data = img.data;
1010
+ const tr = tint.r, tg = tint.g, tb = tint.b;
1011
+ for (let i = 0; i < data.length; i += 4) {
1012
+ if (data[i + 3] === 0) continue;
1013
+ data[i] = linearToSrgbByte(srgbByteToLinear(data[i]) * tr);
1014
+ data[i + 1] = linearToSrgbByte(srgbByteToLinear(data[i + 1]) * tg);
1015
+ data[i + 2] = linearToSrgbByte(srgbByteToLinear(data[i + 2]) * tb);
928
1016
  }
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();
1017
+ ctx.putImageData(img, px, py);
935
1018
  }
936
1019
  function drawImageCover(ctx, img, x, y, width, height, atlasScale) {
937
1020
  const srcW = img.naturalWidth || img.width || 1;
@@ -1339,6 +1422,15 @@ var SOLID_TRIANGLE_LARGE_BORDER_CANONICAL_SIZE = 96;
1339
1422
  var SOLID_TRIANGLE_LARGE_BORDER_WIDTH = "0 48px 96px 48px";
1340
1423
  var cachedSolidTriangleUserAgent;
1341
1424
  var cachedSolidTriangleCanonicalSize = SOLID_TRIANGLE_CANONICAL_SIZE;
1425
+ function cornerTriangleSupported2() {
1426
+ const css = typeof CSS !== "undefined" ? CSS : void 0;
1427
+ return !!css?.supports?.("corner-top-left-shape", "bevel") && !!css.supports("corner-top-right-shape", "bevel");
1428
+ }
1429
+ function solidTrianglePrimitive() {
1430
+ if (cornerTriangleSupported2()) return "corner-bevel";
1431
+ const ua = typeof navigator !== "undefined" ? navigator.userAgent : "";
1432
+ return /\bFirefox\//.test(ua) ? "border-large" : "border";
1433
+ }
1342
1434
  function solidTriangleCanonicalSize() {
1343
1435
  const ua = typeof navigator !== "undefined" ? navigator.userAgent : "";
1344
1436
  if (ua !== cachedSolidTriangleUserAgent) {
@@ -1348,7 +1440,35 @@ function solidTriangleCanonicalSize() {
1348
1440
  return cachedSolidTriangleCanonicalSize;
1349
1441
  }
1350
1442
  function solidTriangleBorderWidth() {
1351
- return solidTriangleCanonicalSize() === SOLID_TRIANGLE_LARGE_BORDER_CANONICAL_SIZE ? SOLID_TRIANGLE_LARGE_BORDER_WIDTH : void 0;
1443
+ return solidTrianglePrimitive() === "border-large" ? SOLID_TRIANGLE_LARGE_BORDER_WIDTH : void 0;
1444
+ }
1445
+ function solidTrianglePaintStyle() {
1446
+ const primitive = solidTrianglePrimitive();
1447
+ if (primitive === "corner-bevel") return void 0;
1448
+ const borderWidth = primitive === "border-large" ? SOLID_TRIANGLE_LARGE_BORDER_WIDTH : void 0;
1449
+ return borderWidth ? { borderWidth } : void 0;
1450
+ }
1451
+ function applySolidTrianglePaintStyle(el) {
1452
+ const primitive = solidTrianglePrimitive();
1453
+ if (primitive === "corner-bevel") {
1454
+ el.style.width = "";
1455
+ el.style.height = "";
1456
+ el.style.backgroundColor = "";
1457
+ el.style.borderWidth = "";
1458
+ el.style.borderTopLeftRadius = "";
1459
+ el.style.borderTopRightRadius = "";
1460
+ el.style.removeProperty("corner-top-left-shape");
1461
+ el.style.removeProperty("corner-top-right-shape");
1462
+ } else {
1463
+ el.style.width = "";
1464
+ el.style.height = "";
1465
+ el.style.backgroundColor = "";
1466
+ el.style.borderTopLeftRadius = "";
1467
+ el.style.borderTopRightRadius = "";
1468
+ el.style.removeProperty("corner-top-left-shape");
1469
+ el.style.removeProperty("corner-top-right-shape");
1470
+ el.style.borderWidth = primitive === "border-large" ? SOLID_TRIANGLE_LARGE_BORDER_WIDTH : "";
1471
+ }
1352
1472
  }
1353
1473
  function parseHex2(hex) {
1354
1474
  const parsed = (0, import_polycss_core7.parsePureColor)(hex);
@@ -1763,9 +1883,10 @@ function solidTriangleStyle(entry, textureLighting, pointerEvents, solidPaintDef
1763
1883
  txCol[2],
1764
1884
  1
1765
1885
  ].map((v) => (Math.round(v * 1e3) / 1e3 || 0).toString()).join(",");
1886
+ const primitiveStyle = solidTrianglePaintStyle();
1766
1887
  return {
1767
1888
  transform: `matrix3d(${canonicalMatrix})`,
1768
- borderWidth: solidTriangleBorderWidth(),
1889
+ ...primitiveStyle ?? {},
1769
1890
  ...sharedStyle
1770
1891
  };
1771
1892
  }
@@ -1994,6 +2115,7 @@ function updateStableTriangleDom(root, polygons, options = {}) {
1994
2115
  const el = leaves[i];
1995
2116
  if (el.style.visibility) el.style.visibility = "";
1996
2117
  el.__polycssStableTriangleBasis = style.basis;
2118
+ applySolidTrianglePaintStyle(el);
1997
2119
  el.style.transform = style.transform;
1998
2120
  if (style.borderWidth !== void 0) el.style.borderWidth = style.borderWidth;
1999
2121
  if (style.color !== void 0) applyStableTriangleColor(el, i, style.color, options);
@@ -2003,8 +2125,8 @@ function updateStableTriangleDom(root, polygons, options = {}) {
2003
2125
 
2004
2126
  // src/scene/atlas/paintDefaults.ts
2005
2127
  var import_polycss_core8 = require("@layoutit/polycss-core");
2006
- function computeTextureAtlasPlan(polygon, index, options = {}) {
2007
- return (0, import_polycss_core8.computeTextureAtlasPlanPublic)(polygon, index, options);
2128
+ function computeTextureAtlasPlan(polygon, index, options = {}, basisHint) {
2129
+ return (0, import_polycss_core8.computeTextureAtlasPlanPublic)(polygon, index, options, void 0, basisHint);
2008
2130
  }
2009
2131
  function getSolidPaintDefaults(plans, textureLighting, strategies) {
2010
2132
  const disabled = new Set(strategies?.disable ?? []);
@@ -2016,20 +2138,40 @@ var import_react7 = require("react");
2016
2138
  function pageShells(pages) {
2017
2139
  return pages.map((page) => ({ width: page.width, height: page.height, url: null }));
2018
2140
  }
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
- });
2141
+ function blobUrlsOf(pages) {
2142
+ return pages.flatMap((page) => page.url?.startsWith("blob:") ? [page.url] : []);
2143
+ }
2144
+ function decodeBlobUrls(urls) {
2145
+ if (urls.length === 0 || typeof Image === "undefined") return Promise.resolve();
2146
+ return Promise.all(urls.map((url) => {
2147
+ const img = new Image();
2148
+ img.src = url;
2149
+ const decoded = img.decode?.();
2150
+ return decoded ? decoded.catch(() => {
2151
+ }) : Promise.resolve();
2152
+ })).then(() => void 0);
2153
+ }
2154
+ function deferRevoke(urls) {
2155
+ if (urls.length === 0) return;
2156
+ const run = () => {
2157
+ for (const url of urls) URL.revokeObjectURL(url);
2158
+ };
2159
+ if (typeof requestAnimationFrame === "function") requestAnimationFrame(run);
2160
+ else setTimeout(run, 0);
2024
2161
  }
2025
- function useTextureAtlas(plans, textureLighting, textureQualityInput, strategies) {
2162
+ function useTextureAtlas(plans, textureLighting, textureQualityInput, strategies, atomic = false) {
2026
2163
  const disabled = (0, import_react7.useMemo)(
2027
2164
  () => new Set(strategies?.disable ?? []),
2028
2165
  // eslint-disable-next-line react-hooks/exhaustive-deps
2029
2166
  [strategies?.disable?.join(",")]
2030
2167
  );
2031
2168
  const atlasPlans = (0, import_react7.useMemo)(
2032
- () => filterAtlasPlans(plans, textureLighting, disabled),
2169
+ () => filterAtlasPlans(
2170
+ plans,
2171
+ textureLighting,
2172
+ disabled,
2173
+ typeof document !== "undefined" ? document : null
2174
+ ),
2033
2175
  [plans, textureLighting, disabled]
2034
2176
  );
2035
2177
  const { packed, atlasScale } = (0, import_react7.useMemo)(
@@ -2040,38 +2182,93 @@ function useTextureAtlas(plans, textureLighting, textureQualityInput, strategies
2040
2182
  ),
2041
2183
  [atlasPlans, textureQualityInput]
2042
2184
  );
2043
- const [pages, setPages] = (0, import_react7.useState)(
2044
- () => pageShells(packed.pages)
2045
- );
2185
+ const [pages, setPages] = (0, import_react7.useState)(() => pageShells(packed.pages));
2186
+ const [frame, setFrame] = (0, import_react7.useState)(() => ({
2187
+ plans,
2188
+ entries: packed.entries,
2189
+ pages: pageShells(packed.pages)
2190
+ }));
2191
+ const shownUrls = (0, import_react7.useRef)([]);
2192
+ const seqRef = (0, import_react7.useRef)(0);
2193
+ const mountedRef = (0, import_react7.useRef)(true);
2046
2194
  (0, import_react7.useEffect)(() => {
2195
+ mountedRef.current = true;
2196
+ return () => {
2197
+ mountedRef.current = false;
2198
+ for (const url of shownUrls.current) URL.revokeObjectURL(url);
2199
+ shownUrls.current = [];
2200
+ };
2201
+ }, []);
2202
+ (0, import_react7.useEffect)(() => {
2203
+ if (atomic) {
2204
+ const seq = ++seqRef.current;
2205
+ const snapPlans = plans;
2206
+ const snapEntries = packed.entries;
2207
+ if (packed.pages.length === 0 || typeof document === "undefined") {
2208
+ deferRevoke(shownUrls.current);
2209
+ shownUrls.current = [];
2210
+ setFrame({ plans: snapPlans, entries: snapEntries, pages: pageShells(packed.pages) });
2211
+ return;
2212
+ }
2213
+ const stale = () => seq !== seqRef.current;
2214
+ let built2 = [];
2215
+ buildAtlasPages(packed.pages, textureLighting, document, atlasScale, stale).then(async (nextPages) => {
2216
+ built2 = blobUrlsOf(nextPages);
2217
+ await decodeBlobUrls(built2);
2218
+ if (!mountedRef.current || stale()) {
2219
+ deferRevoke(built2);
2220
+ return;
2221
+ }
2222
+ const prev = shownUrls.current;
2223
+ shownUrls.current = built2;
2224
+ built2 = [];
2225
+ deferRevoke(prev);
2226
+ setFrame({ plans: snapPlans, entries: snapEntries, pages: nextPages });
2227
+ }).catch(() => {
2228
+ });
2229
+ return;
2230
+ }
2047
2231
  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") {
2232
+ if (packed.pages.length === 0) {
2233
+ deferRevoke(shownUrls.current);
2234
+ shownUrls.current = [];
2235
+ setPages((prev) => prev.length === 0 ? prev : []);
2052
2236
  return () => {
2053
2237
  };
2054
2238
  }
2055
- buildAtlasPages(packed.pages, textureLighting, document, atlasScale, () => cancelled).then((nextPages) => {
2239
+ if (typeof document === "undefined") return () => {
2240
+ };
2241
+ setPages((prev) => prev.some((page) => page.url) ? prev : pageShells(packed.pages));
2242
+ let built = [];
2243
+ buildAtlasPages(packed.pages, textureLighting, document, atlasScale, () => cancelled).then(async (nextPages) => {
2244
+ built = blobUrlsOf(nextPages);
2245
+ await decodeBlobUrls(built);
2056
2246
  if (cancelled) {
2057
- for (const page of nextPages) {
2058
- if (page.url?.startsWith("blob:")) URL.revokeObjectURL(page.url);
2059
- }
2247
+ deferRevoke(built);
2060
2248
  return;
2061
2249
  }
2062
- urls = nextPages.flatMap((page) => page.url?.startsWith("blob:") ? [page.url] : []);
2063
- setPages((prev) => textureAtlasPagesEqual(prev, nextPages) ? prev : nextPages);
2250
+ const stale = shownUrls.current;
2251
+ shownUrls.current = built;
2252
+ built = [];
2253
+ deferRevoke(stale);
2254
+ setPages(nextPages);
2064
2255
  }).catch(() => {
2065
- if (!cancelled) {
2066
- setPages((prev) => textureAtlasPagesEqual(prev, nextPageShells) ? prev : nextPageShells);
2067
- }
2068
2256
  });
2069
2257
  return () => {
2070
2258
  cancelled = true;
2071
- for (const url of urls) URL.revokeObjectURL(url);
2259
+ deferRevoke(built);
2072
2260
  };
2073
- }, [packed, textureLighting, atlasScale]);
2261
+ }, [packed, textureLighting, atlasScale, atomic]);
2262
+ if (atomic) {
2263
+ return {
2264
+ plans: frame.plans,
2265
+ entries: frame.entries,
2266
+ pages: frame.pages,
2267
+ ready: frame.pages.length === 0 || frame.pages.every((page) => !!page.url)
2268
+ };
2269
+ }
2074
2270
  return {
2271
+ plans,
2075
2272
  entries: packed.entries,
2076
2273
  pages,
2077
2274
  ready: pages.length === 0 || pages.every((page) => !!page.url)
@@ -2102,6 +2299,7 @@ var TextureTrianglePoly = (0, import_react8.memo)(function TextureTrianglePoly2(
2102
2299
  {
2103
2300
  className: elementClassName,
2104
2301
  style: { ...triangleStyle, ...styleProp },
2302
+ "data-poly-index": entry.index,
2105
2303
  ...domEventHandlers,
2106
2304
  ...dataAttrs,
2107
2305
  ...domAttrs
@@ -2133,6 +2331,7 @@ function orderBrushInlineStyle(el) {
2133
2331
  }
2134
2332
  var TextureBorderShapePoly = (0, import_react9.memo)(function TextureBorderShapePoly2({
2135
2333
  entry,
2334
+ textureLighting,
2136
2335
  solidPaintDefaults,
2137
2336
  className,
2138
2337
  style: styleProp,
@@ -2145,7 +2344,9 @@ var TextureBorderShapePoly = (0, import_react9.memo)(function TextureBorderShape
2145
2344
  const bDisabled = disabledStrategies?.has("b") ?? false;
2146
2345
  const useIForFullRect = bDisabled && isBorderShapeSupported();
2147
2346
  const borderShape = !fullRect || useIForFullRect ? (0, import_polycss_core9.cssBorderShapeForPlan)(entry) : null;
2148
- const useDefaultPaint = entry.shadedColor === solidPaintDefaults?.paintColor;
2347
+ const dynamic = textureLighting === "dynamic";
2348
+ const base = parseHex2(entry.polygon.color ?? "#cccccc");
2349
+ const useDefaultDynamicColor = dynamic && rgbKey2(base) === solidPaintDefaults?.dynamicColorKey;
2149
2350
  const setElementRef = (0, import_react9.useCallback)((el) => {
2150
2351
  if (!el) return;
2151
2352
  if (borderShape) el.style.setProperty("border-shape", borderShape);
@@ -2155,8 +2356,25 @@ var TextureBorderShapePoly = (0, import_react9.memo)(function TextureBorderShape
2155
2356
  const transform = borderShape ? (0, import_polycss_core9.formatBorderShapeEntryMatrix)(entry) : (0, import_polycss_core9.formatSolidQuadEntryMatrix)(entry);
2156
2357
  const style = {
2157
2358
  transform,
2158
- color: useDefaultPaint ? void 0 : entry.shadedColor,
2359
+ // Baked mode: always emit per-leaf shaded color when known so the
2360
+ // first render matches the post-light-nudge commit path. Vanilla
2361
+ // dropped the `=== solidPaintDefaults.paintColor` shortcut in
2362
+ // commit 0423777 — leaves with undefined shadedColor would inherit
2363
+ // the (often-wrong) wrapper --polycss-paint default, producing
2364
+ // facets that look dimmer than they should until a light change
2365
+ // forced an explicit re-bake.
2366
+ color: dynamic ? void 0 : entry.shadedColor,
2159
2367
  pointerEvents: pointerEvents === "none" ? "none" : void 0,
2368
+ ...dynamic ? {
2369
+ ["--pnx"]: entry.normal[0].toFixed(4),
2370
+ ["--pny"]: entry.normal[1].toFixed(4),
2371
+ ["--pnz"]: entry.normal[2].toFixed(4),
2372
+ ...useDefaultDynamicColor ? null : {
2373
+ ["--psr"]: (base.r / 255).toFixed(4),
2374
+ ["--psg"]: (base.g / 255).toFixed(4),
2375
+ ["--psb"]: (base.b / 255).toFixed(4)
2376
+ }
2377
+ } : null,
2160
2378
  ...styleProp
2161
2379
  };
2162
2380
  const dataAttrs = entry.polygon.data ? Object.fromEntries(
@@ -2169,6 +2387,7 @@ var TextureBorderShapePoly = (0, import_react9.memo)(function TextureBorderShape
2169
2387
  {
2170
2388
  className: elementClassName,
2171
2389
  style,
2390
+ "data-poly-index": entry.index,
2172
2391
  ...domEventHandlers,
2173
2392
  ...dataAttrs,
2174
2393
  ...domAttrs
@@ -2181,6 +2400,7 @@ var TextureBorderShapePoly = (0, import_react9.memo)(function TextureBorderShape
2181
2400
  ref: setElementRef,
2182
2401
  className: elementClassName,
2183
2402
  style,
2403
+ "data-poly-index": entry.index,
2184
2404
  ...domEventHandlers,
2185
2405
  ...dataAttrs,
2186
2406
  ...domAttrs
@@ -2210,7 +2430,8 @@ var TextureProjectiveSolidPoly = (0, import_react10.memo)(function TextureProjec
2210
2430
  // would drop it to 3 decimals and leave visible seam gaps between
2211
2431
  // adjacent projective quads at zoom-out (matches vanilla scene.add).
2212
2432
  transform: `matrix3d(${entry.projectiveMatrix})`,
2213
- color: dynamic || entry.shadedColor === solidPaintDefaults?.paintColor ? void 0 : entry.shadedColor,
2433
+ // Baked: always emit per-leaf shaded color (vanilla commit 0423777).
2434
+ color: dynamic ? void 0 : entry.shadedColor,
2214
2435
  pointerEvents: pointerEvents === "none" ? "none" : void 0,
2215
2436
  ...dynamic && !useDefaultDynamicColor ? {
2216
2437
  ["--pnx"]: entry.normal[0].toFixed(4),
@@ -2235,6 +2456,7 @@ var TextureProjectiveSolidPoly = (0, import_react10.memo)(function TextureProjec
2235
2456
  {
2236
2457
  className: elementClassName,
2237
2458
  style,
2459
+ "data-poly-index": entry.index,
2238
2460
  ...domEventHandlers,
2239
2461
  ...dataAttrs,
2240
2462
  ...domAttrs
@@ -2242,11 +2464,69 @@ var TextureProjectiveSolidPoly = (0, import_react10.memo)(function TextureProjec
2242
2464
  );
2243
2465
  });
2244
2466
 
2245
- // src/scene/atlas/atlasPoly.tsx
2467
+ // src/scene/atlas/cornerShapeSolid.tsx
2246
2468
  var import_react11 = require("react");
2247
2469
  var import_polycss_core10 = require("@layoutit/polycss-core");
2248
2470
  var import_jsx_runtime6 = require("react/jsx-runtime");
2249
- var TextureAtlasPoly = (0, import_react11.memo)(function TextureAtlasPoly2({
2471
+ function formatPaintCss(entry, textureLighting, solidPaintDefaults) {
2472
+ if (textureLighting === "dynamic") {
2473
+ const base = (0, import_polycss_core10.parseHex)(entry.polygon.color ?? "#cccccc");
2474
+ let style = `;--pnx:${entry.normal[0].toFixed(4)};--pny:${entry.normal[1].toFixed(4)};--pnz:${entry.normal[2].toFixed(4)}`;
2475
+ if ((0, import_polycss_core10.rgbKey)(base) !== solidPaintDefaults?.dynamicColorKey) {
2476
+ style += `;--psr:${(base.r / 255).toFixed(4)};--psg:${(base.g / 255).toFixed(4)};--psb:${(base.b / 255).toFixed(4)}`;
2477
+ }
2478
+ return style;
2479
+ }
2480
+ return entry.shadedColor ? `;color:${entry.shadedColor}` : "";
2481
+ }
2482
+ var TextureCornerShapeSolidPoly = (0, import_react11.memo)(function TextureCornerShapeSolidPoly2({
2483
+ entry,
2484
+ geometry,
2485
+ textureLighting,
2486
+ solidPaintDefaults,
2487
+ className,
2488
+ style: styleProp,
2489
+ domAttrs,
2490
+ domEventHandlers,
2491
+ pointerEvents = "auto"
2492
+ }) {
2493
+ const cornerShapeCss = (0, import_polycss_core10.formatCornerShapeElementStyle)(entry, geometry);
2494
+ const paintCss = formatPaintCss(entry, textureLighting, solidPaintDefaults);
2495
+ const pointerCss = pointerEvents === "none" ? ";pointer-events:none" : "";
2496
+ const fullCss = cornerShapeCss + paintCss + pointerCss;
2497
+ const setRef = (0, import_react11.useCallback)((el) => {
2498
+ if (!el) return;
2499
+ el.setAttribute("style", fullCss);
2500
+ if (styleProp) {
2501
+ for (const [k, v] of Object.entries(styleProp)) {
2502
+ if (v === void 0 || v === null) continue;
2503
+ if (k.startsWith("--")) el.style.setProperty(k, String(v));
2504
+ else el.style[k] = String(v);
2505
+ }
2506
+ }
2507
+ }, [fullCss, styleProp]);
2508
+ const dataAttrs = entry.polygon.data ? Object.fromEntries(
2509
+ Object.entries(entry.polygon.data).map(([k, v]) => [`data-${k}`, String(v)])
2510
+ ) : {};
2511
+ const elementClassName = className?.trim() || void 0;
2512
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2513
+ "u",
2514
+ {
2515
+ ref: setRef,
2516
+ className: elementClassName,
2517
+ "data-poly-index": entry.index,
2518
+ ...domEventHandlers,
2519
+ ...dataAttrs,
2520
+ ...domAttrs
2521
+ }
2522
+ );
2523
+ });
2524
+
2525
+ // src/scene/atlas/atlasPoly.tsx
2526
+ var import_react12 = require("react");
2527
+ var import_polycss_core11 = require("@layoutit/polycss-core");
2528
+ var import_jsx_runtime7 = require("react/jsx-runtime");
2529
+ var TextureAtlasPoly = (0, import_react12.memo)(function TextureAtlasPoly2({
2250
2530
  entry,
2251
2531
  page,
2252
2532
  textureLighting,
@@ -2262,17 +2542,23 @@ var TextureAtlasPoly = (0, import_react11.memo)(function TextureAtlasPoly2({
2262
2542
  const atlasCanonicalSize = entry.atlasCanonicalSize ?? ATLAS_CANONICAL_SIZE_EXPLICIT;
2263
2543
  const atlasWidth = entry.canvasW || 1;
2264
2544
  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;
2545
+ const atlasPosition = page ? `${(0, import_polycss_core11.formatCssLengthPx)(-entry.x / atlasWidth * atlasCanonicalSize)} ${(0, import_polycss_core11.formatCssLengthPx)(-entry.y / atlasHeight * atlasCanonicalSize)}` : void 0;
2546
+ const atlasSize = page ? `${(0, import_polycss_core11.formatCssLengthPx)(page.width / atlasWidth * atlasCanonicalSize)} ${(0, import_polycss_core11.formatCssLengthPx)(page.height / atlasHeight * atlasCanonicalSize)}` : void 0;
2267
2547
  const dynamicMask = dynamic && page?.url ? `url(${page.url})` : void 0;
2268
2548
  const background = !dynamic && page?.url ? `url(${page.url}) ${atlasPosition} / ${atlasSize} no-repeat` : void 0;
2269
2549
  const style = {
2270
- transform: (0, import_polycss_core10.formatMatrix3d)(entry.atlasMatrix),
2550
+ transform: (0, import_polycss_core11.formatMatrix3d)(entry.atlasMatrix),
2271
2551
  ["--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,
2552
+ // Listing the `background` shorthand alongside the `background-*` longhands
2553
+ // in one inline style object makes React warn on every update (mixing
2554
+ // shorthand and non-shorthand for the same property). Branch so only the
2555
+ // current mode's keys are assigned — baked gets `background`, dynamic gets
2556
+ // the longhands.
2557
+ ...dynamic ? {
2558
+ backgroundImage: page?.url ? `url(${page.url})` : void 0,
2559
+ backgroundPosition: atlasPosition,
2560
+ backgroundSize: atlasSize
2561
+ } : { background },
2276
2562
  ...dynamic ? {
2277
2563
  ["--pnx"]: entry.normal[0].toFixed(4),
2278
2564
  ["--pny"]: entry.normal[1].toFixed(4),
@@ -2297,11 +2583,12 @@ var TextureAtlasPoly = (0, import_react11.memo)(function TextureAtlasPoly2({
2297
2583
  Object.entries(entry.polygon.data).map(([k, v]) => [`data-${k}`, String(v)])
2298
2584
  ) : {};
2299
2585
  const elementClassName = className?.trim() || void 0;
2300
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2586
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2301
2587
  "s",
2302
2588
  {
2303
2589
  className: elementClassName,
2304
2590
  style,
2591
+ "data-poly-index": entry.index,
2305
2592
  ...domEventHandlers,
2306
2593
  ...dataAttrs,
2307
2594
  ...domAttrs
@@ -2310,14 +2597,14 @@ var TextureAtlasPoly = (0, import_react11.memo)(function TextureAtlasPoly2({
2310
2597
  });
2311
2598
 
2312
2599
  // src/scene/sceneContext.ts
2313
- var import_react12 = require("react");
2314
- var PolySceneContext = (0, import_react12.createContext)(null);
2600
+ var import_react13 = require("react");
2601
+ var PolySceneContext = (0, import_react13.createContext)(null);
2315
2602
  function usePolySceneContext2() {
2316
- return (0, import_react12.useContext)(PolySceneContext);
2603
+ return (0, import_react13.useContext)(PolySceneContext);
2317
2604
  }
2318
2605
 
2319
2606
  // src/scene/PolyScene.tsx
2320
- var import_jsx_runtime7 = require("react/jsx-runtime");
2607
+ var import_jsx_runtime8 = require("react/jsx-runtime");
2321
2608
  function PolySceneInner({
2322
2609
  polygons: polygonsProp,
2323
2610
  centerPolygons: centerPolygonsProp,
@@ -2329,7 +2616,7 @@ function PolySceneInner({
2329
2616
  ambientLight,
2330
2617
  textureLighting = "baked",
2331
2618
  textureQuality,
2332
- seamBleed = import_polycss_core12.DEFAULT_SEAM_BLEED,
2619
+ seamBleed = import_polycss_core13.DEFAULT_SEAM_BLEED,
2333
2620
  strategies,
2334
2621
  autoCenter = false,
2335
2622
  shadow,
@@ -2343,27 +2630,29 @@ function PolySceneInner({
2343
2630
  debugShowBackfaces
2344
2631
  }) {
2345
2632
  const { store, sceneElRef, applyTransformDirect } = useCameraContext();
2346
- const localSceneRef = (0, import_react13.useCallback)(
2633
+ const [sceneEl, setSceneEl] = (0, import_react14.useState)(null);
2634
+ const localSceneRef = (0, import_react14.useCallback)(
2347
2635
  (el) => {
2348
2636
  sceneElRef.current = el;
2637
+ setSceneEl(el);
2349
2638
  },
2350
2639
  [sceneElRef]
2351
2640
  );
2352
- (0, import_react13.useEffect)(() => {
2641
+ (0, import_react14.useEffect)(() => {
2353
2642
  const el = sceneElRef.current;
2354
2643
  if (!el) return;
2355
2644
  el.classList.toggle("polycss-debug-show-backfaces", !!debugShowBackfaces);
2356
2645
  }, [debugShowBackfaces, sceneElRef]);
2357
- const injectedRef = (0, import_react13.useRef)(false);
2358
- (0, import_react13.useEffect)(() => {
2646
+ const injectedRef = (0, import_react14.useRef)(false);
2647
+ (0, import_react14.useEffect)(() => {
2359
2648
  if (injectedRef.current) return;
2360
2649
  if (typeof document !== "undefined") {
2361
2650
  injectPolyBaseStyles(document);
2362
2651
  injectedRef.current = true;
2363
2652
  }
2364
2653
  }, []);
2365
- const inputPolygons = (0, import_react13.useMemo)(() => polygonsProp ?? [], [polygonsProp]);
2366
- const centerInputPolygons = (0, import_react13.useMemo)(
2654
+ const inputPolygons = (0, import_react14.useMemo)(() => polygonsProp ?? [], [polygonsProp]);
2655
+ const centerInputPolygons = (0, import_react14.useMemo)(
2367
2656
  () => centerPolygonsProp ?? null,
2368
2657
  [centerPolygonsProp]
2369
2658
  );
@@ -2377,7 +2666,7 @@ function PolySceneInner({
2377
2666
  const sceneBbox = centerInputPolygons ? centerSceneBbox : renderSceneBbox;
2378
2667
  const directionalForAtlas = textureLighting === "dynamic" ? void 0 : directionalLight;
2379
2668
  const ambientForAtlas = textureLighting === "dynamic" ? void 0 : ambientLight;
2380
- const polyContext = (0, import_react13.useMemo)(() => {
2669
+ const polyContext = (0, import_react14.useMemo)(() => {
2381
2670
  const tileSize = 50;
2382
2671
  return {
2383
2672
  tileSize,
@@ -2388,7 +2677,7 @@ function PolySceneInner({
2388
2677
  seamBleed
2389
2678
  };
2390
2679
  }, [directionalForAtlas, ambientForAtlas, textureLighting, seamBleed]);
2391
- const autoCenterOffset = (0, import_react13.useMemo)(() => {
2680
+ const autoCenterOffset = (0, import_react14.useMemo)(() => {
2392
2681
  if (!autoCenter) return [0, 0, 0];
2393
2682
  return [
2394
2683
  (sceneBbox.min[0] + sceneBbox.max[0]) / 2,
@@ -2396,18 +2685,18 @@ function PolySceneInner({
2396
2685
  (sceneBbox.min[2] + sceneBbox.max[2]) / 2
2397
2686
  ];
2398
2687
  }, [autoCenter, sceneBbox]);
2399
- (0, import_react13.useLayoutEffect)(() => {
2688
+ (0, import_react14.useLayoutEffect)(() => {
2400
2689
  store.setAutoCenterOffset(autoCenterOffset);
2401
2690
  applyTransformDirect();
2402
2691
  }, [store, autoCenterOffset, applyTransformDirect]);
2403
- (0, import_react13.useLayoutEffect)(() => {
2692
+ (0, import_react14.useLayoutEffect)(() => {
2404
2693
  applyTransformDirect();
2405
2694
  });
2406
2695
  const computedClassName = `polycss-scene${className ? ` ${className}` : ""}`;
2407
- const textureAtlasPlans = (0, import_react13.useMemo)(
2696
+ const textureAtlasPlans = (0, import_react14.useMemo)(
2408
2697
  () => {
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, {
2698
+ const repairEdges = (0, import_polycss_core12.buildTextureEdgeRepairSets)(polygons);
2699
+ const seamBleedEdges = seamBleed === "auto" || typeof seamBleed === "number" && Number.isFinite(seamBleed) && seamBleed > 0 ? (0, import_polycss_core12.buildSeamBleedPolygonEdges)(polygons, {
2411
2700
  tileSize: polyContext.tileSize,
2412
2701
  layerElevation: polyContext.layerElevation,
2413
2702
  directionalLight: directionalForAtlas,
@@ -2423,22 +2712,23 @@ function PolySceneInner({
2423
2712
  [polygons, polyContext, seamBleed, directionalForAtlas, ambientForAtlas]
2424
2713
  );
2425
2714
  const textureAtlas = useTextureAtlas(textureAtlasPlans, textureLighting, textureQuality, strategies);
2426
- const dynamicLightVars = (0, import_react13.useMemo)(() => {
2715
+ const dynamicLightVars = (0, import_react14.useMemo)(() => {
2427
2716
  if (textureLighting !== "dynamic") return null;
2428
- const dir = directionalLight?.direction ?? [0.4, -0.7, 0.59];
2717
+ const userDir = directionalLight?.direction ?? [0.4, -0.7, 0.59];
2718
+ const dir = (0, import_polycss_core13.worldDirectionToCss)(userDir);
2429
2719
  const len = Math.hypot(dir[0], dir[1], dir[2]) || 1;
2430
2720
  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];
2721
+ const lightRgb = (0, import_polycss_core13.parseHexColor)(directionalLight?.color ?? "#ffffff")?.rgb ?? [255, 255, 255];
2722
+ const ambRgb = (0, import_polycss_core13.parseHexColor)(ambientLight?.color ?? "#ffffff")?.rgb ?? [255, 255, 255];
2433
2723
  const lightIntensity = directionalLight?.intensity ?? 1;
2434
2724
  const ambientIntensity = ambientLight?.intensity ?? 0.4;
2435
2725
  const ch = (n) => (n / 255).toFixed(4);
2436
2726
  const rawClz = lz;
2437
2727
  const clz = Math.sign(rawClz || 1) * Math.max(Math.abs(rawClz), 0.01);
2438
2728
  return {
2439
- ["--plx"]: lx.toFixed(4),
2440
- ["--ply"]: ly.toFixed(4),
2441
- ["--plz"]: lz.toFixed(4),
2729
+ ["--plx"]: lx.toFixed(2),
2730
+ ["--ply"]: ly.toFixed(2),
2731
+ ["--plz"]: lz.toFixed(2),
2442
2732
  ["--plr"]: ch(lightRgb[0]),
2443
2733
  ["--plg"]: ch(lightRgb[1]),
2444
2734
  ["--plb"]: ch(lightRgb[2]),
@@ -2447,32 +2737,37 @@ function PolySceneInner({
2447
2737
  ["--pag"]: ch(ambRgb[1]),
2448
2738
  ["--pab"]: ch(ambRgb[2]),
2449
2739
  ["--pai"]: ambientIntensity.toFixed(4),
2450
- ["--clx"]: lx.toFixed(4),
2451
- ["--cly"]: ly.toFixed(4),
2452
- ["--clz"]: clz.toFixed(4)
2740
+ ["--clx"]: lx.toFixed(2),
2741
+ ["--cly"]: ly.toFixed(2),
2742
+ ["--clz"]: clz.toFixed(2)
2453
2743
  };
2454
2744
  }, [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)(() => {
2745
+ const shadowCastersRef = (0, import_react14.useRef)(/* @__PURE__ */ new Map());
2746
+ const shadowReceiversRef = (0, import_react14.useRef)(/* @__PURE__ */ new Set());
2747
+ const [groundCssZ, setGroundCssZ] = (0, import_react14.useState)(null);
2748
+ const [shadowCastersVersion, setShadowCastersVersion] = (0, import_react14.useState)(0);
2749
+ const [hasShadowReceiver, setHasShadowReceiver] = (0, import_react14.useState)(false);
2750
+ const recomputeGroundCssZ = (0, import_react14.useCallback)(() => {
2458
2751
  let minWorldZ = Infinity;
2459
- for (const polys of shadowCastersRef.current.values()) {
2460
- for (const poly of polys) {
2752
+ for (const data of shadowCastersRef.current.values()) {
2753
+ for (const poly of data.polygons) {
2461
2754
  for (const v of poly.vertices) {
2462
- if (v[2] < minWorldZ) minWorldZ = v[2];
2755
+ const z = v[2] + (data.position[2] ?? 0);
2756
+ if (z < minWorldZ) minWorldZ = z;
2463
2757
  }
2464
2758
  }
2465
2759
  }
2466
2760
  if (!Number.isFinite(minWorldZ)) return null;
2467
2761
  const lift = shadow?.lift ?? 0.05;
2468
- return (minWorldZ + lift) * import_polycss_core12.BASE_TILE;
2762
+ return (minWorldZ + lift) * import_polycss_core13.BASE_TILE;
2469
2763
  }, [shadow]);
2470
- const registerShadowCaster = (0, import_react13.useCallback)((meshId, meshPolygons) => {
2471
- if (meshPolygons === null) {
2764
+ const registerShadowCaster = (0, import_react14.useCallback)((meshId, data) => {
2765
+ if (data === null) {
2472
2766
  shadowCastersRef.current.delete(meshId);
2473
2767
  } else {
2474
- shadowCastersRef.current.set(meshId, meshPolygons);
2768
+ shadowCastersRef.current.set(meshId, data);
2475
2769
  }
2770
+ setShadowCastersVersion((v) => v + 1);
2476
2771
  const next = recomputeGroundCssZ();
2477
2772
  setGroundCssZ((prev) => prev === next ? prev : next);
2478
2773
  const el = sceneElRef.current;
@@ -2483,7 +2778,12 @@ function PolySceneInner({
2483
2778
  el.style.removeProperty("--shadow-ground-cssz");
2484
2779
  }
2485
2780
  }, [sceneElRef, textureLighting, recomputeGroundCssZ]);
2486
- (0, import_react13.useEffect)(() => {
2781
+ const registerShadowReceiver = (0, import_react14.useCallback)((meshId, registered) => {
2782
+ if (registered) shadowReceiversRef.current.add(meshId);
2783
+ else shadowReceiversRef.current.delete(meshId);
2784
+ setHasShadowReceiver(shadowReceiversRef.current.size > 0);
2785
+ }, []);
2786
+ (0, import_react14.useEffect)(() => {
2487
2787
  const el = sceneElRef.current;
2488
2788
  if (!el) return;
2489
2789
  if (textureLighting === "dynamic" && groundCssZ !== null) {
@@ -2492,19 +2792,19 @@ function PolySceneInner({
2492
2792
  el.style.removeProperty("--shadow-ground-cssz");
2493
2793
  }
2494
2794
  }, [textureLighting, sceneElRef, groundCssZ]);
2495
- (0, import_react13.useEffect)(() => {
2795
+ (0, import_react14.useEffect)(() => {
2496
2796
  setGroundCssZ((prev) => {
2497
2797
  const next = recomputeGroundCssZ();
2498
2798
  return prev === next ? prev : next;
2499
2799
  });
2500
2800
  }, [recomputeGroundCssZ]);
2501
- const disabledStrategies = (0, import_react13.useMemo)(
2801
+ const disabledStrategies = (0, import_react14.useMemo)(
2502
2802
  () => strategies?.disable?.length ? new Set(strategies.disable) : void 0,
2503
2803
  [strategies]
2504
2804
  );
2505
2805
  const polyChildren = textureAtlas.entries.map((entry, index) => {
2506
2806
  if (entry) {
2507
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2807
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2508
2808
  TextureAtlasPoly,
2509
2809
  {
2510
2810
  entry,
@@ -2518,8 +2818,8 @@ function PolySceneInner({
2518
2818
  if (!plan || plan.texture) return null;
2519
2819
  const useU = !disabledStrategies?.has("u");
2520
2820
  const useProjectiveSolid = !disabledStrategies?.has("b");
2521
- if (useU && (0, import_polycss_core11.isSolidTrianglePlan)(plan)) {
2522
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2821
+ if (useU && (0, import_polycss_core12.isSolidTrianglePlan)(plan)) {
2822
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2523
2823
  TextureTrianglePoly,
2524
2824
  {
2525
2825
  entry: plan,
@@ -2528,12 +2828,12 @@ function PolySceneInner({
2528
2828
  plan.index
2529
2829
  );
2530
2830
  }
2531
- if (useProjectiveSolid && (0, import_polycss_core11.isProjectiveQuadPlan)(plan)) {
2532
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TextureProjectiveSolidPoly, { entry: plan, textureLighting }, plan.index);
2831
+ if (useProjectiveSolid && (0, import_polycss_core12.isProjectiveQuadPlan)(plan)) {
2832
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(TextureProjectiveSolidPoly, { entry: plan, textureLighting }, plan.index);
2533
2833
  }
2534
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TextureBorderShapePoly, { entry: plan, disabledStrategies }, plan.index);
2834
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(TextureBorderShapePoly, { entry: plan, textureLighting, disabledStrategies }, plan.index);
2535
2835
  });
2536
- const sceneCtxValue = (0, import_react13.useMemo)(
2836
+ const sceneCtxValue = (0, import_react14.useMemo)(
2537
2837
  () => ({
2538
2838
  textureLighting,
2539
2839
  directionalLight,
@@ -2542,11 +2842,16 @@ function PolySceneInner({
2542
2842
  seamBleed,
2543
2843
  shadow,
2544
2844
  registerShadowCaster,
2545
- groundCssZ
2845
+ registerShadowReceiver,
2846
+ shadowCasters: shadowCastersRef.current,
2847
+ shadowCastersVersion,
2848
+ hasShadowReceiver,
2849
+ groundCssZ,
2850
+ sceneEl
2546
2851
  }),
2547
- [textureLighting, directionalLight, ambientLight, strategies, seamBleed, shadow, registerShadowCaster, groundCssZ]
2852
+ [textureLighting, directionalLight, ambientLight, strategies, seamBleed, shadow, registerShadowCaster, registerShadowReceiver, shadowCastersVersion, hasShadowReceiver, groundCssZ, sceneEl]
2548
2853
  );
2549
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(PolySceneContext.Provider, { value: sceneCtxValue, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
2854
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(PolySceneContext.Provider, { value: sceneCtxValue, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
2550
2855
  "div",
2551
2856
  {
2552
2857
  ref: localSceneRef,
@@ -2564,27 +2869,27 @@ function PolySceneInner({
2564
2869
  }
2565
2870
  ) });
2566
2871
  }
2567
- var PolyScene = (0, import_react13.memo)(PolySceneInner);
2872
+ var PolyScene = (0, import_react14.memo)(PolySceneInner);
2568
2873
 
2569
2874
  // src/scene/PolyMesh.tsx
2570
- var import_react15 = require("react");
2571
- var import_polycss_core15 = require("@layoutit/polycss-core");
2875
+ var import_react16 = require("react");
2876
+ var import_polycss_core16 = require("@layoutit/polycss-core");
2572
2877
 
2573
2878
  // src/scene/useMesh.ts
2574
- var import_react14 = require("react");
2575
- var import_polycss_core13 = require("@layoutit/polycss-core");
2879
+ var import_react15 = require("react");
2880
+ var import_polycss_core14 = require("@layoutit/polycss-core");
2576
2881
  var EMPTY_POLYGONS = [];
2577
2882
  var EMPTY_WARNINGS = [];
2578
2883
  function usePolyMesh(src, options) {
2579
- const [state, setState] = (0, import_react14.useState)({
2884
+ const [state, setState] = (0, import_react15.useState)({
2580
2885
  polygons: EMPTY_POLYGONS,
2581
2886
  voxelSource: void 0,
2582
2887
  loading: !!src,
2583
2888
  error: null,
2584
2889
  warnings: EMPTY_WARNINGS
2585
2890
  });
2586
- const activeResultRef = (0, import_react14.useRef)(null);
2587
- const dispose = (0, import_react14.useCallback)(() => {
2891
+ const activeResultRef = (0, import_react15.useRef)(null);
2892
+ const dispose = (0, import_react15.useCallback)(() => {
2588
2893
  const r = activeResultRef.current;
2589
2894
  if (r) {
2590
2895
  try {
@@ -2594,7 +2899,7 @@ function usePolyMesh(src, options) {
2594
2899
  activeResultRef.current = null;
2595
2900
  }
2596
2901
  }, []);
2597
- (0, import_react14.useEffect)(() => {
2902
+ (0, import_react15.useEffect)(() => {
2598
2903
  if (!src) {
2599
2904
  dispose();
2600
2905
  setState({
@@ -2609,7 +2914,7 @@ function usePolyMesh(src, options) {
2609
2914
  let cancelled = false;
2610
2915
  setState((prev) => ({ ...prev, loading: true, error: null }));
2611
2916
  const prevResult = activeResultRef.current;
2612
- (0, import_polycss_core13.loadMesh)(src, options).then((result) => {
2917
+ (0, import_polycss_core14.loadMesh)(src, options).then((result) => {
2613
2918
  if (cancelled) {
2614
2919
  try {
2615
2920
  result.dispose();
@@ -2646,7 +2951,7 @@ function usePolyMesh(src, options) {
2646
2951
  cancelled = true;
2647
2952
  };
2648
2953
  }, [src, dispose]);
2649
- (0, import_react14.useEffect)(() => {
2954
+ (0, import_react15.useEffect)(() => {
2650
2955
  return () => dispose();
2651
2956
  }, [dispose]);
2652
2957
  return {
@@ -2659,8 +2964,12 @@ function usePolyMesh(src, options) {
2659
2964
  };
2660
2965
  }
2661
2966
 
2967
+ // src/scene/PolyMesh.tsx
2968
+ var import_polycss_core17 = require("@layoutit/polycss-core");
2969
+ var import_react_dom = require("react-dom");
2970
+
2662
2971
  // src/scene/voxelRenderer.ts
2663
- var import_polycss_core14 = require("@layoutit/polycss-core");
2972
+ var import_polycss_core15 = require("@layoutit/polycss-core");
2664
2973
  var DEFAULT_LIGHT_DIR2 = [0.4, -0.7, 0.59];
2665
2974
  var DEFAULT_LIGHT_COLOR2 = "#ffffff";
2666
2975
  var DEFAULT_LIGHT_INTENSITY2 = 1;
@@ -2668,9 +2977,9 @@ var DEFAULT_AMBIENT_COLOR2 = "#ffffff";
2668
2977
  var DEFAULT_AMBIENT_INTENSITY2 = 0.4;
2669
2978
  var DESKTOP_PRIMITIVE_SIZE = 1;
2670
2979
  var MOBILE_PRIMITIVE_SIZE = 8;
2671
- var VOXEL_SEAM_BLEED = import_polycss_core14.PROJECTIVE_QUAD_BLEED;
2980
+ var VOXEL_SEAM_BLEED = import_polycss_core15.PROJECTIVE_QUAD_BLEED;
2672
2981
  var VOXEL_SEAM_EPS = 1e-6;
2673
- var VOXEL_PROJECTIVE_QUAD_GUARDS = (0, import_polycss_core14.resolveProjectiveQuadGuards)({ bleed: 0 });
2982
+ var VOXEL_PROJECTIVE_QUAD_GUARDS = (0, import_polycss_core15.resolveProjectiveQuadGuards)({ bleed: 0 });
2674
2983
  var FACE_NORMALS = {
2675
2984
  t: [0, 0, 1],
2676
2985
  b: [0, 0, -1],
@@ -2691,7 +3000,7 @@ var FACE_BY_NORMAL = /* @__PURE__ */ new Map([
2691
3000
  function visibleFaceSignature(rotation) {
2692
3001
  const visible = [];
2693
3002
  for (const face of FACE_ORDER) {
2694
- if ((0, import_polycss_core14.normalFacesCamera)(FACE_NORMALS[face], rotation)) visible.push(face);
3003
+ if ((0, import_polycss_core15.normalFacesCamera)(FACE_NORMALS[face], rotation)) visible.push(face);
2695
3004
  }
2696
3005
  return visible.join("|");
2697
3006
  }
@@ -2760,11 +3069,11 @@ function polygonBrush(polygon) {
2760
3069
  return {
2761
3070
  axis: "z",
2762
3071
  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,
3072
+ left: minY * import_polycss_core15.BASE_TILE,
3073
+ top: minX * import_polycss_core15.BASE_TILE,
3074
+ width: Math.max(0, (maxY - minY) * import_polycss_core15.BASE_TILE),
3075
+ height: Math.max(0, (maxX - minX) * import_polycss_core15.BASE_TILE),
3076
+ z: minZ * import_polycss_core15.BASE_TILE,
2768
3077
  baseColor,
2769
3078
  bleed: zeroVoxelSeamBleed()
2770
3079
  };
@@ -2773,11 +3082,11 @@ function polygonBrush(polygon) {
2773
3082
  return {
2774
3083
  axis: "x",
2775
3084
  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,
3085
+ left: minY * import_polycss_core15.BASE_TILE,
3086
+ top: minZ * import_polycss_core15.BASE_TILE,
3087
+ width: Math.max(0, (maxY - minY) * import_polycss_core15.BASE_TILE),
3088
+ height: Math.max(0, (maxZ - minZ) * import_polycss_core15.BASE_TILE),
3089
+ z: -minX * import_polycss_core15.BASE_TILE,
2781
3090
  baseColor,
2782
3091
  bleed: zeroVoxelSeamBleed()
2783
3092
  };
@@ -2786,11 +3095,11 @@ function polygonBrush(polygon) {
2786
3095
  return {
2787
3096
  axis: "y",
2788
3097
  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,
3098
+ left: minZ * import_polycss_core15.BASE_TILE,
3099
+ top: minX * import_polycss_core15.BASE_TILE,
3100
+ width: Math.max(0, (maxZ - minZ) * import_polycss_core15.BASE_TILE),
3101
+ height: Math.max(0, (maxX - minX) * import_polycss_core15.BASE_TILE),
3102
+ z: -minY * import_polycss_core15.BASE_TILE,
2794
3103
  baseColor,
2795
3104
  bleed: zeroVoxelSeamBleed()
2796
3105
  };
@@ -2810,7 +3119,7 @@ function worldLineKey(segment) {
2810
3119
  return key;
2811
3120
  }
2812
3121
  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];
3122
+ return [v[1] * import_polycss_core15.BASE_TILE, v[0] * import_polycss_core15.BASE_TILE, v[2] * import_polycss_core15.BASE_TILE];
2814
3123
  }
2815
3124
  function localPointForItem(item, p) {
2816
3125
  if (item.axis === "x") return [p[0], p[2]];
@@ -2886,7 +3195,7 @@ function applyVoxelSeamBleed(polygons, items) {
2886
3195
  }
2887
3196
  }
2888
3197
  function parseColor(input) {
2889
- const parsed = (0, import_polycss_core14.parsePureColor)(input);
3198
+ const parsed = (0, import_polycss_core15.parsePureColor)(input);
2890
3199
  if (!parsed) return { r: 255, g: 255, b: 255, alpha: 1 };
2891
3200
  return {
2892
3201
  r: parsed.rgb[0],
@@ -2897,7 +3206,7 @@ function parseColor(input) {
2897
3206
  }
2898
3207
  function canonicalBrushColor(input) {
2899
3208
  if (!input) return "#cccccc";
2900
- const parsed = (0, import_polycss_core14.parsePureColor)(input);
3209
+ const parsed = (0, import_polycss_core15.parsePureColor)(input);
2901
3210
  if (!parsed) return input;
2902
3211
  const rgb = {
2903
3212
  r: parsed.rgb[0],
@@ -2919,9 +3228,6 @@ function clampChannel(value) {
2919
3228
  return Math.round(Math.max(0, Math.min(255, value)));
2920
3229
  }
2921
3230
  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
3231
  const lightDir = directionalLight?.direction ?? DEFAULT_LIGHT_DIR2;
2926
3232
  const lightLen = Math.hypot(lightDir[0], lightDir[1], lightDir[2]) || 1;
2927
3233
  const lx = lightDir[0] / lightLen;
@@ -2929,16 +3235,15 @@ function shadeBrushColor(normal, baseColor, directionalLight, ambientLight) {
2929
3235
  const lz = lightDir[2] / lightLen;
2930
3236
  const directScale = Math.max(0, directionalLight?.intensity ?? DEFAULT_LIGHT_INTENSITY2) * Math.max(0, normal[0] * lx + normal[1] * ly + normal[2] * lz);
2931
3237
  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);
3238
+ const lightColor = directionalLight?.color ?? DEFAULT_LIGHT_COLOR2;
3239
+ const ambientColor = ambientLight?.color ?? DEFAULT_AMBIENT_COLOR2;
3240
+ const shaded = (0, import_polycss_core15.shadePolygon)(baseColor, directScale, lightColor, ambientColor, ambientIntensity);
3241
+ const base = parseColor(baseColor);
3242
+ if (base.alpha >= 1) return shaded;
3243
+ const r = parseInt(shaded.slice(1, 3), 16);
3244
+ const g = parseInt(shaded.slice(3, 5), 16);
3245
+ const b = parseInt(shaded.slice(5, 7), 16);
3246
+ return `rgba(${r}, ${g}, ${b}, ${base.alpha})`;
2942
3247
  }
2943
3248
  function buildDirectMatrixItems(polygons) {
2944
3249
  if (!polygons?.length) return [];
@@ -3012,7 +3317,7 @@ function voxelSeamEdgeAmounts(item) {
3012
3317
  function rescaleProjectiveMatrix(matrix, primitiveSize) {
3013
3318
  const values = matrix.split(",").map(Number);
3014
3319
  if (values.length !== 16 || values.some((value) => !Number.isFinite(value))) return null;
3015
- const scale = import_polycss_core14.SOLID_QUAD_CANONICAL_SIZE / primitiveSize;
3320
+ const scale = import_polycss_core15.SOLID_QUAD_CANONICAL_SIZE / primitiveSize;
3016
3321
  for (let i = 0; i < 8; i += 1) values[i] *= scale;
3017
3322
  return `matrix3d(${values.map((value) => Number(value.toFixed(6))).join(",")})`;
3018
3323
  }
@@ -3080,7 +3385,7 @@ function affineVoxelMatrix(item, primitiveSize) {
3080
3385
  }
3081
3386
  function directMatrix(item, primitiveSize) {
3082
3387
  const { xAxis, yAxis, normal, tx, ty, tz } = voxelProjectiveBasis(item);
3083
- const projective = (0, import_polycss_core14.computeProjectiveQuadMatrix)(
3388
+ const projective = (0, import_polycss_core15.computeProjectiveQuadMatrix)(
3084
3389
  voxelScreenPts(item),
3085
3390
  xAxis,
3086
3391
  yAxis,
@@ -3114,9 +3419,9 @@ function projectedPoint(item, rotation) {
3114
3419
  let center = itemCenter(item);
3115
3420
  const meshRotation = rotation.meshRotation;
3116
3421
  if (meshRotation) {
3117
- center = (0, import_polycss_core14.rotateVec3)(center, meshRotation[0] ?? 0, meshRotation[1] ?? 0, meshRotation[2] ?? 0);
3422
+ center = (0, import_polycss_core15.rotateVec3)(center, meshRotation[0] ?? 0, meshRotation[1] ?? 0, meshRotation[2] ?? 0);
3118
3423
  }
3119
- const [x, y] = (0, import_polycss_core14.rotateVec3)(center, rotation.rotX, 0, rotation.rotY);
3424
+ const [x, y] = (0, import_polycss_core15.rotateVec3)(center, rotation.rotX, 0, rotation.rotY);
3120
3425
  return { x, y };
3121
3426
  }
3122
3427
  function orderDirectMatrixItems(items, visibleFaces, rotation) {
@@ -3384,7 +3689,9 @@ function findMeshUnderPoint(clientX, clientY, filter) {
3384
3689
  }
3385
3690
 
3386
3691
  // src/scene/PolyMesh.tsx
3387
- var import_jsx_runtime8 = require("react/jsx-runtime");
3692
+ var import_jsx_runtime9 = require("react/jsx-runtime");
3693
+ var reactEdgeOwnersCache = /* @__PURE__ */ new WeakMap();
3694
+ var reactEdgeOwnersCacheKey = /* @__PURE__ */ new WeakMap();
3388
3695
  function solidPaintVars(defaults) {
3389
3696
  const out = {};
3390
3697
  if (defaults.paintColor) out["--polycss-paint"] = defaults.paintColor;
@@ -3395,28 +3702,9 @@ function solidPaintVars(defaults) {
3395
3702
  }
3396
3703
  return Object.keys(out).length > 0 ? out : null;
3397
3704
  }
3398
- function buildTransform(position, scale, rotation) {
3399
- const parts = [];
3400
- if (position) {
3401
- parts.push(`translate3d(${position[0]}px, ${position[1]}px, ${position[2]}px)`);
3402
- }
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
- }
3409
- }
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)`);
3414
- }
3415
- return parts.length > 0 ? parts.join(" ") : void 0;
3416
- }
3417
3705
  function recenterPolygons(polygons) {
3418
3706
  if (polygons.length === 0) return polygons;
3419
- const bbox = (0, import_polycss_core15.computeSceneBbox)(polygons);
3707
+ const bbox = (0, import_polycss_core16.computeSceneBbox)(polygons);
3420
3708
  const cx = (bbox.min[0] + bbox.max[0]) / 2;
3421
3709
  const cy = (bbox.min[1] + bbox.max[1]) / 2;
3422
3710
  const cz = (bbox.min[2] + bbox.max[2]) / 2;
@@ -3433,16 +3721,21 @@ function recenterPolygons(polygons) {
3433
3721
  } : null
3434
3722
  }));
3435
3723
  }
3436
- var PolyMesh = (0, import_react15.forwardRef)(function PolyMesh2({
3724
+ var PolyMesh = (0, import_react16.forwardRef)(function PolyMesh2({
3437
3725
  id,
3438
3726
  src,
3439
3727
  mtl,
3440
3728
  polygons: polygonsProp,
3729
+ voxelSource: voxelSourceProp,
3441
3730
  autoCenter,
3442
3731
  textureLighting,
3443
3732
  textureQuality,
3444
3733
  seamBleed,
3734
+ atomicAtlas,
3735
+ onFrameReady,
3445
3736
  castShadow,
3737
+ receiveShadow,
3738
+ merge = true,
3446
3739
  children,
3447
3740
  fallback,
3448
3741
  errorFallback,
@@ -3466,7 +3759,7 @@ var PolyMesh = (0, import_react15.forwardRef)(function PolyMesh2({
3466
3759
  onPointerLeave,
3467
3760
  onPointerCancel
3468
3761
  }, forwardedRef) {
3469
- const mergedOptions = (0, import_react15.useMemo)(() => {
3762
+ const mergedOptions = (0, import_react16.useMemo)(() => {
3470
3763
  if (!mtl && !parseOptions && meshResolution === void 0) return void 0;
3471
3764
  return {
3472
3765
  ...parseOptions ?? {},
@@ -3476,54 +3769,37 @@ var PolyMesh = (0, import_react15.forwardRef)(function PolyMesh2({
3476
3769
  }, [mtl, parseOptions, meshResolution]);
3477
3770
  const fetched = usePolyMesh(src ?? "", mergedOptions);
3478
3771
  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);
3772
+ const externalVoxelSource = src ? fetched.voxelSource : voxelSourceProp;
3773
+ const [localPolygons, setLocalPolygons] = (0, import_react16.useState)(null);
3774
+ const prevExternalRef = (0, import_react16.useRef)(externalPolygons);
3482
3775
  if (prevExternalRef.current !== externalPolygons) {
3483
3776
  prevExternalRef.current = externalPolygons;
3484
3777
  if (localPolygons !== null) setLocalPolygons(null);
3485
3778
  }
3486
- const sourcePolygons = localPolygons ?? externalPolygons;
3779
+ const rawSourcePolygons = localPolygons ?? externalPolygons;
3780
+ const sourcePolygons = (0, import_react16.useMemo)(
3781
+ () => merge ? (0, import_polycss_core16.optimizeMeshPolygons)(rawSourcePolygons, meshResolution !== void 0 ? { meshResolution } : void 0) : rawSourcePolygons,
3782
+ [rawSourcePolygons, merge, meshResolution]
3783
+ );
3487
3784
  const hasRenderProp = typeof children === "function";
3488
3785
  const renderPolygon = hasRenderProp ? children : null;
3489
3786
  const staticChildren = hasRenderProp ? null : children;
3490
3787
  const hasStaticChildren = staticChildren !== null && staticChildren !== void 0 && staticChildren !== false;
3491
- const polygons = (0, import_react15.useMemo)(
3788
+ const polygons = (0, import_react16.useMemo)(
3492
3789
  () => autoCenter ? recenterPolygons(sourcePolygons) : sourcePolygons,
3493
3790
  [sourcePolygons, autoCenter]
3494
3791
  );
3495
- 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 });
3792
+ const transform = (0, import_polycss_core16.buildPolyMeshTransform)({ position, scale, rotation });
3793
+ const wrapperRef = (0, import_react16.useRef)(null);
3794
+ const propsRef = (0, import_react16.useRef)({ position, scale, rotation });
3519
3795
  propsRef.current = { position, scale, rotation };
3520
- const polygonsRef = (0, import_react15.useRef)(polygons);
3796
+ const polygonsRef = (0, import_react16.useRef)(polygons);
3521
3797
  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)(() => {
3798
+ const [bakedRotation, setBakedRotation] = (0, import_react16.useState)(rotation);
3799
+ const stableTriangleColorFrameRef = (0, import_react16.useRef)(0);
3800
+ const setPolygonsImplRef = (0, import_react16.useRef)(() => {
3525
3801
  });
3526
- const handle = (0, import_react15.useMemo)(() => ({
3802
+ const handle = (0, import_react16.useMemo)(() => ({
3527
3803
  get element() {
3528
3804
  return wrapperRef.current;
3529
3805
  },
@@ -3544,17 +3820,17 @@ var PolyMesh = (0, import_react15.forwardRef)(function PolyMesh2({
3544
3820
  setLocalPolygons([...current]);
3545
3821
  }
3546
3822
  }), [id]);
3547
- (0, import_react15.useImperativeHandle)(forwardedRef, () => handle, [handle]);
3548
- (0, import_react15.useEffect)(() => {
3823
+ (0, import_react16.useImperativeHandle)(forwardedRef, () => handle, [handle]);
3824
+ (0, import_react16.useEffect)(() => {
3549
3825
  const el = wrapperRef.current;
3550
3826
  if (!el) return;
3551
3827
  registerMeshElement(el, handle);
3552
3828
  return () => unregisterMeshElement(el);
3553
3829
  }, [handle]);
3554
- const cameraCtx = (0, import_react15.useContext)(PolyCameraContext);
3830
+ const cameraCtx = (0, import_react16.useContext)(PolyCameraContext);
3555
3831
  const cameraElRef = cameraCtx?.cameraElRef ?? null;
3556
- const pointerDownAtRef = (0, import_react15.useRef)(null);
3557
- const makeEvent = (0, import_react15.useCallback)(
3832
+ const pointerDownAtRef = (0, import_react16.useRef)(null);
3833
+ const makeEvent = (0, import_react16.useCallback)(
3558
3834
  function makeEvent2(nativeEvent, clientX, clientY) {
3559
3835
  const intersections = [];
3560
3836
  if (typeof document !== "undefined" && typeof document.elementsFromPoint === "function") {
@@ -3593,7 +3869,7 @@ var PolyMesh = (0, import_react15.forwardRef)(function PolyMesh2({
3593
3869
  },
3594
3870
  [cameraElRef, handle]
3595
3871
  );
3596
- const wrapperHandlers = (0, import_react15.useMemo)(() => {
3872
+ const wrapperHandlers = (0, import_react16.useMemo)(() => {
3597
3873
  const dispatch = (polyHandler, reactEvent, nativeEvent, clientX, clientY) => {
3598
3874
  if (!polyHandler) return;
3599
3875
  const polyEvent = makeEvent(nativeEvent, clientX, clientY);
@@ -3677,90 +3953,144 @@ var PolyMesh = (0, import_react15.forwardRef)(function PolyMesh2({
3677
3953
  const sceneCtx = usePolySceneContext2();
3678
3954
  const effectiveTextureLighting = textureLighting ?? sceneCtx?.textureLighting ?? "baked";
3679
3955
  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;
3956
+ const disabledStrategies = (0, import_react16.useMemo)(
3957
+ () => effectiveStrategies?.disable?.length ? new Set(effectiveStrategies.disable) : void 0,
3958
+ [effectiveStrategies]
3959
+ );
3960
+ const effectiveSeamBleed = seamBleed ?? sceneCtx?.seamBleed ?? import_polycss_core16.DEFAULT_SEAM_BLEED;
3961
+ const effectiveDirectional = sceneCtx?.directionalLight;
3962
+ const effectiveAmbient = sceneCtx?.ambientLight;
3683
3963
  const directVoxelEnabled = Boolean(
3684
3964
  externalVoxelSource && localPolygons === null && !renderPolygon && !hasStaticChildren && effectiveTextureLighting === "baked" && !castShadow
3685
3965
  );
3686
3966
  const sceneDirectionalLight = sceneCtx?.directionalLight;
3687
- const dynamicLightOverride = (0, import_react15.useMemo)(() => {
3967
+ const dynamicLightOverride = (0, import_react16.useMemo)(() => {
3688
3968
  if (effectiveTextureLighting !== "dynamic") return null;
3689
3969
  if (!rotation || rotation[0] === 0 && rotation[1] === 0 && rotation[2] === 0) return null;
3690
3970
  if (!sceneDirectionalLight) return null;
3691
3971
  const dir = sceneDirectionalLight.direction;
3692
- const localDir = (0, import_polycss_core15.inverseRotateVec3)(dir, rotation);
3972
+ const localDir = (0, import_polycss_core16.inverseRotateVec3)(dir, rotation);
3693
3973
  const len = Math.hypot(localDir[0], localDir[1], localDir[2]) || 1;
3694
3974
  return {
3695
- ["--plx"]: (localDir[0] / len).toFixed(4),
3696
- ["--ply"]: (localDir[1] / len).toFixed(4),
3697
- ["--plz"]: (localDir[2] / len).toFixed(4)
3975
+ ["--plx"]: (localDir[0] / len).toFixed(2),
3976
+ ["--ply"]: (localDir[1] / len).toFixed(2),
3977
+ ["--plz"]: (localDir[2] / len).toFixed(2)
3698
3978
  };
3699
3979
  }, [effectiveTextureLighting, rotation, sceneDirectionalLight]);
3700
- const bakedDirectional = (0, import_react15.useMemo)(() => {
3980
+ const bakedDirectional = (0, import_react16.useMemo)(() => {
3701
3981
  if (!effectiveDirectional) return effectiveDirectional;
3702
3982
  const rot = bakedRotation ?? [0, 0, 0];
3703
- if (rot[0] === 0 && rot[1] === 0 && rot[2] === 0) return effectiveDirectional;
3983
+ const cssLight = (0, import_polycss_core17.worldDirectionalLightToCss)(effectiveDirectional);
3984
+ if (rot[0] === 0 && rot[1] === 0 && rot[2] === 0) return cssLight;
3704
3985
  return {
3705
- ...effectiveDirectional,
3706
- direction: (0, import_polycss_core15.inverseRotateVec3)(effectiveDirectional.direction, rot)
3986
+ ...cssLight,
3987
+ direction: (0, import_polycss_core16.inverseRotateVec3)(cssLight.direction, rot)
3707
3988
  };
3708
3989
  }, [effectiveDirectional, bakedRotation]);
3709
- const atlasPlans = (0, import_react15.useMemo)(
3990
+ const lightOccludedPolyIndices = void 0;
3991
+ const atlasPlans = (0, import_react16.useMemo)(
3710
3992
  () => {
3711
3993
  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, {
3994
+ const repairEdges = (0, import_polycss_core12.buildTextureEdgeRepairSets)(polygons);
3995
+ const seamBleedEdges = effectiveSeamBleed === "auto" || typeof effectiveSeamBleed === "number" && Number.isFinite(effectiveSeamBleed) && effectiveSeamBleed > 0 ? (0, import_polycss_core12.buildSeamBleedPolygonEdges)(polygons, {
3714
3996
  directionalLight: bakedDirectional,
3715
3997
  ambientLight: effectiveAmbient
3716
3998
  }) : null;
3717
- return polygons.map((p, i) => computeTextureAtlasPlan(p, i, {
3999
+ const basisHints = (0, import_polycss_core17.buildBasisHints)(polygons, {
3718
4000
  directionalLight: bakedDirectional,
3719
- ambientLight: effectiveAmbient,
3720
- seamBleed: seamBleedEdges?.has(i) ? effectiveSeamBleed : void 0,
3721
- seamEdges: seamBleedEdges?.get(i),
3722
- textureEdgeRepairEdges: repairEdges[i]
3723
- }));
4001
+ ambientLight: effectiveAmbient
4002
+ });
4003
+ return polygons.map((p, i) => computeTextureAtlasPlan(
4004
+ p,
4005
+ i,
4006
+ {
4007
+ directionalLight: bakedDirectional,
4008
+ ambientLight: effectiveAmbient,
4009
+ seamBleed: seamBleedEdges?.has(i) ? effectiveSeamBleed : void 0,
4010
+ seamEdges: seamBleedEdges?.get(i),
4011
+ textureEdgeRepairEdges: repairEdges[i],
4012
+ lightOccludedPolyIndices
4013
+ },
4014
+ basisHints[i]
4015
+ ));
3724
4016
  },
3725
- [renderPolygon, directVoxelEnabled, polygons, bakedDirectional, effectiveAmbient, effectiveSeamBleed]
4017
+ [renderPolygon, directVoxelEnabled, polygons, bakedDirectional, effectiveAmbient, effectiveSeamBleed, lightOccludedPolyIndices]
3726
4018
  );
3727
4019
  const textureAtlas = useTextureAtlas(
3728
4020
  atlasPlans,
3729
4021
  effectiveTextureLighting,
3730
4022
  textureQuality,
3731
- effectiveStrategies
4023
+ effectiveStrategies,
4024
+ atomicAtlas
3732
4025
  );
3733
- const solidPaintDefaults = (0, import_react15.useMemo)(
3734
- () => !renderPolygon ? getSolidPaintDefaults(atlasPlans, effectiveTextureLighting, effectiveStrategies) : {},
3735
- [renderPolygon, atlasPlans, effectiveTextureLighting, effectiveStrategies]
4026
+ const solidPaintDefaults = (0, import_react16.useMemo)(
4027
+ () => !renderPolygon ? getSolidPaintDefaults(textureAtlas.plans, effectiveTextureLighting, effectiveStrategies) : {},
4028
+ [renderPolygon, textureAtlas.plans, effectiveTextureLighting, effectiveStrategies]
3736
4029
  );
3737
- const defaultPaintVars = (0, import_react15.useMemo)(
4030
+ const onFrameReadyRef = (0, import_react16.useRef)(onFrameReady);
4031
+ onFrameReadyRef.current = onFrameReady;
4032
+ (0, import_react16.useLayoutEffect)(() => {
4033
+ if (atomicAtlas && textureAtlas.ready) onFrameReadyRef.current?.();
4034
+ }, [textureAtlas.entries]);
4035
+ const defaultPaintVars = (0, import_react16.useMemo)(
3738
4036
  () => solidPaintVars(solidPaintDefaults),
3739
4037
  [solidPaintDefaults]
3740
4038
  );
3741
- const meshIdRef = (0, import_react15.useRef)(/* @__PURE__ */ Symbol());
4039
+ const meshIdRef = (0, import_react16.useRef)(/* @__PURE__ */ Symbol());
3742
4040
  const sceneRegisterShadowCaster = sceneCtx?.registerShadowCaster;
3743
- (0, import_react15.useEffect)(() => {
4041
+ const renderedPolygonIndices = (0, import_react16.useMemo)(() => {
4042
+ const dedupDrop = (0, import_polycss_core16.findOverlappingPolygonDuplicates)(polygons, {
4043
+ normalTolerance: 0.1,
4044
+ distanceTolerance: 0.5,
4045
+ overlapFraction: 0.95,
4046
+ preserveDoubleSidedBackfaces: false
4047
+ });
4048
+ const s = /* @__PURE__ */ new Set();
4049
+ for (let i = 0; i < atlasPlans.length; i++) {
4050
+ if (atlasPlans[i] && !dedupDrop.has(i)) s.add(i);
4051
+ }
4052
+ return s;
4053
+ }, [atlasPlans, polygons]);
4054
+ (0, import_react16.useEffect)(() => {
3744
4055
  if (!sceneRegisterShadowCaster) return;
3745
4056
  if (castShadow) {
3746
- sceneRegisterShadowCaster(meshIdRef.current, polygons);
4057
+ sceneRegisterShadowCaster(meshIdRef.current, {
4058
+ polygons,
4059
+ position: position ?? [0, 0, 0],
4060
+ scale,
4061
+ rotation,
4062
+ renderedPolygonIndices
4063
+ });
3747
4064
  } else {
3748
4065
  sceneRegisterShadowCaster(meshIdRef.current, null);
3749
4066
  }
3750
4067
  return () => {
3751
4068
  sceneRegisterShadowCaster(meshIdRef.current, null);
3752
4069
  };
3753
- }, [sceneRegisterShadowCaster, castShadow, polygons]);
4070
+ }, [sceneRegisterShadowCaster, castShadow, polygons, position, scale, rotation, renderedPolygonIndices]);
4071
+ const sceneRegisterShadowReceiver = sceneCtx?.registerShadowReceiver;
4072
+ (0, import_react16.useEffect)(() => {
4073
+ if (!sceneRegisterShadowReceiver) return;
4074
+ sceneRegisterShadowReceiver(meshIdRef.current, !!receiveShadow);
4075
+ return () => {
4076
+ sceneRegisterShadowReceiver(meshIdRef.current, false);
4077
+ };
4078
+ }, [sceneRegisterShadowReceiver, receiveShadow]);
3754
4079
  const bakedShadowGroundCssZ = sceneCtx?.groundCssZ ?? null;
3755
4080
  const sceneShadow = sceneCtx?.shadow;
3756
- const shadowSvgNode = (0, import_react15.useMemo)(() => {
4081
+ const sceneHasReceiver = sceneCtx?.hasShadowReceiver ?? false;
4082
+ const shadowSvgNode = (0, import_react16.useMemo)(() => {
3757
4083
  if (!castShadow || renderPolygon) return null;
4084
+ if (sceneHasReceiver) return null;
3758
4085
  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, {
4086
+ const userGroundLightDir = sceneDirectionalLight?.direction ?? [0.4, -0.7, 0.59];
4087
+ const lightDir = (0, import_polycss_core16.worldDirectionToCss)(userGroundLightDir);
4088
+ const meshPosZ = position?.[2] ?? 0;
4089
+ const localGroundCssZ = bakedShadowGroundCssZ - meshPosZ * import_polycss_core16.BASE_TILE;
4090
+ const shadowDedupDrop = (0, import_polycss_core16.findOverlappingPolygonDuplicates)(polygons, {
3761
4091
  normalTolerance: 0.1,
3762
4092
  distanceTolerance: 0.5,
3763
- overlapFraction: 0.4,
4093
+ overlapFraction: 0.95,
3764
4094
  preserveDoubleSidedBackfaces: false
3765
4095
  });
3766
4096
  const projections = [];
@@ -3774,15 +4104,15 @@ var PolyMesh = (0, import_react15.forwardRef)(function PolyMesh2({
3774
4104
  const projected = [];
3775
4105
  for (const v of polygon.vertices) {
3776
4106
  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
4107
+ v[1] * import_polycss_core16.BASE_TILE,
4108
+ v[0] * import_polycss_core16.BASE_TILE,
4109
+ v[2] * import_polycss_core16.BASE_TILE
3780
4110
  ];
3781
4111
  if (cssVertex[0] < fpMinX) fpMinX = cssVertex[0];
3782
4112
  if (cssVertex[1] < fpMinY) fpMinY = cssVertex[1];
3783
4113
  if (cssVertex[0] > fpMaxX) fpMaxX = cssVertex[0];
3784
4114
  if (cssVertex[1] > fpMaxY) fpMaxY = cssVertex[1];
3785
- const p = (0, import_polycss_core15.projectCssVertexToGround)(cssVertex, lightDir, bakedShadowGroundCssZ);
4115
+ const p = (0, import_polycss_core16.projectCssVertexToGround)(cssVertex, lightDir, localGroundCssZ);
3786
4116
  projected.push(p);
3787
4117
  if (p[0] < minX) minX = p[0];
3788
4118
  if (p[1] < minY) minY = p[1];
@@ -3802,17 +4132,17 @@ var PolyMesh = (0, import_react15.forwardRef)(function PolyMesh2({
3802
4132
  if (!(width > 0) || !(height > 0)) return null;
3803
4133
  const shadowColor = sceneShadow?.color ?? "#000000";
3804
4134
  const shadowOpacity = sceneShadow?.opacity ?? 0.25;
3805
- const parsed = (0, import_polycss_core15.parseHexColor)(shadowColor)?.rgb ?? [0, 0, 0];
4135
+ const parsed = (0, import_polycss_core16.parseHexColor)(shadowColor)?.rgb ?? [0, 0, 0];
3806
4136
  let d = "";
3807
4137
  for (const verts of projections) {
3808
- const ccw = (0, import_polycss_core15.ensureCcw2D)(verts);
4138
+ const ccw = (0, import_polycss_core16.ensureCcw2D)(verts);
3809
4139
  d += `M${(ccw[0][0] - bx0).toFixed(3)},${(ccw[0][1] - by0).toFixed(3)}`;
3810
4140
  for (let j = 1; j < ccw.length; j++) {
3811
4141
  d += `L${(ccw[j][0] - bx0).toFixed(3)},${(ccw[j][1] - by0).toFixed(3)}`;
3812
4142
  }
3813
4143
  d += "Z";
3814
4144
  }
3815
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4145
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
3816
4146
  "svg",
3817
4147
  {
3818
4148
  className: "polycss-shadow polycss-shadow-svg",
@@ -3828,16 +4158,16 @@ var PolyMesh = (0, import_react15.forwardRef)(function PolyMesh2({
3828
4158
  transformOrigin: "0 0",
3829
4159
  pointerEvents: "none",
3830
4160
  willChange: "transform",
3831
- transform: `translate3d(${bx0.toFixed(3)}px,${by0.toFixed(3)}px,${bakedShadowGroundCssZ.toFixed(3)}px)`
4161
+ transform: `translate3d(${bx0.toFixed(3)}px,${by0.toFixed(3)}px,${localGroundCssZ.toFixed(3)}px)`
3832
4162
  },
3833
- children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4163
+ children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
3834
4164
  "path",
3835
4165
  {
3836
4166
  d,
3837
4167
  fill: `rgb(${parsed[0]},${parsed[1]},${parsed[2]})`,
3838
4168
  fillRule: "nonzero",
3839
4169
  stroke: `rgb(${parsed[0]},${parsed[1]},${parsed[2]})`,
3840
- strokeWidth: "2",
4170
+ strokeWidth: "3",
3841
4171
  strokeLinejoin: "round",
3842
4172
  opacity: shadowOpacity.toFixed(4)
3843
4173
  }
@@ -3845,7 +4175,125 @@ var PolyMesh = (0, import_react15.forwardRef)(function PolyMesh2({
3845
4175
  },
3846
4176
  "shadow-svg"
3847
4177
  );
3848
- }, [castShadow, renderPolygon, polygons, atlasPlans, sceneDirectionalLight, bakedShadowGroundCssZ, sceneShadow]);
4178
+ }, [castShadow, renderPolygon, polygons, atlasPlans, sceneDirectionalLight, bakedShadowGroundCssZ, sceneShadow, sceneHasReceiver]);
4179
+ const shadowCasters = sceneCtx?.shadowCasters;
4180
+ const shadowCastersVersion = sceneCtx?.shadowCastersVersion ?? 0;
4181
+ const [cameraTick, setCameraTick] = (0, import_react16.useState)(0);
4182
+ (0, import_react16.useEffect)(() => {
4183
+ if (!receiveShadow) return;
4184
+ return cameraCtx?.store.subscribe(() => setCameraTick((n) => n + 1));
4185
+ }, [receiveShadow, cameraCtx?.store]);
4186
+ void cameraTick;
4187
+ const selfShadowEdgeMap = (0, import_react16.useMemo)(
4188
+ () => receiveShadow ? (0, import_polycss_core16.buildSharedEdgeMap)(polygons) : void 0,
4189
+ [polygons, receiveShadow]
4190
+ );
4191
+ const receiverShadowSvgs = (0, import_react16.useMemo)(() => {
4192
+ if (!receiveShadow) return null;
4193
+ if (!shadowCasters || shadowCasters.size === 0) return null;
4194
+ const userLightDir = sceneDirectionalLight?.direction ?? [0.4, -0.7, 0.59];
4195
+ const lightDir = (0, import_polycss_core16.worldDirectionToCss)(userLightDir);
4196
+ const shadowLift = sceneShadow?.lift ?? 1e-3;
4197
+ const planes = (0, import_polycss_core16.prepareReceiverFacePlanes)(
4198
+ polygons,
4199
+ position ?? [0, 0, 0],
4200
+ scale,
4201
+ /* @__PURE__ */ new Set(),
4202
+ shadowLift,
4203
+ rotation
4204
+ );
4205
+ if (planes.length === 0) return null;
4206
+ const casterInputs = [];
4207
+ for (const [casterId, data] of shadowCasters) {
4208
+ const rendered = data.renderedPolygonIndices;
4209
+ const items = (0, import_polycss_core16.prepareCasterPolyItems)(
4210
+ data.polygons,
4211
+ data.position,
4212
+ data.scale,
4213
+ rendered ? (idx) => rendered.has(idx) : () => true,
4214
+ data.rotation ?? null
4215
+ );
4216
+ const isSelf = data.polygons === polygons;
4217
+ const selfMap = isSelf ? selfShadowEdgeMap : void 0;
4218
+ let edgeOwners;
4219
+ if (!isSelf && data.polygons.length >= 40) {
4220
+ const dposArr = data.position;
4221
+ const drot = data.rotation ?? null;
4222
+ const dsKey = JSON.stringify(data.scale ?? null);
4223
+ const eoKey = `${dposArr[0]},${dposArr[1]},${dposArr[2]}|${drot ? drot.join(",") : "n"}|${dsKey}`;
4224
+ let cachedOwners = reactEdgeOwnersCache.get(data.polygons);
4225
+ if (cachedOwners === void 0 || reactEdgeOwnersCacheKey.get(data.polygons) !== eoKey) {
4226
+ cachedOwners = (0, import_polycss_core16.prepareCasterEdgeOwners)(data.polygons, dposArr, data.scale, drot);
4227
+ reactEdgeOwnersCache.set(data.polygons, cachedOwners);
4228
+ reactEdgeOwnersCacheKey.set(data.polygons, eoKey);
4229
+ }
4230
+ edgeOwners = cachedOwners;
4231
+ }
4232
+ casterInputs.push({
4233
+ id: casterId,
4234
+ items,
4235
+ selfShadowEdgeMap: selfMap,
4236
+ edgeOwners,
4237
+ casterPolygonCount: data.polygons.length
4238
+ });
4239
+ }
4240
+ const cameraState = cameraCtx?.store.getState().cameraState;
4241
+ const cameraRot = {
4242
+ rotX: cameraState?.rotX ?? 65,
4243
+ rotY: cameraState?.rotY ?? 45,
4244
+ meshRotation: rotation
4245
+ };
4246
+ const specs = (0, import_polycss_core16.computeReceiverShadowFaces)({
4247
+ receiverPlanes: planes,
4248
+ receiverPolygons: polygons,
4249
+ receiverHasTexture: polygons.some((p) => p.texture !== void 0),
4250
+ casters: casterInputs,
4251
+ lightDir,
4252
+ cameraRot,
4253
+ ambientLight: sceneCtx?.ambientLight,
4254
+ directionalLight: sceneDirectionalLight,
4255
+ shadow: { color: sceneShadow?.color, opacity: sceneShadow?.opacity ?? 0.25, maxExtend: sceneShadow?.maxExtend }
4256
+ });
4257
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_jsx_runtime9.Fragment, { children: specs.map((spec) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4258
+ "svg",
4259
+ {
4260
+ className: "polycss-shadow polycss-shadow-svg polycss-shadow-receiver",
4261
+ "data-poly-shadow-type": "receiver",
4262
+ "data-poly-shadow-receiver-face": spec.faceIndex,
4263
+ "data-poly-shadow-receiver-polys": JSON.stringify(spec.memberPolyIndices),
4264
+ width: spec.width,
4265
+ height: spec.height,
4266
+ viewBox: `0 0 ${spec.width} ${spec.height}`,
4267
+ style: {
4268
+ position: "absolute",
4269
+ top: 0,
4270
+ left: 0,
4271
+ display: "block",
4272
+ overflow: "hidden",
4273
+ transformOrigin: "0 0",
4274
+ pointerEvents: "none",
4275
+ willChange: "transform",
4276
+ transform: spec.matrixCss
4277
+ },
4278
+ children: spec.paths.map((p, i) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4279
+ "path",
4280
+ {
4281
+ d: p.d,
4282
+ fill: spec.fill,
4283
+ stroke: spec.fill,
4284
+ strokeWidth: "3",
4285
+ strokeLinejoin: "round",
4286
+ opacity: spec.opacity.toFixed(4),
4287
+ "data-poly-shadow-caster-polys": JSON.stringify(p.casterPolygonIndices)
4288
+ },
4289
+ i
4290
+ ))
4291
+ },
4292
+ `receiver-${spec.faceIndex}`
4293
+ )) });
4294
+ }, [receiveShadow, shadowCasters, shadowCastersVersion, polygons, position, scale, rotation, sceneDirectionalLight, sceneShadow, sceneCtx?.ambientLight, cameraCtx?.store, cameraTick, selfShadowEdgeMap]);
4295
+ const portalSceneEl = sceneCtx?.sceneEl ?? null;
4296
+ const portaledReceiverShadowSvgs = portalSceneEl && receiverShadowSvgs ? (0, import_react_dom.createPortal)(receiverShadowSvgs, portalSceneEl) : null;
3849
4297
  setPolygonsImplRef.current = (nextPolygons) => {
3850
4298
  const nextRenderedPolygons = autoCenter ? recenterPolygons(nextPolygons) : nextPolygons;
3851
4299
  polygonsRef.current = nextRenderedPolygons;
@@ -3865,8 +4313,8 @@ var PolyMesh = (0, import_react15.forwardRef)(function PolyMesh2({
3865
4313
  }
3866
4314
  setLocalPolygons([...nextPolygons]);
3867
4315
  };
3868
- const voxelRendererRef = (0, import_react15.useRef)(null);
3869
- (0, import_react15.useLayoutEffect)(() => {
4316
+ const voxelRendererRef = (0, import_react16.useRef)(null);
4317
+ (0, import_react16.useLayoutEffect)(() => {
3870
4318
  const root = wrapperRef.current;
3871
4319
  voxelRendererRef.current?.dispose();
3872
4320
  voxelRendererRef.current = null;
@@ -3907,7 +4355,6 @@ var PolyMesh = (0, import_react15.forwardRef)(function PolyMesh2({
3907
4355
  ]);
3908
4356
  const wrapperStyle = {
3909
4357
  transform,
3910
- ...transformOrigin ? { transformOrigin } : null,
3911
4358
  ...dynamicLightOverride,
3912
4359
  ...style,
3913
4360
  ...defaultPaintVars
@@ -3915,10 +4362,10 @@ var PolyMesh = (0, import_react15.forwardRef)(function PolyMesh2({
3915
4362
  const renderedPolygons = renderPolygon ? polygons.map((p, i) => (
3916
4363
  // Render-prop: caller controls how each polygon renders. We still
3917
4364
  // 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)
4365
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(RenderPropPolygon, { polygon: p, index: i, children: renderPolygon }, i)
3919
4366
  )) : textureAtlas.entries.map((entry, index) => {
3920
4367
  if (entry) {
3921
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4368
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
3922
4369
  TextureAtlasPoly,
3923
4370
  {
3924
4371
  entry,
@@ -3929,10 +4376,10 @@ var PolyMesh = (0, import_react15.forwardRef)(function PolyMesh2({
3929
4376
  entry.index
3930
4377
  );
3931
4378
  }
3932
- const plan = atlasPlans[index];
4379
+ const plan = textureAtlas.plans[index];
3933
4380
  if (!plan || plan.texture) return null;
3934
- if ((0, import_polycss_core11.isProjectiveQuadPlan)(plan)) {
3935
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4381
+ if ((0, import_polycss_core12.isProjectiveQuadPlan)(plan)) {
4382
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
3936
4383
  TextureProjectiveSolidPoly,
3937
4384
  {
3938
4385
  entry: plan,
@@ -3942,26 +4389,44 @@ var PolyMesh = (0, import_react15.forwardRef)(function PolyMesh2({
3942
4389
  plan.index
3943
4390
  );
3944
4391
  }
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)(
4392
+ if ((0, import_polycss_core12.isSolidTrianglePlan)(plan)) {
4393
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4394
+ TextureTrianglePoly,
4395
+ {
4396
+ entry: plan,
4397
+ textureLighting: effectiveTextureLighting,
4398
+ solidPaintDefaults
4399
+ },
4400
+ plan.index
4401
+ );
4402
+ }
4403
+ const cornerGeo = !disabledStrategies?.has("i") ? (0, import_polycss_core17.cornerShapeGeometryForPlan)(plan) : null;
4404
+ if (cornerGeo) {
4405
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4406
+ TextureCornerShapeSolidPoly,
4407
+ {
4408
+ entry: plan,
4409
+ geometry: cornerGeo,
4410
+ textureLighting: effectiveTextureLighting,
4411
+ solidPaintDefaults
4412
+ },
4413
+ plan.index
4414
+ );
4415
+ }
4416
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
3954
4417
  TextureBorderShapePoly,
3955
4418
  {
3956
4419
  entry: plan,
3957
- solidPaintDefaults
4420
+ textureLighting: effectiveTextureLighting,
4421
+ solidPaintDefaults,
4422
+ disabledStrategies
3958
4423
  },
3959
4424
  plan.index
3960
4425
  );
3961
4426
  });
3962
4427
  if (src) {
3963
4428
  if (fetched.loading && fetched.polygons.length === 0) {
3964
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4429
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
3965
4430
  "div",
3966
4431
  {
3967
4432
  ref: wrapperRef,
@@ -3974,7 +4439,7 @@ var PolyMesh = (0, import_react15.forwardRef)(function PolyMesh2({
3974
4439
  );
3975
4440
  }
3976
4441
  if (fetched.error && fetched.polygons.length === 0) {
3977
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4442
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
3978
4443
  "div",
3979
4444
  {
3980
4445
  ref: wrapperRef,
@@ -3987,7 +4452,7 @@ var PolyMesh = (0, import_react15.forwardRef)(function PolyMesh2({
3987
4452
  );
3988
4453
  }
3989
4454
  }
3990
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
4455
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
3991
4456
  "div",
3992
4457
  {
3993
4458
  ref: wrapperRef,
@@ -3997,6 +4462,7 @@ var PolyMesh = (0, import_react15.forwardRef)(function PolyMesh2({
3997
4462
  ...wrapperHandlers,
3998
4463
  children: [
3999
4464
  shadowSvgNode,
4465
+ portaledReceiverShadowSvgs,
4000
4466
  renderedPolygons,
4001
4467
  staticChildren
4002
4468
  ]
@@ -4008,12 +4474,89 @@ function RenderPropPolygon({
4008
4474
  index,
4009
4475
  children
4010
4476
  }) {
4011
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_jsx_runtime8.Fragment, { children: children(polygon, index) });
4477
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_jsx_runtime9.Fragment, { children: children(polygon, index) });
4478
+ }
4479
+
4480
+ // src/scene/PolyIframe.tsx
4481
+ var import_polycss_core18 = require("@layoutit/polycss-core");
4482
+ var import_jsx_runtime10 = require("react/jsx-runtime");
4483
+ function buildIframeTransform(position, rotation, scale, cssWidth, cssHeight) {
4484
+ const sx = typeof scale === "number" ? scale : scale?.[0] ?? 1;
4485
+ const sy = typeof scale === "number" ? scale : scale?.[1] ?? 1;
4486
+ const sz = typeof scale === "number" ? scale : scale?.[2] ?? 1;
4487
+ const hasScale = sx !== 1 || sy !== 1 || sz !== 1;
4488
+ const hasRotation = !!rotation && (!!rotation[0] || !!rotation[1] || !!rotation[2]);
4489
+ const cssX = (position?.[1] ?? 0) * import_polycss_core18.BASE_TILE;
4490
+ const cssY = (position?.[0] ?? 0) * import_polycss_core18.BASE_TILE;
4491
+ const cssZ = (position?.[2] ?? 0) * import_polycss_core18.BASE_TILE;
4492
+ const parts = [];
4493
+ parts.push(`translate3d(${cssX}px, ${cssY}px, ${cssZ}px)`);
4494
+ if (hasRotation) {
4495
+ if (rotation[0]) parts.push(`rotateY(${-rotation[0]}deg)`);
4496
+ if (rotation[1]) parts.push(`rotateX(${-rotation[1]}deg)`);
4497
+ if (rotation[2]) parts.push(`rotateZ(${-rotation[2]}deg)`);
4498
+ }
4499
+ if (hasScale) parts.push(`scale3d(${sx}, ${sy}, ${sz})`);
4500
+ parts.push(`translate(${-cssWidth / 2}px, ${-cssHeight / 2}px)`);
4501
+ return parts.join(" ");
4502
+ }
4503
+ function PolyIframe({
4504
+ src,
4505
+ width,
4506
+ height,
4507
+ position,
4508
+ rotation,
4509
+ scale,
4510
+ allow,
4511
+ sandbox,
4512
+ loading,
4513
+ referrerPolicy,
4514
+ title,
4515
+ className,
4516
+ style
4517
+ }) {
4518
+ const cssW = width * import_polycss_core18.BASE_TILE;
4519
+ const cssH = height * import_polycss_core18.BASE_TILE;
4520
+ const transform = buildIframeTransform(position, rotation, scale, cssW, cssH);
4521
+ const wrapperStyle = {
4522
+ position: "absolute",
4523
+ left: 0,
4524
+ top: 0,
4525
+ transformOrigin: "0 0",
4526
+ transformStyle: "preserve-3d",
4527
+ transform,
4528
+ ...style
4529
+ };
4530
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
4531
+ "div",
4532
+ {
4533
+ className: `polycss-iframe${className ? ` ${className}` : ""}`,
4534
+ style: wrapperStyle,
4535
+ children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
4536
+ "iframe",
4537
+ {
4538
+ src,
4539
+ allow,
4540
+ sandbox,
4541
+ loading,
4542
+ referrerPolicy,
4543
+ title,
4544
+ style: {
4545
+ width: cssW,
4546
+ height: cssH,
4547
+ border: 0,
4548
+ display: "block",
4549
+ background: "#000"
4550
+ }
4551
+ }
4552
+ )
4553
+ }
4554
+ );
4012
4555
  }
4013
4556
 
4014
4557
  // src/scene/PolyGround.tsx
4015
- var import_react16 = require("react");
4016
- var import_jsx_runtime9 = require("react/jsx-runtime");
4558
+ var import_react17 = require("react");
4559
+ var import_jsx_runtime11 = require("react/jsx-runtime");
4017
4560
  function PolyGround({
4018
4561
  size = 6,
4019
4562
  z = 0,
@@ -4021,7 +4564,7 @@ function PolyGround({
4021
4564
  color = "#7d848e",
4022
4565
  className
4023
4566
  }) {
4024
- const polygons = (0, import_react16.useMemo)(() => {
4567
+ const polygons = (0, import_react17.useMemo)(() => {
4025
4568
  const half = size / 2;
4026
4569
  const [cx, cy] = center;
4027
4570
  const vertices = [
@@ -4032,7 +4575,7 @@ function PolyGround({
4032
4575
  ];
4033
4576
  return [{ vertices, color }];
4034
4577
  }, [size, z, center, color]);
4035
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4578
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
4036
4579
  PolyMesh,
4037
4580
  {
4038
4581
  polygons,
@@ -4043,9 +4586,9 @@ function PolyGround({
4043
4586
  }
4044
4587
 
4045
4588
  // src/scene/usePolyMaterial.ts
4046
- var import_react17 = require("react");
4589
+ var import_react18 = require("react");
4047
4590
  function usePolyMaterial(options) {
4048
- return (0, import_react17.useMemo)(
4591
+ return (0, import_react18.useMemo)(
4049
4592
  () => ({ texture: options.texture, key: options.key }),
4050
4593
  // eslint-disable-next-line react-hooks/exhaustive-deps
4051
4594
  [options.texture, options.key]
@@ -4053,8 +4596,8 @@ function usePolyMaterial(options) {
4053
4596
  }
4054
4597
 
4055
4598
  // src/shapes/Poly.tsx
4056
- var import_react18 = require("react");
4057
- var import_jsx_runtime10 = require("react/jsx-runtime");
4599
+ var import_react19 = require("react");
4600
+ var import_jsx_runtime12 = require("react/jsx-runtime");
4058
4601
  var DIRECT_TEXTURE_CSS_DECIMALS = 4;
4059
4602
  function formatCssLength(value, decimals = DIRECT_TEXTURE_CSS_DECIMALS) {
4060
4603
  const next = value.toFixed(decimals).replace(/\.?0+$/, "");
@@ -4106,7 +4649,7 @@ function MaterialDirectPoly({
4106
4649
  Object.entries(plan.polygon.data).map(([k, v]) => [`data-${k}`, String(v)])
4107
4650
  ) : {};
4108
4651
  const elementClassName = className?.trim() || void 0;
4109
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
4652
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
4110
4653
  "i",
4111
4654
  {
4112
4655
  className: elementClassName,
@@ -4159,7 +4702,7 @@ function PolyInner({
4159
4702
  const textureQuality = textureQualityProp ?? context?.textureQuality;
4160
4703
  const polygonColor = baseColorProp ?? color;
4161
4704
  const effectiveTexture = material?.texture ?? texture;
4162
- const atlasPlan = (0, import_react18.useMemo)(
4705
+ const atlasPlan = (0, import_react19.useMemo)(
4163
4706
  () => computeTextureAtlasPlan(
4164
4707
  { vertices, color: polygonColor, texture: effectiveTexture, uvs, data },
4165
4708
  0,
@@ -4180,11 +4723,11 @@ function PolyInner({
4180
4723
  context?.directionalLight
4181
4724
  ]
4182
4725
  );
4183
- const materialUvRect = (0, import_react18.useMemo)(
4726
+ const materialUvRect = (0, import_react19.useMemo)(
4184
4727
  () => material && uvs ? isAxisAlignedRectUVs(uvs) : null,
4185
4728
  [material, uvs]
4186
4729
  );
4187
- const atlasPlans = (0, import_react18.useMemo)(
4730
+ const atlasPlans = (0, import_react19.useMemo)(
4188
4731
  () => materialUvRect ? [] : [atlasPlan],
4189
4732
  [materialUvRect, atlasPlan]
4190
4733
  );
@@ -4237,7 +4780,7 @@ function PolyInner({
4237
4780
  const wrapperTransform = transformParts.length > 0 ? transformParts.join(" ") : void 0;
4238
4781
  let front = null;
4239
4782
  if (materialUvRect && material && atlasPlan) {
4240
- front = /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
4783
+ front = /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
4241
4784
  MaterialDirectPoly,
4242
4785
  {
4243
4786
  plan: atlasPlan,
@@ -4253,7 +4796,7 @@ function PolyInner({
4253
4796
  } else {
4254
4797
  const atlasEntry = textureAtlas.entries[0];
4255
4798
  if (atlasEntry) {
4256
- front = /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
4799
+ front = /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
4257
4800
  TextureAtlasPoly,
4258
4801
  {
4259
4802
  entry: atlasEntry,
@@ -4267,7 +4810,7 @@ function PolyInner({
4267
4810
  }
4268
4811
  );
4269
4812
  } else if (atlasPlan && !atlasPlan.texture) {
4270
- front = (0, import_polycss_core11.isSolidTrianglePlan)(atlasPlan) ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
4813
+ front = (0, import_polycss_core12.isSolidTrianglePlan)(atlasPlan) ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
4271
4814
  TextureTrianglePoly,
4272
4815
  {
4273
4816
  entry: atlasPlan,
@@ -4278,7 +4821,7 @@ function PolyInner({
4278
4821
  domEventHandlers,
4279
4822
  pointerEvents: pointerEventsProp ?? "auto"
4280
4823
  }
4281
- ) : (0, import_polycss_core11.isProjectiveQuadPlan)(atlasPlan) ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
4824
+ ) : (0, import_polycss_core12.isProjectiveQuadPlan)(atlasPlan) ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
4282
4825
  TextureProjectiveSolidPoly,
4283
4826
  {
4284
4827
  entry: atlasPlan,
@@ -4289,10 +4832,11 @@ function PolyInner({
4289
4832
  domEventHandlers,
4290
4833
  pointerEvents: pointerEventsProp ?? "auto"
4291
4834
  }
4292
- ) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
4835
+ ) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
4293
4836
  TextureBorderShapePoly,
4294
4837
  {
4295
4838
  entry: atlasPlan,
4839
+ textureLighting,
4296
4840
  className,
4297
4841
  style: styleProp,
4298
4842
  domAttrs,
@@ -4304,7 +4848,7 @@ function PolyInner({
4304
4848
  }
4305
4849
  if (!front) return null;
4306
4850
  if (!wrapperTransform) return front;
4307
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
4851
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
4308
4852
  "div",
4309
4853
  {
4310
4854
  className: "polycss-poly-wrapper",
@@ -4317,12 +4861,12 @@ function PolyInner({
4317
4861
  }
4318
4862
  );
4319
4863
  }
4320
- var Poly = (0, import_react18.memo)(PolyInner);
4864
+ var Poly = (0, import_react19.memo)(PolyInner);
4321
4865
 
4322
4866
  // 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");
4867
+ var import_react20 = require("react");
4868
+ var import_polycss_core19 = require("@layoutit/polycss-core");
4869
+ var import_jsx_runtime13 = require("react/jsx-runtime");
4326
4870
  function PolyBox({
4327
4871
  size,
4328
4872
  center,
@@ -4336,12 +4880,12 @@ function PolyBox({
4336
4880
  faces,
4337
4881
  ...meshProps
4338
4882
  }) {
4339
- const polygons = (0, import_react19.useMemo)(
4340
- () => (0, import_polycss_core16.boxPolygons)({ size, center, min, max, color, texture, material, uvs, data, faces }),
4883
+ const polygons = (0, import_react20.useMemo)(
4884
+ () => (0, import_polycss_core19.boxPolygons)({ size, center, min, max, color, texture, material, uvs, data, faces }),
4341
4885
  // eslint-disable-next-line react-hooks/exhaustive-deps
4342
4886
  [size, center, min, max, color, texture, material, uvs, data, faces]
4343
4887
  );
4344
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(PolyMesh, { polygons, ...meshProps });
4888
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PolyMesh, { polygons, ...meshProps });
4345
4889
  }
4346
4890
  function PolyPlane({
4347
4891
  axis,
@@ -4351,12 +4895,12 @@ function PolyPlane({
4351
4895
  color,
4352
4896
  ...meshProps
4353
4897
  }) {
4354
- const polygons = (0, import_react19.useMemo)(
4355
- () => (0, import_polycss_core16.planePolygons)({ axis, size, offset, along, color }),
4898
+ const polygons = (0, import_react20.useMemo)(
4899
+ () => (0, import_polycss_core19.planePolygons)({ axis, size, offset, along, color }),
4356
4900
  // eslint-disable-next-line react-hooks/exhaustive-deps
4357
4901
  [axis, size, offset, along, color]
4358
4902
  );
4359
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(PolyMesh, { polygons, ...meshProps });
4903
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PolyMesh, { polygons, ...meshProps });
4360
4904
  }
4361
4905
  function PolyRing({
4362
4906
  axis,
@@ -4366,12 +4910,12 @@ function PolyRing({
4366
4910
  color,
4367
4911
  ...meshProps
4368
4912
  }) {
4369
- const polygons = (0, import_react19.useMemo)(
4370
- () => (0, import_polycss_core16.ringPolygons)({ axis, radius, halfThickness, segments, color }),
4913
+ const polygons = (0, import_react20.useMemo)(
4914
+ () => (0, import_polycss_core19.ringPolygons)({ axis, radius, halfThickness, segments, color }),
4371
4915
  // eslint-disable-next-line react-hooks/exhaustive-deps
4372
4916
  [axis, radius, halfThickness, segments, color]
4373
4917
  );
4374
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(PolyMesh, { polygons, ...meshProps });
4918
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PolyMesh, { polygons, ...meshProps });
4375
4919
  }
4376
4920
  function PolyOctahedron({
4377
4921
  center,
@@ -4379,48 +4923,48 @@ function PolyOctahedron({
4379
4923
  color,
4380
4924
  ...meshProps
4381
4925
  }) {
4382
- const polygons = (0, import_react19.useMemo)(
4383
- () => (0, import_polycss_core16.octahedronPolygons)({ center, size, color }),
4926
+ const polygons = (0, import_react20.useMemo)(
4927
+ () => (0, import_polycss_core19.octahedronPolygons)({ center, size, color }),
4384
4928
  // eslint-disable-next-line react-hooks/exhaustive-deps
4385
4929
  [center, size, color]
4386
4930
  );
4387
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(PolyMesh, { polygons, ...meshProps });
4931
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PolyMesh, { polygons, ...meshProps });
4388
4932
  }
4389
4933
  function PolyTetrahedron({
4390
4934
  size,
4391
4935
  color,
4392
4936
  ...meshProps
4393
4937
  }) {
4394
- const polygons = (0, import_react19.useMemo)(
4395
- () => (0, import_polycss_core16.tetrahedronPolygons)({ size, color }),
4938
+ const polygons = (0, import_react20.useMemo)(
4939
+ () => (0, import_polycss_core19.tetrahedronPolygons)({ size, color }),
4396
4940
  // eslint-disable-next-line react-hooks/exhaustive-deps
4397
4941
  [size, color]
4398
4942
  );
4399
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(PolyMesh, { polygons, ...meshProps });
4943
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PolyMesh, { polygons, ...meshProps });
4400
4944
  }
4401
4945
  function PolyIcosahedron({
4402
4946
  size,
4403
4947
  color,
4404
4948
  ...meshProps
4405
4949
  }) {
4406
- const polygons = (0, import_react19.useMemo)(
4407
- () => (0, import_polycss_core16.icosahedronPolygons)({ size, color }),
4950
+ const polygons = (0, import_react20.useMemo)(
4951
+ () => (0, import_polycss_core19.icosahedronPolygons)({ size, color }),
4408
4952
  // eslint-disable-next-line react-hooks/exhaustive-deps
4409
4953
  [size, color]
4410
4954
  );
4411
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(PolyMesh, { polygons, ...meshProps });
4955
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PolyMesh, { polygons, ...meshProps });
4412
4956
  }
4413
4957
  function PolyDodecahedron({
4414
4958
  size,
4415
4959
  color,
4416
4960
  ...meshProps
4417
4961
  }) {
4418
- const polygons = (0, import_react19.useMemo)(
4419
- () => (0, import_polycss_core16.dodecahedronPolygons)({ size, color }),
4962
+ const polygons = (0, import_react20.useMemo)(
4963
+ () => (0, import_polycss_core19.dodecahedronPolygons)({ size, color }),
4420
4964
  // eslint-disable-next-line react-hooks/exhaustive-deps
4421
4965
  [size, color]
4422
4966
  );
4423
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(PolyMesh, { polygons, ...meshProps });
4967
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PolyMesh, { polygons, ...meshProps });
4424
4968
  }
4425
4969
  function PolySphere({
4426
4970
  radius,
@@ -4428,12 +4972,12 @@ function PolySphere({
4428
4972
  color,
4429
4973
  ...meshProps
4430
4974
  }) {
4431
- const polygons = (0, import_react19.useMemo)(
4432
- () => (0, import_polycss_core16.spherePolygons)({ radius, subdivisions, color }),
4975
+ const polygons = (0, import_react20.useMemo)(
4976
+ () => (0, import_polycss_core19.spherePolygons)({ radius, subdivisions, color }),
4433
4977
  // eslint-disable-next-line react-hooks/exhaustive-deps
4434
4978
  [radius, subdivisions, color]
4435
4979
  );
4436
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(PolyMesh, { polygons, ...meshProps });
4980
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PolyMesh, { polygons, ...meshProps });
4437
4981
  }
4438
4982
  function PolyCylinder({
4439
4983
  radius,
@@ -4443,12 +4987,12 @@ function PolyCylinder({
4443
4987
  color,
4444
4988
  ...meshProps
4445
4989
  }) {
4446
- const polygons = (0, import_react19.useMemo)(
4447
- () => (0, import_polycss_core16.cylinderPolygons)({ radius, radiusTop, height, radialSegments, color }),
4990
+ const polygons = (0, import_react20.useMemo)(
4991
+ () => (0, import_polycss_core19.cylinderPolygons)({ radius, radiusTop, height, radialSegments, color }),
4448
4992
  // eslint-disable-next-line react-hooks/exhaustive-deps
4449
4993
  [radius, radiusTop, height, radialSegments, color]
4450
4994
  );
4451
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(PolyMesh, { polygons, ...meshProps });
4995
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PolyMesh, { polygons, ...meshProps });
4452
4996
  }
4453
4997
  function PolyCone({
4454
4998
  radius,
@@ -4457,12 +5001,12 @@ function PolyCone({
4457
5001
  color,
4458
5002
  ...meshProps
4459
5003
  }) {
4460
- const polygons = (0, import_react19.useMemo)(
4461
- () => (0, import_polycss_core16.conePolygons)({ radius, height, radialSegments, color }),
5004
+ const polygons = (0, import_react20.useMemo)(
5005
+ () => (0, import_polycss_core19.conePolygons)({ radius, height, radialSegments, color }),
4462
5006
  // eslint-disable-next-line react-hooks/exhaustive-deps
4463
5007
  [radius, height, radialSegments, color]
4464
5008
  );
4465
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(PolyMesh, { polygons, ...meshProps });
5009
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PolyMesh, { polygons, ...meshProps });
4466
5010
  }
4467
5011
  function PolyTorus({
4468
5012
  radius,
@@ -4472,17 +5016,17 @@ function PolyTorus({
4472
5016
  color,
4473
5017
  ...meshProps
4474
5018
  }) {
4475
- const polygons = (0, import_react19.useMemo)(
4476
- () => (0, import_polycss_core16.torusPolygons)({ radius, tube, radialSegments, tubularSegments, color }),
5019
+ const polygons = (0, import_react20.useMemo)(
5020
+ () => (0, import_polycss_core19.torusPolygons)({ radius, tube, radialSegments, tubularSegments, color }),
4477
5021
  // eslint-disable-next-line react-hooks/exhaustive-deps
4478
5022
  [radius, tube, radialSegments, tubularSegments, color]
4479
5023
  );
4480
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(PolyMesh, { polygons, ...meshProps });
5024
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PolyMesh, { polygons, ...meshProps });
4481
5025
  }
4482
5026
 
4483
5027
  // src/controls/PolyFirstPersonControls.tsx
4484
- var import_react20 = require("react");
4485
- var import_polycss_core17 = require("@layoutit/polycss-core");
5028
+ var import_react21 = require("react");
5029
+ var import_polycss_core20 = require("@layoutit/polycss-core");
4486
5030
  var DEFAULTS = {
4487
5031
  enabled: true,
4488
5032
  lookEnabled: true,
@@ -4540,28 +5084,28 @@ function emitEvent(registry, type) {
4540
5084
  }
4541
5085
  }
4542
5086
  }
4543
- var PolyFirstPersonControls = (0, import_react20.forwardRef)(function PolyFirstPersonControls2(props, ref) {
5087
+ var PolyFirstPersonControls = (0, import_react21.forwardRef)(function PolyFirstPersonControls2(props, ref) {
4544
5088
  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)(() => {
5089
+ const onChangeRef = (0, import_react21.useRef)(props.onChange);
5090
+ const onInteractionStartRef = (0, import_react21.useRef)(props.onInteractionStart);
5091
+ const onInteractionEndRef = (0, import_react21.useRef)(props.onInteractionEnd);
5092
+ (0, import_react21.useEffect)(() => {
4549
5093
  onChangeRef.current = props.onChange;
4550
5094
  onInteractionStartRef.current = props.onInteractionStart;
4551
5095
  onInteractionEndRef.current = props.onInteractionEnd;
4552
5096
  });
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, () => ({
5097
+ const optsRef = (0, import_react21.useRef)(resolveOptions(DEFAULTS, props));
5098
+ const cameraOriginRef = (0, import_react21.useRef)([0, 0, 0]);
5099
+ const rafIdRef = (0, import_react21.useRef)(null);
5100
+ const lastTimeRef = (0, import_react21.useRef)(0);
5101
+ const stoppedRef = (0, import_react21.useRef)(false);
5102
+ const pointerLockedRef = (0, import_react21.useRef)(false);
5103
+ const interactingRef = (0, import_react21.useRef)(false);
5104
+ const keysHeldRef = (0, import_react21.useRef)(/* @__PURE__ */ new Set());
5105
+ const verticalVelRef = (0, import_react21.useRef)(0);
5106
+ const jumpOffsetRef = (0, import_react21.useRef)(0);
5107
+ const registryRef = (0, import_react21.useRef)(makeRegistry());
5108
+ (0, import_react21.useImperativeHandle)(ref, () => ({
4565
5109
  update(partial) {
4566
5110
  const prev = optsRef.current;
4567
5111
  optsRef.current = resolveOptions(prev, partial);
@@ -4658,7 +5202,7 @@ var PolyFirstPersonControls = (0, import_react20.forwardRef)(function PolyFirstP
4658
5202
  const host = cameraElRef.current;
4659
5203
  const perspStr = host ? getComputedStyle(host).perspective : "";
4660
5204
  const n = parseFloat(perspStr);
4661
- return (Number.isFinite(n) && n > 0 ? n : 8e3) / import_polycss_core17.BASE_TILE;
5205
+ return (Number.isFinite(n) && n > 0 ? n : 32e3) / import_polycss_core20.BASE_TILE;
4662
5206
  }
4663
5207
  function deriveTarget() {
4664
5208
  const s = cameraRef.current.state;
@@ -4747,7 +5291,7 @@ var PolyFirstPersonControls = (0, import_react20.forwardRef)(function PolyFirstP
4747
5291
  cancelAnimationFrame(rafIdRef.current);
4748
5292
  rafIdRef.current = null;
4749
5293
  }
4750
- (0, import_react20.useEffect)(() => {
5294
+ (0, import_react21.useEffect)(() => {
4751
5295
  const host = cameraElRef.current;
4752
5296
  if (!host) return;
4753
5297
  const doc = host.ownerDocument ?? document;
@@ -4885,7 +5429,7 @@ var PolyFirstPersonControls = (0, import_react20.forwardRef)(function PolyFirstP
4885
5429
  }
4886
5430
  };
4887
5431
  }, [cameraElRef, cameraRef, applyTransformDirect, store]);
4888
- (0, import_react20.useEffect)(() => {
5432
+ (0, import_react21.useEffect)(() => {
4889
5433
  optsRef.current = resolveOptions(optsRef.current, props);
4890
5434
  const host = cameraElRef.current;
4891
5435
  if (host && !stoppedRef.current) {
@@ -4896,10 +5440,10 @@ var PolyFirstPersonControls = (0, import_react20.forwardRef)(function PolyFirstP
4896
5440
  });
4897
5441
 
4898
5442
  // src/controls/PolyMapControls.tsx
4899
- var import_react21 = require("react");
5443
+ var import_react22 = require("react");
4900
5444
 
4901
5445
  // src/controls/sharedControls.ts
4902
- var import_polycss_core18 = require("@layoutit/polycss-core");
5446
+ var import_polycss_core21 = require("@layoutit/polycss-core");
4903
5447
  var POINTER_DRAG_SPEED = 4;
4904
5448
  function invertFactor(invert) {
4905
5449
  if (invert === true) return -1;
@@ -4910,7 +5454,7 @@ function applyOrbit(dx, dy, s, handle, invert) {
4910
5454
  const f = invertFactor(invert);
4911
5455
  const dX = dx / POINTER_DRAG_SPEED * f;
4912
5456
  const dY = dy / POINTER_DRAG_SPEED * f;
4913
- const rotX = Math.max(0, Math.min(100, s.rotX - dY));
5457
+ const rotX = s.rotX - dY;
4914
5458
  const rotY = ((s.rotY - dX) % 360 + 360) % 360;
4915
5459
  handle.update({ rotX, rotY });
4916
5460
  }
@@ -4920,7 +5464,7 @@ function applyPan(dx, dy, s, handle, _invert) {
4920
5464
  const cosRotX = cosRotXRaw >= 0 ? Math.max(0.1, cosRotXRaw) : Math.min(-0.1, cosRotXRaw);
4921
5465
  const cZ = Math.cos(s.rotY * Math.PI / 180);
4922
5466
  const sZ = Math.sin(s.rotY * Math.PI / 180);
4923
- const k = z * import_polycss_core18.BASE_TILE;
5467
+ const k = z * import_polycss_core21.BASE_TILE;
4924
5468
  const targetD0 = (dx * sZ - dy * cZ / cosRotX) / k;
4925
5469
  const targetD1 = -(dx * cZ + dy * sZ / cosRotX) / k;
4926
5470
  const t = s.target;
@@ -5069,19 +5613,19 @@ function PolyMapControls({
5069
5613
  onInteractionEnd
5070
5614
  }) {
5071
5615
  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)(() => {
5616
+ const dragRef = (0, import_react22.useRef)(drag);
5617
+ const wheelRef = (0, import_react22.useRef)(wheel);
5618
+ const dollyRef = (0, import_react22.useRef)(dolly);
5619
+ const invertRef = (0, import_react22.useRef)(invert);
5620
+ const zoomMinRef = (0, import_react22.useRef)(minZoom);
5621
+ const zoomMaxRef = (0, import_react22.useRef)(maxZoom);
5622
+ const distanceMinRef = (0, import_react22.useRef)(minDistance);
5623
+ const distanceMaxRef = (0, import_react22.useRef)(maxDistance);
5624
+ const animateRef = (0, import_react22.useRef)(animate);
5625
+ const onChangeRef = (0, import_react22.useRef)(onChange);
5626
+ const onInteractionStartRef = (0, import_react22.useRef)(onInteractionStart);
5627
+ const onInteractionEndRef = (0, import_react22.useRef)(onInteractionEnd);
5628
+ (0, import_react22.useEffect)(() => {
5085
5629
  dragRef.current = drag;
5086
5630
  wheelRef.current = wheel;
5087
5631
  dollyRef.current = dolly;
@@ -5126,8 +5670,8 @@ function PolyMapControls({
5126
5670
  console.error("[polycss/react] PolyMapControls onInteractionEnd threw:", err);
5127
5671
  }
5128
5672
  };
5129
- const animationPausedShared = (0, import_react21.useRef)({ value: false }).current;
5130
- (0, import_react21.useEffect)(() => {
5673
+ const animationPausedShared = (0, import_react22.useRef)({ value: false }).current;
5674
+ (0, import_react22.useEffect)(() => {
5131
5675
  if (!drag) return;
5132
5676
  const el = cameraElRef.current;
5133
5677
  if (!el) return;
@@ -5237,12 +5781,12 @@ function PolyMapControls({
5237
5781
  el.style.userSelect = "";
5238
5782
  };
5239
5783
  }, [drag, applyTransformDirect, cameraElRef, cameraRef, store]);
5240
- (0, import_react21.useEffect)(
5784
+ (0, import_react22.useEffect)(
5241
5785
  () => makeWheelEffect({ wheel, dollyRef, wheelRef, zoomMinRef, zoomMaxRef, distanceMinRef, distanceMaxRef, cameraElRef, cameraRef, applyTransformDirect, store, fireStart, fireChange, fireEnd }),
5242
5786
  // eslint-disable-next-line react-hooks/exhaustive-deps
5243
5787
  [wheel, applyTransformDirect, cameraElRef, cameraRef, store]
5244
5788
  );
5245
- (0, import_react21.useEffect)(
5789
+ (0, import_react22.useEffect)(
5246
5790
  () => makeAnimateEffect({ animateOn: !!animate, animateRef, animationPausedShared, applyTransformDirect, cameraRef, store, fireChange }),
5247
5791
  // eslint-disable-next-line react-hooks/exhaustive-deps
5248
5792
  [!!animate, animationPausedShared, applyTransformDirect, cameraRef, store]
@@ -5251,7 +5795,7 @@ function PolyMapControls({
5251
5795
  }
5252
5796
 
5253
5797
  // src/controls/PolyOrbitControls.tsx
5254
- var import_react22 = require("react");
5798
+ var import_react23 = require("react");
5255
5799
  function PolyOrbitControls({
5256
5800
  drag = true,
5257
5801
  wheel = true,
@@ -5267,19 +5811,19 @@ function PolyOrbitControls({
5267
5811
  onInteractionEnd
5268
5812
  }) {
5269
5813
  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)(() => {
5814
+ const dragRef = (0, import_react23.useRef)(drag);
5815
+ const wheelRef = (0, import_react23.useRef)(wheel);
5816
+ const dollyRef = (0, import_react23.useRef)(dolly);
5817
+ const invertRef = (0, import_react23.useRef)(invert);
5818
+ const zoomMinRef = (0, import_react23.useRef)(minZoom);
5819
+ const zoomMaxRef = (0, import_react23.useRef)(maxZoom);
5820
+ const distanceMinRef = (0, import_react23.useRef)(minDistance);
5821
+ const distanceMaxRef = (0, import_react23.useRef)(maxDistance);
5822
+ const animateRef = (0, import_react23.useRef)(animate);
5823
+ const onChangeRef = (0, import_react23.useRef)(onChange);
5824
+ const onInteractionStartRef = (0, import_react23.useRef)(onInteractionStart);
5825
+ const onInteractionEndRef = (0, import_react23.useRef)(onInteractionEnd);
5826
+ (0, import_react23.useEffect)(() => {
5283
5827
  dragRef.current = drag;
5284
5828
  wheelRef.current = wheel;
5285
5829
  dollyRef.current = dolly;
@@ -5324,8 +5868,8 @@ function PolyOrbitControls({
5324
5868
  console.error("[polycss/react] PolyOrbitControls onInteractionEnd threw:", err);
5325
5869
  }
5326
5870
  };
5327
- const animationPausedShared = (0, import_react22.useRef)({ value: false }).current;
5328
- (0, import_react22.useEffect)(() => {
5871
+ const animationPausedShared = (0, import_react23.useRef)({ value: false }).current;
5872
+ (0, import_react23.useEffect)(() => {
5329
5873
  if (!drag) return;
5330
5874
  const el = cameraElRef.current;
5331
5875
  if (!el) return;
@@ -5435,12 +5979,12 @@ function PolyOrbitControls({
5435
5979
  el.style.userSelect = "";
5436
5980
  };
5437
5981
  }, [drag, applyTransformDirect, cameraElRef, cameraRef, store]);
5438
- (0, import_react22.useEffect)(
5982
+ (0, import_react23.useEffect)(
5439
5983
  () => makeWheelEffect({ wheel, dollyRef, wheelRef, zoomMinRef, zoomMaxRef, distanceMinRef, distanceMaxRef, cameraElRef, cameraRef, applyTransformDirect, store, fireStart, fireChange, fireEnd }),
5440
5984
  // eslint-disable-next-line react-hooks/exhaustive-deps
5441
5985
  [wheel, applyTransformDirect, cameraElRef, cameraRef, store]
5442
5986
  );
5443
- (0, import_react22.useEffect)(
5987
+ (0, import_react23.useEffect)(
5444
5988
  () => makeAnimateEffect({ animateOn: !!animate, animateRef, animationPausedShared, applyTransformDirect, cameraRef, store, fireChange }),
5445
5989
  // eslint-disable-next-line react-hooks/exhaustive-deps
5446
5990
  [!!animate, animationPausedShared, applyTransformDirect, cameraRef, store]
@@ -5449,10 +5993,10 @@ function PolyOrbitControls({
5449
5993
  }
5450
5994
 
5451
5995
  // 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);
5996
+ var import_react24 = require("react");
5997
+ var import_polycss_core22 = require("@layoutit/polycss-core");
5998
+ var import_jsx_runtime14 = require("react/jsx-runtime");
5999
+ var FALLBACK_CAMERA_STORE = createSceneStore(import_polycss_core22.DEFAULT_CAMERA_STATE);
5456
6000
  var COLOR_X = "#ff3653";
5457
6001
  var COLOR_Y = "#8adb00";
5458
6002
  var COLOR_Z = "#2c8fff";
@@ -5729,7 +6273,7 @@ function startRingDrag(opts) {
5729
6273
  const startRotation = target.getRotation() ?? [0, 0, 0];
5730
6274
  onMouseDown?.();
5731
6275
  onDraggingChanged?.(true);
5732
- const qStart = (0, import_polycss_core19.quatFromEulerXYZ)(startRotation);
6276
+ const qStart = (0, import_polycss_core22.quatFromEulerXYZ)(startRotation);
5733
6277
  const handleMove = (ev) => {
5734
6278
  const a = Math.atan2(ev.clientY - centerY, ev.clientX - centerX);
5735
6279
  let d = a - lastAngle;
@@ -5741,8 +6285,8 @@ function startRingDrag(opts) {
5741
6285
  degrees = snap(degrees, rotationSnap);
5742
6286
  const axisVec = [0, 0, 0];
5743
6287
  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));
6288
+ const qDelta = (0, import_polycss_core22.quatFromAxisAngle)(axisVec, degrees * Math.PI / 180);
6289
+ const newRotation = (0, import_polycss_core22.eulerXYZFromQuat)((0, import_polycss_core22.quatMultiply)(qDelta, qStart));
5746
6290
  onObjectChange?.({ object: target, rotation: newRotation });
5747
6291
  onChange?.();
5748
6292
  };
@@ -5781,11 +6325,11 @@ function PolyTransformControls({
5781
6325
  onMouseUp,
5782
6326
  onDraggingChanged
5783
6327
  }) {
5784
- const [, forceRender] = (0, import_react23.useState)(0);
5785
- (0, import_react23.useEffect)(() => {
6328
+ const [, forceRender] = (0, import_react24.useState)(0);
6329
+ (0, import_react24.useEffect)(() => {
5786
6330
  forceRender((n) => n + 1);
5787
6331
  }, [object]);
5788
- const cameraCtxForRot = (0, import_react23.useContext)(PolyCameraContext);
6332
+ const cameraCtxForRot = (0, import_react24.useContext)(PolyCameraContext);
5789
6333
  const rotX = useStoreSelector(
5790
6334
  cameraCtxForRot?.store ?? FALLBACK_CAMERA_STORE,
5791
6335
  (s) => s.cameraState.rotX
@@ -5794,11 +6338,11 @@ function PolyTransformControls({
5794
6338
  cameraCtxForRot?.store ?? FALLBACK_CAMERA_STORE,
5795
6339
  (s) => s.cameraState.rotY
5796
6340
  );
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);
6341
+ const [hoveredKey, setHoveredKey] = (0, import_react24.useState)(null);
6342
+ const [draggingKey, setDraggingKey] = (0, import_react24.useState)(null);
6343
+ const cameraCtx = (0, import_react24.useContext)(PolyCameraContext);
5800
6344
  const cameraElRef = cameraCtx?.cameraElRef;
5801
- const dragRef = (0, import_react23.useRef)({
6345
+ const dragRef = (0, import_react24.useRef)({
5802
6346
  target: null,
5803
6347
  mode: "translate",
5804
6348
  shaftLengthCss: 0,
@@ -5807,7 +6351,7 @@ function PolyTransformControls({
5807
6351
  translationSnap: null,
5808
6352
  rotationSnap: null
5809
6353
  });
5810
- (0, import_react23.useEffect)(() => {
6354
+ (0, import_react24.useEffect)(() => {
5811
6355
  const cameraEl = cameraElRef?.current;
5812
6356
  if (!cameraEl) return;
5813
6357
  const onPointerDown = (event) => {
@@ -5929,10 +6473,13 @@ function PolyTransformControls({
5929
6473
  const position = target.getPosition() ?? [0, 0, 0];
5930
6474
  const polygons = target.getPolygons();
5931
6475
  const bboxCenter = gizmoCenterForMesh(polygons);
6476
+ const cssPosX = position[1] * SCENE_TILE_SIZE;
6477
+ const cssPosY = position[0] * SCENE_TILE_SIZE;
6478
+ const cssPosZ = position[2] * SCENE_TILE_SIZE;
5932
6479
  const wrapperPos = [
5933
- position[0] + bboxCenter[0],
5934
- position[1] + bboxCenter[1],
5935
- position[2] + bboxCenter[2]
6480
+ cssPosX + bboxCenter[0],
6481
+ cssPosY + bboxCenter[1],
6482
+ cssPosZ + bboxCenter[2]
5936
6483
  ];
5937
6484
  const baseLength = gizmoLengthForMesh(polygons);
5938
6485
  const shaftLengthCss = baseLength * size;
@@ -5961,7 +6508,7 @@ function PolyTransformControls({
5961
6508
  // own; descendants opt in via the default `auto`.
5962
6509
  zIndex: 1e3
5963
6510
  };
5964
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
6511
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
5965
6512
  "div",
5966
6513
  {
5967
6514
  className: "polycss-transform-controls",
@@ -5977,7 +6524,7 @@ function PolyTransformControls({
5977
6524
  const dragging = draggingKey === spec.key;
5978
6525
  const alpha = dragging ? ALPHA_DRAGGING : hovered ? ALPHA_HOVER : ALPHA_IDLE;
5979
6526
  const backFacing = isAxisBackFacing(spec.cssAxis, spec.sign, rotX, rotY);
5980
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
6527
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
5981
6528
  TranslateArrow,
5982
6529
  {
5983
6530
  cssAxis: spec.cssAxis,
@@ -6009,7 +6556,7 @@ function PolyTransformControls({
6009
6556
  const hovered = hoveredKey === spec.key;
6010
6557
  const dragging = draggingKey === spec.key;
6011
6558
  const alpha = dragging ? ALPHA_DRAGGING : hovered ? ALPHA_HOVER : ALPHA_IDLE;
6012
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
6559
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
6013
6560
  TranslatePlane,
6014
6561
  {
6015
6562
  axisA: spec.axisA,
@@ -6041,7 +6588,7 @@ function PolyTransformControls({
6041
6588
  const hovered = hoveredKey === spec.key;
6042
6589
  const dragging = draggingKey === spec.key;
6043
6590
  const alpha = dragging ? ALPHA_DRAGGING : hovered ? ALPHA_HOVER : ALPHA_IDLE;
6044
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
6591
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
6045
6592
  RotateRing,
6046
6593
  {
6047
6594
  cssAxis: spec.cssAxis,
@@ -6086,7 +6633,7 @@ function TranslateArrow({
6086
6633
  onDraggingStart,
6087
6634
  onDraggingStop
6088
6635
  }) {
6089
- const cbRef = (0, import_react23.useRef)({
6636
+ const cbRef = (0, import_react24.useRef)({
6090
6637
  onChange,
6091
6638
  onObjectChange,
6092
6639
  onMouseDown,
@@ -6108,9 +6655,9 @@ function TranslateArrow({
6108
6655
  enabled,
6109
6656
  translationSnap
6110
6657
  };
6111
- const polygons = (0, import_react23.useMemo)(() => {
6658
+ const polygons = (0, import_react24.useMemo)(() => {
6112
6659
  const lengthWorld = shaftLengthCss / SCENE_TILE_SIZE;
6113
- return (0, import_polycss_core19.arrowPolygons)({
6660
+ return (0, import_polycss_core22.arrowPolygons)({
6114
6661
  axis: WORLD_AXIS_FOR_CSS[cssAxis],
6115
6662
  sign,
6116
6663
  shaftLength: lengthWorld,
@@ -6121,7 +6668,7 @@ function TranslateArrow({
6121
6668
  shaft: includeShaft
6122
6669
  });
6123
6670
  }, [cssAxis, sign, color, shaftLengthCss, includeShaft]);
6124
- const onPointerDown = (0, import_react23.useCallback)(
6671
+ const onPointerDown = (0, import_react24.useCallback)(
6125
6672
  (e) => {
6126
6673
  if (!cbRef.current.enabled) return;
6127
6674
  e.stopPropagation();
@@ -6150,7 +6697,7 @@ function TranslateArrow({
6150
6697
  },
6151
6698
  [cssAxis, sign, target, shaftLengthCss]
6152
6699
  );
6153
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
6700
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
6154
6701
  PolyMesh,
6155
6702
  {
6156
6703
  polygons,
@@ -6183,7 +6730,7 @@ function TranslatePlane({
6183
6730
  onDraggingStart,
6184
6731
  onDraggingStop
6185
6732
  }) {
6186
- const cbRef = (0, import_react23.useRef)({
6733
+ const cbRef = (0, import_react24.useRef)({
6187
6734
  onChange,
6188
6735
  onObjectChange,
6189
6736
  onMouseDown,
@@ -6205,7 +6752,7 @@ function TranslatePlane({
6205
6752
  enabled,
6206
6753
  translationSnap
6207
6754
  };
6208
- const polygons = (0, import_react23.useMemo)(() => {
6755
+ const polygons = (0, import_react24.useMemo)(() => {
6209
6756
  const lengthWorld = shaftLengthCss / SCENE_TILE_SIZE;
6210
6757
  const worldPerp = WORLD_AXIS_FOR_CSS[perpAxis];
6211
6758
  const worldA = (worldPerp + 1) % 3;
@@ -6215,14 +6762,14 @@ function TranslatePlane({
6215
6762
  const signA = isAxisBackFacing(cssAForOffset, 1, rotX, rotY) ? -1 : 1;
6216
6763
  const signB = isAxisBackFacing(cssBForOffset, 1, rotX, rotY) ? -1 : 1;
6217
6764
  const mag = lengthWorld * PLANE_OFFSET_RATIO;
6218
- return (0, import_polycss_core19.planePolygons)({
6765
+ return (0, import_polycss_core22.planePolygons)({
6219
6766
  axis: worldPerp,
6220
6767
  size: lengthWorld * PLANE_HALF_SIZE_RATIO,
6221
6768
  offset: [signA * mag, signB * mag],
6222
6769
  color
6223
6770
  });
6224
6771
  }, [perpAxis, color, shaftLengthCss, rotX, rotY]);
6225
- const onPointerDown = (0, import_react23.useCallback)(
6772
+ const onPointerDown = (0, import_react24.useCallback)(
6226
6773
  (e) => {
6227
6774
  if (!cbRef.current.enabled) return;
6228
6775
  e.stopPropagation();
@@ -6251,7 +6798,7 @@ function TranslatePlane({
6251
6798
  },
6252
6799
  [axisA, axisB, target, shaftLengthCss]
6253
6800
  );
6254
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
6801
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
6255
6802
  PolyMesh,
6256
6803
  {
6257
6804
  polygons,
@@ -6280,7 +6827,7 @@ function RotateRing({
6280
6827
  onDraggingStart,
6281
6828
  onDraggingStop
6282
6829
  }) {
6283
- const cbRef = (0, import_react23.useRef)({
6830
+ const cbRef = (0, import_react24.useRef)({
6284
6831
  onChange,
6285
6832
  onObjectChange,
6286
6833
  onMouseDown,
@@ -6302,10 +6849,10 @@ function RotateRing({
6302
6849
  enabled,
6303
6850
  rotationSnap
6304
6851
  };
6305
- const polygons = (0, import_react23.useMemo)(() => {
6852
+ const polygons = (0, import_react24.useMemo)(() => {
6306
6853
  const radiusWorld = radiusCss / SCENE_TILE_SIZE;
6307
6854
  const outerWorld = radiusWorld * RING_QUAD_OUTER_RATIO;
6308
- return (0, import_polycss_core19.ringQuadPolygons)({
6855
+ return (0, import_polycss_core22.ringQuadPolygons)({
6309
6856
  axis: WORLD_AXIS_FOR_CSS[cssAxis],
6310
6857
  outerRadius: outerWorld,
6311
6858
  color
@@ -6313,7 +6860,7 @@ function RotateRing({
6313
6860
  }, [cssAxis, color, radiusCss]);
6314
6861
  const ringInnerRatio = (1 - RING_HALF_THICKNESS_RATIO) / RING_QUAD_OUTER_RATIO;
6315
6862
  const ringOuterRatio = (1 + RING_HALF_THICKNESS_RATIO) / RING_QUAD_OUTER_RATIO;
6316
- const onPointerDown = (0, import_react23.useCallback)(
6863
+ const onPointerDown = (0, import_react24.useCallback)(
6317
6864
  (e) => {
6318
6865
  if (!cbRef.current.enabled) return;
6319
6866
  e.stopPropagation();
@@ -6340,7 +6887,7 @@ function RotateRing({
6340
6887
  },
6341
6888
  [cssAxis, target]
6342
6889
  );
6343
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
6890
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
6344
6891
  PolyMesh,
6345
6892
  {
6346
6893
  polygons,
@@ -6358,9 +6905,9 @@ function RotateRing({
6358
6905
  }
6359
6906
 
6360
6907
  // 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);
6908
+ var import_react25 = require("react");
6909
+ var import_jsx_runtime15 = require("react/jsx-runtime");
6910
+ var SelectContext = (0, import_react25.createContext)(null);
6364
6911
  function PolySelect({
6365
6912
  multiple = false,
6366
6913
  filter,
@@ -6371,19 +6918,19 @@ function PolySelect({
6371
6918
  className,
6372
6919
  style
6373
6920
  }) {
6374
- const [selected, setSelectedState] = (0, import_react24.useState)([]);
6375
- const filterRef = (0, import_react24.useRef)(filter);
6921
+ const [selected, setSelectedState] = (0, import_react25.useState)([]);
6922
+ const filterRef = (0, import_react25.useRef)(filter);
6376
6923
  filterRef.current = filter;
6377
- const onChangeRef = (0, import_react24.useRef)(onChange);
6924
+ const onChangeRef = (0, import_react25.useRef)(onChange);
6378
6925
  onChangeRef.current = onChange;
6379
- const multipleRef = (0, import_react24.useRef)(multiple);
6926
+ const multipleRef = (0, import_react25.useRef)(multiple);
6380
6927
  multipleRef.current = multiple;
6381
- const apply = (0, import_react24.useCallback)((next) => {
6928
+ const apply = (0, import_react25.useCallback)((next) => {
6382
6929
  const filtered = filterRef.current ? filterRef.current(next) : next;
6383
6930
  setSelectedState(filtered);
6384
6931
  if (onChangeRef.current) onChangeRef.current(filtered);
6385
6932
  }, []);
6386
- const api = (0, import_react24.useMemo)(() => {
6933
+ const api = (0, import_react25.useMemo)(() => {
6387
6934
  return {
6388
6935
  selected,
6389
6936
  set: (next) => apply(next),
@@ -6416,14 +6963,14 @@ function PolySelect({
6416
6963
  has: (h) => selected.includes(h)
6417
6964
  };
6418
6965
  }, [selected, apply]);
6419
- const wrapperRef = (0, import_react24.useRef)(null);
6420
- const cameraCtx = (0, import_react24.useContext)(PolyCameraContext);
6966
+ const wrapperRef = (0, import_react25.useRef)(null);
6967
+ const cameraCtx = (0, import_react25.useContext)(PolyCameraContext);
6421
6968
  const cameraElRef = cameraCtx?.cameraElRef;
6422
- const clearOnMissRef = (0, import_react24.useRef)(clearOnMiss);
6969
+ const clearOnMissRef = (0, import_react25.useRef)(clearOnMiss);
6423
6970
  clearOnMissRef.current = clearOnMiss;
6424
- const onPointerMissedRef = (0, import_react24.useRef)(onPointerMissed);
6971
+ const onPointerMissedRef = (0, import_react25.useRef)(onPointerMissed);
6425
6972
  onPointerMissedRef.current = onPointerMissed;
6426
- const findMeshUnderPoint2 = (0, import_react24.useCallback)(
6973
+ const findMeshUnderPoint2 = (0, import_react25.useCallback)(
6427
6974
  (clientX, clientY) => findMeshUnderPoint(
6428
6975
  clientX,
6429
6976
  clientY,
@@ -6431,7 +6978,7 @@ function PolySelect({
6431
6978
  ),
6432
6979
  []
6433
6980
  );
6434
- (0, import_react24.useEffect)(() => {
6981
+ (0, import_react25.useEffect)(() => {
6435
6982
  const cameraEl = cameraElRef?.current;
6436
6983
  if (!cameraEl) return;
6437
6984
  const onClick = (event) => {
@@ -6478,7 +7025,7 @@ function PolySelect({
6478
7025
  }
6479
7026
  };
6480
7027
  const wrapperStyle = { display: "contents", ...style };
6481
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(SelectContext.Provider, { value: api, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
7028
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(SelectContext.Provider, { value: api, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
6482
7029
  "div",
6483
7030
  {
6484
7031
  ref: wrapperRef,
@@ -6491,10 +7038,10 @@ function PolySelect({
6491
7038
  ) });
6492
7039
  }
6493
7040
  function usePolySelect() {
6494
- return (0, import_react24.useContext)(SelectContext)?.selected ?? [];
7041
+ return (0, import_react25.useContext)(SelectContext)?.selected ?? [];
6495
7042
  }
6496
7043
  function usePolySelectionApi() {
6497
- const ctx = (0, import_react24.useContext)(SelectContext);
7044
+ const ctx = (0, import_react25.useContext)(SelectContext);
6498
7045
  if (!ctx) {
6499
7046
  throw new Error("polycss: usePolySelectionApi must be used inside <PolySelect>.");
6500
7047
  }
@@ -6502,9 +7049,9 @@ function usePolySelectionApi() {
6502
7049
  }
6503
7050
 
6504
7051
  // 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");
7052
+ var import_react26 = require("react");
7053
+ var import_polycss_core23 = require("@layoutit/polycss-core");
7054
+ var import_jsx_runtime16 = require("react/jsx-runtime");
6508
7055
  function PolyAxesHelper({
6509
7056
  size,
6510
7057
  thickness,
@@ -6513,18 +7060,17 @@ function PolyAxesHelper({
6513
7060
  yColor,
6514
7061
  zColor
6515
7062
  }) {
6516
- const polygons = (0, import_react25.useMemo)(
6517
- () => (0, import_polycss_core20.axesHelperPolygons)({ size, thickness, negative, xColor, yColor, zColor }),
7063
+ const polygons = (0, import_react26.useMemo)(
7064
+ () => (0, import_polycss_core23.axesHelperPolygons)({ size, thickness, negative, xColor, yColor, zColor }),
6518
7065
  [size, thickness, negative, xColor, yColor, zColor]
6519
7066
  );
6520
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(PolyMesh, { polygons });
7067
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(PolyMesh, { polygons, merge: false });
6521
7068
  }
6522
7069
 
6523
7070
  // 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;
7071
+ var import_react27 = require("react");
7072
+ var import_polycss_core24 = require("@layoutit/polycss-core");
7073
+ var import_jsx_runtime17 = require("react/jsx-runtime");
6528
7074
  function PolyDirectionalLightHelper({
6529
7075
  light,
6530
7076
  target,
@@ -6533,26 +7079,27 @@ function PolyDirectionalLightHelper({
6533
7079
  color
6534
7080
  }) {
6535
7081
  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 }),
7082
+ const polygons = (0, import_react27.useMemo)(
7083
+ () => (0, import_polycss_core24.octahedronPolygons)({ center: [0, 0, 0], size, color: swatch }),
6538
7084
  [size, swatch]
6539
7085
  );
6540
- const meshPosition = (0, import_react26.useMemo)(() => {
7086
+ const meshPosition = (0, import_react27.useMemo)(() => {
6541
7087
  const [dx, dy, dz] = light.direction;
6542
7088
  const len = Math.hypot(dx, dy, dz) || 1;
6543
7089
  const tx = target?.[0] ?? 0;
6544
7090
  const ty = target?.[1] ?? 0;
6545
7091
  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];
7092
+ return [
7093
+ tx + dx / len * distance,
7094
+ ty + dy / len * distance,
7095
+ tz + dz / len * distance
7096
+ ];
6550
7097
  }, [light.direction, target, distance]);
6551
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(PolyMesh, { polygons, position: meshPosition });
7098
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(PolyMesh, { polygons, position: meshPosition, merge: false });
6552
7099
  }
6553
7100
 
6554
7101
  // src/styles/colorResolver.ts
6555
- var import_polycss_core22 = require("@layoutit/polycss-core");
7102
+ var import_polycss_core25 = require("@layoutit/polycss-core");
6556
7103
 
6557
7104
  // src/renderStats.ts
6558
7105
  var ZERO_SURFACE_LEAF_COUNTS = {
@@ -6606,7 +7153,7 @@ function collectPolyRenderStats(root, optionsOrPolygonCount) {
6606
7153
  surfaceLeafCounts.clippedSolid += queryCount(scope, "i");
6607
7154
  surfaceLeafCounts.atlas += queryCount(scope, "s");
6608
7155
  surfaceLeafCounts.stableTriangle += queryCount(scope, "u");
6609
- shadowLeafCount += queryCount(scope, "q");
7156
+ shadowLeafCount += queryCount(scope, "q, .polycss-shadow-svg");
6610
7157
  bucketCount += queryCount(scope, ".polycss-bucket");
6611
7158
  }
6612
7159
  const mountedPolygonLeafCount = surfaceLeafCounts.quad + surfaceLeafCounts.clippedSolid + surfaceLeafCounts.atlas + surfaceLeafCounts.stableTriangle;
@@ -6620,17 +7167,17 @@ function collectPolyRenderStats(root, optionsOrPolygonCount) {
6620
7167
  }
6621
7168
 
6622
7169
  // src/animation/usePolyAnimation.ts
6623
- var import_react27 = require("react");
6624
- var import_polycss_core23 = require("@layoutit/polycss-core");
7170
+ var import_react28 = require("react");
7171
+ var import_polycss_core26 = require("@layoutit/polycss-core");
6625
7172
  function resolveRoot(rootArg) {
6626
7173
  if (!rootArg) return null;
6627
7174
  if ("current" in rootArg) return rootArg.current;
6628
7175
  return rootArg;
6629
7176
  }
6630
7177
  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)(() => {
7178
+ const internalRef = (0, import_react28.useRef)(null);
7179
+ const mixerRef = (0, import_react28.useRef)(null);
7180
+ (0, import_react28.useEffect)(() => {
6634
7181
  if (!clips || clips.length === 0 || !controller) {
6635
7182
  mixerRef.current = null;
6636
7183
  return;
@@ -6640,14 +7187,14 @@ function usePolyAnimation(clips, controller, root) {
6640
7187
  mixerRef.current = null;
6641
7188
  return;
6642
7189
  }
6643
- mixerRef.current = (0, import_polycss_core23.createPolyAnimationMixer)(resolvedRoot, controller);
7190
+ mixerRef.current = (0, import_polycss_core26.createPolyAnimationMixer)(resolvedRoot, controller);
6644
7191
  return () => {
6645
7192
  mixerRef.current?.stopAllAction();
6646
7193
  mixerRef.current?.uncacheRoot();
6647
7194
  mixerRef.current = null;
6648
7195
  };
6649
7196
  }, [clips, controller]);
6650
- (0, import_react27.useEffect)(() => {
7197
+ (0, import_react28.useEffect)(() => {
6651
7198
  if (!clips || clips.length === 0 || !controller) return;
6652
7199
  let rafId;
6653
7200
  let lastTime = null;
@@ -6670,7 +7217,7 @@ function usePolyAnimation(clips, controller, root) {
6670
7217
  }, [clips, controller]);
6671
7218
  const resolvedClips = clips ?? [];
6672
7219
  const resolvedNames = resolvedClips.map((c) => c.name);
6673
- const actions = (0, import_react27.useMemo)(() => {
7220
+ const actions = (0, import_react28.useMemo)(() => {
6674
7221
  const target = {};
6675
7222
  for (const clip of resolvedClips) {
6676
7223
  Object.defineProperty(target, clip.name, {
@@ -6698,7 +7245,7 @@ function usePolyAnimation(clips, controller, root) {
6698
7245
  }
6699
7246
 
6700
7247
  // src/index.ts
6701
- var import_polycss_core24 = require("@layoutit/polycss-core");
7248
+ var import_polycss_core27 = require("@layoutit/polycss-core");
6702
7249
  // Annotate the CommonJS export names for ESM import in node:
6703
7250
  0 && (module.exports = {
6704
7251
  BASE_TILE,
@@ -6722,6 +7269,7 @@ var import_polycss_core24 = require("@layoutit/polycss-core");
6722
7269
  PolyFirstPersonControls,
6723
7270
  PolyGround,
6724
7271
  PolyIcosahedron,
7272
+ PolyIframe,
6725
7273
  PolyMapControls,
6726
7274
  PolyMesh,
6727
7275
  PolyOctahedron,
@@ -6743,6 +7291,7 @@ var import_polycss_core24 = require("@layoutit/polycss-core");
6743
7291
  bakeSolidTextureSampledPolygons,
6744
7292
  bakeSolidTextureSamples,
6745
7293
  boxPolygons,
7294
+ buildPolyMeshTransform,
6746
7295
  buildSceneContext,
6747
7296
  cameraCullNormalGroups,
6748
7297
  cameraCullNormalGroupsFromPolygons,
@@ -6776,6 +7325,7 @@ var import_polycss_core24 = require("@layoutit/polycss-core");
6776
7325
  normalizePolygons,
6777
7326
  octahedronPolygons,
6778
7327
  optimizeAnimatedMeshPolygons,
7328
+ optimizeMeshParseResult,
6779
7329
  optimizeMeshPolygons,
6780
7330
  parseColor,
6781
7331
  parseGltf,
@@ -6784,6 +7334,7 @@ var import_polycss_core24 = require("@layoutit/polycss-core");
6784
7334
  parseObj,
6785
7335
  parsePureColor,
6786
7336
  parseRgbColor,
7337
+ parseStl,
6787
7338
  parseVox,
6788
7339
  planePolygons,
6789
7340
  pointInMeshElement,
@@ -6799,6 +7350,7 @@ var import_polycss_core24 = require("@layoutit/polycss-core");
6799
7350
  seamOverlapPolygons,
6800
7351
  seamOverlapReport,
6801
7352
  shadeColor,
7353
+ simplifyTriangleMeshPolygons,
6802
7354
  tetrahedronPolygons,
6803
7355
  torusPolygons,
6804
7356
  useCameraContext,
@@ -6808,5 +7360,7 @@ var import_polycss_core24 = require("@layoutit/polycss-core");
6808
7360
  usePolyMesh,
6809
7361
  usePolySceneContext,
6810
7362
  usePolySelect,
6811
- usePolySelectionApi
7363
+ usePolySelectionApi,
7364
+ worldDirectionToPolyCss,
7365
+ worldPositionToPolyCss
6812
7366
  });