@layoutit/polycss-react 0.2.5 → 0.2.6

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_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,
23
+ BASE_TILE: () => import_polycss_core28.BASE_TILE,
24
+ CAMERA_BACKFACE_CULL_EPS: () => import_polycss_core28.CAMERA_BACKFACE_CULL_EPS,
25
+ DEFAULT_CAMERA_STATE: () => import_polycss_core28.DEFAULT_CAMERA_STATE,
26
+ DEFAULT_PROJECTION: () => import_polycss_core28.DEFAULT_PROJECTION,
27
+ DEFAULT_SEAM_FACET_SPLIT_OPTIONS: () => import_polycss_core28.DEFAULT_SEAM_FACET_SPLIT_OPTIONS,
28
+ DEFAULT_SEAM_OVERLAP_OPTIONS: () => import_polycss_core28.DEFAULT_SEAM_OVERLAP_OPTIONS,
29
+ LoopOnce: () => import_polycss_core28.LoopOnce,
30
+ LoopPingPong: () => import_polycss_core28.LoopPingPong,
31
+ LoopRepeat: () => import_polycss_core28.LoopRepeat,
32
32
  Poly: () => Poly,
33
33
  PolyAxesHelper: () => PolyAxesHelper,
34
34
  PolyBox: () => PolyBox,
@@ -56,78 +56,86 @@ __export(src_exports, {
56
56
  PolyTetrahedron: () => PolyTetrahedron,
57
57
  PolyTorus: () => PolyTorus,
58
58
  PolyTransformControls: () => PolyTransformControls,
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
- buildPolySceneTransform: () => import_polycss_core27.buildPolySceneTransform,
68
- buildSceneContext: () => import_polycss_core27.buildSceneContext,
69
- cameraCullNormalGroups: () => import_polycss_core27.cameraCullNormalGroups,
70
- cameraCullNormalGroupsFromPolygons: () => import_polycss_core27.cameraCullNormalGroupsFromPolygons,
71
- cameraCullNormalKey: () => import_polycss_core27.cameraCullNormalKey,
72
- cameraCullVisibleSignature: () => import_polycss_core27.cameraCullVisibleSignature,
73
- cameraFacingDepth: () => import_polycss_core27.cameraFacingDepth,
74
- clampChannel: () => import_polycss_core27.clampChannel,
59
+ VOXEL_CAMERA_CULL_AXIS_EPS: () => import_polycss_core28.VOXEL_CAMERA_CULL_AXIS_EPS,
60
+ VOXEL_CAMERA_CULL_NORMAL_LIMIT: () => import_polycss_core28.VOXEL_CAMERA_CULL_NORMAL_LIMIT,
61
+ arrowPolygons: () => import_polycss_core28.arrowPolygons,
62
+ axesHelperPolygons: () => import_polycss_core28.axesHelperPolygons,
63
+ bakeSolidTextureSampledPolygons: () => import_polycss_core28.bakeSolidTextureSampledPolygons,
64
+ bakeSolidTextureSamples: () => import_polycss_core28.bakeSolidTextureSamples,
65
+ boxPolygons: () => import_polycss_core28.boxPolygons,
66
+ buildPolyCameraSceneTransform: () => import_polycss_core28.buildPolyCameraSceneTransform,
67
+ buildPolyMeshTransform: () => import_polycss_core28.buildPolyMeshTransform,
68
+ buildPolySceneTransform: () => import_polycss_core28.buildPolySceneTransform,
69
+ buildSceneContext: () => import_polycss_core28.buildSceneContext,
70
+ cameraCullNormalGroups: () => import_polycss_core28.cameraCullNormalGroups,
71
+ cameraCullNormalGroupsFromPolygons: () => import_polycss_core28.cameraCullNormalGroupsFromPolygons,
72
+ cameraCullNormalKey: () => import_polycss_core28.cameraCullNormalKey,
73
+ cameraCullVisibleSignature: () => import_polycss_core28.cameraCullVisibleSignature,
74
+ cameraFacingDepth: () => import_polycss_core28.cameraFacingDepth,
75
+ capturePolyCameraSnapshot: () => import_polycss_core28.capturePolyCameraSnapshot,
76
+ clampChannel: () => import_polycss_core28.clampChannel,
75
77
  collectPolyRenderStats: () => collectPolyRenderStats,
76
- computeSceneBbox: () => import_polycss_core27.computeSceneBbox,
77
- computeShapeLighting: () => import_polycss_core27.computeShapeLighting,
78
- computeTexturePaintMetrics: () => import_polycss_core27.computeTexturePaintMetrics,
79
- conePolygons: () => import_polycss_core27.conePolygons,
80
- coverPlanarPolygons: () => import_polycss_core27.coverPlanarPolygons,
81
- createIsometricCamera: () => import_polycss_core27.createIsometricCamera,
82
- createPolyAnimationMixer: () => import_polycss_core27.createPolyAnimationMixer,
83
- cullInteriorPolygons: () => import_polycss_core27.cullInteriorPolygons,
84
- cylinderPolygons: () => import_polycss_core27.cylinderPolygons,
85
- dodecahedronPolygons: () => import_polycss_core27.dodecahedronPolygons,
78
+ collectPolyTextureReadiness: () => collectPolyTextureReadiness,
79
+ computeSceneBbox: () => import_polycss_core28.computeSceneBbox,
80
+ computeShapeLighting: () => import_polycss_core28.computeShapeLighting,
81
+ computeTexturePaintMetrics: () => import_polycss_core28.computeTexturePaintMetrics,
82
+ conePolygons: () => import_polycss_core28.conePolygons,
83
+ coverPlanarPolygons: () => import_polycss_core28.coverPlanarPolygons,
84
+ createIsometricCamera: () => import_polycss_core28.createIsometricCamera,
85
+ createPolyAnimationMixer: () => import_polycss_core28.createPolyAnimationMixer,
86
+ cssDistanceToWorld: () => import_polycss_core28.cssDistanceToWorld,
87
+ cssPositionToWorld: () => import_polycss_core28.cssPositionToWorld,
88
+ cullInteriorPolygons: () => import_polycss_core28.cullInteriorPolygons,
89
+ cylinderPolygons: () => import_polycss_core28.cylinderPolygons,
90
+ dodecahedronPolygons: () => import_polycss_core28.dodecahedronPolygons,
86
91
  findMeshUnderPoint: () => findMeshUnderPoint,
87
92
  findPolyMeshHandle: () => findPolyMeshHandle,
88
- formatColor: () => import_polycss_core27.formatColor,
89
- icosahedronPolygons: () => import_polycss_core27.icosahedronPolygons,
93
+ formatColor: () => import_polycss_core28.formatColor,
94
+ icosahedronPolygons: () => import_polycss_core28.icosahedronPolygons,
90
95
  injectPolyBaseStyles: () => injectPolyBaseStyles,
91
- inverseRotateVec3: () => import_polycss_core27.inverseRotateVec3,
92
- isAxisAlignedSurfaceNormal: () => import_polycss_core27.isAxisAlignedSurfaceNormal,
93
- isVoxelCameraCullableNormalGroups: () => import_polycss_core27.isVoxelCameraCullableNormalGroups,
94
- loadMesh: () => import_polycss_core27.loadMesh,
95
- mergePolygons: () => import_polycss_core27.mergePolygons,
96
- normalFacesCamera: () => import_polycss_core27.normalFacesCamera,
97
- normalizeInvertMultiplier: () => import_polycss_core27.normalizeInvertMultiplier,
98
- normalizePolygons: () => import_polycss_core27.normalizePolygons,
99
- octahedronPolygons: () => import_polycss_core27.octahedronPolygons,
100
- optimizeAnimatedMeshPolygons: () => import_polycss_core27.optimizeAnimatedMeshPolygons,
101
- optimizeMeshParseResult: () => import_polycss_core27.optimizeMeshParseResult,
102
- optimizeMeshPolygons: () => import_polycss_core27.optimizeMeshPolygons,
103
- parseColor: () => import_polycss_core27.parseColor,
104
- parseGltf: () => import_polycss_core27.parseGltf,
105
- parseHexColor: () => import_polycss_core27.parseHexColor,
106
- parseMtl: () => import_polycss_core27.parseMtl,
107
- parseObj: () => import_polycss_core27.parseObj,
108
- parsePureColor: () => import_polycss_core27.parsePureColor,
109
- parseRgbColor: () => import_polycss_core27.parseRgbColor,
110
- parseStl: () => import_polycss_core27.parseStl,
111
- parseVox: () => import_polycss_core27.parseVox,
112
- planePolygons: () => import_polycss_core27.planePolygons,
96
+ inverseRotateVec3: () => import_polycss_core28.inverseRotateVec3,
97
+ isAxisAlignedSurfaceNormal: () => import_polycss_core28.isAxisAlignedSurfaceNormal,
98
+ isVoxelCameraCullableNormalGroups: () => import_polycss_core28.isVoxelCameraCullableNormalGroups,
99
+ loadMesh: () => import_polycss_core28.loadMesh,
100
+ mergePolygons: () => import_polycss_core28.mergePolygons,
101
+ normalFacesCamera: () => import_polycss_core28.normalFacesCamera,
102
+ normalizeInvertMultiplier: () => import_polycss_core28.normalizeInvertMultiplier,
103
+ normalizePolygons: () => import_polycss_core28.normalizePolygons,
104
+ octahedronPolygons: () => import_polycss_core28.octahedronPolygons,
105
+ optimizeAnimatedMeshPolygons: () => import_polycss_core28.optimizeAnimatedMeshPolygons,
106
+ optimizeMeshParseResult: () => import_polycss_core28.optimizeMeshParseResult,
107
+ optimizeMeshPolygons: () => import_polycss_core28.optimizeMeshPolygons,
108
+ parseColor: () => import_polycss_core28.parseColor,
109
+ parseGltf: () => import_polycss_core28.parseGltf,
110
+ parseHexColor: () => import_polycss_core28.parseHexColor,
111
+ parseMtl: () => import_polycss_core28.parseMtl,
112
+ parseObj: () => import_polycss_core28.parseObj,
113
+ parsePureColor: () => import_polycss_core28.parsePureColor,
114
+ parseRgbColor: () => import_polycss_core28.parseRgbColor,
115
+ parseStl: () => import_polycss_core28.parseStl,
116
+ parseVox: () => import_polycss_core28.parseVox,
117
+ planePolygons: () => import_polycss_core28.planePolygons,
113
118
  pointInMeshElement: () => pointInMeshElement,
114
- polyCssDistanceToWorld: () => import_polycss_core27.cssDistanceToWorld,
115
- polyCssPositionToWorld: () => import_polycss_core27.cssPositionToWorld,
116
- polygonCssSurfaceNormal: () => import_polycss_core27.polygonCssSurfaceNormal,
117
- polygonFaces: () => import_polycss_core27.polygonFaces,
118
- polygonFacesCamera: () => import_polycss_core27.polygonFacesCamera,
119
- repairMeshSeams: () => import_polycss_core27.repairMeshSeams,
120
- ringPolygons: () => import_polycss_core27.ringPolygons,
121
- rotateVec3: () => import_polycss_core27.rotateVec3,
122
- seamFacetSplitPolygons: () => import_polycss_core27.seamFacetSplitPolygons,
123
- seamFacetSplitReport: () => import_polycss_core27.seamFacetSplitReport,
124
- seamOverlapDiagnostics: () => import_polycss_core27.seamOverlapDiagnostics,
125
- seamOverlapPolygons: () => import_polycss_core27.seamOverlapPolygons,
126
- seamOverlapReport: () => import_polycss_core27.seamOverlapReport,
127
- shadeColor: () => import_polycss_core27.shadeColor,
128
- simplifyTriangleMeshPolygons: () => import_polycss_core27.simplifyTriangleMeshPolygons,
129
- tetrahedronPolygons: () => import_polycss_core27.tetrahedronPolygons,
130
- torusPolygons: () => import_polycss_core27.torusPolygons,
119
+ polyCameraTargetToCss: () => import_polycss_core28.polyCameraTargetToCss,
120
+ polyCssDistanceToWorld: () => import_polycss_core28.polyCssDistanceToWorld,
121
+ polyCssPositionToWorld: () => import_polycss_core28.polyCssPositionToWorld,
122
+ polygonCssSurfaceNormal: () => import_polycss_core28.polygonCssSurfaceNormal,
123
+ polygonFaces: () => import_polycss_core28.polygonFaces,
124
+ polygonFacesCamera: () => import_polycss_core28.polygonFacesCamera,
125
+ queryPolyLeaves: () => queryPolyLeaves,
126
+ repairMeshSeams: () => import_polycss_core28.repairMeshSeams,
127
+ resolvePolyCameraAppliedPerspectiveStyle: () => import_polycss_core28.resolvePolyCameraAppliedPerspectiveStyle,
128
+ ringPolygons: () => import_polycss_core28.ringPolygons,
129
+ rotateVec3: () => import_polycss_core28.rotateVec3,
130
+ seamFacetSplitPolygons: () => import_polycss_core28.seamFacetSplitPolygons,
131
+ seamFacetSplitReport: () => import_polycss_core28.seamFacetSplitReport,
132
+ seamOverlapDiagnostics: () => import_polycss_core28.seamOverlapDiagnostics,
133
+ seamOverlapPolygons: () => import_polycss_core28.seamOverlapPolygons,
134
+ seamOverlapReport: () => import_polycss_core28.seamOverlapReport,
135
+ shadeColor: () => import_polycss_core28.shadeColor,
136
+ simplifyTriangleMeshPolygons: () => import_polycss_core28.simplifyTriangleMeshPolygons,
137
+ tetrahedronPolygons: () => import_polycss_core28.tetrahedronPolygons,
138
+ torusPolygons: () => import_polycss_core28.torusPolygons,
131
139
  useCameraContext: () => useCameraContext,
132
140
  usePolyAnimation: () => usePolyAnimation,
133
141
  usePolyCamera: () => usePolyCamera,
@@ -136,10 +144,14 @@ __export(src_exports, {
136
144
  usePolySceneContext: () => usePolySceneContext,
137
145
  usePolySelect: () => usePolySelect,
138
146
  usePolySelectionApi: () => usePolySelectionApi,
139
- worldDirectionToPolyCss: () => import_polycss_core27.worldDirectionToCss,
140
- worldDirectionalLightToPolyCss: () => import_polycss_core27.worldDirectionalLightToCss,
141
- worldDistanceToPolyCss: () => import_polycss_core27.worldDistanceToCss,
142
- worldPositionToPolyCss: () => import_polycss_core27.worldPositionToCss
147
+ worldDirectionToCss: () => import_polycss_core28.worldDirectionToCss,
148
+ worldDirectionToPolyCss: () => import_polycss_core28.worldDirectionToPolyCss,
149
+ worldDirectionalLightToCss: () => import_polycss_core28.worldDirectionalLightToCss,
150
+ worldDirectionalLightToPolyCss: () => import_polycss_core28.worldDirectionalLightToPolyCss,
151
+ worldDistanceToCss: () => import_polycss_core28.worldDistanceToCss,
152
+ worldDistanceToPolyCss: () => import_polycss_core28.worldDistanceToPolyCss,
153
+ worldPositionToCss: () => import_polycss_core28.worldPositionToCss,
154
+ worldPositionToPolyCss: () => import_polycss_core28.worldPositionToPolyCss
143
155
  });
144
156
  module.exports = __toCommonJS(src_exports);
145
157
 
@@ -205,6 +217,18 @@ function useStoreSelector(store, selector) {
205
217
  }
206
218
 
207
219
  // src/camera/useCamera.ts
220
+ function writeCameraSnapshotAttrs(el, camera, projection, perspectiveStyle) {
221
+ if (!el) return;
222
+ const snapshot = (0, import_polycss_core.capturePolyCameraSnapshot)(camera, { projection, perspectiveStyle });
223
+ el.dataset.polycssCameraProjection = snapshot.projection;
224
+ el.dataset.polycssCameraPerspective = snapshot.perspectiveStyle;
225
+ el.dataset.polycssCameraAppliedPerspective = snapshot.appliedPerspectiveStyle;
226
+ el.dataset.polycssCameraZoom = String(snapshot.state.zoom);
227
+ el.dataset.polycssCameraDistance = String(snapshot.state.distance);
228
+ el.dataset.polycssCameraRotX = String(snapshot.state.rotX);
229
+ el.dataset.polycssCameraRotY = String(snapshot.state.rotY);
230
+ el.dataset.polycssCameraTarget = snapshot.state.target.join(",");
231
+ }
208
232
  function usePolyCamera(options) {
209
233
  const handleRef = (0, import_react3.useRef)(null);
210
234
  if (!handleRef.current) {
@@ -234,24 +258,33 @@ function usePolyCamera(options) {
234
258
  handle.update(next);
235
259
  const el = sceneElRef.current;
236
260
  if (el) {
237
- el.style.transform = (0, import_polycss_core.buildPolySceneTransform)({
238
- ...handle.state,
261
+ el.style.transform = (0, import_polycss_core.buildPolyCameraSceneTransform)(handle.state, {
239
262
  autoCenterOffset: store.getState().autoCenterOffset
240
263
  });
241
264
  }
242
265
  store.updateCameraFromRef(handle);
243
266
  store.notifyAll();
244
267
  }
245
- }, [options.zoom, options.target, options.rotX, options.rotY, options.distance, store]);
268
+ writeCameraSnapshotAttrs(cameraElRef.current, handle, options.projection, options.perspectiveStyle);
269
+ }, [
270
+ options.zoom,
271
+ options.target,
272
+ options.rotX,
273
+ options.rotY,
274
+ options.distance,
275
+ options.projection,
276
+ options.perspectiveStyle,
277
+ store
278
+ ]);
246
279
  const applyTransformDirect = (0, import_react3.useCallback)(() => {
247
280
  const el = sceneElRef.current;
248
281
  if (!el) return;
249
282
  const handle = handleRef.current;
250
- el.style.transform = (0, import_polycss_core.buildPolySceneTransform)({
251
- ...handle.state,
283
+ el.style.transform = (0, import_polycss_core.buildPolyCameraSceneTransform)(handle.state, {
252
284
  autoCenterOffset: store.getState().autoCenterOffset
253
285
  });
254
- }, [store]);
286
+ writeCameraSnapshotAttrs(cameraElRef.current, handle, options.projection, options.perspectiveStyle);
287
+ }, [store, options.projection, options.perspectiveStyle]);
255
288
  return {
256
289
  store,
257
290
  cameraRef: handleRef,
@@ -275,18 +308,26 @@ function PolyPerspectiveCameraInner({
275
308
  className,
276
309
  style
277
310
  }) {
311
+ const perspectiveValue = `${typeof perspective === "number" ? perspective : DEFAULT_PERSPECTIVE}px`;
278
312
  const {
279
313
  store,
280
314
  cameraRef,
281
315
  sceneElRef,
282
316
  cameraElRef,
283
317
  applyTransformDirect
284
- } = usePolyCamera({ zoom, target, rotX, rotY, distance });
318
+ } = usePolyCamera({
319
+ zoom,
320
+ target,
321
+ rotX,
322
+ rotY,
323
+ distance,
324
+ projection: "perspective",
325
+ perspectiveStyle: perspectiveValue
326
+ });
285
327
  const contextValue = (0, import_react4.useMemo)(
286
328
  () => ({ store, cameraRef, sceneElRef, cameraElRef, applyTransformDirect }),
287
329
  [store, cameraRef, sceneElRef, cameraElRef, applyTransformDirect]
288
330
  );
289
- const perspectiveValue = `${typeof perspective === "number" ? perspective : DEFAULT_PERSPECTIVE}px`;
290
331
  const cameraStyle = {
291
332
  ...style,
292
333
  perspective: perspectiveValue
@@ -297,6 +338,14 @@ function PolyPerspectiveCameraInner({
297
338
  ref: cameraElRef,
298
339
  className: `polycss-camera${className ? ` ${className}` : ""}`,
299
340
  style: cameraStyle,
341
+ "data-polycss-camera-projection": "perspective",
342
+ "data-polycss-camera-perspective": perspectiveValue,
343
+ "data-polycss-camera-applied-perspective": perspectiveValue,
344
+ "data-polycss-camera-zoom": cameraRef.current.state.zoom,
345
+ "data-polycss-camera-distance": cameraRef.current.state.distance,
346
+ "data-polycss-camera-rot-x": cameraRef.current.state.rotX,
347
+ "data-polycss-camera-rot-y": cameraRef.current.state.rotY,
348
+ "data-polycss-camera-target": cameraRef.current.state.target.join(","),
300
349
  children
301
350
  }
302
351
  ) });
@@ -322,7 +371,15 @@ function PolyOrthographicCameraInner({
322
371
  sceneElRef,
323
372
  cameraElRef,
324
373
  applyTransformDirect
325
- } = usePolyCamera({ zoom, target, rotX, rotY, distance });
374
+ } = usePolyCamera({
375
+ zoom,
376
+ target,
377
+ rotX,
378
+ rotY,
379
+ distance,
380
+ projection: "orthographic",
381
+ perspectiveStyle: "none"
382
+ });
326
383
  const contextValue = (0, import_react5.useMemo)(
327
384
  () => ({ store, cameraRef, sceneElRef, cameraElRef, applyTransformDirect }),
328
385
  [store, cameraRef, sceneElRef, cameraElRef, applyTransformDirect]
@@ -342,6 +399,14 @@ function PolyOrthographicCameraInner({
342
399
  ref: cameraElRef,
343
400
  className: `polycss-camera${className ? ` ${className}` : ""}`,
344
401
  style: cameraStyle,
402
+ "data-polycss-camera-projection": "orthographic",
403
+ "data-polycss-camera-perspective": "none",
404
+ "data-polycss-camera-applied-perspective": "1000000px",
405
+ "data-polycss-camera-zoom": cameraRef.current.state.zoom,
406
+ "data-polycss-camera-distance": cameraRef.current.state.distance,
407
+ "data-polycss-camera-rot-x": cameraRef.current.state.rotX,
408
+ "data-polycss-camera-rot-y": cameraRef.current.state.rotY,
409
+ "data-polycss-camera-target": cameraRef.current.state.target.join(","),
345
410
  children
346
411
  }
347
412
  ) });
@@ -524,8 +589,8 @@ var CORE_BASE_STYLES = `
524
589
  }
525
590
 
526
591
  .polycss-scene s {
527
- width: var(--polycss-atlas-size, 64px);
528
- height: var(--polycss-atlas-size, 64px);
592
+ width: var(--polycss-atlas-width, var(--polycss-atlas-size, 64px));
593
+ height: var(--polycss-atlas-height, var(--polycss-atlas-size, 64px));
529
594
  }
530
595
 
531
596
  .polycss-scene u {
@@ -922,13 +987,16 @@ function isSolidTriangleSupported(doc) {
922
987
 
923
988
  // src/scene/atlas/filterPlans.ts
924
989
  var import_polycss_core4 = require("@layoutit/polycss-core");
925
- function filterAtlasPlans(plans, textureLighting, disabled, doc) {
990
+ function filterAtlasPlans(plans, textureLighting, disabled, doc, textureBackend, textureImageRendering, textureProjection) {
926
991
  const d = doc ?? (typeof document !== "undefined" ? document : null);
927
992
  return (0, import_polycss_core4.filterAtlasPlans)(plans, textureLighting, disabled, {
928
993
  solidTriangleSupported: isSolidTriangleSupported(doc),
929
994
  projectiveQuadSupported: doc ? projectiveQuadSupported(doc) : true,
930
995
  borderShapeSupported: isBorderShapeSupported(doc),
931
- cornerShapeSupported: d ? cornerShapeSupported(d) : false
996
+ cornerShapeSupported: d ? cornerShapeSupported(d) : false,
997
+ textureBackend,
998
+ textureImageRendering,
999
+ textureProjection
932
1000
  });
933
1001
  }
934
1002
 
@@ -941,8 +1009,13 @@ function isMobileDocument(doc) {
941
1009
  if (!media) return false;
942
1010
  return media("(pointer: coarse)").matches || media("(hover: none)").matches;
943
1011
  }
944
- function packTextureAtlasPlansWithScale(plans, textureQualityInput, doc) {
945
- return (0, import_polycss_core5.packTextureAtlasPlansWithScaleCore)(plans, textureQualityInput, isMobileDocument(doc));
1012
+ function packTextureAtlasPlansWithScale(plans, textureQualityInput, doc, textureLeafSizing) {
1013
+ return (0, import_polycss_core5.packTextureAtlasPlansWithScaleCore)(
1014
+ plans,
1015
+ textureQualityInput,
1016
+ isMobileDocument(doc),
1017
+ textureLeafSizing
1018
+ );
946
1019
  }
947
1020
 
948
1021
  // src/scene/atlas/buildAtlasPages.ts
@@ -2148,7 +2221,7 @@ function deferRevoke(urls) {
2148
2221
  if (typeof requestAnimationFrame === "function") requestAnimationFrame(run);
2149
2222
  else setTimeout(run, 0);
2150
2223
  }
2151
- function useTextureAtlas(plans, textureLighting, textureQualityInput, strategies, atomic = false) {
2224
+ function useTextureAtlas(plans, textureLighting, textureQualityInput, textureLeafSizing, textureBackend, textureImageRendering, textureProjection, strategies, atomic = false) {
2152
2225
  const disabled = (0, import_react7.useMemo)(
2153
2226
  () => new Set(strategies?.disable ?? []),
2154
2227
  // eslint-disable-next-line react-hooks/exhaustive-deps
@@ -2159,17 +2232,21 @@ function useTextureAtlas(plans, textureLighting, textureQualityInput, strategies
2159
2232
  plans,
2160
2233
  textureLighting,
2161
2234
  disabled,
2162
- typeof document !== "undefined" ? document : null
2235
+ typeof document !== "undefined" ? document : null,
2236
+ textureBackend,
2237
+ textureImageRendering,
2238
+ textureProjection
2163
2239
  ),
2164
- [plans, textureLighting, disabled]
2240
+ [plans, textureLighting, disabled, textureBackend, textureImageRendering, textureProjection]
2165
2241
  );
2166
2242
  const { packed, atlasScale } = (0, import_react7.useMemo)(
2167
2243
  () => packTextureAtlasPlansWithScale(
2168
2244
  atlasPlans,
2169
2245
  textureQualityInput,
2170
- typeof document !== "undefined" ? document : null
2246
+ typeof document !== "undefined" ? document : null,
2247
+ textureLeafSizing
2171
2248
  ),
2172
- [atlasPlans, textureQualityInput]
2249
+ [atlasPlans, textureQualityInput, textureLeafSizing]
2173
2250
  );
2174
2251
  const [pages, setPages] = (0, import_react7.useState)(() => pageShells(packed.pages));
2175
2252
  const [frame, setFrame] = (0, import_react7.useState)(() => ({
@@ -2519,6 +2596,7 @@ var TextureAtlasPoly = (0, import_react12.memo)(function TextureAtlasPoly2({
2519
2596
  entry,
2520
2597
  page,
2521
2598
  textureLighting,
2599
+ textureImageRendering,
2522
2600
  solidPaintDefaults: _solidPaintDefaults,
2523
2601
  className,
2524
2602
  style: styleProp,
@@ -2528,26 +2606,26 @@ var TextureAtlasPoly = (0, import_react12.memo)(function TextureAtlasPoly2({
2528
2606
  }) {
2529
2607
  const ATLAS_CANONICAL_SIZE_EXPLICIT = 64;
2530
2608
  const dynamic = textureLighting === "dynamic";
2609
+ const resolvedImageRendering = (0, import_polycss_core11.resolvePolyTextureImageRendering)(entry.polygon, textureImageRendering);
2531
2610
  const atlasCanonicalSize = entry.atlasCanonicalSize ?? ATLAS_CANONICAL_SIZE_EXPLICIT;
2611
+ const atlasLeafWidth = entry.atlasLeafWidth ?? atlasCanonicalSize;
2612
+ const atlasLeafHeight = entry.atlasLeafHeight ?? atlasCanonicalSize;
2532
2613
  const atlasWidth = entry.canvasW || 1;
2533
2614
  const atlasHeight = entry.canvasH || 1;
2534
- const atlasPosition = page ? `${(0, import_polycss_core11.formatCssLengthPx)(-entry.x / atlasWidth * atlasCanonicalSize)} ${(0, import_polycss_core11.formatCssLengthPx)(-entry.y / atlasHeight * atlasCanonicalSize)}` : void 0;
2535
- const atlasSize = page ? `${(0, import_polycss_core11.formatCssLengthPx)(page.width / atlasWidth * atlasCanonicalSize)} ${(0, import_polycss_core11.formatCssLengthPx)(page.height / atlasHeight * atlasCanonicalSize)}` : void 0;
2615
+ const atlasPosition = page ? `${(0, import_polycss_core11.formatCssLengthPx)(-entry.x / atlasWidth * atlasLeafWidth)} ${(0, import_polycss_core11.formatCssLengthPx)(-entry.y / atlasHeight * atlasLeafHeight)}` : void 0;
2616
+ const atlasSize = page ? `${(0, import_polycss_core11.formatCssLengthPx)(page.width / atlasWidth * atlasLeafWidth)} ${(0, import_polycss_core11.formatCssLengthPx)(page.height / atlasHeight * atlasLeafHeight)}` : void 0;
2536
2617
  const dynamicMask = dynamic && page?.url ? `url(${page.url})` : void 0;
2537
- const background = !dynamic && page?.url ? `url(${page.url}) ${atlasPosition} / ${atlasSize} no-repeat` : void 0;
2538
2618
  const style = {
2539
2619
  transform: (0, import_polycss_core11.formatMatrix3d)(entry.atlasMatrix),
2540
2620
  ["--polycss-atlas-size"]: `${atlasCanonicalSize}px`,
2541
- // Listing the `background` shorthand alongside the `background-*` longhands
2542
- // in one inline style object makes React warn on every update (mixing
2543
- // shorthand and non-shorthand for the same property). Branch so only the
2544
- // current mode's keys are assigned — baked gets `background`, dynamic gets
2545
- // the longhands.
2546
- ...dynamic ? {
2547
- backgroundImage: page?.url ? `url(${page.url})` : void 0,
2548
- backgroundPosition: atlasPosition,
2549
- backgroundSize: atlasSize
2550
- } : { background },
2621
+ ["--polycss-atlas-width"]: (0, import_polycss_core11.formatCssLengthPx)(atlasLeafWidth),
2622
+ ["--polycss-atlas-height"]: (0, import_polycss_core11.formatCssLengthPx)(atlasLeafHeight),
2623
+ ["--polycss-atlas-leaf-sizing"]: entry.atlasLeafSizing ?? "canonical",
2624
+ backgroundImage: page?.url ? `url(${page.url})` : void 0,
2625
+ backgroundPosition: atlasPosition,
2626
+ backgroundSize: atlasSize,
2627
+ backgroundRepeat: page?.url ? "no-repeat" : void 0,
2628
+ imageRendering: resolvedImageRendering === "pixelated" ? "pixelated" : void 0,
2551
2629
  ...dynamic ? {
2552
2630
  ["--pnx"]: entry.normal[0].toFixed(4),
2553
2631
  ["--pny"]: entry.normal[1].toFixed(4),
@@ -2578,6 +2656,74 @@ var TextureAtlasPoly = (0, import_react12.memo)(function TextureAtlasPoly2({
2578
2656
  className: elementClassName,
2579
2657
  style,
2580
2658
  "data-poly-index": entry.index,
2659
+ "data-polycss-leaf": "polygon",
2660
+ "data-polycss-texture-backend": "atlas",
2661
+ "data-polycss-texture-leaf-sizing": entry.atlasLeafSizing ?? "canonical",
2662
+ "data-polycss-texture-ready": page?.url ? "true" : "false",
2663
+ "data-polycss-texture-image-rendering": resolvedImageRendering,
2664
+ "data-polycss-texture-projection": "affine",
2665
+ "data-polycss-texture-lighting": textureLighting,
2666
+ "data-polycss-texture-leaf-width": atlasLeafWidth,
2667
+ "data-polycss-texture-leaf-height": atlasLeafHeight,
2668
+ "data-polycss-double-sided": entry.polygon.doubleSided ? "true" : void 0,
2669
+ ...domEventHandlers,
2670
+ ...dataAttrs,
2671
+ ...domAttrs
2672
+ }
2673
+ );
2674
+ });
2675
+ var TextureImagePoly = (0, import_react12.memo)(function TextureImagePoly2({
2676
+ plan,
2677
+ geometry,
2678
+ className,
2679
+ style: styleProp,
2680
+ domAttrs,
2681
+ domEventHandlers,
2682
+ pointerEvents = "auto"
2683
+ }) {
2684
+ const style = {
2685
+ transform: (0, import_polycss_core11.formatMatrix3d)(geometry.matrix),
2686
+ ["--polycss-atlas-width"]: (0, import_polycss_core11.formatCssLengthPx)(geometry.leafWidth),
2687
+ ["--polycss-atlas-height"]: (0, import_polycss_core11.formatCssLengthPx)(geometry.leafHeight),
2688
+ ["--polycss-atlas-leaf-sizing"]: "image",
2689
+ backgroundImage: `url(${geometry.url})`,
2690
+ backgroundPosition: `${(0, import_polycss_core11.formatCssLengthPx)(geometry.backgroundPosition[0])} ${(0, import_polycss_core11.formatCssLengthPx)(geometry.backgroundPosition[1])}`,
2691
+ backgroundSize: `${(0, import_polycss_core11.formatCssLengthPx)(geometry.backgroundSize[0])} ${(0, import_polycss_core11.formatCssLengthPx)(geometry.backgroundSize[1])}`,
2692
+ backgroundRepeat: "no-repeat",
2693
+ backgroundBlendMode: "normal",
2694
+ maskImage: "none",
2695
+ WebkitMaskImage: "none",
2696
+ imageRendering: geometry.imageRendering === "pixelated" ? "pixelated" : void 0,
2697
+ ["--pnx"]: plan.normal[0].toFixed(4),
2698
+ ["--pny"]: plan.normal[1].toFixed(4),
2699
+ ["--pnz"]: plan.normal[2].toFixed(4),
2700
+ pointerEvents: pointerEvents === "none" ? "none" : void 0,
2701
+ ...styleProp
2702
+ };
2703
+ const dataAttrs = plan.polygon.data ? Object.fromEntries(
2704
+ Object.entries(plan.polygon.data).map(([k, v]) => [`data-${k}`, String(v)])
2705
+ ) : {};
2706
+ const elementClassName = className?.trim() || void 0;
2707
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2708
+ "s",
2709
+ {
2710
+ className: elementClassName,
2711
+ style,
2712
+ "data-poly-index": plan.index,
2713
+ "data-polycss-leaf": "polygon",
2714
+ "data-polycss-texture-backend": "image",
2715
+ "data-polycss-texture-leaf-sizing": "image",
2716
+ "data-polycss-texture-ready": "true",
2717
+ "data-polycss-texture-image-rendering": geometry.imageRendering,
2718
+ "data-polycss-texture-projection": geometry.projection,
2719
+ "data-polycss-texture-lighting": geometry.lighting,
2720
+ "data-polycss-texture-leaf-width": geometry.leafWidth,
2721
+ "data-polycss-texture-leaf-height": geometry.leafHeight,
2722
+ "data-polycss-double-sided": plan.polygon.doubleSided ? "true" : void 0,
2723
+ "data-polycss-texture-source-x": geometry.sourceRect.x,
2724
+ "data-polycss-texture-source-y": geometry.sourceRect.y,
2725
+ "data-polycss-texture-source-width": geometry.sourceRect.width,
2726
+ "data-polycss-texture-source-height": geometry.sourceRect.height,
2581
2727
  ...domEventHandlers,
2582
2728
  ...dataAttrs,
2583
2729
  ...domAttrs
@@ -2605,6 +2751,10 @@ function PolySceneInner({
2605
2751
  ambientLight,
2606
2752
  textureLighting = "baked",
2607
2753
  textureQuality,
2754
+ textureLeafSizing,
2755
+ textureImageRendering,
2756
+ textureBackend,
2757
+ textureProjection,
2608
2758
  seamBleed = import_polycss_core13.DEFAULT_SEAM_BLEED,
2609
2759
  strategies,
2610
2760
  autoCenter = false,
@@ -2700,7 +2850,16 @@ function PolySceneInner({
2700
2850
  },
2701
2851
  [polygons, polyContext, seamBleed, directionalForAtlas, ambientForAtlas]
2702
2852
  );
2703
- const textureAtlas = useTextureAtlas(textureAtlasPlans, textureLighting, textureQuality, strategies);
2853
+ const textureAtlas = useTextureAtlas(
2854
+ textureAtlasPlans,
2855
+ textureLighting,
2856
+ textureQuality,
2857
+ textureLeafSizing,
2858
+ textureBackend,
2859
+ textureImageRendering,
2860
+ textureProjection,
2861
+ strategies
2862
+ );
2704
2863
  const dynamicLightVars = (0, import_react14.useMemo)(() => {
2705
2864
  if (textureLighting !== "dynamic") return null;
2706
2865
  const userDir = directionalLight?.direction ?? [0.4, -0.7, 0.59];
@@ -2798,12 +2957,28 @@ function PolySceneInner({
2798
2957
  {
2799
2958
  entry,
2800
2959
  page: textureAtlas.pages[entry.pageIndex],
2801
- textureLighting
2960
+ textureLighting,
2961
+ textureImageRendering
2802
2962
  },
2803
2963
  entry.index
2804
2964
  );
2805
2965
  }
2806
2966
  const plan = textureAtlasPlans[index];
2967
+ const imageGeometry = plan ? (0, import_polycss_core13.resolvePolyTextureLeafGeometry)(plan, {
2968
+ imageRendering: textureImageRendering,
2969
+ backend: textureBackend,
2970
+ projection: textureProjection
2971
+ }) : null;
2972
+ if (plan && imageGeometry) {
2973
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2974
+ TextureImagePoly,
2975
+ {
2976
+ plan,
2977
+ geometry: imageGeometry
2978
+ },
2979
+ plan.index
2980
+ );
2981
+ }
2807
2982
  if (!plan || plan.texture) return null;
2808
2983
  const useU = !disabledStrategies?.has("u");
2809
2984
  const useProjectiveSolid = !disabledStrategies?.has("b");
@@ -2829,6 +3004,10 @@ function PolySceneInner({
2829
3004
  ambientLight,
2830
3005
  strategies,
2831
3006
  seamBleed,
3007
+ textureLeafSizing,
3008
+ textureImageRendering,
3009
+ textureBackend,
3010
+ textureProjection,
2832
3011
  shadow,
2833
3012
  registerShadowCaster,
2834
3013
  registerShadowReceiver,
@@ -2838,7 +3017,7 @@ function PolySceneInner({
2838
3017
  groundCssZ,
2839
3018
  sceneEl
2840
3019
  }),
2841
- [textureLighting, directionalLight, ambientLight, strategies, seamBleed, shadow, registerShadowCaster, registerShadowReceiver, shadowCastersVersion, hasShadowReceiver, groundCssZ, sceneEl]
3020
+ [textureLighting, directionalLight, ambientLight, strategies, seamBleed, textureLeafSizing, textureImageRendering, textureBackend, textureProjection, shadow, registerShadowCaster, registerShadowReceiver, shadowCastersVersion, hasShadowReceiver, groundCssZ, sceneEl]
2842
3021
  );
2843
3022
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(PolySceneContext.Provider, { value: sceneCtxValue, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
2844
3023
  "div",
@@ -3719,6 +3898,10 @@ var PolyMesh = (0, import_react16.forwardRef)(function PolyMesh2({
3719
3898
  autoCenter,
3720
3899
  textureLighting,
3721
3900
  textureQuality,
3901
+ textureLeafSizing,
3902
+ textureImageRendering,
3903
+ textureBackend,
3904
+ textureProjection,
3722
3905
  seamBleed,
3723
3906
  atomicAtlas,
3724
3907
  onFrameReady,
@@ -3788,6 +3971,12 @@ var PolyMesh = (0, import_react16.forwardRef)(function PolyMesh2({
3788
3971
  const stableTriangleColorFrameRef = (0, import_react16.useRef)(0);
3789
3972
  const setPolygonsImplRef = (0, import_react16.useRef)(() => {
3790
3973
  });
3974
+ const textureReadyRef = (0, import_react16.useRef)(true);
3975
+ const textureReadyWaitersRef = (0, import_react16.useRef)([]);
3976
+ const resolveTextureReadyWaiters = (0, import_react16.useCallback)(() => {
3977
+ const waiters = textureReadyWaitersRef.current.splice(0);
3978
+ for (const resolve of waiters) resolve();
3979
+ }, []);
3791
3980
  const handle = (0, import_react16.useMemo)(() => ({
3792
3981
  get element() {
3793
3982
  return wrapperRef.current;
@@ -3801,6 +3990,12 @@ var PolyMesh = (0, import_react16.forwardRef)(function PolyMesh2({
3801
3990
  setPolygonsImplRef.current(nextPolygons);
3802
3991
  },
3803
3992
  rebakeAtlas: () => setBakedRotation(propsRef.current.rotation),
3993
+ whenTexturesReady() {
3994
+ if (textureReadyRef.current) return Promise.resolve();
3995
+ return new Promise((resolve) => {
3996
+ textureReadyWaitersRef.current.push(resolve);
3997
+ });
3998
+ },
3804
3999
  updatePolygon(target, partial) {
3805
4000
  const current = polygonsRef.current;
3806
4001
  const idx = typeof target === "number" ? target : current.indexOf(target);
@@ -3947,6 +4142,10 @@ var PolyMesh = (0, import_react16.forwardRef)(function PolyMesh2({
3947
4142
  [effectiveStrategies]
3948
4143
  );
3949
4144
  const effectiveSeamBleed = seamBleed ?? sceneCtx?.seamBleed ?? import_polycss_core16.DEFAULT_SEAM_BLEED;
4145
+ const effectiveTextureLeafSizing = textureLeafSizing ?? sceneCtx?.textureLeafSizing;
4146
+ const effectiveTextureImageRendering = textureImageRendering ?? sceneCtx?.textureImageRendering;
4147
+ const effectiveTextureBackend = textureBackend ?? sceneCtx?.textureBackend;
4148
+ const effectiveTextureProjection = textureProjection ?? sceneCtx?.textureProjection;
3950
4149
  const effectiveDirectional = sceneCtx?.directionalLight;
3951
4150
  const effectiveAmbient = sceneCtx?.ambientLight;
3952
4151
  const directVoxelEnabled = Boolean(
@@ -4009,9 +4208,18 @@ var PolyMesh = (0, import_react16.forwardRef)(function PolyMesh2({
4009
4208
  atlasPlans,
4010
4209
  effectiveTextureLighting,
4011
4210
  textureQuality,
4211
+ effectiveTextureLeafSizing,
4212
+ effectiveTextureBackend,
4213
+ effectiveTextureImageRendering,
4214
+ effectiveTextureProjection,
4012
4215
  effectiveStrategies,
4013
4216
  atomicAtlas
4014
4217
  );
4218
+ textureReadyRef.current = textureAtlas.ready;
4219
+ (0, import_react16.useEffect)(() => {
4220
+ if (textureAtlas.ready) resolveTextureReadyWaiters();
4221
+ }, [textureAtlas.ready, resolveTextureReadyWaiters]);
4222
+ (0, import_react16.useEffect)(() => resolveTextureReadyWaiters, [resolveTextureReadyWaiters]);
4015
4223
  const solidPaintDefaults = (0, import_react16.useMemo)(
4016
4224
  () => !renderPolygon ? getSolidPaintDefaults(textureAtlas.plans, effectiveTextureLighting, effectiveStrategies) : {},
4017
4225
  [renderPolygon, textureAtlas.plans, effectiveTextureLighting, effectiveStrategies]
@@ -4360,12 +4568,28 @@ var PolyMesh = (0, import_react16.forwardRef)(function PolyMesh2({
4360
4568
  entry,
4361
4569
  page: textureAtlas.pages[entry.pageIndex],
4362
4570
  textureLighting: effectiveTextureLighting,
4571
+ textureImageRendering: effectiveTextureImageRendering,
4363
4572
  solidPaintDefaults
4364
4573
  },
4365
4574
  entry.index
4366
4575
  );
4367
4576
  }
4368
4577
  const plan = textureAtlas.plans[index];
4578
+ const imageGeometry = plan ? (0, import_polycss_core16.resolvePolyTextureLeafGeometry)(plan, {
4579
+ imageRendering: effectiveTextureImageRendering,
4580
+ backend: effectiveTextureBackend,
4581
+ projection: effectiveTextureProjection
4582
+ }) : null;
4583
+ if (plan && imageGeometry) {
4584
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4585
+ TextureImagePoly,
4586
+ {
4587
+ plan,
4588
+ geometry: imageGeometry
4589
+ },
4590
+ plan.index
4591
+ );
4592
+ }
4369
4593
  if (!plan || plan.texture) return null;
4370
4594
  if ((0, import_polycss_core12.isProjectiveQuadPlan)(plan)) {
4371
4595
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
@@ -4586,6 +4810,7 @@ function usePolyMaterial(options) {
4586
4810
 
4587
4811
  // src/shapes/Poly.tsx
4588
4812
  var import_react19 = require("react");
4813
+ var import_polycss_core19 = require("@layoutit/polycss-core");
4589
4814
  var import_jsx_runtime12 = require("react/jsx-runtime");
4590
4815
  var DIRECT_TEXTURE_CSS_DECIMALS = 4;
4591
4816
  function formatCssLength(value, decimals = DIRECT_TEXTURE_CSS_DECIMALS) {
@@ -4616,7 +4841,8 @@ function MaterialDirectPoly({
4616
4841
  style: styleProp,
4617
4842
  domAttrs,
4618
4843
  domEventHandlers,
4619
- pointerEvents = "auto"
4844
+ pointerEvents = "auto",
4845
+ imageRendering
4620
4846
  }) {
4621
4847
  const { u0, u1, v0, v1 } = uvRect;
4622
4848
  const du = u1 - u0;
@@ -4631,6 +4857,8 @@ function MaterialDirectPoly({
4631
4857
  backgroundImage: `url(${material.texture})`,
4632
4858
  backgroundSize: `${formatCssLength(sourceW)} ${formatCssLength(sourceH)}`,
4633
4859
  backgroundPosition: `${formatCssLength(-offsetX)} ${formatCssLength(-offsetY)}`,
4860
+ backgroundRepeat: "no-repeat",
4861
+ imageRendering: imageRendering === "pixelated" ? "pixelated" : void 0,
4634
4862
  pointerEvents: pointerEvents === "none" ? "none" : void 0,
4635
4863
  ...styleProp
4636
4864
  };
@@ -4643,6 +4871,14 @@ function MaterialDirectPoly({
4643
4871
  {
4644
4872
  className: elementClassName,
4645
4873
  style,
4874
+ "data-poly-index": plan.index,
4875
+ "data-polycss-leaf": "polygon",
4876
+ "data-polycss-texture-backend": "image",
4877
+ "data-polycss-texture-ready": "true",
4878
+ "data-polycss-texture-image-rendering": imageRendering ?? "auto",
4879
+ "data-polycss-texture-projection": "affine",
4880
+ "data-polycss-texture-lighting": "source",
4881
+ "data-polycss-double-sided": plan.polygon.doubleSided ? "true" : void 0,
4646
4882
  ...domEventHandlers,
4647
4883
  ...dataAttrs,
4648
4884
  ...domAttrs
@@ -4653,8 +4889,11 @@ function PolyInner({
4653
4889
  vertices,
4654
4890
  color,
4655
4891
  texture,
4892
+ textureImageSource,
4893
+ texturePresentation,
4656
4894
  uvs,
4657
4895
  data,
4896
+ doubleSided,
4658
4897
  material,
4659
4898
  position,
4660
4899
  scale,
@@ -4682,6 +4921,10 @@ function PolyInner({
4682
4921
  context,
4683
4922
  textureLighting: textureLightingProp,
4684
4923
  textureQuality: textureQualityProp,
4924
+ textureLeafSizing: textureLeafSizingProp,
4925
+ textureImageRendering: textureImageRenderingProp,
4926
+ textureBackend: textureBackendProp,
4927
+ textureProjection: textureProjectionProp,
4685
4928
  baseColor: baseColorProp,
4686
4929
  ...dataAttrs
4687
4930
  }) {
@@ -4689,11 +4932,25 @@ function PolyInner({
4689
4932
  const layerElevation = context?.layerElevation ?? tileSize;
4690
4933
  const textureLighting = textureLightingProp ?? context?.textureLighting ?? "baked";
4691
4934
  const textureQuality = textureQualityProp ?? context?.textureQuality;
4935
+ const textureLeafSizing = textureLeafSizingProp ?? context?.textureLeafSizing;
4936
+ const textureImageRendering = textureImageRenderingProp ?? context?.textureImageRendering;
4937
+ const textureBackend = textureBackendProp ?? context?.textureBackend;
4938
+ const textureProjection = textureProjectionProp ?? context?.textureProjection;
4692
4939
  const polygonColor = baseColorProp ?? color;
4693
4940
  const effectiveTexture = material?.texture ?? texture;
4694
4941
  const atlasPlan = (0, import_react19.useMemo)(
4695
4942
  () => computeTextureAtlasPlan(
4696
- { vertices, color: polygonColor, texture: effectiveTexture, uvs, data },
4943
+ {
4944
+ vertices,
4945
+ color: polygonColor,
4946
+ texture: effectiveTexture,
4947
+ textureImageSource,
4948
+ texturePresentation,
4949
+ uvs,
4950
+ data,
4951
+ doubleSided,
4952
+ material
4953
+ },
4697
4954
  0,
4698
4955
  {
4699
4956
  tileSize,
@@ -4705,8 +4962,12 @@ function PolyInner({
4705
4962
  vertices,
4706
4963
  polygonColor,
4707
4964
  effectiveTexture,
4965
+ textureImageSource,
4966
+ texturePresentation,
4708
4967
  uvs,
4709
4968
  data,
4969
+ doubleSided,
4970
+ material,
4710
4971
  tileSize,
4711
4972
  layerElevation,
4712
4973
  context?.directionalLight
@@ -4720,7 +4981,15 @@ function PolyInner({
4720
4981
  () => materialUvRect ? [] : [atlasPlan],
4721
4982
  [materialUvRect, atlasPlan]
4722
4983
  );
4723
- const textureAtlas = useTextureAtlas(atlasPlans, textureLighting, textureQuality);
4984
+ const textureAtlas = useTextureAtlas(
4985
+ atlasPlans,
4986
+ textureLighting,
4987
+ textureQuality,
4988
+ textureLeafSizing,
4989
+ textureBackend,
4990
+ textureImageRendering,
4991
+ textureProjection
4992
+ );
4724
4993
  const domEventHandlers = {
4725
4994
  onClick,
4726
4995
  onDoubleClick,
@@ -4779,7 +5048,8 @@ function PolyInner({
4779
5048
  style: styleProp,
4780
5049
  domAttrs,
4781
5050
  domEventHandlers,
4782
- pointerEvents: pointerEventsProp ?? "auto"
5051
+ pointerEvents: pointerEventsProp ?? "auto",
5052
+ imageRendering: textureImageRendering
4783
5053
  }
4784
5054
  );
4785
5055
  } else {
@@ -4791,6 +5061,7 @@ function PolyInner({
4791
5061
  entry: atlasEntry,
4792
5062
  page: textureAtlas.pages[atlasEntry.pageIndex],
4793
5063
  textureLighting,
5064
+ textureImageRendering,
4794
5065
  className,
4795
5066
  style: styleProp,
4796
5067
  domAttrs,
@@ -4798,7 +5069,28 @@ function PolyInner({
4798
5069
  pointerEvents: pointerEventsProp ?? "auto"
4799
5070
  }
4800
5071
  );
4801
- } else if (atlasPlan && !atlasPlan.texture) {
5072
+ } else {
5073
+ const imageGeometry = atlasPlan ? (0, import_polycss_core19.resolvePolyTextureLeafGeometry)(atlasPlan, {
5074
+ imageRendering: textureImageRendering,
5075
+ backend: textureBackend,
5076
+ projection: textureProjection
5077
+ }) : null;
5078
+ if (atlasPlan && imageGeometry) {
5079
+ front = /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
5080
+ TextureImagePoly,
5081
+ {
5082
+ plan: atlasPlan,
5083
+ geometry: imageGeometry,
5084
+ className,
5085
+ style: styleProp,
5086
+ domAttrs,
5087
+ domEventHandlers,
5088
+ pointerEvents: pointerEventsProp ?? "auto"
5089
+ }
5090
+ );
5091
+ }
5092
+ }
5093
+ if (!front && atlasPlan && !atlasPlan.texture) {
4802
5094
  front = (0, import_polycss_core12.isSolidTrianglePlan)(atlasPlan) ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
4803
5095
  TextureTrianglePoly,
4804
5096
  {
@@ -4854,7 +5146,7 @@ var Poly = (0, import_react19.memo)(PolyInner);
4854
5146
 
4855
5147
  // src/shapes/PolyShapes.tsx
4856
5148
  var import_react20 = require("react");
4857
- var import_polycss_core19 = require("@layoutit/polycss-core");
5149
+ var import_polycss_core20 = require("@layoutit/polycss-core");
4858
5150
  var import_jsx_runtime13 = require("react/jsx-runtime");
4859
5151
  function PolyBox({
4860
5152
  size,
@@ -4870,7 +5162,7 @@ function PolyBox({
4870
5162
  ...meshProps
4871
5163
  }) {
4872
5164
  const polygons = (0, import_react20.useMemo)(
4873
- () => (0, import_polycss_core19.boxPolygons)({ size, center, min, max, color, texture, material, uvs, data, faces }),
5165
+ () => (0, import_polycss_core20.boxPolygons)({ size, center, min, max, color, texture, material, uvs, data, faces }),
4874
5166
  // eslint-disable-next-line react-hooks/exhaustive-deps
4875
5167
  [size, center, min, max, color, texture, material, uvs, data, faces]
4876
5168
  );
@@ -4885,7 +5177,7 @@ function PolyPlane({
4885
5177
  ...meshProps
4886
5178
  }) {
4887
5179
  const polygons = (0, import_react20.useMemo)(
4888
- () => (0, import_polycss_core19.planePolygons)({ axis, size, offset, along, color }),
5180
+ () => (0, import_polycss_core20.planePolygons)({ axis, size, offset, along, color }),
4889
5181
  // eslint-disable-next-line react-hooks/exhaustive-deps
4890
5182
  [axis, size, offset, along, color]
4891
5183
  );
@@ -4900,7 +5192,7 @@ function PolyRing({
4900
5192
  ...meshProps
4901
5193
  }) {
4902
5194
  const polygons = (0, import_react20.useMemo)(
4903
- () => (0, import_polycss_core19.ringPolygons)({ axis, radius, halfThickness, segments, color }),
5195
+ () => (0, import_polycss_core20.ringPolygons)({ axis, radius, halfThickness, segments, color }),
4904
5196
  // eslint-disable-next-line react-hooks/exhaustive-deps
4905
5197
  [axis, radius, halfThickness, segments, color]
4906
5198
  );
@@ -4913,7 +5205,7 @@ function PolyOctahedron({
4913
5205
  ...meshProps
4914
5206
  }) {
4915
5207
  const polygons = (0, import_react20.useMemo)(
4916
- () => (0, import_polycss_core19.octahedronPolygons)({ center, size, color }),
5208
+ () => (0, import_polycss_core20.octahedronPolygons)({ center, size, color }),
4917
5209
  // eslint-disable-next-line react-hooks/exhaustive-deps
4918
5210
  [center, size, color]
4919
5211
  );
@@ -4925,7 +5217,7 @@ function PolyTetrahedron({
4925
5217
  ...meshProps
4926
5218
  }) {
4927
5219
  const polygons = (0, import_react20.useMemo)(
4928
- () => (0, import_polycss_core19.tetrahedronPolygons)({ size, color }),
5220
+ () => (0, import_polycss_core20.tetrahedronPolygons)({ size, color }),
4929
5221
  // eslint-disable-next-line react-hooks/exhaustive-deps
4930
5222
  [size, color]
4931
5223
  );
@@ -4937,7 +5229,7 @@ function PolyIcosahedron({
4937
5229
  ...meshProps
4938
5230
  }) {
4939
5231
  const polygons = (0, import_react20.useMemo)(
4940
- () => (0, import_polycss_core19.icosahedronPolygons)({ size, color }),
5232
+ () => (0, import_polycss_core20.icosahedronPolygons)({ size, color }),
4941
5233
  // eslint-disable-next-line react-hooks/exhaustive-deps
4942
5234
  [size, color]
4943
5235
  );
@@ -4949,7 +5241,7 @@ function PolyDodecahedron({
4949
5241
  ...meshProps
4950
5242
  }) {
4951
5243
  const polygons = (0, import_react20.useMemo)(
4952
- () => (0, import_polycss_core19.dodecahedronPolygons)({ size, color }),
5244
+ () => (0, import_polycss_core20.dodecahedronPolygons)({ size, color }),
4953
5245
  // eslint-disable-next-line react-hooks/exhaustive-deps
4954
5246
  [size, color]
4955
5247
  );
@@ -4962,7 +5254,7 @@ function PolySphere({
4962
5254
  ...meshProps
4963
5255
  }) {
4964
5256
  const polygons = (0, import_react20.useMemo)(
4965
- () => (0, import_polycss_core19.spherePolygons)({ radius, subdivisions, color }),
5257
+ () => (0, import_polycss_core20.spherePolygons)({ radius, subdivisions, color }),
4966
5258
  // eslint-disable-next-line react-hooks/exhaustive-deps
4967
5259
  [radius, subdivisions, color]
4968
5260
  );
@@ -4977,7 +5269,7 @@ function PolyCylinder({
4977
5269
  ...meshProps
4978
5270
  }) {
4979
5271
  const polygons = (0, import_react20.useMemo)(
4980
- () => (0, import_polycss_core19.cylinderPolygons)({ radius, radiusTop, height, radialSegments, color }),
5272
+ () => (0, import_polycss_core20.cylinderPolygons)({ radius, radiusTop, height, radialSegments, color }),
4981
5273
  // eslint-disable-next-line react-hooks/exhaustive-deps
4982
5274
  [radius, radiusTop, height, radialSegments, color]
4983
5275
  );
@@ -4991,7 +5283,7 @@ function PolyCone({
4991
5283
  ...meshProps
4992
5284
  }) {
4993
5285
  const polygons = (0, import_react20.useMemo)(
4994
- () => (0, import_polycss_core19.conePolygons)({ radius, height, radialSegments, color }),
5286
+ () => (0, import_polycss_core20.conePolygons)({ radius, height, radialSegments, color }),
4995
5287
  // eslint-disable-next-line react-hooks/exhaustive-deps
4996
5288
  [radius, height, radialSegments, color]
4997
5289
  );
@@ -5006,7 +5298,7 @@ function PolyTorus({
5006
5298
  ...meshProps
5007
5299
  }) {
5008
5300
  const polygons = (0, import_react20.useMemo)(
5009
- () => (0, import_polycss_core19.torusPolygons)({ radius, tube, radialSegments, tubularSegments, color }),
5301
+ () => (0, import_polycss_core20.torusPolygons)({ radius, tube, radialSegments, tubularSegments, color }),
5010
5302
  // eslint-disable-next-line react-hooks/exhaustive-deps
5011
5303
  [radius, tube, radialSegments, tubularSegments, color]
5012
5304
  );
@@ -5015,7 +5307,7 @@ function PolyTorus({
5015
5307
 
5016
5308
  // src/controls/PolyFirstPersonControls.tsx
5017
5309
  var import_react21 = require("react");
5018
- var import_polycss_core20 = require("@layoutit/polycss-core");
5310
+ var import_polycss_core21 = require("@layoutit/polycss-core");
5019
5311
  var DEFAULTS = {
5020
5312
  enabled: true,
5021
5313
  lookEnabled: true,
@@ -5191,7 +5483,7 @@ var PolyFirstPersonControls = (0, import_react21.forwardRef)(function PolyFirstP
5191
5483
  const host = cameraElRef.current;
5192
5484
  const perspStr = host ? getComputedStyle(host).perspective : "";
5193
5485
  const n = parseFloat(perspStr);
5194
- return (Number.isFinite(n) && n > 0 ? n : 32e3) / import_polycss_core20.BASE_TILE;
5486
+ return (Number.isFinite(n) && n > 0 ? n : 32e3) / import_polycss_core21.BASE_TILE;
5195
5487
  }
5196
5488
  function deriveTarget() {
5197
5489
  const s = cameraRef.current.state;
@@ -5432,7 +5724,7 @@ var PolyFirstPersonControls = (0, import_react21.forwardRef)(function PolyFirstP
5432
5724
  var import_react22 = require("react");
5433
5725
 
5434
5726
  // src/controls/sharedControls.ts
5435
- var import_polycss_core21 = require("@layoutit/polycss-core");
5727
+ var import_polycss_core22 = require("@layoutit/polycss-core");
5436
5728
  var POINTER_DRAG_SPEED = 4;
5437
5729
  function invertFactor(invert) {
5438
5730
  if (invert === true) return -1;
@@ -5453,7 +5745,7 @@ function applyPan(dx, dy, s, handle, _invert) {
5453
5745
  const cosRotX = cosRotXRaw >= 0 ? Math.max(0.1, cosRotXRaw) : Math.min(-0.1, cosRotXRaw);
5454
5746
  const cZ = Math.cos(s.rotY * Math.PI / 180);
5455
5747
  const sZ = Math.sin(s.rotY * Math.PI / 180);
5456
- const k = z * import_polycss_core21.BASE_TILE;
5748
+ const k = z * import_polycss_core22.BASE_TILE;
5457
5749
  const targetD0 = (dx * sZ - dy * cZ / cosRotX) / k;
5458
5750
  const targetD1 = -(dx * cZ + dy * sZ / cosRotX) / k;
5459
5751
  const t = s.target;
@@ -5983,9 +6275,9 @@ function PolyOrbitControls({
5983
6275
 
5984
6276
  // src/controls/TransformControls.tsx
5985
6277
  var import_react24 = require("react");
5986
- var import_polycss_core22 = require("@layoutit/polycss-core");
6278
+ var import_polycss_core23 = require("@layoutit/polycss-core");
5987
6279
  var import_jsx_runtime14 = require("react/jsx-runtime");
5988
- var FALLBACK_CAMERA_STORE = createSceneStore(import_polycss_core22.DEFAULT_CAMERA_STATE);
6280
+ var FALLBACK_CAMERA_STORE = createSceneStore(import_polycss_core23.DEFAULT_CAMERA_STATE);
5989
6281
  var COLOR_X = "#ff3653";
5990
6282
  var COLOR_Y = "#8adb00";
5991
6283
  var COLOR_Z = "#2c8fff";
@@ -6262,7 +6554,7 @@ function startRingDrag(opts) {
6262
6554
  const startRotation = target.getRotation() ?? [0, 0, 0];
6263
6555
  onMouseDown?.();
6264
6556
  onDraggingChanged?.(true);
6265
- const qStart = (0, import_polycss_core22.quatFromEulerXYZ)(startRotation);
6557
+ const qStart = (0, import_polycss_core23.quatFromEulerXYZ)(startRotation);
6266
6558
  const handleMove = (ev) => {
6267
6559
  const a = Math.atan2(ev.clientY - centerY, ev.clientX - centerX);
6268
6560
  let d = a - lastAngle;
@@ -6274,8 +6566,8 @@ function startRingDrag(opts) {
6274
6566
  degrees = snap(degrees, rotationSnap);
6275
6567
  const axisVec = [0, 0, 0];
6276
6568
  axisVec[cssAxis] = 1;
6277
- const qDelta = (0, import_polycss_core22.quatFromAxisAngle)(axisVec, degrees * Math.PI / 180);
6278
- const newRotation = (0, import_polycss_core22.eulerXYZFromQuat)((0, import_polycss_core22.quatMultiply)(qDelta, qStart));
6569
+ const qDelta = (0, import_polycss_core23.quatFromAxisAngle)(axisVec, degrees * Math.PI / 180);
6570
+ const newRotation = (0, import_polycss_core23.eulerXYZFromQuat)((0, import_polycss_core23.quatMultiply)(qDelta, qStart));
6279
6571
  onObjectChange?.({ object: target, rotation: newRotation });
6280
6572
  onChange?.();
6281
6573
  };
@@ -6646,7 +6938,7 @@ function TranslateArrow({
6646
6938
  };
6647
6939
  const polygons = (0, import_react24.useMemo)(() => {
6648
6940
  const lengthWorld = shaftLengthCss / SCENE_TILE_SIZE;
6649
- return (0, import_polycss_core22.arrowPolygons)({
6941
+ return (0, import_polycss_core23.arrowPolygons)({
6650
6942
  axis: WORLD_AXIS_FOR_CSS[cssAxis],
6651
6943
  sign,
6652
6944
  shaftLength: lengthWorld,
@@ -6751,7 +7043,7 @@ function TranslatePlane({
6751
7043
  const signA = isAxisBackFacing(cssAForOffset, 1, rotX, rotY) ? -1 : 1;
6752
7044
  const signB = isAxisBackFacing(cssBForOffset, 1, rotX, rotY) ? -1 : 1;
6753
7045
  const mag = lengthWorld * PLANE_OFFSET_RATIO;
6754
- return (0, import_polycss_core22.planePolygons)({
7046
+ return (0, import_polycss_core23.planePolygons)({
6755
7047
  axis: worldPerp,
6756
7048
  size: lengthWorld * PLANE_HALF_SIZE_RATIO,
6757
7049
  offset: [signA * mag, signB * mag],
@@ -6841,7 +7133,7 @@ function RotateRing({
6841
7133
  const polygons = (0, import_react24.useMemo)(() => {
6842
7134
  const radiusWorld = radiusCss / SCENE_TILE_SIZE;
6843
7135
  const outerWorld = radiusWorld * RING_QUAD_OUTER_RATIO;
6844
- return (0, import_polycss_core22.ringQuadPolygons)({
7136
+ return (0, import_polycss_core23.ringQuadPolygons)({
6845
7137
  axis: WORLD_AXIS_FOR_CSS[cssAxis],
6846
7138
  outerRadius: outerWorld,
6847
7139
  color
@@ -7039,7 +7331,7 @@ function usePolySelectionApi() {
7039
7331
 
7040
7332
  // src/helpers/PolyAxesHelper.tsx
7041
7333
  var import_react26 = require("react");
7042
- var import_polycss_core23 = require("@layoutit/polycss-core");
7334
+ var import_polycss_core24 = require("@layoutit/polycss-core");
7043
7335
  var import_jsx_runtime16 = require("react/jsx-runtime");
7044
7336
  function PolyAxesHelper({
7045
7337
  size,
@@ -7050,7 +7342,7 @@ function PolyAxesHelper({
7050
7342
  zColor
7051
7343
  }) {
7052
7344
  const polygons = (0, import_react26.useMemo)(
7053
- () => (0, import_polycss_core23.axesHelperPolygons)({ size, thickness, negative, xColor, yColor, zColor }),
7345
+ () => (0, import_polycss_core24.axesHelperPolygons)({ size, thickness, negative, xColor, yColor, zColor }),
7054
7346
  [size, thickness, negative, xColor, yColor, zColor]
7055
7347
  );
7056
7348
  return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(PolyMesh, { polygons, merge: false });
@@ -7058,7 +7350,7 @@ function PolyAxesHelper({
7058
7350
 
7059
7351
  // src/helpers/PolyDirectionalLightHelper.tsx
7060
7352
  var import_react27 = require("react");
7061
- var import_polycss_core24 = require("@layoutit/polycss-core");
7353
+ var import_polycss_core25 = require("@layoutit/polycss-core");
7062
7354
  var import_jsx_runtime17 = require("react/jsx-runtime");
7063
7355
  function PolyDirectionalLightHelper({
7064
7356
  light,
@@ -7069,7 +7361,7 @@ function PolyDirectionalLightHelper({
7069
7361
  }) {
7070
7362
  const swatch = color ?? light.color ?? "#ffd54a";
7071
7363
  const polygons = (0, import_react27.useMemo)(
7072
- () => (0, import_polycss_core24.octahedronPolygons)({ center: [0, 0, 0], size, color: swatch }),
7364
+ () => (0, import_polycss_core25.octahedronPolygons)({ center: [0, 0, 0], size, color: swatch }),
7073
7365
  [size, swatch]
7074
7366
  );
7075
7367
  const meshPosition = (0, import_react27.useMemo)(() => {
@@ -7088,7 +7380,7 @@ function PolyDirectionalLightHelper({
7088
7380
  }
7089
7381
 
7090
7382
  // src/styles/colorResolver.ts
7091
- var import_polycss_core25 = require("@layoutit/polycss-core");
7383
+ var import_polycss_core26 = require("@layoutit/polycss-core");
7092
7384
 
7093
7385
  // src/renderStats.ts
7094
7386
  var ZERO_SURFACE_LEAF_COUNTS = {
@@ -7097,11 +7389,56 @@ var ZERO_SURFACE_LEAF_COUNTS = {
7097
7389
  atlas: 0,
7098
7390
  stableTriangle: 0
7099
7391
  };
7392
+ var READY_TEXTURE_READINESS = {
7393
+ ready: true,
7394
+ textureLeafCount: 0,
7395
+ readyTextureLeafCount: 0,
7396
+ pendingTextureLeafCount: 0,
7397
+ atlasTextureLeafCount: 0,
7398
+ imageTextureLeafCount: 0
7399
+ };
7400
+ function emptyTextureStats() {
7401
+ return {
7402
+ ready: true,
7403
+ leafCount: 0,
7404
+ atlasCount: 0,
7405
+ imageCount: 0,
7406
+ pendingCount: 0,
7407
+ sizingCounts: { canonical: 0, local: 0, raster: 0, image: 0, unknown: 0 },
7408
+ imageRenderingCounts: { auto: 0, pixelated: 0, unknown: 0 },
7409
+ projectionCounts: { affine: 0, projective: 0, fallback: 0, unknown: 0 },
7410
+ minLeafWidth: 0,
7411
+ maxLeafWidth: 0,
7412
+ minLeafHeight: 0,
7413
+ maxLeafHeight: 0,
7414
+ doubleSidedCount: 0
7415
+ };
7416
+ }
7417
+ function emptySnapshotStats() {
7418
+ return {
7419
+ runtimeAtlasUrlCount: 0,
7420
+ snapshotAtlasCount: 0,
7421
+ snapshotBackgroundCount: 0,
7422
+ selfContained: true
7423
+ };
7424
+ }
7425
+ function emptyCameraStats() {
7426
+ return {
7427
+ rootFound: false,
7428
+ projection: "unknown",
7429
+ perspectiveStyle: "",
7430
+ appliedPerspectiveStyle: ""
7431
+ };
7432
+ }
7100
7433
  var EMPTY_POLY_RENDER_STATS = {
7101
7434
  polygonCount: 0,
7102
7435
  mountedPolygonLeafCount: 0,
7103
7436
  shadowLeafCount: 0,
7104
7437
  surfaceLeafCounts: ZERO_SURFACE_LEAF_COUNTS,
7438
+ textureReadiness: READY_TEXTURE_READINESS,
7439
+ textureStats: emptyTextureStats(),
7440
+ snapshotStats: emptySnapshotStats(),
7441
+ cameraStats: emptyCameraStats(),
7105
7442
  bucketCount: 0
7106
7443
  };
7107
7444
  function asOptions(optionsOrPolygonCount) {
@@ -7113,6 +7450,28 @@ function asOptions(optionsOrPolygonCount) {
7113
7450
  function queryCount(scope, selector) {
7114
7451
  return scope.querySelectorAll(selector).length;
7115
7452
  }
7453
+ function matchingElementCount(scope, selector) {
7454
+ return (matchesSelector(scope, selector) ? 1 : 0) + queryCount(scope, selector);
7455
+ }
7456
+ function styleHasRuntimeUrl(styleText) {
7457
+ if (!styleText) return false;
7458
+ const urlPattern = /url\((?:"([^"]*)"|'([^']*)'|([^)]*))\)/g;
7459
+ let match;
7460
+ while (match = urlPattern.exec(styleText)) {
7461
+ const raw = (match[1] ?? match[2] ?? match[3] ?? "").trim();
7462
+ if (raw && !raw.startsWith("data:")) return true;
7463
+ }
7464
+ return false;
7465
+ }
7466
+ function runtimeUrlElementCount(scope) {
7467
+ let count = 0;
7468
+ const root = scope instanceof HTMLElement ? scope : null;
7469
+ if (root && styleHasRuntimeUrl(root.getAttribute("style"))) count++;
7470
+ for (const el of Array.from(scope.querySelectorAll("[style]"))) {
7471
+ if (el instanceof HTMLElement && styleHasRuntimeUrl(el.getAttribute("style"))) count++;
7472
+ }
7473
+ return count;
7474
+ }
7116
7475
  function matchesSelector(root, selector) {
7117
7476
  const candidate = root;
7118
7477
  return typeof candidate.matches === "function" && candidate.matches(selector);
@@ -7124,12 +7483,196 @@ function collectScopes(root, selector) {
7124
7483
  scopes.push(...Array.from(root.querySelectorAll(selector)));
7125
7484
  return scopes;
7126
7485
  }
7486
+ function leafStrategyForTag(tagName) {
7487
+ switch (tagName.toLowerCase()) {
7488
+ case "b":
7489
+ return "quad";
7490
+ case "i":
7491
+ return "clippedSolid";
7492
+ case "s":
7493
+ return "atlas";
7494
+ case "u":
7495
+ return "stableTriangle";
7496
+ default:
7497
+ return null;
7498
+ }
7499
+ }
7500
+ function parseOptionalInteger(value) {
7501
+ if (value == null || value === "") return void 0;
7502
+ const parsed = Number.parseInt(value, 10);
7503
+ return Number.isFinite(parsed) ? parsed : void 0;
7504
+ }
7505
+ function parseOptionalNumber(value) {
7506
+ if (value == null || value === "") return void 0;
7507
+ const parsed = Number(value);
7508
+ return Number.isFinite(parsed) ? parsed : void 0;
7509
+ }
7510
+ function parseOptionalVec3(value) {
7511
+ if (value == null || value === "") return void 0;
7512
+ const parts = value.split(",").map((part) => Number(part));
7513
+ if (parts.length !== 3 || parts.some((part) => !Number.isFinite(part))) return void 0;
7514
+ return parts;
7515
+ }
7516
+ function parseCameraProjection(value) {
7517
+ return value === "orthographic" || value === "perspective" ? value : "unknown";
7518
+ }
7519
+ function textureReadyForElement(el, textureBackend) {
7520
+ if (!textureBackend) return void 0;
7521
+ const attr = el.getAttribute("data-polycss-texture-ready");
7522
+ if (attr === "true") return true;
7523
+ if (attr === "false") return false;
7524
+ return el.style.opacity !== "0";
7525
+ }
7526
+ function queryPolyLeaves(root, optionsOrPolygonCount) {
7527
+ if (!root) return [];
7528
+ const options = asOptions(optionsOrPolygonCount);
7529
+ const out = [];
7530
+ for (const scope of collectScopes(root, options.scopeSelector)) {
7531
+ for (const el of Array.from(scope.querySelectorAll("b,i,s,u"))) {
7532
+ const htmlEl = el;
7533
+ const strategy = leafStrategyForTag(htmlEl.tagName);
7534
+ if (!strategy) continue;
7535
+ const textureBackend = htmlEl.getAttribute("data-polycss-texture-backend") ?? (strategy === "atlas" ? "atlas" : void 0);
7536
+ const textureReady = textureReadyForElement(htmlEl, textureBackend);
7537
+ out.push({
7538
+ element: htmlEl,
7539
+ strategy,
7540
+ polygonIndex: parseOptionalInteger(htmlEl.getAttribute("data-poly-index")),
7541
+ textureBackend,
7542
+ textureReady,
7543
+ textureLeafSizing: htmlEl.getAttribute("data-polycss-texture-leaf-sizing") ?? void 0,
7544
+ textureImageRendering: htmlEl.getAttribute("data-polycss-texture-image-rendering") ?? void 0,
7545
+ textureProjection: htmlEl.getAttribute("data-polycss-texture-projection") ?? void 0,
7546
+ textureLeafWidth: parseOptionalNumber(htmlEl.getAttribute("data-polycss-texture-leaf-width")),
7547
+ textureLeafHeight: parseOptionalNumber(htmlEl.getAttribute("data-polycss-texture-leaf-height")),
7548
+ doubleSided: htmlEl.getAttribute("data-polycss-double-sided") === "true"
7549
+ });
7550
+ }
7551
+ }
7552
+ return out;
7553
+ }
7554
+ function collectPolyTextureReadiness(root, optionsOrPolygonCount) {
7555
+ const readiness = { ...READY_TEXTURE_READINESS };
7556
+ for (const leaf of queryPolyLeaves(root, optionsOrPolygonCount)) {
7557
+ if (!leaf.textureBackend) continue;
7558
+ readiness.textureLeafCount++;
7559
+ if (leaf.textureBackend === "image") readiness.imageTextureLeafCount++;
7560
+ else readiness.atlasTextureLeafCount++;
7561
+ if (leaf.textureReady === false) readiness.pendingTextureLeafCount++;
7562
+ else readiness.readyTextureLeafCount++;
7563
+ }
7564
+ readiness.ready = readiness.pendingTextureLeafCount === 0;
7565
+ return readiness;
7566
+ }
7567
+ function collectPolyTextureStats(root, optionsOrPolygonCount) {
7568
+ const stats = emptyTextureStats();
7569
+ let hasWidth = false;
7570
+ let hasHeight = false;
7571
+ for (const leaf of queryPolyLeaves(root, optionsOrPolygonCount)) {
7572
+ if (!leaf.textureBackend) continue;
7573
+ stats.leafCount++;
7574
+ if (leaf.textureBackend === "image") stats.imageCount++;
7575
+ else stats.atlasCount++;
7576
+ if (leaf.textureReady === false) stats.pendingCount++;
7577
+ switch (leaf.textureLeafSizing) {
7578
+ case "canonical":
7579
+ case "local":
7580
+ case "raster":
7581
+ case "image":
7582
+ stats.sizingCounts[leaf.textureLeafSizing]++;
7583
+ break;
7584
+ default:
7585
+ stats.sizingCounts.unknown++;
7586
+ break;
7587
+ }
7588
+ switch (leaf.textureImageRendering) {
7589
+ case "auto":
7590
+ case "pixelated":
7591
+ stats.imageRenderingCounts[leaf.textureImageRendering]++;
7592
+ break;
7593
+ default:
7594
+ stats.imageRenderingCounts.unknown++;
7595
+ break;
7596
+ }
7597
+ switch (leaf.textureProjection) {
7598
+ case "affine":
7599
+ case "projective":
7600
+ case "fallback":
7601
+ stats.projectionCounts[leaf.textureProjection]++;
7602
+ break;
7603
+ default:
7604
+ stats.projectionCounts.unknown++;
7605
+ break;
7606
+ }
7607
+ if (typeof leaf.textureLeafWidth === "number") {
7608
+ stats.minLeafWidth = hasWidth ? Math.min(stats.minLeafWidth, leaf.textureLeafWidth) : leaf.textureLeafWidth;
7609
+ stats.maxLeafWidth = hasWidth ? Math.max(stats.maxLeafWidth, leaf.textureLeafWidth) : leaf.textureLeafWidth;
7610
+ hasWidth = true;
7611
+ }
7612
+ if (typeof leaf.textureLeafHeight === "number") {
7613
+ stats.minLeafHeight = hasHeight ? Math.min(stats.minLeafHeight, leaf.textureLeafHeight) : leaf.textureLeafHeight;
7614
+ stats.maxLeafHeight = hasHeight ? Math.max(stats.maxLeafHeight, leaf.textureLeafHeight) : leaf.textureLeafHeight;
7615
+ hasHeight = true;
7616
+ }
7617
+ if (leaf.doubleSided) stats.doubleSidedCount++;
7618
+ }
7619
+ stats.pendingCount = Math.min(stats.pendingCount, stats.leafCount);
7620
+ stats.ready = stats.pendingCount === 0;
7621
+ return stats;
7622
+ }
7623
+ function collectPolySnapshotStats(root, optionsOrPolygonCount) {
7624
+ const stats = emptySnapshotStats();
7625
+ if (!root) return stats;
7626
+ const options = asOptions(optionsOrPolygonCount);
7627
+ for (const scope of collectScopes(root, options.scopeSelector)) {
7628
+ stats.runtimeAtlasUrlCount += runtimeUrlElementCount(scope);
7629
+ stats.snapshotAtlasCount += matchingElementCount(scope, "[data-polycss-snapshot-atlas]");
7630
+ stats.snapshotBackgroundCount += matchingElementCount(scope, "[data-polycss-snapshot-bg]");
7631
+ }
7632
+ stats.selfContained = stats.runtimeAtlasUrlCount === 0;
7633
+ return stats;
7634
+ }
7635
+ function findCameraRoot(scope) {
7636
+ const element = scope instanceof HTMLElement ? scope : null;
7637
+ if (element) {
7638
+ if (element.classList.contains("polycss-camera")) return element;
7639
+ const closest = element.closest(".polycss-camera");
7640
+ if (closest instanceof HTMLElement) return closest;
7641
+ }
7642
+ const found = scope.querySelector(".polycss-camera");
7643
+ return found instanceof HTMLElement ? found : null;
7644
+ }
7645
+ function collectPolyCameraStats(root, optionsOrPolygonCount) {
7646
+ if (!root) return emptyCameraStats();
7647
+ const options = asOptions(optionsOrPolygonCount);
7648
+ let cameraRoot = null;
7649
+ for (const scope of collectScopes(root, options.scopeSelector)) {
7650
+ cameraRoot = findCameraRoot(scope);
7651
+ if (cameraRoot) break;
7652
+ }
7653
+ if (!cameraRoot) return emptyCameraStats();
7654
+ return {
7655
+ rootFound: true,
7656
+ projection: parseCameraProjection(cameraRoot.getAttribute("data-polycss-camera-projection")),
7657
+ perspectiveStyle: cameraRoot.getAttribute("data-polycss-camera-perspective") ?? "",
7658
+ appliedPerspectiveStyle: cameraRoot.getAttribute("data-polycss-camera-applied-perspective") ?? cameraRoot.style.perspective ?? "",
7659
+ zoom: parseOptionalNumber(cameraRoot.getAttribute("data-polycss-camera-zoom")),
7660
+ distance: parseOptionalNumber(cameraRoot.getAttribute("data-polycss-camera-distance")),
7661
+ rotX: parseOptionalNumber(cameraRoot.getAttribute("data-polycss-camera-rot-x")),
7662
+ rotY: parseOptionalNumber(cameraRoot.getAttribute("data-polycss-camera-rot-y")),
7663
+ target: parseOptionalVec3(cameraRoot.getAttribute("data-polycss-camera-target"))
7664
+ };
7665
+ }
7127
7666
  function collectPolyRenderStats(root, optionsOrPolygonCount) {
7128
7667
  const options = asOptions(optionsOrPolygonCount);
7129
7668
  if (!root) {
7130
7669
  return {
7131
7670
  ...EMPTY_POLY_RENDER_STATS,
7132
7671
  surfaceLeafCounts: { ...ZERO_SURFACE_LEAF_COUNTS },
7672
+ textureReadiness: { ...READY_TEXTURE_READINESS },
7673
+ textureStats: emptyTextureStats(),
7674
+ snapshotStats: emptySnapshotStats(),
7675
+ cameraStats: emptyCameraStats(),
7133
7676
  polygonCount: options.polygonCount ?? 0
7134
7677
  };
7135
7678
  }
@@ -7151,13 +7694,17 @@ function collectPolyRenderStats(root, optionsOrPolygonCount) {
7151
7694
  mountedPolygonLeafCount,
7152
7695
  shadowLeafCount,
7153
7696
  surfaceLeafCounts,
7697
+ textureReadiness: collectPolyTextureReadiness(root, options),
7698
+ textureStats: collectPolyTextureStats(root, options),
7699
+ snapshotStats: collectPolySnapshotStats(root, options),
7700
+ cameraStats: collectPolyCameraStats(root, options),
7154
7701
  bucketCount
7155
7702
  };
7156
7703
  }
7157
7704
 
7158
7705
  // src/animation/usePolyAnimation.ts
7159
7706
  var import_react28 = require("react");
7160
- var import_polycss_core26 = require("@layoutit/polycss-core");
7707
+ var import_polycss_core27 = require("@layoutit/polycss-core");
7161
7708
  function resolveRoot(rootArg) {
7162
7709
  if (!rootArg) return null;
7163
7710
  if ("current" in rootArg) return rootArg.current;
@@ -7176,7 +7723,7 @@ function usePolyAnimation(clips, controller, root) {
7176
7723
  mixerRef.current = null;
7177
7724
  return;
7178
7725
  }
7179
- mixerRef.current = (0, import_polycss_core26.createPolyAnimationMixer)(resolvedRoot, controller);
7726
+ mixerRef.current = (0, import_polycss_core27.createPolyAnimationMixer)(resolvedRoot, controller);
7180
7727
  return () => {
7181
7728
  mixerRef.current?.stopAllAction();
7182
7729
  mixerRef.current?.uncacheRoot();
@@ -7234,7 +7781,7 @@ function usePolyAnimation(clips, controller, root) {
7234
7781
  }
7235
7782
 
7236
7783
  // src/index.ts
7237
- var import_polycss_core27 = require("@layoutit/polycss-core");
7784
+ var import_polycss_core28 = require("@layoutit/polycss-core");
7238
7785
  // Annotate the CommonJS export names for ESM import in node:
7239
7786
  0 && (module.exports = {
7240
7787
  BASE_TILE,
@@ -7280,6 +7827,7 @@ var import_polycss_core27 = require("@layoutit/polycss-core");
7280
7827
  bakeSolidTextureSampledPolygons,
7281
7828
  bakeSolidTextureSamples,
7282
7829
  boxPolygons,
7830
+ buildPolyCameraSceneTransform,
7283
7831
  buildPolyMeshTransform,
7284
7832
  buildPolySceneTransform,
7285
7833
  buildSceneContext,
@@ -7288,8 +7836,10 @@ var import_polycss_core27 = require("@layoutit/polycss-core");
7288
7836
  cameraCullNormalKey,
7289
7837
  cameraCullVisibleSignature,
7290
7838
  cameraFacingDepth,
7839
+ capturePolyCameraSnapshot,
7291
7840
  clampChannel,
7292
7841
  collectPolyRenderStats,
7842
+ collectPolyTextureReadiness,
7293
7843
  computeSceneBbox,
7294
7844
  computeShapeLighting,
7295
7845
  computeTexturePaintMetrics,
@@ -7297,6 +7847,8 @@ var import_polycss_core27 = require("@layoutit/polycss-core");
7297
7847
  coverPlanarPolygons,
7298
7848
  createIsometricCamera,
7299
7849
  createPolyAnimationMixer,
7850
+ cssDistanceToWorld,
7851
+ cssPositionToWorld,
7300
7852
  cullInteriorPolygons,
7301
7853
  cylinderPolygons,
7302
7854
  dodecahedronPolygons,
@@ -7328,12 +7880,15 @@ var import_polycss_core27 = require("@layoutit/polycss-core");
7328
7880
  parseVox,
7329
7881
  planePolygons,
7330
7882
  pointInMeshElement,
7883
+ polyCameraTargetToCss,
7331
7884
  polyCssDistanceToWorld,
7332
7885
  polyCssPositionToWorld,
7333
7886
  polygonCssSurfaceNormal,
7334
7887
  polygonFaces,
7335
7888
  polygonFacesCamera,
7889
+ queryPolyLeaves,
7336
7890
  repairMeshSeams,
7891
+ resolvePolyCameraAppliedPerspectiveStyle,
7337
7892
  ringPolygons,
7338
7893
  rotateVec3,
7339
7894
  seamFacetSplitPolygons,
@@ -7353,8 +7908,12 @@ var import_polycss_core27 = require("@layoutit/polycss-core");
7353
7908
  usePolySceneContext,
7354
7909
  usePolySelect,
7355
7910
  usePolySelectionApi,
7911
+ worldDirectionToCss,
7356
7912
  worldDirectionToPolyCss,
7913
+ worldDirectionalLightToCss,
7357
7914
  worldDirectionalLightToPolyCss,
7915
+ worldDistanceToCss,
7358
7916
  worldDistanceToPolyCss,
7917
+ worldPositionToCss,
7359
7918
  worldPositionToPolyCss
7360
7919
  });