@layoutit/polycss-vue 0.2.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,188 +1,202 @@
1
- > **Status: pre-1.0. APIs may still change before a stable 1.0 release.**
1
+ # PolyCSS
2
2
 
3
- # @layoutit/polycss-vue
3
+ A CSS polygon mesh library. A 3D engine for the DOM. Renders OBJ/MTL, STL, glTF/GLB, and VOX as real HTML elements transformed with CSS `matrix3d(...)`. Supports colors, textures, lighting, shadows, shapes and animations. Works with React, Vue or plain JavaScript.
4
4
 
5
- Native Vue 3 components for CSS-based polygon mesh rendering. Loads OBJ, glTF, GLB, and MagicaVoxel `.vox` files; renders each polygon as a real DOM element (atlas-backed `<i>` for both textured and flat-color faces) positioned with `transform: matrix3d(...)`. No WebGL, no canvas-as-scene.
5
+ Visit [polycss.com](https://polycss.com) for docs and model examples.
6
6
 
7
- ## Install
7
+ <img width="1600" height="300" alt="PolyCSS primitives banner" src="https://github.com/user-attachments/assets/b05e2204-9323-4f83-8d1b-01ea0dd000db" />
8
+
9
+ ## Installation
8
10
 
9
11
  ```bash
10
- npm install @layoutit/polycss-vue
11
- ```
12
12
 
13
- Requires Vue 3 as a peer dependency.
13
+ # Vanilla
14
+ npm install @layoutit/polycss
14
15
 
15
- ## Quickstart
16
+ # React
17
+ npm install @layoutit/polycss-react
16
18
 
17
- ```vue
18
- <template>
19
- <PolyCamera :rot-x="65" :rot-y="45" :perspective="1000">
20
- <PolyScene>
21
- <PolyMesh src="/cottage.glb" />
22
- </PolyScene>
23
- </PolyCamera>
24
- </template>
19
+ # Vue
20
+ npm install @layoutit/polycss-vue
25
21
 
26
- <script setup lang="ts">
27
- import { PolyCamera, PolyScene, PolyMesh } from "@layoutit/polycss-vue";
28
- </script>
29
22
  ```
30
23
 
31
- ## Component reference
32
24
 
33
- ### `<PolyScene>`
25
+ You can also load PolyCSS directly from a CDN. Here is a minimal custom-element scene:
34
26
 
35
- Root of every Vue polycss render tree. Renders polygons and meshes inside a `<PolyCamera>` context, and owns scene-level lighting and atlas options.
27
+ ```html
28
+ <script type="module" src="https://esm.sh/@layoutit/polycss/elements"></script>
36
29
 
37
- | Prop | Type | Default | Description |
38
- |---|---|---|---|
39
- | `directional-light` | `PolyDirectionalLight` | None | Directional light config |
40
- | `ambient-light` | `PolyAmbientLight` | None | Ambient light config |
41
- | `texture-lighting` | `"baked" \| "dynamic"` | `"baked"` | Texture lighting mode |
42
- | `atlas-scale` | `number \| "auto"` | `"auto"` | Atlas bitmap budget and compositor sprite size |
43
- | `polygons` | `Polygon[]` | None | Static polygon array (composes with slot) |
30
+ <poly-camera rot-x="65" rot-y="45">
31
+ <poly-scene>
32
+ <poly-orbit-controls drag wheel></poly-orbit-controls>
33
+ <poly-box size="100" color="#ffd166"></poly-box>
34
+ </poly-scene>
35
+ </poly-camera>
36
+ ```
44
37
 
45
- For pointer drag, wheel zoom, and autorotate, mount `<PolyOrbitControls>` (or `<PolyMapControls>` for pan-first map-style input) inside `<PolyCamera>`: it receives the camera context. Mirrors Three.js's split between camera state and input.
38
+ <img width="2500" height="1145" alt="PolyCSS intro" src="https://github.com/user-attachments/assets/0e5df0d8-04a8-4e50-8e3a-1097a96ce42f" />
46
39
 
47
- ### `<PolyMesh>`
40
+ ## Framework Components
48
41
 
49
- Loads a mesh from a URL and renders its polygons. Manages blob-URL lifecycle automatically.
42
+ React and Vue expose the same component model. `<PolyCamera>` owns the viewpoint, `<PolyScene>` owns lighting and atlas options, and `<PolyMesh>` loads or receives polygon data.
50
43
 
51
- | Prop | Type | Description |
52
- |---|---|---|
53
- | `src` | `string` | URL to `.obj`, `.glb`, `.gltf`, or `.vox` |
54
- | `polygons` | `Polygon[]` | Pre-parsed polygons (alternative to `src`) |
55
- | `position` | `Vec3` | `[x, y, z]` offset in scene space |
56
- | `scale` | `number \| Vec3` | Uniform or per-axis scale |
57
- | `rotation` | `Vec3` | Euler angles in degrees `[x, y, z]` |
58
- | `atlas-scale` | `number \| "auto"` | Atlas bitmap budget and compositor sprite size |
59
- | `auto-center` | `boolean` | Shift mesh so its bbox center is at origin |
60
- | `mtl` | `string` | Companion `.mtl` URL for OBJ models |
44
+ ```tsx
45
+ import { PolyCamera, PolyScene, PolyOrbitControls, PolyMesh } from "@layoutit/polycss-react";
61
46
 
62
- Named slot: `#polygon="{ polygon, index }"`: per-polygon scoped slot for rendering overrides. The default slot is for static children inside the mesh wrapper.
47
+ export default function App() {
48
+ return (
49
+ <PolyCamera rotX={65} rotY={45}>
50
+ <PolyScene textureLighting="dynamic">
51
+ <PolyOrbitControls drag wheel />
52
+ <PolyMesh src="/gallery/obj/cottage.obj" mtl="/gallery/obj/cottage.mtl" />
53
+ </PolyScene>
54
+ </PolyCamera>
55
+ );
56
+ }
57
+ ```
63
58
 
64
- ### `<Poly>`
59
+ ## API Reference
65
60
 
66
- Single polygon. Renders one atlas-backed `<i>` for UV-textured and flat-color faces. Accepts standard Vue event bindings and class/style.
61
+ ### PolyCamera
67
62
 
68
- | Prop | Type | Description |
69
- |---|---|---|
70
- | `vertices` | `Vec3[]` | Required: 3+ `[x, y, z]` points |
71
- | `color` | `string` | CSS color; used when no texture is set |
72
- | `texture` | `string` | Image URL for UV-mapped rendering |
73
- | `uvs` | `Vec2[]` | UV coordinates, one per vertex |
74
- | `data` | `Record<string, string \| number \| boolean>` | Reflected as `data-*` DOM attributes |
75
- | `position` | `Vec3` | Local offset |
76
- | `scale` | `number \| Vec3` | Scale |
77
- | `rotation` | `Vec3` | Euler rotation in degrees |
78
- | `atlas-scale` | `number \| "auto"` | Atlas bitmap budget and compositor sprite size |
63
+ - `rotX`, `rotY` control the orbit angle in degrees.
64
+ - `zoom` scales the projected scene.
65
+ - `target` pans the camera target in world coordinates.
66
+ - `distance` adds dolly pull-back.
67
+ - `PolyCamera` is the orthographic default. Use `PolyPerspectiveCamera` when you want perspective depth.
79
68
 
80
- ### `<PolyCamera>`
69
+ ### PolyScene
81
70
 
82
- Camera wrapper for perspective, rotation, zoom, target, and dolly distance. Vue scenes must render inside `<PolyCamera>` (or `<PolyPerspectiveCamera>` / `<PolyOrthographicCamera>`) so controls and scenes share camera state.
71
+ - `polygons` renders a static `Polygon[]` directly.
72
+ - `directionalLight` and `ambientLight` control scene lighting.
73
+ - `textureLighting` chooses `"baked"` or `"dynamic"`.
74
+ - `textureQuality` controls atlas raster budget.
75
+ - `strategies` can disable selected render strategies for diagnostics.
76
+ - `autoCenter` rotates around the rendered mesh bounds instead of world origin.
83
77
 
84
- ### Composables
78
+ ### PolyMesh
85
79
 
86
- | Composable | Description |
87
- |---|---|
88
- | `usePolyCamera(options)` | Internal camera integration composable |
89
- | `usePolySceneContext(polygons, options)` | Lower-level hook for custom scene wrappers |
90
- | `usePolyMesh(srcRef, options?)` | Reactive mesh loader. Returns reactive `{ polygons, loading, error, warnings, dispose }`. |
80
+ - `src` loads `.obj`, `.gltf`, `.glb`, or `.vox` files.
81
+ - `mtl` loads companion OBJ materials.
82
+ - `polygons` accepts pre-parsed geometry.
83
+ - `position`, `scale`, and `rotation` transform the mesh wrapper.
84
+ - `autoCenter` shifts the mesh bbox center to local origin.
85
+ - `meshResolution` chooses `"lossy"` (default) or `"lossless"` optimization. STL imports use the conservative lossless path in both modes.
86
+ - `castShadow` emits CSS-projected shadows in dynamic lighting mode.
91
87
 
92
- ### Utility
88
+ ### Controls
93
89
 
94
- | Export | Description |
95
- |---|---|
96
- | `injectPolyBaseStyles(doc?)` | Inject polycss base CSS into the document. Idempotent. |
90
+ - `<PolyOrbitControls>` adds drag orbit, shift-drag pan, wheel zoom, and optional auto-rotate.
91
+ - `<PolyMapControls>` uses pan-first map-style input.
92
+ - `<PolyFirstPersonControls>` provides keyboard and pointer-look navigation.
93
+ - `<PolyTransformControls>` adds translate/rotate gizmos for selected mesh handles.
97
94
 
98
- ## Re-exports from `@layoutit/polycss-core`
95
+ ### Snapshot Export
99
96
 
100
- All types and core functions are re-exported:
97
+ The vanilla package exports `exportPolySceneSnapshot(target)`. It clones the current rendered `.polycss-camera` / `.polycss-scene` DOM, injects only the PolyCSS CSS needed by that snapshot, inlines CSS `url(...)` image assets as `data:image/...;base64,...`, strips scripts and inline event handlers, and returns a standalone HTML document string with no PolyCSS runtime import. It works with rendered React/Vue scenes too; import it from `@layoutit/polycss` and pass the rendered camera or scene element.
101
98
 
102
99
  ```ts
103
- import type { Polygon, Vec2, Vec3, PolyDirectionalLight, PolyAmbientLight, ParseResult } from "@layoutit/polycss-vue";
104
- import { parseObj, parseGltf, parseVox, loadMesh, normalizePolygons, mergePolygons } from "@layoutit/polycss-vue";
100
+ import { exportPolySceneSnapshot } from "@layoutit/polycss";
101
+
102
+ const html = await exportPolySceneSnapshot(scene.host);
105
103
  ```
106
104
 
107
- ## Examples
105
+ If any referenced asset cannot be inlined, the function throws `PolySceneSnapshotError` with `code: "ASSET_INLINE_FAILED"`.
108
106
 
109
- ### With lighting and multiple meshes
107
+ ### Polygon Data Model
110
108
 
111
- ```vue
112
- <template>
113
- <PolyCamera :rot-x="65" :rot-y="45">
114
- <PolyScene :directional-light="light">
115
- <PolyMesh src="/cottage.glb" />
116
- <PolyMesh src="/tree.glb" :position="[8, 0, 0]" :scale="0.5" />
117
- </PolyScene>
118
- </PolyCamera>
119
- </template>
109
+ Each polygon describes one renderable face:
120
110
 
121
- <script setup lang="ts">
122
- import { PolyCamera, PolyScene, PolyMesh } from "@layoutit/polycss-vue";
123
- const light = { direction: [0.5, -0.7, 0.6] as [number, number, number], color: "#ffe4a8" };
124
- </script>
111
+ ```ts
112
+ const polygons = [
113
+ {
114
+ vertices: [[0, 0, 0], [60, 0, 0], [0, 60, 0]],
115
+ color: "#f97316",
116
+ },
117
+ {
118
+ vertices: [[0, 0, 0], [60, 0, 0], [60, 60, 0], [0, 60, 0]],
119
+ texture: "/texture.png",
120
+ uvs: [[0, 0], [1, 0], [1, 1], [0, 1]],
121
+ },
122
+ ];
125
123
  ```
126
124
 
127
- ### Per-polygon interactive
125
+ Render polygons directly when you need per-face DOM events or custom styling:
128
126
 
129
- ```vue
130
- <template>
131
- <PolyCamera :rot-x="65" :rot-y="45">
132
- <PolyScene>
127
+ ```tsx
128
+ <PolyCamera>
129
+ <PolyScene>
130
+ {polygons.map((polygon, index) => (
133
131
  <Poly
134
- v-for="(p, i) in polygons"
135
- :key="i"
136
- v-bind="p"
137
- @click="() => alert(`clicked polygon ${i}`)"
138
- @mouseenter="hoveredId = i"
139
- @mouseleave="hoveredId = null"
140
- :class="{ highlight: hoveredId === i }"
132
+ key={index}
133
+ {...polygon}
134
+ onClick={() => console.log("clicked polygon", index)}
135
+ className="my-polygon"
141
136
  />
142
- </PolyScene>
143
- </PolyCamera>
144
- </template>
145
-
146
- <script setup lang="ts">
147
- import { ref } from "vue";
148
- import { PolyCamera, PolyScene, Poly } from "@layoutit/polycss-vue";
149
- import type { Polygon } from "@layoutit/polycss-vue";
150
-
151
- defineProps<{ polygons: Polygon[] }>();
152
- const hoveredId = ref<number | null>(null);
153
- </script>
154
-
155
- <style>
156
- .highlight { filter: brightness(1.5); }
157
- </style>
137
+ ))}
138
+ </PolyScene>
139
+ </PolyCamera>
158
140
  ```
159
141
 
160
- ### `PolyMesh` with scoped slot
161
-
162
- ```vue
163
- <template>
164
- <PolyCamera :rot-x="65" :rot-y="45">
165
- <PolyScene>
166
- <PolyMesh src="/character.glb" :position="[5, 0, 0]" :scale="2">
167
- <template #polygon="{ polygon, index }">
168
- <Poly
169
- v-bind="polygon"
170
- @click="selected = index"
171
- :class="{ outlined: selected === index }"
172
- />
173
- </template>
174
- </PolyMesh>
175
- </PolyScene>
176
- </PolyCamera>
177
- </template>
178
-
179
- <script setup lang="ts">
180
- import { ref } from "vue";
181
- import { PolyCamera, PolyScene, PolyMesh, Poly } from "@layoutit/polycss-vue";
182
- const selected = ref<number | null>(null);
183
- </script>
142
+ ## Loading Mesh Files
143
+
144
+ Use `loadMesh()` to parse supported model formats:
145
+
146
+ ```ts
147
+ import { createPolyCamera, createPolyScene, loadMesh } from "@layoutit/polycss";
148
+
149
+ const host = document.getElementById("polycss")!;
150
+ const camera = createPolyCamera({ rotX: 65, rotY: 45 });
151
+ const scene = createPolyScene(host, { camera });
152
+
153
+ const mesh = await loadMesh("https://polycss.com/gallery/obj/cottage.obj", {
154
+ mtlUrl: "https://polycss.com/gallery/obj/cottage.mtl",
155
+ });
156
+
157
+ scene.add(mesh);
184
158
  ```
185
159
 
186
- ## Docs
160
+ Supported formats:
161
+
162
+ - OBJ + MTL, including `map_Kd` textures and UV coordinates.
163
+ - STL triangle meshes, including binary Magics face colors. STL has no standard units, textures, UVs, or hierarchy, so imports skip lossy simplification and ray-based interior culling.
164
+ - glTF / GLB, including embedded images and `TEXCOORD_0`.
165
+ - MagicaVoxel `.vox`, with direct voxel fast paths when eligible.
166
+ - Generated primitives: box, plane, ring, sphere, torus, cylinder, cone, and Platonic solids.
167
+
168
+ ## Performance
169
+
170
+ PolyCSS renders through the DOM, so performance is mostly shaped by two things: the number of mounted leaves, and the amount of texture atlas area the browser has to paint. The renderer tries to keep the common cases cheap. Simple surfaces stay as solid CSS elements, while textured, irregular, or high-detail geometry falls back to atlas-backed slices only when needed.
171
+
172
+ Each visible polygon is emitted as one leaf element; the renderer chooses the least expensive CSS primitive that can represent the polygon, then uses `matrix3d(...)` to place that primitive in 3D space.
173
+
174
+ - `<b>` uses `background: currentColor` on a fixed box for solid rectangles and stable quads.
175
+ - `<u>` uses `corner-shape` for stable triangles and beveled-corner solids, with a `border-width` triangle fallback when needed.
176
+ - `<i>` clips solid polygons with `border-shape: polygon(...)` when the browser supports it.
177
+ - `<s>` maps a packed texture-atlas slice with `background-image`, and is the fallback for textured or unsupported shapes.
178
+
179
+ ## Packages
180
+
181
+ | Package | Description |
182
+ |---|---|
183
+ | `@layoutit/polycss-core` | Pure math, parsers, lighting, camera helpers, mesh optimization. Zero browser globals. |
184
+ | `@layoutit/polycss` | Vanilla custom elements and imperative `createPolyScene` API. |
185
+ | `@layoutit/polycss-react` | React components, hooks, controls, and core re-exports. |
186
+ | `@layoutit/polycss-vue` | Vue 3 components, composables, controls, and core re-exports. |
187
+
188
+ ## Made with PolyCSS
189
+
190
+ [Layoutit Voxels](https://voxels.layoutit.com)
191
+ -> A CSS Voxel editor
192
+
193
+ <img width="1000" height="600" alt="layoutit-voxels" src="https://polycss.com/layoutit-voxels.png" />
194
+
195
+ [Layoutit Terra](https://terra.layoutit.com)
196
+ -> A CSS Terrain Generator
197
+
198
+ <img width="1000" height="601" alt="layoutit-terra" src="https://polycss.com/layoutit-terra.png" />
199
+
200
+ ## License
187
201
 
188
- Full documentation at [polycss.com](https://polycss.com).
202
+ MIT.