@layoutit/polycss 0.2.6 → 0.2.8

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.
@@ -0,0 +1,359 @@
1
+ import { CameraHandle, Vec3, Polygon, MeshResolution, ParseResult, PolyDirectionalLight, PolyPointLight, PolyAmbientLight, PolyTextureLightingMode, TextureQuality, PolyTextureLeafSizing, PolyTextureImageRendering, PolyTextureBackend, PolyTextureProjection, PolySeamBleed, PolyRenderStrategiesOption } from '@layoutit/polycss-core';
2
+
3
+ interface PolyCameraOptions {
4
+ zoom?: number;
5
+ target?: Vec3;
6
+ rotX?: number;
7
+ rotY?: number;
8
+ /** Camera pull-back in CSS pixels (dolly). Default 0. */
9
+ distance?: number;
10
+ }
11
+ interface PolyPerspectiveCameraOptions extends PolyCameraOptions {
12
+ /** CSS perspective distance in pixels. Default 32000. */
13
+ perspective?: number;
14
+ }
15
+ interface PolyOrthographicCameraOptions extends PolyCameraOptions {
16
+ }
17
+ /** Extends CameraHandle with projection info for the container element. */
18
+ interface PolyPerspectiveCameraHandle extends CameraHandle {
19
+ readonly type: "perspective";
20
+ /** CSS `perspective` value to set on the camera container element. */
21
+ readonly perspectiveStyle: string;
22
+ }
23
+ interface PolyOrthographicCameraHandle extends CameraHandle {
24
+ readonly type: "orthographic";
25
+ /** CSS `perspective` value to set on the camera container element ("none"). */
26
+ readonly perspectiveStyle: "none";
27
+ }
28
+ /**
29
+ * Creates a perspective camera handle. The `perspectiveStyle` property
30
+ * returns the CSS value to apply to the camera container's `perspective`
31
+ * property (default `"32000px"`).
32
+ */
33
+ declare function createPolyPerspectiveCamera(options?: PolyPerspectiveCameraOptions): PolyPerspectiveCameraHandle;
34
+ /**
35
+ * Creates an orthographic camera handle. The `perspectiveStyle` property
36
+ * returns `"none"` — pass it to the container element's CSS `perspective`
37
+ * to disable perspective projection.
38
+ */
39
+ declare function createPolyOrthographicCamera(options?: PolyOrthographicCameraOptions): PolyOrthographicCameraHandle;
40
+ /**
41
+ * Ergonomic alias for `createPolyOrthographicCamera`. The default camera in
42
+ * PolyCSS is orthographic because the engine's structural advantages
43
+ * (integer-pixel atlas slicing, DOM-as-render-tree) are most visible in
44
+ * iso/voxel/diagrammatic scenes. Use `createPolyPerspectiveCamera` explicitly
45
+ * when depth foreshortening is needed.
46
+ */
47
+ declare const createPolyCamera: typeof createPolyOrthographicCamera;
48
+
49
+ /**
50
+ * Public + internal types for the scene module, extracted from
51
+ * createPolyScene.ts so other scene/* helpers can import them without
52
+ * pulling in the whole factory body. createPolyScene.ts re-exports the
53
+ * public ones so the polycss package public surface is unchanged.
54
+ */
55
+
56
+ interface PolySceneOptions {
57
+ /**
58
+ * Camera handle created by `createPolyCamera`, `createPolyOrthographicCamera`,
59
+ * or `createPolyPerspectiveCamera`. Required — `createPolyScene` will throw if
60
+ * this field is missing.
61
+ */
62
+ camera: PolyPerspectiveCameraHandle | PolyOrthographicCameraHandle;
63
+ directionalLight?: PolyDirectionalLight;
64
+ /** Point lights (world-space positions). Direction-only per-face Lambert
65
+ * shading (no distance falloff). Baked-mode only. */
66
+ pointLights?: PolyPointLight[];
67
+ ambientLight?: PolyAmbientLight;
68
+ /** Textured polygon lighting mode. Defaults to "baked". */
69
+ textureLighting?: PolyTextureLightingMode;
70
+ /** Atlas bitmap budget and CSS sprite size. `"auto"` uses a
71
+ * device-appropriate memory budget (~4 MB mobile / ~16 MB desktop) and
72
+ * desktop/mobile sprite sizing. Numeric values 0.1..1 force an explicit
73
+ * raster scale and the 64px sprite. */
74
+ textureQuality?: TextureQuality;
75
+ /** Texture leaf primitive sizing. Defaults to "canonical". */
76
+ textureLeafSizing?: PolyTextureLeafSizing;
77
+ /** Default image filtering for atlas and direct image texture leaves. */
78
+ textureImageRendering?: PolyTextureImageRendering;
79
+ /** Default texture backend request. Defaults to "auto". */
80
+ textureBackend?: PolyTextureBackend;
81
+ /** Default texture projection request. Defaults to "affine". */
82
+ textureProjection?: PolyTextureProjection;
83
+ /** Solid seam overscan. `"auto"` computes a fitted per-edge amount from the polygon plan. */
84
+ seamBleed?: PolySeamBleed;
85
+ /**
86
+ * Skip specific render-strategy tags. Polygons that would normally use a
87
+ * disabled tag fall through the chain (b → i → s, u → i → s, i → s).
88
+ * `<s>` is the universal fallback and cannot be disabled.
89
+ */
90
+ strategies?: PolyRenderStrategiesOption;
91
+ /**
92
+ * When `true`, rotation pivots around the union bbox of all added meshes
93
+ * instead of world (0,0,0). The scene wraps polygons in an inner div
94
+ * translated by `-bboxCenter`. Updates whenever a mesh is added/removed
95
+ * or `setOptions` is called. Mirrors React's `<PolyScene autoCenter>`.
96
+ */
97
+ autoCenter?: boolean;
98
+ /**
99
+ * Shadow appearance for meshes with `castShadow: true`. Works in both
100
+ * lighting modes — dynamic mode projects via CSS vars so shadows
101
+ * follow a moving light, baked mode CPU-bakes the projection into
102
+ * each leaf's inline `matrix3d` and drops back-facing polys from the
103
+ * DOM entirely. Defaults: `{ color: "#000000", opacity: 0.25, lift: 0.05, maxExtend: 2000 }`.
104
+ */
105
+ shadow?: {
106
+ /** Shadow color as a CSS hex string. Default: `"#000000"`. */
107
+ color?: string;
108
+ /** Shadow opacity 0..1. Default: `0.25`. */
109
+ opacity?: number;
110
+ /**
111
+ * Raises the shadow plane slightly above the model bbox floor along
112
+ * +Z (Z up) so it sits on top of a receiver mesh placed at the bbox
113
+ * bottom, rather than below it where the receiver would occlude the
114
+ * shadow. In world units. Default: `0.05`.
115
+ */
116
+ lift?: number;
117
+ /**
118
+ * Maximum CSS pixels the shadow may extend beyond the mesh's
119
+ * footprint (the no-shear silhouette directly under the mesh). The
120
+ * footprint area is always preserved; only the sheared tail at low
121
+ * light elevations is truncated. Default: `2000`.
122
+ *
123
+ * **Trade-off:** larger values give longer shadows but the SVG
124
+ * backing store grows quadratically with this value, which can
125
+ * cause repaint flicker at extreme low-elevation angles. Pass a
126
+ * very large number (e.g. `Infinity`) to disable the cap entirely.
127
+ */
128
+ maxExtend?: number;
129
+ /**
130
+ * Experimental: cast a single low-resolution parametric **silhouette**
131
+ * outline per caster instead of projecting its full geometry. Lighter DOM
132
+ * + cheaper projection, at the cost of an approximate (convex) outline.
133
+ * Casts onto every receiver through the normal pipeline. Default: `false`.
134
+ */
135
+ parametric?: boolean;
136
+ /**
137
+ * Parametric-shadow detail: the max number of points in the silhouette
138
+ * outline. Lower → blobbier + lighter; higher → closer to the exact convex
139
+ * outline. Only used when `parametric` is true. Default: `16`.
140
+ *
141
+ * Can be overridden per mesh via `PolyMeshTransform.shadowDefinition`.
142
+ */
143
+ definition?: number;
144
+ /**
145
+ * Progressive refinement: the definition used WHILE the directional light
146
+ * is actively changing (a drag). When set (and `parametric` is true), each
147
+ * light-direction change emits at `min(definition, dragDefinition)` for a
148
+ * laggless drag, then a debounced pass re-emits at full `definition` once
149
+ * the light settles — mirroring the atlas-rebake-at-rest escape hatch.
150
+ * Self-shadow recompute is O(faces × bands), so dropping detail during
151
+ * motion is the only way to keep a complex mesh smooth. Unset → no
152
+ * progressive pass (every change renders at full `definition`).
153
+ */
154
+ dragDefinition?: number;
155
+ /**
156
+ * Parametric-shadow render style (only used when `parametric` is true):
157
+ * - `"vector"` (default) — smooth concave contour outline.
158
+ * - `"pixel"` — the coverage is greedy-meshed into axis-aligned rectangles,
159
+ * giving a blocky/voxel shadow. Holes (courtyards, the coliseum arena)
160
+ * come free as absent cells. `definition` is the pixel-grid resolution
161
+ * (lower → chunkier); the block size is the aesthetic.
162
+ */
163
+ style?: "vector" | "pixel";
164
+ /**
165
+ * Re-emit shadows while a mesh is animating (skeletal/GLB deformation), so
166
+ * the shadow follows the pose instead of freezing at the rest pose. Each
167
+ * `setPolygons` from the animation loop triggers a re-projection, throttled
168
+ * internally (~12fps) so it stays affordable. Strongly recommended only
169
+ * with `parametric: true` (a low-res silhouette is cheap to reproject every
170
+ * few frames; the exact path is not). Default: `false`.
171
+ */
172
+ followAnimation?: boolean;
173
+ };
174
+ /**
175
+ * When `true`, emit `data-poly-shadow-*` attribution attributes on every
176
+ * shadow SVG and path (type, receiver mesh id, receiver face index,
177
+ * member poly indices, caster ids, caster poly indices). Useful for
178
+ * DevTools inspection and per-poly attribution in debug benches. When
179
+ * `false` (default), these attributes are suppressed entirely — production
180
+ * scenes ship a cleaner DOM and avoid serializing per-frame JSON.
181
+ */
182
+ debugShadowAttrs?: boolean;
183
+ }
184
+ interface PolyMeshTransform {
185
+ /** Stable identifier — exposed on the handle and reflected on the
186
+ * wrapper as `data-poly-mesh-id`. Used by selection helpers to
187
+ * resolve clicks back to the mesh and to dedupe selection state. */
188
+ id?: string;
189
+ position?: Vec3;
190
+ scale?: number | Vec3;
191
+ rotation?: Vec3;
192
+ /**
193
+ * Whether `scene.add()` should merge coplanar polygons before rendering.
194
+ * Defaults to `true`. Set `false` for animated/deforming meshes whose
195
+ * triangle topology must remain stable from frame to frame.
196
+ */
197
+ merge?: boolean;
198
+ /**
199
+ * Mesh optimization intent. Defaults to `"lossy"` (bounded geometric
200
+ * approximation when it reduces polygon count). Set `"lossless"` to preserve
201
+ * the authored surface — only exact coplanar merges are applied.
202
+ */
203
+ meshResolution?: MeshResolution;
204
+ /**
205
+ * Keep polygon leaf DOM nodes stable across setPolygons() calls when the
206
+ * mesh topology is unchanged. Intended for animated/deforming meshes.
207
+ */
208
+ stableDom?: boolean;
209
+ /**
210
+ * When `true`, this mesh's polygons are NOT included in the scene's
211
+ * auto-center bbox. Use for debug overlays / helpers that shouldn't
212
+ * shift the camera target when toggled. Defaults to `false`.
213
+ */
214
+ excludeFromAutoCenter?: boolean;
215
+ /**
216
+ * When `true`, this mesh casts a shadow onto the scene's shadow ground
217
+ * plane (and onto any meshes marked `receiveShadow: true`). The shadow
218
+ * emits as one per-mesh `<svg>` whose path is the union of every
219
+ * casting polygon's projection. Works in both lighting modes.
220
+ * Defaults to `false`.
221
+ */
222
+ castShadow?: boolean;
223
+ /**
224
+ * **(experimental)** When `true`, this mesh acts as a shadow receiver:
225
+ * each of its polygon faces becomes a target plane that casting meshes'
226
+ * shadows project onto and get clipped to. Useful for "shadow on table"
227
+ * scenarios. Currently only convex face outlines clip cleanly. When no
228
+ * receivers are present the global ground plane is used as today.
229
+ * Defaults to `false`.
230
+ */
231
+ receiveShadow?: boolean;
232
+ /**
233
+ * Per-mesh parametric-shadow detail (overrides the scene's
234
+ * `shadow.definition` for THIS mesh's cast/self shadow). Lets a detailed
235
+ * caster stay high-resolution while a simple prop runs cheap in the same
236
+ * scene. Only used when `shadow.parametric` is true. Unset → inherit the
237
+ * scene definition.
238
+ */
239
+ shadowDefinition?: number;
240
+ }
241
+ interface PolyMeshHandle {
242
+ /** The polygons that were loaded after normalization and automatic merge. */
243
+ polygons: Polygon[];
244
+ /** The `.polycss-mesh` wrapper div for this mesh. Exposed so layered
245
+ * helpers (selection, transform controls) can resolve a click target
246
+ * back to its owning mesh, attach event listeners, or measure the
247
+ * mesh's screen position via `getBoundingClientRect`. */
248
+ readonly element: HTMLElement;
249
+ /** Identifier passed via `PolyMeshTransform.id` (if any). Reflected on
250
+ * the wrapper as `data-poly-mesh-id`. */
251
+ readonly id?: string;
252
+ /** Current transform snapshot (position / rotation / scale). Returned
253
+ * by reference — treat as read-only and use `setTransform` to mutate. */
254
+ readonly transform: PolyMeshTransform;
255
+ /** Remove the mesh from the scene. */
256
+ remove(): void;
257
+ /** Replace polygon geometry without tearing down the scene or controls. */
258
+ setPolygons(polygons: Polygon[], options?: {
259
+ merge?: boolean;
260
+ stableDom?: boolean;
261
+ recomputeAutoCenter?: boolean;
262
+ }): void;
263
+ /**
264
+ * Update a single polygon in place. `target` is either a polygon
265
+ * reference (as returned by `getPolygons()`) or its index. `partial`
266
+ * fields are merged onto the polygon; the mesh is then re-rendered.
267
+ * Skips the merge pass, so this is cheaper than `setPolygons` for
268
+ * targeted edits like color picker updates from an inspector UI.
269
+ * Silently no-ops if `target` isn't found.
270
+ */
271
+ updatePolygon(target: Polygon | number, partial: Partial<Polygon>): void;
272
+ /** Update transform without re-parsing. */
273
+ setTransform(t: Partial<PolyMeshTransform>): void;
274
+ /** Revoke any blob URLs the parse created. Idempotent. */
275
+ dispose(): void;
276
+ /**
277
+ * Re-rasterize the atlas using the directional light inverse-rotated into
278
+ * the mesh's local frame. Call this after a mesh rotation has been
279
+ * committed (e.g., on pointer release in rotate-mode transform controls) to
280
+ * correct stale baked shading.
281
+ *
282
+ * **Background:** Baked atlas tiles encode `baseColor × Lambert(worldNormal,
283
+ * worldLight)`. When the mesh wrapper rotates via CSS, the polygon's normal
284
+ * in world space changes but the baked color doesn't — faces stay lit/unlit
285
+ * incorrectly. `rebakeAtlas()` inverse-rotates the world light into the
286
+ * mesh's local frame and re-runs the rasterizer; because
287
+ * `dot(localNormal, localLight) === dot(worldNormal, worldLight)` the
288
+ * output is correct for any rotation.
289
+ *
290
+ * **Performance note:** This does NOT run on every `setTransform` call —
291
+ * only when explicitly invoked, so dragging remains smooth. Call it on
292
+ * pointer release (or any point where you want to commit the new shading).
293
+ */
294
+ rebakeAtlas(): void;
295
+ /** Resolves when this mesh's current texture generation has usable backgrounds. */
296
+ whenTexturesReady(): Promise<void>;
297
+ /** Current `position` from the transform (matches framework API). */
298
+ getPosition(): Vec3 | undefined;
299
+ /** Current `rotation` from the transform (matches framework API). */
300
+ getRotation(): Vec3 | undefined;
301
+ /** Current `scale` from the transform (matches framework API). */
302
+ getScale(): number | Vec3 | undefined;
303
+ /** Polygons currently being rendered (matches framework API). */
304
+ getPolygons(): Polygon[];
305
+ }
306
+ interface PolySceneHandle {
307
+ /** Add a mesh to the scene. Returns a handle for later removal. */
308
+ add(mesh: ParseResult, opts?: PolyMeshTransform): PolyMeshHandle;
309
+ /** Update scene-level config (lighting, autoCenter, strategies, etc.). Camera state is on `scene.camera`. */
310
+ setOptions(partial: Partial<Omit<PolySceneOptions, "camera">>): void;
311
+ /** Tear down the scene; revokes all blob URLs of registered meshes. */
312
+ destroy(): void;
313
+ /**
314
+ * The host element passed to `createPolyScene`. Exposed for layered
315
+ * helpers like `createPolyOrbitControls` that need to attach event listeners
316
+ * without tracking the host separately.
317
+ */
318
+ readonly host: HTMLElement;
319
+ /**
320
+ * The `.polycss-camera` wrapper element created by `createPolyScene` between
321
+ * the host and the `.polycss-scene` element. Carries the CSS `perspective`
322
+ * that matches React/Vue's `<div class="polycss-camera">` wrapper shape.
323
+ * FPV controls toggle `.polycss-fpv-host` on this element.
324
+ */
325
+ readonly cameraEl: HTMLElement;
326
+ /**
327
+ * The `.polycss-scene` root element inside `cameraEl`. Mesh wrappers, shadow
328
+ * roots, and helper DOM are mounted under this element.
329
+ */
330
+ readonly sceneElement: HTMLElement;
331
+ /**
332
+ * The camera handle this scene is bound to. Controls update camera state
333
+ * via `scene.camera.update({...})` then call `scene.applyCamera()` to
334
+ * re-apply the transform.
335
+ */
336
+ readonly camera: PolyPerspectiveCameraHandle | PolyOrthographicCameraHandle;
337
+ /**
338
+ * Re-applies the scene transform from the current camera state. Call this
339
+ * after mutating `scene.camera.update({...})` to make the change visible.
340
+ * Controls call this once per interaction event after updating camera state.
341
+ */
342
+ applyCamera(): void;
343
+ /**
344
+ * Snapshot of the current non-camera scene options (lighting, autoCenter,
345
+ * textureQuality, strategies, shadow). Returned by reference — treat as
346
+ * read-only; use `setOptions` to update.
347
+ */
348
+ getOptions(): Readonly<Omit<PolySceneOptions, "camera">>;
349
+ /** Snapshot of mesh handles currently in the scene (insertion order).
350
+ * Used by selection helpers to enumerate hit-test candidates. */
351
+ meshes(): readonly PolyMeshHandle[];
352
+ /** Resolves when every mesh currently in the scene has usable texture backgrounds. */
353
+ whenTexturesReady(): Promise<void>;
354
+ /** Resolve a `.polycss-mesh` element back to its handle, or `null` if
355
+ * the element doesn't belong to this scene. */
356
+ findMeshByElement(element: Element | null): PolyMeshHandle | null;
357
+ }
358
+
359
+ export { type PolySceneHandle as P, type PolyMeshHandle as a, type PolyCameraOptions as b, type PolyMeshTransform as c, type PolyOrthographicCameraHandle as d, type PolyOrthographicCameraOptions as e, type PolyPerspectiveCameraHandle as f, type PolyPerspectiveCameraOptions as g, type PolySceneOptions as h, createPolyCamera as i, createPolyOrthographicCamera as j, createPolyPerspectiveCamera as k };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@layoutit/polycss",
3
- "version": "0.2.6",
3
+ "version": "0.2.8",
4
4
  "description": "PolyCSS vanilla / custom-elements + imperative API. Renders OBJ / STL / glTF / GLB mesh polygons as DOM via CSS matrix3d.",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -41,13 +41,28 @@
41
41
  "types": "./dist/elements.d.ts",
42
42
  "import": "./dist/elements.js",
43
43
  "require": "./dist/elements.cjs"
44
+ },
45
+ "./three": {
46
+ "types": "./dist/three.d.ts",
47
+ "import": "./dist/three.js",
48
+ "require": "./dist/three.cjs"
49
+ }
50
+ },
51
+ "typesVersions": {
52
+ "*": {
53
+ "three": [
54
+ "dist/three.d.ts"
55
+ ],
56
+ "elements": [
57
+ "dist/elements.d.ts"
58
+ ]
44
59
  }
45
60
  },
46
61
  "publishConfig": {
47
62
  "access": "public"
48
63
  },
49
64
  "dependencies": {
50
- "@layoutit/polycss-core": "^0.2.6"
65
+ "@layoutit/polycss-core": "^0.2.8"
51
66
  },
52
67
  "devDependencies": {
53
68
  "tsup": "^8.0.1",