@layoutit/polycss-react 0.2.4 → 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,77 +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
- 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,
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,
74
77
  collectPolyRenderStats: () => collectPolyRenderStats,
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,
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,
85
91
  findMeshUnderPoint: () => findMeshUnderPoint,
86
92
  findPolyMeshHandle: () => findPolyMeshHandle,
87
- formatColor: () => import_polycss_core27.formatColor,
88
- icosahedronPolygons: () => import_polycss_core27.icosahedronPolygons,
93
+ formatColor: () => import_polycss_core28.formatColor,
94
+ icosahedronPolygons: () => import_polycss_core28.icosahedronPolygons,
89
95
  injectPolyBaseStyles: () => injectPolyBaseStyles,
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,
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,
112
118
  pointInMeshElement: () => pointInMeshElement,
113
- polyCssDistanceToWorld: () => import_polycss_core27.cssDistanceToWorld,
114
- polyCssPositionToWorld: () => import_polycss_core27.cssPositionToWorld,
115
- polygonCssSurfaceNormal: () => import_polycss_core27.polygonCssSurfaceNormal,
116
- polygonFaces: () => import_polycss_core27.polygonFaces,
117
- polygonFacesCamera: () => import_polycss_core27.polygonFacesCamera,
118
- repairMeshSeams: () => import_polycss_core27.repairMeshSeams,
119
- ringPolygons: () => import_polycss_core27.ringPolygons,
120
- rotateVec3: () => import_polycss_core27.rotateVec3,
121
- seamFacetSplitPolygons: () => import_polycss_core27.seamFacetSplitPolygons,
122
- seamFacetSplitReport: () => import_polycss_core27.seamFacetSplitReport,
123
- seamOverlapDiagnostics: () => import_polycss_core27.seamOverlapDiagnostics,
124
- seamOverlapPolygons: () => import_polycss_core27.seamOverlapPolygons,
125
- seamOverlapReport: () => import_polycss_core27.seamOverlapReport,
126
- shadeColor: () => import_polycss_core27.shadeColor,
127
- simplifyTriangleMeshPolygons: () => import_polycss_core27.simplifyTriangleMeshPolygons,
128
- tetrahedronPolygons: () => import_polycss_core27.tetrahedronPolygons,
129
- 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,
130
139
  useCameraContext: () => useCameraContext,
131
140
  usePolyAnimation: () => usePolyAnimation,
132
141
  usePolyCamera: () => usePolyCamera,
@@ -135,9 +144,14 @@ __export(src_exports, {
135
144
  usePolySceneContext: () => usePolySceneContext,
136
145
  usePolySelect: () => usePolySelect,
137
146
  usePolySelectionApi: () => usePolySelectionApi,
138
- worldDirectionToPolyCss: () => import_polycss_core27.worldDirectionToCss,
139
- worldDistanceToPolyCss: () => import_polycss_core27.worldDistanceToCss,
140
- 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
141
155
  });
142
156
  module.exports = __toCommonJS(src_exports);
143
157
 
@@ -203,6 +217,18 @@ function useStoreSelector(store, selector) {
203
217
  }
204
218
 
205
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
+ }
206
232
  function usePolyCamera(options) {
207
233
  const handleRef = (0, import_react3.useRef)(null);
208
234
  if (!handleRef.current) {
@@ -232,40 +258,33 @@ function usePolyCamera(options) {
232
258
  handle.update(next);
233
259
  const el = sceneElRef.current;
234
260
  if (el) {
235
- const s = handle.state;
236
- const [ox, oy, oz] = store.getState().autoCenterOffset;
237
- const tileSize = import_polycss_core.BASE_TILE;
238
- const wx = s.target[0] + ox;
239
- const wy = s.target[1] + oy;
240
- const wz = s.target[2] + oz;
241
- const cssX = wy * tileSize;
242
- const cssY = wx * tileSize;
243
- const cssZ = wz * tileSize;
244
- const cssZoom = s.zoom / tileSize;
245
- const distancePart = s.distance !== 0 ? `translateZ(${-s.distance}px) ` : "";
246
- el.style.transform = `${distancePart}scale(${cssZoom}) rotateX(${s.rotX}deg) rotate(${s.rotY}deg) translate3d(${-cssX}px, ${-cssY}px, ${-cssZ}px)`;
261
+ el.style.transform = (0, import_polycss_core.buildPolyCameraSceneTransform)(handle.state, {
262
+ autoCenterOffset: store.getState().autoCenterOffset
263
+ });
247
264
  }
248
265
  store.updateCameraFromRef(handle);
249
266
  store.notifyAll();
250
267
  }
251
- }, [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
+ ]);
252
279
  const applyTransformDirect = (0, import_react3.useCallback)(() => {
253
280
  const el = sceneElRef.current;
254
281
  if (!el) return;
255
282
  const handle = handleRef.current;
256
- const s = handle.state;
257
- const [ox, oy, oz] = store.getState().autoCenterOffset;
258
- const tileSize = import_polycss_core.BASE_TILE;
259
- const wx = s.target[0] + ox;
260
- const wy = s.target[1] + oy;
261
- const wz = s.target[2] + oz;
262
- const cssX = wy * tileSize;
263
- const cssY = wx * tileSize;
264
- const cssZ = wz * tileSize;
265
- const cssZoom = s.zoom / tileSize;
266
- const distancePart = s.distance !== 0 ? `translateZ(${-s.distance}px) ` : "";
267
- el.style.transform = `${distancePart}scale(${cssZoom}) rotateX(${s.rotX}deg) rotate(${s.rotY}deg) translate3d(${-cssX}px, ${-cssY}px, ${-cssZ}px)`;
268
- }, [store]);
283
+ el.style.transform = (0, import_polycss_core.buildPolyCameraSceneTransform)(handle.state, {
284
+ autoCenterOffset: store.getState().autoCenterOffset
285
+ });
286
+ writeCameraSnapshotAttrs(cameraElRef.current, handle, options.projection, options.perspectiveStyle);
287
+ }, [store, options.projection, options.perspectiveStyle]);
269
288
  return {
270
289
  store,
271
290
  cameraRef: handleRef,
@@ -289,18 +308,26 @@ function PolyPerspectiveCameraInner({
289
308
  className,
290
309
  style
291
310
  }) {
311
+ const perspectiveValue = `${typeof perspective === "number" ? perspective : DEFAULT_PERSPECTIVE}px`;
292
312
  const {
293
313
  store,
294
314
  cameraRef,
295
315
  sceneElRef,
296
316
  cameraElRef,
297
317
  applyTransformDirect
298
- } = 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
+ });
299
327
  const contextValue = (0, import_react4.useMemo)(
300
328
  () => ({ store, cameraRef, sceneElRef, cameraElRef, applyTransformDirect }),
301
329
  [store, cameraRef, sceneElRef, cameraElRef, applyTransformDirect]
302
330
  );
303
- const perspectiveValue = `${typeof perspective === "number" ? perspective : DEFAULT_PERSPECTIVE}px`;
304
331
  const cameraStyle = {
305
332
  ...style,
306
333
  perspective: perspectiveValue
@@ -311,6 +338,14 @@ function PolyPerspectiveCameraInner({
311
338
  ref: cameraElRef,
312
339
  className: `polycss-camera${className ? ` ${className}` : ""}`,
313
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(","),
314
349
  children
315
350
  }
316
351
  ) });
@@ -336,7 +371,15 @@ function PolyOrthographicCameraInner({
336
371
  sceneElRef,
337
372
  cameraElRef,
338
373
  applyTransformDirect
339
- } = 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
+ });
340
383
  const contextValue = (0, import_react5.useMemo)(
341
384
  () => ({ store, cameraRef, sceneElRef, cameraElRef, applyTransformDirect }),
342
385
  [store, cameraRef, sceneElRef, cameraElRef, applyTransformDirect]
@@ -356,6 +399,14 @@ function PolyOrthographicCameraInner({
356
399
  ref: cameraElRef,
357
400
  className: `polycss-camera${className ? ` ${className}` : ""}`,
358
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(","),
359
410
  children
360
411
  }
361
412
  ) });
@@ -538,8 +589,8 @@ var CORE_BASE_STYLES = `
538
589
  }
539
590
 
540
591
  .polycss-scene s {
541
- width: var(--polycss-atlas-size, 64px);
542
- 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));
543
594
  }
544
595
 
545
596
  .polycss-scene u {
@@ -936,13 +987,16 @@ function isSolidTriangleSupported(doc) {
936
987
 
937
988
  // src/scene/atlas/filterPlans.ts
938
989
  var import_polycss_core4 = require("@layoutit/polycss-core");
939
- function filterAtlasPlans(plans, textureLighting, disabled, doc) {
990
+ function filterAtlasPlans(plans, textureLighting, disabled, doc, textureBackend, textureImageRendering, textureProjection) {
940
991
  const d = doc ?? (typeof document !== "undefined" ? document : null);
941
992
  return (0, import_polycss_core4.filterAtlasPlans)(plans, textureLighting, disabled, {
942
993
  solidTriangleSupported: isSolidTriangleSupported(doc),
943
994
  projectiveQuadSupported: doc ? projectiveQuadSupported(doc) : true,
944
995
  borderShapeSupported: isBorderShapeSupported(doc),
945
- cornerShapeSupported: d ? cornerShapeSupported(d) : false
996
+ cornerShapeSupported: d ? cornerShapeSupported(d) : false,
997
+ textureBackend,
998
+ textureImageRendering,
999
+ textureProjection
946
1000
  });
947
1001
  }
948
1002
 
@@ -955,8 +1009,13 @@ function isMobileDocument(doc) {
955
1009
  if (!media) return false;
956
1010
  return media("(pointer: coarse)").matches || media("(hover: none)").matches;
957
1011
  }
958
- function packTextureAtlasPlansWithScale(plans, textureQualityInput, doc) {
959
- 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
+ );
960
1019
  }
961
1020
 
962
1021
  // src/scene/atlas/buildAtlasPages.ts
@@ -2162,7 +2221,7 @@ function deferRevoke(urls) {
2162
2221
  if (typeof requestAnimationFrame === "function") requestAnimationFrame(run);
2163
2222
  else setTimeout(run, 0);
2164
2223
  }
2165
- function useTextureAtlas(plans, textureLighting, textureQualityInput, strategies, atomic = false) {
2224
+ function useTextureAtlas(plans, textureLighting, textureQualityInput, textureLeafSizing, textureBackend, textureImageRendering, textureProjection, strategies, atomic = false) {
2166
2225
  const disabled = (0, import_react7.useMemo)(
2167
2226
  () => new Set(strategies?.disable ?? []),
2168
2227
  // eslint-disable-next-line react-hooks/exhaustive-deps
@@ -2173,17 +2232,21 @@ function useTextureAtlas(plans, textureLighting, textureQualityInput, strategies
2173
2232
  plans,
2174
2233
  textureLighting,
2175
2234
  disabled,
2176
- typeof document !== "undefined" ? document : null
2235
+ typeof document !== "undefined" ? document : null,
2236
+ textureBackend,
2237
+ textureImageRendering,
2238
+ textureProjection
2177
2239
  ),
2178
- [plans, textureLighting, disabled]
2240
+ [plans, textureLighting, disabled, textureBackend, textureImageRendering, textureProjection]
2179
2241
  );
2180
2242
  const { packed, atlasScale } = (0, import_react7.useMemo)(
2181
2243
  () => packTextureAtlasPlansWithScale(
2182
2244
  atlasPlans,
2183
2245
  textureQualityInput,
2184
- typeof document !== "undefined" ? document : null
2246
+ typeof document !== "undefined" ? document : null,
2247
+ textureLeafSizing
2185
2248
  ),
2186
- [atlasPlans, textureQualityInput]
2249
+ [atlasPlans, textureQualityInput, textureLeafSizing]
2187
2250
  );
2188
2251
  const [pages, setPages] = (0, import_react7.useState)(() => pageShells(packed.pages));
2189
2252
  const [frame, setFrame] = (0, import_react7.useState)(() => ({
@@ -2533,6 +2596,7 @@ var TextureAtlasPoly = (0, import_react12.memo)(function TextureAtlasPoly2({
2533
2596
  entry,
2534
2597
  page,
2535
2598
  textureLighting,
2599
+ textureImageRendering,
2536
2600
  solidPaintDefaults: _solidPaintDefaults,
2537
2601
  className,
2538
2602
  style: styleProp,
@@ -2542,26 +2606,26 @@ var TextureAtlasPoly = (0, import_react12.memo)(function TextureAtlasPoly2({
2542
2606
  }) {
2543
2607
  const ATLAS_CANONICAL_SIZE_EXPLICIT = 64;
2544
2608
  const dynamic = textureLighting === "dynamic";
2609
+ const resolvedImageRendering = (0, import_polycss_core11.resolvePolyTextureImageRendering)(entry.polygon, textureImageRendering);
2545
2610
  const atlasCanonicalSize = entry.atlasCanonicalSize ?? ATLAS_CANONICAL_SIZE_EXPLICIT;
2611
+ const atlasLeafWidth = entry.atlasLeafWidth ?? atlasCanonicalSize;
2612
+ const atlasLeafHeight = entry.atlasLeafHeight ?? atlasCanonicalSize;
2546
2613
  const atlasWidth = entry.canvasW || 1;
2547
2614
  const atlasHeight = entry.canvasH || 1;
2548
- const atlasPosition = page ? `${(0, import_polycss_core11.formatCssLengthPx)(-entry.x / atlasWidth * atlasCanonicalSize)} ${(0, import_polycss_core11.formatCssLengthPx)(-entry.y / atlasHeight * atlasCanonicalSize)}` : void 0;
2549
- 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;
2550
2617
  const dynamicMask = dynamic && page?.url ? `url(${page.url})` : void 0;
2551
- const background = !dynamic && page?.url ? `url(${page.url}) ${atlasPosition} / ${atlasSize} no-repeat` : void 0;
2552
2618
  const style = {
2553
2619
  transform: (0, import_polycss_core11.formatMatrix3d)(entry.atlasMatrix),
2554
2620
  ["--polycss-atlas-size"]: `${atlasCanonicalSize}px`,
2555
- // Listing the `background` shorthand alongside the `background-*` longhands
2556
- // in one inline style object makes React warn on every update (mixing
2557
- // shorthand and non-shorthand for the same property). Branch so only the
2558
- // current mode's keys are assigned — baked gets `background`, dynamic gets
2559
- // the longhands.
2560
- ...dynamic ? {
2561
- backgroundImage: page?.url ? `url(${page.url})` : void 0,
2562
- backgroundPosition: atlasPosition,
2563
- backgroundSize: atlasSize
2564
- } : { 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,
2565
2629
  ...dynamic ? {
2566
2630
  ["--pnx"]: entry.normal[0].toFixed(4),
2567
2631
  ["--pny"]: entry.normal[1].toFixed(4),
@@ -2592,6 +2656,74 @@ var TextureAtlasPoly = (0, import_react12.memo)(function TextureAtlasPoly2({
2592
2656
  className: elementClassName,
2593
2657
  style,
2594
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,
2595
2727
  ...domEventHandlers,
2596
2728
  ...dataAttrs,
2597
2729
  ...domAttrs
@@ -2619,6 +2751,10 @@ function PolySceneInner({
2619
2751
  ambientLight,
2620
2752
  textureLighting = "baked",
2621
2753
  textureQuality,
2754
+ textureLeafSizing,
2755
+ textureImageRendering,
2756
+ textureBackend,
2757
+ textureProjection,
2622
2758
  seamBleed = import_polycss_core13.DEFAULT_SEAM_BLEED,
2623
2759
  strategies,
2624
2760
  autoCenter = false,
@@ -2714,7 +2850,16 @@ function PolySceneInner({
2714
2850
  },
2715
2851
  [polygons, polyContext, seamBleed, directionalForAtlas, ambientForAtlas]
2716
2852
  );
2717
- 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
+ );
2718
2863
  const dynamicLightVars = (0, import_react14.useMemo)(() => {
2719
2864
  if (textureLighting !== "dynamic") return null;
2720
2865
  const userDir = directionalLight?.direction ?? [0.4, -0.7, 0.59];
@@ -2812,12 +2957,28 @@ function PolySceneInner({
2812
2957
  {
2813
2958
  entry,
2814
2959
  page: textureAtlas.pages[entry.pageIndex],
2815
- textureLighting
2960
+ textureLighting,
2961
+ textureImageRendering
2816
2962
  },
2817
2963
  entry.index
2818
2964
  );
2819
2965
  }
2820
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
+ }
2821
2982
  if (!plan || plan.texture) return null;
2822
2983
  const useU = !disabledStrategies?.has("u");
2823
2984
  const useProjectiveSolid = !disabledStrategies?.has("b");
@@ -2843,6 +3004,10 @@ function PolySceneInner({
2843
3004
  ambientLight,
2844
3005
  strategies,
2845
3006
  seamBleed,
3007
+ textureLeafSizing,
3008
+ textureImageRendering,
3009
+ textureBackend,
3010
+ textureProjection,
2846
3011
  shadow,
2847
3012
  registerShadowCaster,
2848
3013
  registerShadowReceiver,
@@ -2852,7 +3017,7 @@ function PolySceneInner({
2852
3017
  groundCssZ,
2853
3018
  sceneEl
2854
3019
  }),
2855
- [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]
2856
3021
  );
2857
3022
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(PolySceneContext.Provider, { value: sceneCtxValue, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
2858
3023
  "div",
@@ -3733,6 +3898,10 @@ var PolyMesh = (0, import_react16.forwardRef)(function PolyMesh2({
3733
3898
  autoCenter,
3734
3899
  textureLighting,
3735
3900
  textureQuality,
3901
+ textureLeafSizing,
3902
+ textureImageRendering,
3903
+ textureBackend,
3904
+ textureProjection,
3736
3905
  seamBleed,
3737
3906
  atomicAtlas,
3738
3907
  onFrameReady,
@@ -3802,6 +3971,12 @@ var PolyMesh = (0, import_react16.forwardRef)(function PolyMesh2({
3802
3971
  const stableTriangleColorFrameRef = (0, import_react16.useRef)(0);
3803
3972
  const setPolygonsImplRef = (0, import_react16.useRef)(() => {
3804
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
+ }, []);
3805
3980
  const handle = (0, import_react16.useMemo)(() => ({
3806
3981
  get element() {
3807
3982
  return wrapperRef.current;
@@ -3815,6 +3990,12 @@ var PolyMesh = (0, import_react16.forwardRef)(function PolyMesh2({
3815
3990
  setPolygonsImplRef.current(nextPolygons);
3816
3991
  },
3817
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
+ },
3818
3999
  updatePolygon(target, partial) {
3819
4000
  const current = polygonsRef.current;
3820
4001
  const idx = typeof target === "number" ? target : current.indexOf(target);
@@ -3961,6 +4142,10 @@ var PolyMesh = (0, import_react16.forwardRef)(function PolyMesh2({
3961
4142
  [effectiveStrategies]
3962
4143
  );
3963
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;
3964
4149
  const effectiveDirectional = sceneCtx?.directionalLight;
3965
4150
  const effectiveAmbient = sceneCtx?.ambientLight;
3966
4151
  const directVoxelEnabled = Boolean(
@@ -4023,9 +4208,18 @@ var PolyMesh = (0, import_react16.forwardRef)(function PolyMesh2({
4023
4208
  atlasPlans,
4024
4209
  effectiveTextureLighting,
4025
4210
  textureQuality,
4211
+ effectiveTextureLeafSizing,
4212
+ effectiveTextureBackend,
4213
+ effectiveTextureImageRendering,
4214
+ effectiveTextureProjection,
4026
4215
  effectiveStrategies,
4027
4216
  atomicAtlas
4028
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]);
4029
4223
  const solidPaintDefaults = (0, import_react16.useMemo)(
4030
4224
  () => !renderPolygon ? getSolidPaintDefaults(textureAtlas.plans, effectiveTextureLighting, effectiveStrategies) : {},
4031
4225
  [renderPolygon, textureAtlas.plans, effectiveTextureLighting, effectiveStrategies]
@@ -4374,12 +4568,28 @@ var PolyMesh = (0, import_react16.forwardRef)(function PolyMesh2({
4374
4568
  entry,
4375
4569
  page: textureAtlas.pages[entry.pageIndex],
4376
4570
  textureLighting: effectiveTextureLighting,
4571
+ textureImageRendering: effectiveTextureImageRendering,
4377
4572
  solidPaintDefaults
4378
4573
  },
4379
4574
  entry.index
4380
4575
  );
4381
4576
  }
4382
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
+ }
4383
4593
  if (!plan || plan.texture) return null;
4384
4594
  if ((0, import_polycss_core12.isProjectiveQuadPlan)(plan)) {
4385
4595
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
@@ -4600,6 +4810,7 @@ function usePolyMaterial(options) {
4600
4810
 
4601
4811
  // src/shapes/Poly.tsx
4602
4812
  var import_react19 = require("react");
4813
+ var import_polycss_core19 = require("@layoutit/polycss-core");
4603
4814
  var import_jsx_runtime12 = require("react/jsx-runtime");
4604
4815
  var DIRECT_TEXTURE_CSS_DECIMALS = 4;
4605
4816
  function formatCssLength(value, decimals = DIRECT_TEXTURE_CSS_DECIMALS) {
@@ -4630,7 +4841,8 @@ function MaterialDirectPoly({
4630
4841
  style: styleProp,
4631
4842
  domAttrs,
4632
4843
  domEventHandlers,
4633
- pointerEvents = "auto"
4844
+ pointerEvents = "auto",
4845
+ imageRendering
4634
4846
  }) {
4635
4847
  const { u0, u1, v0, v1 } = uvRect;
4636
4848
  const du = u1 - u0;
@@ -4645,6 +4857,8 @@ function MaterialDirectPoly({
4645
4857
  backgroundImage: `url(${material.texture})`,
4646
4858
  backgroundSize: `${formatCssLength(sourceW)} ${formatCssLength(sourceH)}`,
4647
4859
  backgroundPosition: `${formatCssLength(-offsetX)} ${formatCssLength(-offsetY)}`,
4860
+ backgroundRepeat: "no-repeat",
4861
+ imageRendering: imageRendering === "pixelated" ? "pixelated" : void 0,
4648
4862
  pointerEvents: pointerEvents === "none" ? "none" : void 0,
4649
4863
  ...styleProp
4650
4864
  };
@@ -4657,6 +4871,14 @@ function MaterialDirectPoly({
4657
4871
  {
4658
4872
  className: elementClassName,
4659
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,
4660
4882
  ...domEventHandlers,
4661
4883
  ...dataAttrs,
4662
4884
  ...domAttrs
@@ -4667,8 +4889,11 @@ function PolyInner({
4667
4889
  vertices,
4668
4890
  color,
4669
4891
  texture,
4892
+ textureImageSource,
4893
+ texturePresentation,
4670
4894
  uvs,
4671
4895
  data,
4896
+ doubleSided,
4672
4897
  material,
4673
4898
  position,
4674
4899
  scale,
@@ -4696,6 +4921,10 @@ function PolyInner({
4696
4921
  context,
4697
4922
  textureLighting: textureLightingProp,
4698
4923
  textureQuality: textureQualityProp,
4924
+ textureLeafSizing: textureLeafSizingProp,
4925
+ textureImageRendering: textureImageRenderingProp,
4926
+ textureBackend: textureBackendProp,
4927
+ textureProjection: textureProjectionProp,
4699
4928
  baseColor: baseColorProp,
4700
4929
  ...dataAttrs
4701
4930
  }) {
@@ -4703,11 +4932,25 @@ function PolyInner({
4703
4932
  const layerElevation = context?.layerElevation ?? tileSize;
4704
4933
  const textureLighting = textureLightingProp ?? context?.textureLighting ?? "baked";
4705
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;
4706
4939
  const polygonColor = baseColorProp ?? color;
4707
4940
  const effectiveTexture = material?.texture ?? texture;
4708
4941
  const atlasPlan = (0, import_react19.useMemo)(
4709
4942
  () => computeTextureAtlasPlan(
4710
- { 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
+ },
4711
4954
  0,
4712
4955
  {
4713
4956
  tileSize,
@@ -4719,8 +4962,12 @@ function PolyInner({
4719
4962
  vertices,
4720
4963
  polygonColor,
4721
4964
  effectiveTexture,
4965
+ textureImageSource,
4966
+ texturePresentation,
4722
4967
  uvs,
4723
4968
  data,
4969
+ doubleSided,
4970
+ material,
4724
4971
  tileSize,
4725
4972
  layerElevation,
4726
4973
  context?.directionalLight
@@ -4734,7 +4981,15 @@ function PolyInner({
4734
4981
  () => materialUvRect ? [] : [atlasPlan],
4735
4982
  [materialUvRect, atlasPlan]
4736
4983
  );
4737
- 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
+ );
4738
4993
  const domEventHandlers = {
4739
4994
  onClick,
4740
4995
  onDoubleClick,
@@ -4793,7 +5048,8 @@ function PolyInner({
4793
5048
  style: styleProp,
4794
5049
  domAttrs,
4795
5050
  domEventHandlers,
4796
- pointerEvents: pointerEventsProp ?? "auto"
5051
+ pointerEvents: pointerEventsProp ?? "auto",
5052
+ imageRendering: textureImageRendering
4797
5053
  }
4798
5054
  );
4799
5055
  } else {
@@ -4805,6 +5061,7 @@ function PolyInner({
4805
5061
  entry: atlasEntry,
4806
5062
  page: textureAtlas.pages[atlasEntry.pageIndex],
4807
5063
  textureLighting,
5064
+ textureImageRendering,
4808
5065
  className,
4809
5066
  style: styleProp,
4810
5067
  domAttrs,
@@ -4812,7 +5069,28 @@ function PolyInner({
4812
5069
  pointerEvents: pointerEventsProp ?? "auto"
4813
5070
  }
4814
5071
  );
4815
- } 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) {
4816
5094
  front = (0, import_polycss_core12.isSolidTrianglePlan)(atlasPlan) ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
4817
5095
  TextureTrianglePoly,
4818
5096
  {
@@ -4868,7 +5146,7 @@ var Poly = (0, import_react19.memo)(PolyInner);
4868
5146
 
4869
5147
  // src/shapes/PolyShapes.tsx
4870
5148
  var import_react20 = require("react");
4871
- var import_polycss_core19 = require("@layoutit/polycss-core");
5149
+ var import_polycss_core20 = require("@layoutit/polycss-core");
4872
5150
  var import_jsx_runtime13 = require("react/jsx-runtime");
4873
5151
  function PolyBox({
4874
5152
  size,
@@ -4884,7 +5162,7 @@ function PolyBox({
4884
5162
  ...meshProps
4885
5163
  }) {
4886
5164
  const polygons = (0, import_react20.useMemo)(
4887
- () => (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 }),
4888
5166
  // eslint-disable-next-line react-hooks/exhaustive-deps
4889
5167
  [size, center, min, max, color, texture, material, uvs, data, faces]
4890
5168
  );
@@ -4899,7 +5177,7 @@ function PolyPlane({
4899
5177
  ...meshProps
4900
5178
  }) {
4901
5179
  const polygons = (0, import_react20.useMemo)(
4902
- () => (0, import_polycss_core19.planePolygons)({ axis, size, offset, along, color }),
5180
+ () => (0, import_polycss_core20.planePolygons)({ axis, size, offset, along, color }),
4903
5181
  // eslint-disable-next-line react-hooks/exhaustive-deps
4904
5182
  [axis, size, offset, along, color]
4905
5183
  );
@@ -4914,7 +5192,7 @@ function PolyRing({
4914
5192
  ...meshProps
4915
5193
  }) {
4916
5194
  const polygons = (0, import_react20.useMemo)(
4917
- () => (0, import_polycss_core19.ringPolygons)({ axis, radius, halfThickness, segments, color }),
5195
+ () => (0, import_polycss_core20.ringPolygons)({ axis, radius, halfThickness, segments, color }),
4918
5196
  // eslint-disable-next-line react-hooks/exhaustive-deps
4919
5197
  [axis, radius, halfThickness, segments, color]
4920
5198
  );
@@ -4927,7 +5205,7 @@ function PolyOctahedron({
4927
5205
  ...meshProps
4928
5206
  }) {
4929
5207
  const polygons = (0, import_react20.useMemo)(
4930
- () => (0, import_polycss_core19.octahedronPolygons)({ center, size, color }),
5208
+ () => (0, import_polycss_core20.octahedronPolygons)({ center, size, color }),
4931
5209
  // eslint-disable-next-line react-hooks/exhaustive-deps
4932
5210
  [center, size, color]
4933
5211
  );
@@ -4939,7 +5217,7 @@ function PolyTetrahedron({
4939
5217
  ...meshProps
4940
5218
  }) {
4941
5219
  const polygons = (0, import_react20.useMemo)(
4942
- () => (0, import_polycss_core19.tetrahedronPolygons)({ size, color }),
5220
+ () => (0, import_polycss_core20.tetrahedronPolygons)({ size, color }),
4943
5221
  // eslint-disable-next-line react-hooks/exhaustive-deps
4944
5222
  [size, color]
4945
5223
  );
@@ -4951,7 +5229,7 @@ function PolyIcosahedron({
4951
5229
  ...meshProps
4952
5230
  }) {
4953
5231
  const polygons = (0, import_react20.useMemo)(
4954
- () => (0, import_polycss_core19.icosahedronPolygons)({ size, color }),
5232
+ () => (0, import_polycss_core20.icosahedronPolygons)({ size, color }),
4955
5233
  // eslint-disable-next-line react-hooks/exhaustive-deps
4956
5234
  [size, color]
4957
5235
  );
@@ -4963,7 +5241,7 @@ function PolyDodecahedron({
4963
5241
  ...meshProps
4964
5242
  }) {
4965
5243
  const polygons = (0, import_react20.useMemo)(
4966
- () => (0, import_polycss_core19.dodecahedronPolygons)({ size, color }),
5244
+ () => (0, import_polycss_core20.dodecahedronPolygons)({ size, color }),
4967
5245
  // eslint-disable-next-line react-hooks/exhaustive-deps
4968
5246
  [size, color]
4969
5247
  );
@@ -4976,7 +5254,7 @@ function PolySphere({
4976
5254
  ...meshProps
4977
5255
  }) {
4978
5256
  const polygons = (0, import_react20.useMemo)(
4979
- () => (0, import_polycss_core19.spherePolygons)({ radius, subdivisions, color }),
5257
+ () => (0, import_polycss_core20.spherePolygons)({ radius, subdivisions, color }),
4980
5258
  // eslint-disable-next-line react-hooks/exhaustive-deps
4981
5259
  [radius, subdivisions, color]
4982
5260
  );
@@ -4991,7 +5269,7 @@ function PolyCylinder({
4991
5269
  ...meshProps
4992
5270
  }) {
4993
5271
  const polygons = (0, import_react20.useMemo)(
4994
- () => (0, import_polycss_core19.cylinderPolygons)({ radius, radiusTop, height, radialSegments, color }),
5272
+ () => (0, import_polycss_core20.cylinderPolygons)({ radius, radiusTop, height, radialSegments, color }),
4995
5273
  // eslint-disable-next-line react-hooks/exhaustive-deps
4996
5274
  [radius, radiusTop, height, radialSegments, color]
4997
5275
  );
@@ -5005,7 +5283,7 @@ function PolyCone({
5005
5283
  ...meshProps
5006
5284
  }) {
5007
5285
  const polygons = (0, import_react20.useMemo)(
5008
- () => (0, import_polycss_core19.conePolygons)({ radius, height, radialSegments, color }),
5286
+ () => (0, import_polycss_core20.conePolygons)({ radius, height, radialSegments, color }),
5009
5287
  // eslint-disable-next-line react-hooks/exhaustive-deps
5010
5288
  [radius, height, radialSegments, color]
5011
5289
  );
@@ -5020,7 +5298,7 @@ function PolyTorus({
5020
5298
  ...meshProps
5021
5299
  }) {
5022
5300
  const polygons = (0, import_react20.useMemo)(
5023
- () => (0, import_polycss_core19.torusPolygons)({ radius, tube, radialSegments, tubularSegments, color }),
5301
+ () => (0, import_polycss_core20.torusPolygons)({ radius, tube, radialSegments, tubularSegments, color }),
5024
5302
  // eslint-disable-next-line react-hooks/exhaustive-deps
5025
5303
  [radius, tube, radialSegments, tubularSegments, color]
5026
5304
  );
@@ -5029,7 +5307,7 @@ function PolyTorus({
5029
5307
 
5030
5308
  // src/controls/PolyFirstPersonControls.tsx
5031
5309
  var import_react21 = require("react");
5032
- var import_polycss_core20 = require("@layoutit/polycss-core");
5310
+ var import_polycss_core21 = require("@layoutit/polycss-core");
5033
5311
  var DEFAULTS = {
5034
5312
  enabled: true,
5035
5313
  lookEnabled: true,
@@ -5205,7 +5483,7 @@ var PolyFirstPersonControls = (0, import_react21.forwardRef)(function PolyFirstP
5205
5483
  const host = cameraElRef.current;
5206
5484
  const perspStr = host ? getComputedStyle(host).perspective : "";
5207
5485
  const n = parseFloat(perspStr);
5208
- 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;
5209
5487
  }
5210
5488
  function deriveTarget() {
5211
5489
  const s = cameraRef.current.state;
@@ -5446,7 +5724,7 @@ var PolyFirstPersonControls = (0, import_react21.forwardRef)(function PolyFirstP
5446
5724
  var import_react22 = require("react");
5447
5725
 
5448
5726
  // src/controls/sharedControls.ts
5449
- var import_polycss_core21 = require("@layoutit/polycss-core");
5727
+ var import_polycss_core22 = require("@layoutit/polycss-core");
5450
5728
  var POINTER_DRAG_SPEED = 4;
5451
5729
  function invertFactor(invert) {
5452
5730
  if (invert === true) return -1;
@@ -5467,7 +5745,7 @@ function applyPan(dx, dy, s, handle, _invert) {
5467
5745
  const cosRotX = cosRotXRaw >= 0 ? Math.max(0.1, cosRotXRaw) : Math.min(-0.1, cosRotXRaw);
5468
5746
  const cZ = Math.cos(s.rotY * Math.PI / 180);
5469
5747
  const sZ = Math.sin(s.rotY * Math.PI / 180);
5470
- const k = z * import_polycss_core21.BASE_TILE;
5748
+ const k = z * import_polycss_core22.BASE_TILE;
5471
5749
  const targetD0 = (dx * sZ - dy * cZ / cosRotX) / k;
5472
5750
  const targetD1 = -(dx * cZ + dy * sZ / cosRotX) / k;
5473
5751
  const t = s.target;
@@ -5997,9 +6275,9 @@ function PolyOrbitControls({
5997
6275
 
5998
6276
  // src/controls/TransformControls.tsx
5999
6277
  var import_react24 = require("react");
6000
- var import_polycss_core22 = require("@layoutit/polycss-core");
6278
+ var import_polycss_core23 = require("@layoutit/polycss-core");
6001
6279
  var import_jsx_runtime14 = require("react/jsx-runtime");
6002
- var FALLBACK_CAMERA_STORE = createSceneStore(import_polycss_core22.DEFAULT_CAMERA_STATE);
6280
+ var FALLBACK_CAMERA_STORE = createSceneStore(import_polycss_core23.DEFAULT_CAMERA_STATE);
6003
6281
  var COLOR_X = "#ff3653";
6004
6282
  var COLOR_Y = "#8adb00";
6005
6283
  var COLOR_Z = "#2c8fff";
@@ -6276,7 +6554,7 @@ function startRingDrag(opts) {
6276
6554
  const startRotation = target.getRotation() ?? [0, 0, 0];
6277
6555
  onMouseDown?.();
6278
6556
  onDraggingChanged?.(true);
6279
- const qStart = (0, import_polycss_core22.quatFromEulerXYZ)(startRotation);
6557
+ const qStart = (0, import_polycss_core23.quatFromEulerXYZ)(startRotation);
6280
6558
  const handleMove = (ev) => {
6281
6559
  const a = Math.atan2(ev.clientY - centerY, ev.clientX - centerX);
6282
6560
  let d = a - lastAngle;
@@ -6288,8 +6566,8 @@ function startRingDrag(opts) {
6288
6566
  degrees = snap(degrees, rotationSnap);
6289
6567
  const axisVec = [0, 0, 0];
6290
6568
  axisVec[cssAxis] = 1;
6291
- const qDelta = (0, import_polycss_core22.quatFromAxisAngle)(axisVec, degrees * Math.PI / 180);
6292
- 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));
6293
6571
  onObjectChange?.({ object: target, rotation: newRotation });
6294
6572
  onChange?.();
6295
6573
  };
@@ -6660,7 +6938,7 @@ function TranslateArrow({
6660
6938
  };
6661
6939
  const polygons = (0, import_react24.useMemo)(() => {
6662
6940
  const lengthWorld = shaftLengthCss / SCENE_TILE_SIZE;
6663
- return (0, import_polycss_core22.arrowPolygons)({
6941
+ return (0, import_polycss_core23.arrowPolygons)({
6664
6942
  axis: WORLD_AXIS_FOR_CSS[cssAxis],
6665
6943
  sign,
6666
6944
  shaftLength: lengthWorld,
@@ -6765,7 +7043,7 @@ function TranslatePlane({
6765
7043
  const signA = isAxisBackFacing(cssAForOffset, 1, rotX, rotY) ? -1 : 1;
6766
7044
  const signB = isAxisBackFacing(cssBForOffset, 1, rotX, rotY) ? -1 : 1;
6767
7045
  const mag = lengthWorld * PLANE_OFFSET_RATIO;
6768
- return (0, import_polycss_core22.planePolygons)({
7046
+ return (0, import_polycss_core23.planePolygons)({
6769
7047
  axis: worldPerp,
6770
7048
  size: lengthWorld * PLANE_HALF_SIZE_RATIO,
6771
7049
  offset: [signA * mag, signB * mag],
@@ -6855,7 +7133,7 @@ function RotateRing({
6855
7133
  const polygons = (0, import_react24.useMemo)(() => {
6856
7134
  const radiusWorld = radiusCss / SCENE_TILE_SIZE;
6857
7135
  const outerWorld = radiusWorld * RING_QUAD_OUTER_RATIO;
6858
- return (0, import_polycss_core22.ringQuadPolygons)({
7136
+ return (0, import_polycss_core23.ringQuadPolygons)({
6859
7137
  axis: WORLD_AXIS_FOR_CSS[cssAxis],
6860
7138
  outerRadius: outerWorld,
6861
7139
  color
@@ -7053,7 +7331,7 @@ function usePolySelectionApi() {
7053
7331
 
7054
7332
  // src/helpers/PolyAxesHelper.tsx
7055
7333
  var import_react26 = require("react");
7056
- var import_polycss_core23 = require("@layoutit/polycss-core");
7334
+ var import_polycss_core24 = require("@layoutit/polycss-core");
7057
7335
  var import_jsx_runtime16 = require("react/jsx-runtime");
7058
7336
  function PolyAxesHelper({
7059
7337
  size,
@@ -7064,7 +7342,7 @@ function PolyAxesHelper({
7064
7342
  zColor
7065
7343
  }) {
7066
7344
  const polygons = (0, import_react26.useMemo)(
7067
- () => (0, import_polycss_core23.axesHelperPolygons)({ size, thickness, negative, xColor, yColor, zColor }),
7345
+ () => (0, import_polycss_core24.axesHelperPolygons)({ size, thickness, negative, xColor, yColor, zColor }),
7068
7346
  [size, thickness, negative, xColor, yColor, zColor]
7069
7347
  );
7070
7348
  return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(PolyMesh, { polygons, merge: false });
@@ -7072,7 +7350,7 @@ function PolyAxesHelper({
7072
7350
 
7073
7351
  // src/helpers/PolyDirectionalLightHelper.tsx
7074
7352
  var import_react27 = require("react");
7075
- var import_polycss_core24 = require("@layoutit/polycss-core");
7353
+ var import_polycss_core25 = require("@layoutit/polycss-core");
7076
7354
  var import_jsx_runtime17 = require("react/jsx-runtime");
7077
7355
  function PolyDirectionalLightHelper({
7078
7356
  light,
@@ -7083,7 +7361,7 @@ function PolyDirectionalLightHelper({
7083
7361
  }) {
7084
7362
  const swatch = color ?? light.color ?? "#ffd54a";
7085
7363
  const polygons = (0, import_react27.useMemo)(
7086
- () => (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 }),
7087
7365
  [size, swatch]
7088
7366
  );
7089
7367
  const meshPosition = (0, import_react27.useMemo)(() => {
@@ -7102,7 +7380,7 @@ function PolyDirectionalLightHelper({
7102
7380
  }
7103
7381
 
7104
7382
  // src/styles/colorResolver.ts
7105
- var import_polycss_core25 = require("@layoutit/polycss-core");
7383
+ var import_polycss_core26 = require("@layoutit/polycss-core");
7106
7384
 
7107
7385
  // src/renderStats.ts
7108
7386
  var ZERO_SURFACE_LEAF_COUNTS = {
@@ -7111,11 +7389,56 @@ var ZERO_SURFACE_LEAF_COUNTS = {
7111
7389
  atlas: 0,
7112
7390
  stableTriangle: 0
7113
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
+ }
7114
7433
  var EMPTY_POLY_RENDER_STATS = {
7115
7434
  polygonCount: 0,
7116
7435
  mountedPolygonLeafCount: 0,
7117
7436
  shadowLeafCount: 0,
7118
7437
  surfaceLeafCounts: ZERO_SURFACE_LEAF_COUNTS,
7438
+ textureReadiness: READY_TEXTURE_READINESS,
7439
+ textureStats: emptyTextureStats(),
7440
+ snapshotStats: emptySnapshotStats(),
7441
+ cameraStats: emptyCameraStats(),
7119
7442
  bucketCount: 0
7120
7443
  };
7121
7444
  function asOptions(optionsOrPolygonCount) {
@@ -7127,6 +7450,28 @@ function asOptions(optionsOrPolygonCount) {
7127
7450
  function queryCount(scope, selector) {
7128
7451
  return scope.querySelectorAll(selector).length;
7129
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
+ }
7130
7475
  function matchesSelector(root, selector) {
7131
7476
  const candidate = root;
7132
7477
  return typeof candidate.matches === "function" && candidate.matches(selector);
@@ -7138,12 +7483,196 @@ function collectScopes(root, selector) {
7138
7483
  scopes.push(...Array.from(root.querySelectorAll(selector)));
7139
7484
  return scopes;
7140
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
+ }
7141
7666
  function collectPolyRenderStats(root, optionsOrPolygonCount) {
7142
7667
  const options = asOptions(optionsOrPolygonCount);
7143
7668
  if (!root) {
7144
7669
  return {
7145
7670
  ...EMPTY_POLY_RENDER_STATS,
7146
7671
  surfaceLeafCounts: { ...ZERO_SURFACE_LEAF_COUNTS },
7672
+ textureReadiness: { ...READY_TEXTURE_READINESS },
7673
+ textureStats: emptyTextureStats(),
7674
+ snapshotStats: emptySnapshotStats(),
7675
+ cameraStats: emptyCameraStats(),
7147
7676
  polygonCount: options.polygonCount ?? 0
7148
7677
  };
7149
7678
  }
@@ -7165,13 +7694,17 @@ function collectPolyRenderStats(root, optionsOrPolygonCount) {
7165
7694
  mountedPolygonLeafCount,
7166
7695
  shadowLeafCount,
7167
7696
  surfaceLeafCounts,
7697
+ textureReadiness: collectPolyTextureReadiness(root, options),
7698
+ textureStats: collectPolyTextureStats(root, options),
7699
+ snapshotStats: collectPolySnapshotStats(root, options),
7700
+ cameraStats: collectPolyCameraStats(root, options),
7168
7701
  bucketCount
7169
7702
  };
7170
7703
  }
7171
7704
 
7172
7705
  // src/animation/usePolyAnimation.ts
7173
7706
  var import_react28 = require("react");
7174
- var import_polycss_core26 = require("@layoutit/polycss-core");
7707
+ var import_polycss_core27 = require("@layoutit/polycss-core");
7175
7708
  function resolveRoot(rootArg) {
7176
7709
  if (!rootArg) return null;
7177
7710
  if ("current" in rootArg) return rootArg.current;
@@ -7190,7 +7723,7 @@ function usePolyAnimation(clips, controller, root) {
7190
7723
  mixerRef.current = null;
7191
7724
  return;
7192
7725
  }
7193
- mixerRef.current = (0, import_polycss_core26.createPolyAnimationMixer)(resolvedRoot, controller);
7726
+ mixerRef.current = (0, import_polycss_core27.createPolyAnimationMixer)(resolvedRoot, controller);
7194
7727
  return () => {
7195
7728
  mixerRef.current?.stopAllAction();
7196
7729
  mixerRef.current?.uncacheRoot();
@@ -7248,7 +7781,7 @@ function usePolyAnimation(clips, controller, root) {
7248
7781
  }
7249
7782
 
7250
7783
  // src/index.ts
7251
- var import_polycss_core27 = require("@layoutit/polycss-core");
7784
+ var import_polycss_core28 = require("@layoutit/polycss-core");
7252
7785
  // Annotate the CommonJS export names for ESM import in node:
7253
7786
  0 && (module.exports = {
7254
7787
  BASE_TILE,
@@ -7294,15 +7827,19 @@ var import_polycss_core27 = require("@layoutit/polycss-core");
7294
7827
  bakeSolidTextureSampledPolygons,
7295
7828
  bakeSolidTextureSamples,
7296
7829
  boxPolygons,
7830
+ buildPolyCameraSceneTransform,
7297
7831
  buildPolyMeshTransform,
7832
+ buildPolySceneTransform,
7298
7833
  buildSceneContext,
7299
7834
  cameraCullNormalGroups,
7300
7835
  cameraCullNormalGroupsFromPolygons,
7301
7836
  cameraCullNormalKey,
7302
7837
  cameraCullVisibleSignature,
7303
7838
  cameraFacingDepth,
7839
+ capturePolyCameraSnapshot,
7304
7840
  clampChannel,
7305
7841
  collectPolyRenderStats,
7842
+ collectPolyTextureReadiness,
7306
7843
  computeSceneBbox,
7307
7844
  computeShapeLighting,
7308
7845
  computeTexturePaintMetrics,
@@ -7310,6 +7847,8 @@ var import_polycss_core27 = require("@layoutit/polycss-core");
7310
7847
  coverPlanarPolygons,
7311
7848
  createIsometricCamera,
7312
7849
  createPolyAnimationMixer,
7850
+ cssDistanceToWorld,
7851
+ cssPositionToWorld,
7313
7852
  cullInteriorPolygons,
7314
7853
  cylinderPolygons,
7315
7854
  dodecahedronPolygons,
@@ -7341,12 +7880,15 @@ var import_polycss_core27 = require("@layoutit/polycss-core");
7341
7880
  parseVox,
7342
7881
  planePolygons,
7343
7882
  pointInMeshElement,
7883
+ polyCameraTargetToCss,
7344
7884
  polyCssDistanceToWorld,
7345
7885
  polyCssPositionToWorld,
7346
7886
  polygonCssSurfaceNormal,
7347
7887
  polygonFaces,
7348
7888
  polygonFacesCamera,
7889
+ queryPolyLeaves,
7349
7890
  repairMeshSeams,
7891
+ resolvePolyCameraAppliedPerspectiveStyle,
7350
7892
  ringPolygons,
7351
7893
  rotateVec3,
7352
7894
  seamFacetSplitPolygons,
@@ -7366,7 +7908,12 @@ var import_polycss_core27 = require("@layoutit/polycss-core");
7366
7908
  usePolySceneContext,
7367
7909
  usePolySelect,
7368
7910
  usePolySelectionApi,
7911
+ worldDirectionToCss,
7369
7912
  worldDirectionToPolyCss,
7913
+ worldDirectionalLightToCss,
7914
+ worldDirectionalLightToPolyCss,
7915
+ worldDistanceToCss,
7370
7916
  worldDistanceToPolyCss,
7917
+ worldPositionToCss,
7371
7918
  worldPositionToPolyCss
7372
7919
  });