@visant/extrude3d 0.1.0
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 +126 -0
- package/dist/fonts.d.ts +14 -0
- package/dist/fonts.d.ts.map +1 -0
- package/dist/fonts.js +62 -0
- package/dist/fonts.js.map +1 -0
- package/dist/geometry.d.ts +64 -0
- package/dist/geometry.d.ts.map +1 -0
- package/dist/geometry.js +283 -0
- package/dist/geometry.js.map +1 -0
- package/dist/glb.d.ts +40 -0
- package/dist/glb.d.ts.map +1 -0
- package/dist/glb.js +165 -0
- package/dist/glb.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/materials.d.ts +111 -0
- package/dist/materials.d.ts.map +1 -0
- package/dist/materials.js +441 -0
- package/dist/materials.js.map +1 -0
- package/dist/types.d.ts +70 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +64 -0
package/README.md
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# @visant/extrude3d
|
|
2
|
+
|
|
3
|
+
**SVG / text → extruded 3D geometry**, in pure [three.js](https://threejs.org). Parse an SVG (or opentype.js glyphs) into shapes, extrude them with bevel / curve / smoothness controls, smooth crease normals, project triplanar UVs, center + scale — then either hand the `BufferGeometry` to your renderer or serialize it to a binary glTF (`.glb`).
|
|
4
|
+
|
|
5
|
+
No React, no [@react-three](https://github.com/pmndrs/react-three-fiber). Just functions over three.js objects, so the same code runs in the browser and in plain Node.
|
|
6
|
+
|
|
7
|
+
It powers the Visant Labs Studio3D engine (the `useExtrudedGeometry` hook and `<ExtrudedSVG>` component are thin wrappers around this) and the server-side GLB export.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm i @visant/extrude3d three
|
|
13
|
+
# optional — only if you use textToSvg():
|
|
14
|
+
npm i opentype.js
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
`three` is a peer dependency. `opentype.js` is an **optional** peer — this package never imports it; `textToSvg` takes any font object that structurally matches `OpenTypeFontLike`.
|
|
18
|
+
|
|
19
|
+
> The browser geometry path uses `three/examples/jsm` (`SVGLoader`, `BufferGeometryUtils`). The `./glb` path is DOM-free except that `SVGLoader` needs a `DOMParser` — in Node, install one (e.g. via `jsdom`) on `globalThis` before calling.
|
|
20
|
+
|
|
21
|
+
## Quick start
|
|
22
|
+
|
|
23
|
+
### Browser: SVG → BufferGeometry
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import { buildExtrudedGeometry } from '@visant/extrude3d';
|
|
27
|
+
import * as THREE from 'three';
|
|
28
|
+
|
|
29
|
+
const result = buildExtrudedGeometry(svgString, {
|
|
30
|
+
depth: 2, // extrusion depth knob
|
|
31
|
+
smoothness: 0.5, // 0–1 → bevel segments + curve subdivisions
|
|
32
|
+
bevelEnabled: true,
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
if (result) {
|
|
36
|
+
const mesh = new THREE.Mesh(
|
|
37
|
+
result.geometry,
|
|
38
|
+
new THREE.MeshStandardMaterial({ color: '#c0c0c0' })
|
|
39
|
+
);
|
|
40
|
+
// center + fit to a 4-unit box, matching the Studio3D engine:
|
|
41
|
+
mesh.position.set(-result.center.x, -result.center.y, -result.center.z);
|
|
42
|
+
mesh.scale.setScalar(result.baseScale);
|
|
43
|
+
|
|
44
|
+
// you own the geometry — dispose when done:
|
|
45
|
+
// result.geometry.dispose();
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Node: SVG → GLB buffer
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
import { svgToGlb } from '@visant/extrude3d/glb';
|
|
53
|
+
import { JSDOM } from 'jsdom';
|
|
54
|
+
import { writeFile } from 'node:fs/promises';
|
|
55
|
+
|
|
56
|
+
// SVGLoader needs a DOMParser in Node:
|
|
57
|
+
globalThis.DOMParser = new JSDOM('').window.DOMParser;
|
|
58
|
+
|
|
59
|
+
const { glb } = svgToGlb(svgString, {
|
|
60
|
+
depth: 0.9,
|
|
61
|
+
smoothness: 0.5,
|
|
62
|
+
color: '#c0c0c0',
|
|
63
|
+
metalness: 0.6,
|
|
64
|
+
roughness: 0.3,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
await writeFile('logo.glb', glb); // glb is a Uint8Array (glTF magic header)
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Text → SVG → geometry
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
import * as opentype from 'opentype.js';
|
|
74
|
+
import { textToSvg, buildExtrudedGeometry } from '@visant/extrude3d';
|
|
75
|
+
|
|
76
|
+
const font = opentype.parse(fontArrayBuffer);
|
|
77
|
+
const svg = textToSvg('Visant', font); // centered 200×200 SVG of glyph paths
|
|
78
|
+
const result = buildExtrudedGeometry(svg, { depth: 2, smoothness: 0.6 });
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Material presets
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
import { getSimpleMaterialProps, materialPresets } from '@visant/extrude3d/materials';
|
|
85
|
+
|
|
86
|
+
// flat prop bag for a <meshPhysicalMaterial>:
|
|
87
|
+
const props = getSimpleMaterialProps('chrome', '#ff0066');
|
|
88
|
+
// → { color, metalness, roughness, clearcoat, transmission, ior, ... }
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Exports
|
|
92
|
+
|
|
93
|
+
| Entry | Purpose |
|
|
94
|
+
| --- | --- |
|
|
95
|
+
| `.` | everything below, flat |
|
|
96
|
+
| `./materials` | PBR preset library (`materialPresets`, `MATERIAL_LIB`, `getSimpleMaterialProps`, `resolveMaterial`) |
|
|
97
|
+
| `./fonts` | `textToSvg(text, font)` — opentype glyphs → centered SVG |
|
|
98
|
+
| `./glb` | `svgToGlb(svg, opts)` — dependency-free binary glTF serializer |
|
|
99
|
+
|
|
100
|
+
### Geometry API
|
|
101
|
+
|
|
102
|
+
| Export | Signature |
|
|
103
|
+
| --- | --- |
|
|
104
|
+
| `buildExtrudedGeometry` | `(svg \| Shape[], opts) => { geometry, center, baseScale, shapeCount } \| null` |
|
|
105
|
+
| `parseShapesFromSVG` | `(svg) => THREE.Shape[]` — SVGLoader fills + tessellated strokes, drops the viewBox rect |
|
|
106
|
+
| `buildExtrudeSettings` | `(maxFlatDim, shapeCount, opts) => ExtrudeSettings` — the engine's depth/bevel/segment math |
|
|
107
|
+
| `measureFlatMaxDim` | `(shapes) => number` — larger of flat width/height (≥ 1) |
|
|
108
|
+
| `smoothCreaseNormals` | `(geometry, creaseAngleRad) => BufferGeometry` — averages normals below the crease angle |
|
|
109
|
+
| `recomputeTriplanarUVs` | `(geometry, box3) => void` — box-projected UVs, in place |
|
|
110
|
+
|
|
111
|
+
## Options
|
|
112
|
+
|
|
113
|
+
`buildExtrudedGeometry(svg, opts)`:
|
|
114
|
+
|
|
115
|
+
| Option | Default | Meaning |
|
|
116
|
+
| --- | --- | --- |
|
|
117
|
+
| `depth` | — | extrusion depth knob (scaled by flat bounds) |
|
|
118
|
+
| `smoothness` | — | 0–1 → bevel segments (`4 + s·8`) + curve subdivisions (`32 + s·64`) |
|
|
119
|
+
| `bevelEnabled` | `true` | |
|
|
120
|
+
| `bevelThickness` / `bevelSize` | `0.5` | scaled by `min(maxFlatDim·0.05, 1)`, clamped to half the depth |
|
|
121
|
+
| `vertexBudget` | `600000` | total vertex budget; segment counts shrink to fit |
|
|
122
|
+
| `creaseAngle` | `Math.PI/6` | crease-smoothing threshold; `null` → plain `computeVertexNormals` |
|
|
123
|
+
|
|
124
|
+
## License
|
|
125
|
+
|
|
126
|
+
MIT
|
package/dist/fonts.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { OpenTypeFontLike } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Render `text` with an opentype.js font into a centered, auto-fitted SVG
|
|
4
|
+
* string whose `<path>` glyphs are ready for {@link buildExtrudedGeometry}.
|
|
5
|
+
*
|
|
6
|
+
* The font is fitted into a 200×200 viewBox (10px padding), shrinking the font
|
|
7
|
+
* size in 4px steps until it fits, then centered. Per-glyph kerning is applied.
|
|
8
|
+
* Returns `''` for empty/whitespace-only text or on any failure.
|
|
9
|
+
*
|
|
10
|
+
* Pure: the caller supplies an already-loaded font object (this package never
|
|
11
|
+
* imports or depends on `opentype.js`).
|
|
12
|
+
*/
|
|
13
|
+
export declare function textToSvg(text: string, font: OpenTypeFontLike): string;
|
|
14
|
+
//# sourceMappingURL=fonts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fonts.d.ts","sourceRoot":"","sources":["../src/fonts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,gBAAgB,GAAG,MAAM,CAOtE"}
|
package/dist/fonts.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Render `text` with an opentype.js font into a centered, auto-fitted SVG
|
|
3
|
+
* string whose `<path>` glyphs are ready for {@link buildExtrudedGeometry}.
|
|
4
|
+
*
|
|
5
|
+
* The font is fitted into a 200×200 viewBox (10px padding), shrinking the font
|
|
6
|
+
* size in 4px steps until it fits, then centered. Per-glyph kerning is applied.
|
|
7
|
+
* Returns `''` for empty/whitespace-only text or on any failure.
|
|
8
|
+
*
|
|
9
|
+
* Pure: the caller supplies an already-loaded font object (this package never
|
|
10
|
+
* imports or depends on `opentype.js`).
|
|
11
|
+
*/
|
|
12
|
+
export function textToSvg(text, font) {
|
|
13
|
+
try {
|
|
14
|
+
return _textToSvg(text, font);
|
|
15
|
+
}
|
|
16
|
+
catch (err) {
|
|
17
|
+
console.warn('textToSvg failed:', err);
|
|
18
|
+
return '';
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function _textToSvg(text, font) {
|
|
22
|
+
const size = 200;
|
|
23
|
+
const available = size - 20;
|
|
24
|
+
let fontSize = 180;
|
|
25
|
+
let fullPath = font.getPath(text, 0, 0, fontSize);
|
|
26
|
+
let bb = fullPath.getBoundingBox();
|
|
27
|
+
let w = bb.x2 - bb.x1;
|
|
28
|
+
let h = bb.y2 - bb.y1;
|
|
29
|
+
while ((w > available || h > available) && fontSize > 8) {
|
|
30
|
+
fontSize -= 4;
|
|
31
|
+
fullPath = font.getPath(text, 0, 0, fontSize);
|
|
32
|
+
bb = fullPath.getBoundingBox();
|
|
33
|
+
w = bb.x2 - bb.x1;
|
|
34
|
+
h = bb.y2 - bb.y1;
|
|
35
|
+
}
|
|
36
|
+
const offsetX = (size - w) / 2 - bb.x1;
|
|
37
|
+
const offsetY = (size - h) / 2 - bb.y1;
|
|
38
|
+
const glyphs = font.stringToGlyphs(text);
|
|
39
|
+
let x = offsetX;
|
|
40
|
+
const paths = [];
|
|
41
|
+
const unitsPerEm = font.unitsPerEm || 1000;
|
|
42
|
+
for (let i = 0; i < glyphs.length; i++) {
|
|
43
|
+
const glyph = glyphs[i];
|
|
44
|
+
const glyphPath = glyph.getPath(x, offsetY, fontSize);
|
|
45
|
+
const d = glyphPath.toPathData(2);
|
|
46
|
+
if (d) {
|
|
47
|
+
paths.push(`<path d="${d}" fill="black" fill-rule="evenodd"/>`);
|
|
48
|
+
}
|
|
49
|
+
const advance = (glyph.advanceWidth || 0) * (fontSize / unitsPerEm);
|
|
50
|
+
if (i < glyphs.length - 1) {
|
|
51
|
+
const kerning = font.getKerningValue(glyphs[i], glyphs[i + 1]);
|
|
52
|
+
x += advance + kerning * (fontSize / unitsPerEm);
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
x += advance;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (paths.length === 0)
|
|
59
|
+
return '';
|
|
60
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 ${size} ${size}">${paths.join('')}</svg>`;
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=fonts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fonts.js","sourceRoot":"","sources":["../src/fonts.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import * as THREE from 'three';
|
|
2
|
+
import type { BuildExtrudedGeometryOptions, ExtrudedGeometryResult } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Average face normals within a vertex's coincident group when the angle
|
|
5
|
+
* between them is below `creaseAngleRad` (sharp edges above it stay crisp).
|
|
6
|
+
* Falls back to `computeVertexNormals` past {@link SMOOTH_VERTEX_LIMIT} verts.
|
|
7
|
+
* Returns a new, non-indexed geometry (the input is left untouched).
|
|
8
|
+
*/
|
|
9
|
+
export declare function smoothCreaseNormals(geometry: THREE.BufferGeometry, creaseAngleRad: number): THREE.BufferGeometry;
|
|
10
|
+
/**
|
|
11
|
+
* Recompute box-projected (triplanar) UVs in place, projecting each vertex onto
|
|
12
|
+
* the axis plane that best matches its normal and normalizing by the bounding
|
|
13
|
+
* box's largest dimension.
|
|
14
|
+
*/
|
|
15
|
+
export declare function recomputeTriplanarUVs(geo: THREE.BufferGeometry, bb: THREE.Box3): void;
|
|
16
|
+
/**
|
|
17
|
+
* True when `shape` is (approximately) the SVG's full viewBox rectangle — used
|
|
18
|
+
* to drop background rects that SVGLoader emits as a fill shape.
|
|
19
|
+
*/
|
|
20
|
+
export declare function isViewBoxRect(shape: THREE.Shape, vbW: number, vbH: number): boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Parse an SVG string into three.js `Shape`s ready for extrusion. Fills become
|
|
23
|
+
* shapes directly (dropping the viewBox-sized background rect); strokes are
|
|
24
|
+
* tessellated into a ribbon polygon of `strokeWidth`; paths with no explicit
|
|
25
|
+
* fill/stroke fall back to fill shapes.
|
|
26
|
+
*
|
|
27
|
+
* Accepts both whitespace- and comma-separated viewBox values.
|
|
28
|
+
*/
|
|
29
|
+
export declare function parseShapesFromSVG(svgString: string): THREE.Shape[];
|
|
30
|
+
/** Three.js ExtrudeGeometry settings, derived from the engine's knobs. */
|
|
31
|
+
export interface ExtrudeSettings {
|
|
32
|
+
depth: number;
|
|
33
|
+
bevelEnabled: boolean;
|
|
34
|
+
bevelThickness: number;
|
|
35
|
+
bevelSize: number;
|
|
36
|
+
bevelSegments: number;
|
|
37
|
+
curveSegments: number;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Translate the engine's high-level knobs (`depth`, `smoothness`, bevel
|
|
41
|
+
* thickness/size) + the SVG's flat bounds + shape complexity into concrete
|
|
42
|
+
* three.js `ExtrudeGeometry` settings, including the vertex-budget reduction.
|
|
43
|
+
* This is the exact math used by both the client geometry pipeline and the
|
|
44
|
+
* server GLB export, so they never drift.
|
|
45
|
+
*/
|
|
46
|
+
export declare function buildExtrudeSettings(maxFlatDim: number, shapeCount: number, opts: BuildExtrudedGeometryOptions): ExtrudeSettings;
|
|
47
|
+
/**
|
|
48
|
+
* Measure the flat (un-extruded) bounding box of a set of shapes and return the
|
|
49
|
+
* larger of width/height (clamped to ≥ 1) — the `maxFlatDim` that drives depth
|
|
50
|
+
* and bevel scaling. Disposes the temporary geometry it builds.
|
|
51
|
+
*/
|
|
52
|
+
export declare function measureFlatMaxDim(shapes: THREE.Shape[]): number;
|
|
53
|
+
/**
|
|
54
|
+
* SVG (or pre-parsed shapes) → a single extruded, merged, normal-smoothed,
|
|
55
|
+
* triplanar-UV'd `BufferGeometry`, plus its center and a fit-to-4-units scale.
|
|
56
|
+
*
|
|
57
|
+
* This is the pure heart of the Studio3D engine's `useExtrudedGeometry` hook
|
|
58
|
+
* (which keeps React state, progress, cancellation and disposal). The geometry
|
|
59
|
+
* math here is identical to the legacy hook — extracting it changes nothing.
|
|
60
|
+
*
|
|
61
|
+
* The caller owns the returned `geometry` and must `dispose()` it.
|
|
62
|
+
*/
|
|
63
|
+
export declare function buildExtrudedGeometry(svg: string | THREE.Shape[], opts: BuildExtrudedGeometryOptions): ExtrudedGeometryResult | null;
|
|
64
|
+
//# sourceMappingURL=geometry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"geometry.d.ts","sourceRoot":"","sources":["../src/geometry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,OAAO,KAAK,EACV,4BAA4B,EAC5B,sBAAsB,EACvB,MAAM,YAAY,CAAC;AAIpB;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,KAAK,CAAC,cAAc,EAC9B,cAAc,EAAE,MAAM,GACrB,KAAK,CAAC,cAAc,CA+EtB;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,KAAK,CAAC,cAAc,EAAE,EAAE,EAAE,KAAK,CAAC,IAAI,GAAG,IAAI,CA8BrF;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CASnF;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,KAAK,CAAC,KAAK,EAAE,CA8DnE;AAED,0EAA0E;AAC1E,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,OAAO,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,4BAA4B,GACjC,eAAe,CAoCjB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,KAAK,CAAC,KAAK,EAAE,GAAG,MAAM,CAQ/D;AAED;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,MAAM,GAAG,KAAK,CAAC,KAAK,EAAE,EAC3B,IAAI,EAAE,4BAA4B,GACjC,sBAAsB,GAAG,IAAI,CAsC/B"}
|
package/dist/geometry.js
ADDED
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
import * as THREE from 'three';
|
|
2
|
+
import { SVGLoader } from 'three/examples/jsm/loaders/SVGLoader.js';
|
|
3
|
+
import * as BufferGeometryUtils from 'three/examples/jsm/utils/BufferGeometryUtils.js';
|
|
4
|
+
const SMOOTH_VERTEX_LIMIT = 300_000;
|
|
5
|
+
/**
|
|
6
|
+
* Average face normals within a vertex's coincident group when the angle
|
|
7
|
+
* between them is below `creaseAngleRad` (sharp edges above it stay crisp).
|
|
8
|
+
* Falls back to `computeVertexNormals` past {@link SMOOTH_VERTEX_LIMIT} verts.
|
|
9
|
+
* Returns a new, non-indexed geometry (the input is left untouched).
|
|
10
|
+
*/
|
|
11
|
+
export function smoothCreaseNormals(geometry, creaseAngleRad) {
|
|
12
|
+
const tempGeo = geometry.index ? geometry.toNonIndexed() : geometry.clone();
|
|
13
|
+
const posAttr = tempGeo.attributes.position;
|
|
14
|
+
if (!posAttr)
|
|
15
|
+
return geometry;
|
|
16
|
+
const count = posAttr.count;
|
|
17
|
+
if (count > SMOOTH_VERTEX_LIMIT) {
|
|
18
|
+
tempGeo.computeVertexNormals();
|
|
19
|
+
return tempGeo;
|
|
20
|
+
}
|
|
21
|
+
const flatNormals = [];
|
|
22
|
+
for (let i = 0; i < count; i += 3) {
|
|
23
|
+
const vA = new THREE.Vector3(posAttr.getX(i), posAttr.getY(i), posAttr.getZ(i));
|
|
24
|
+
const vB = new THREE.Vector3(posAttr.getX(i + 1), posAttr.getY(i + 1), posAttr.getZ(i + 1));
|
|
25
|
+
const vC = new THREE.Vector3(posAttr.getX(i + 2), posAttr.getY(i + 2), posAttr.getZ(i + 2));
|
|
26
|
+
const cb = new THREE.Vector3().subVectors(vC, vB);
|
|
27
|
+
const ab = new THREE.Vector3().subVectors(vA, vB);
|
|
28
|
+
cb.cross(ab).normalize();
|
|
29
|
+
flatNormals.push(cb.clone(), cb.clone(), cb.clone());
|
|
30
|
+
}
|
|
31
|
+
const posToIndices = new Map();
|
|
32
|
+
const precision = 100000;
|
|
33
|
+
for (let i = 0; i < count; i++) {
|
|
34
|
+
const px = Math.round(posAttr.getX(i) * precision);
|
|
35
|
+
const py = Math.round(posAttr.getY(i) * precision);
|
|
36
|
+
const pz = Math.round(posAttr.getZ(i) * precision);
|
|
37
|
+
const hash = `${px},${py},${pz}`;
|
|
38
|
+
if (!posToIndices.has(hash)) {
|
|
39
|
+
posToIndices.set(hash, []);
|
|
40
|
+
}
|
|
41
|
+
posToIndices.get(hash).push(i);
|
|
42
|
+
}
|
|
43
|
+
const cosThreshold = Math.cos(creaseAngleRad);
|
|
44
|
+
const newNormals = new Float32Array(count * 3);
|
|
45
|
+
for (const indices of posToIndices.values()) {
|
|
46
|
+
const visited = new Set();
|
|
47
|
+
for (const idx of indices) {
|
|
48
|
+
if (visited.has(idx))
|
|
49
|
+
continue;
|
|
50
|
+
const n1 = flatNormals[idx];
|
|
51
|
+
const smoothGroup = [idx];
|
|
52
|
+
visited.add(idx);
|
|
53
|
+
for (const otherIdx of indices) {
|
|
54
|
+
if (visited.has(otherIdx))
|
|
55
|
+
continue;
|
|
56
|
+
const n2 = flatNormals[otherIdx];
|
|
57
|
+
if (n1.dot(n2) >= cosThreshold) {
|
|
58
|
+
smoothGroup.push(otherIdx);
|
|
59
|
+
visited.add(otherIdx);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
const avgNormal = new THREE.Vector3();
|
|
63
|
+
for (const i of smoothGroup) {
|
|
64
|
+
avgNormal.add(flatNormals[i]);
|
|
65
|
+
}
|
|
66
|
+
avgNormal.normalize();
|
|
67
|
+
for (const i of smoothGroup) {
|
|
68
|
+
newNormals[i * 3] = avgNormal.x;
|
|
69
|
+
newNormals[i * 3 + 1] = avgNormal.y;
|
|
70
|
+
newNormals[i * 3 + 2] = avgNormal.z;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
tempGeo.setAttribute('normal', new THREE.BufferAttribute(newNormals, 3));
|
|
75
|
+
return tempGeo;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Recompute box-projected (triplanar) UVs in place, projecting each vertex onto
|
|
79
|
+
* the axis plane that best matches its normal and normalizing by the bounding
|
|
80
|
+
* box's largest dimension.
|
|
81
|
+
*/
|
|
82
|
+
export function recomputeTriplanarUVs(geo, bb) {
|
|
83
|
+
const bbSize = new THREE.Vector3();
|
|
84
|
+
bb.getSize(bbSize);
|
|
85
|
+
const uvAttr = geo.attributes.uv;
|
|
86
|
+
const posAttr = geo.attributes.position;
|
|
87
|
+
const normalAttr = geo.attributes.normal;
|
|
88
|
+
const maxDimUv = Math.max(bbSize.x, bbSize.y, bbSize.z) || 1;
|
|
89
|
+
for (let j = 0; j < uvAttr.count; j++) {
|
|
90
|
+
const px = posAttr.getX(j);
|
|
91
|
+
const py = posAttr.getY(j);
|
|
92
|
+
const pz = posAttr.getZ(j);
|
|
93
|
+
const nx = Math.abs(normalAttr.getX(j));
|
|
94
|
+
const ny = Math.abs(normalAttr.getY(j));
|
|
95
|
+
const nz = Math.abs(normalAttr.getZ(j));
|
|
96
|
+
let u, v;
|
|
97
|
+
if (nz >= nx && nz >= ny) {
|
|
98
|
+
u = (px - bb.min.x) / maxDimUv;
|
|
99
|
+
v = 1 - (py - bb.min.y) / maxDimUv;
|
|
100
|
+
}
|
|
101
|
+
else if (nx >= ny) {
|
|
102
|
+
u = (pz - bb.min.z) / maxDimUv;
|
|
103
|
+
v = 1 - (py - bb.min.y) / maxDimUv;
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
u = (px - bb.min.x) / maxDimUv;
|
|
107
|
+
v = (pz - bb.min.z) / maxDimUv;
|
|
108
|
+
}
|
|
109
|
+
uvAttr.setXY(j, u, v);
|
|
110
|
+
}
|
|
111
|
+
uvAttr.needsUpdate = true;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* True when `shape` is (approximately) the SVG's full viewBox rectangle — used
|
|
115
|
+
* to drop background rects that SVGLoader emits as a fill shape.
|
|
116
|
+
*/
|
|
117
|
+
export function isViewBoxRect(shape, vbW, vbH) {
|
|
118
|
+
const pts = shape.getPoints(4);
|
|
119
|
+
if (pts.length !== 4 && pts.length !== 5)
|
|
120
|
+
return false;
|
|
121
|
+
const bb = new THREE.Box2();
|
|
122
|
+
for (const p of pts)
|
|
123
|
+
bb.expandByPoint(p);
|
|
124
|
+
const size = new THREE.Vector2();
|
|
125
|
+
bb.getSize(size);
|
|
126
|
+
const tolerance = 0.01;
|
|
127
|
+
return Math.abs(size.x - vbW) / vbW < tolerance && Math.abs(size.y - vbH) / vbH < tolerance;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Parse an SVG string into three.js `Shape`s ready for extrusion. Fills become
|
|
131
|
+
* shapes directly (dropping the viewBox-sized background rect); strokes are
|
|
132
|
+
* tessellated into a ribbon polygon of `strokeWidth`; paths with no explicit
|
|
133
|
+
* fill/stroke fall back to fill shapes.
|
|
134
|
+
*
|
|
135
|
+
* Accepts both whitespace- and comma-separated viewBox values.
|
|
136
|
+
*/
|
|
137
|
+
export function parseShapesFromSVG(svgString) {
|
|
138
|
+
const loader = new SVGLoader();
|
|
139
|
+
const svgData = loader.parse(svgString);
|
|
140
|
+
const allShapes = [];
|
|
141
|
+
const vbMatch = svgString.match(/viewBox\s*=\s*["']\s*([\d.\-]+)[\s,]+([\d.\-]+)[\s,]+([\d.\-]+)[\s,]+([\d.\-]+)/);
|
|
142
|
+
const vbW = vbMatch ? parseFloat(vbMatch[3]) : null;
|
|
143
|
+
const vbH = vbMatch ? parseFloat(vbMatch[4]) : null;
|
|
144
|
+
svgData.paths.forEach((path) => {
|
|
145
|
+
const style = path.userData?.style;
|
|
146
|
+
const hasFill = style?.fill && style.fill !== 'none' && style.fill !== 'transparent';
|
|
147
|
+
const hasStroke = style?.stroke && style.stroke !== 'none' && style.stroke !== 'transparent';
|
|
148
|
+
if (hasFill) {
|
|
149
|
+
const shapes = SVGLoader.createShapes(path);
|
|
150
|
+
for (const shape of shapes) {
|
|
151
|
+
if (vbW && vbH && isViewBoxRect(shape, vbW, vbH))
|
|
152
|
+
continue;
|
|
153
|
+
allShapes.push(shape);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
if (hasStroke) {
|
|
157
|
+
const strokeWidth = parseFloat(style?.strokeWidth ?? '2');
|
|
158
|
+
const divisions = 12;
|
|
159
|
+
path.subPaths.forEach((subPath) => {
|
|
160
|
+
const points = subPath.getPoints(divisions);
|
|
161
|
+
if (points.length < 2)
|
|
162
|
+
return;
|
|
163
|
+
const shape = new THREE.Shape();
|
|
164
|
+
const halfWidth = strokeWidth / 2;
|
|
165
|
+
const leftSide = [];
|
|
166
|
+
const rightSide = [];
|
|
167
|
+
for (let i = 0; i < points.length; i++) {
|
|
168
|
+
const curr = points[i];
|
|
169
|
+
const prev = points[Math.max(0, i - 1)];
|
|
170
|
+
const next = points[Math.min(points.length - 1, i + 1)];
|
|
171
|
+
const dx = next.x - prev.x;
|
|
172
|
+
const dy = next.y - prev.y;
|
|
173
|
+
const len = Math.sqrt(dx * dx + dy * dy) || 1;
|
|
174
|
+
const nx = -dy / len;
|
|
175
|
+
const ny = dx / len;
|
|
176
|
+
leftSide.push(new THREE.Vector2(curr.x + nx * halfWidth, curr.y + ny * halfWidth));
|
|
177
|
+
rightSide.push(new THREE.Vector2(curr.x - nx * halfWidth, curr.y - ny * halfWidth));
|
|
178
|
+
}
|
|
179
|
+
shape.moveTo(leftSide[0].x, leftSide[0].y);
|
|
180
|
+
for (let i = 1; i < leftSide.length; i++)
|
|
181
|
+
shape.lineTo(leftSide[i].x, leftSide[i].y);
|
|
182
|
+
for (let i = rightSide.length - 1; i >= 0; i--)
|
|
183
|
+
shape.lineTo(rightSide[i].x, rightSide[i].y);
|
|
184
|
+
shape.closePath();
|
|
185
|
+
allShapes.push(shape);
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
if (!hasFill && !hasStroke) {
|
|
189
|
+
allShapes.push(...SVGLoader.createShapes(path));
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
return allShapes;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Translate the engine's high-level knobs (`depth`, `smoothness`, bevel
|
|
196
|
+
* thickness/size) + the SVG's flat bounds + shape complexity into concrete
|
|
197
|
+
* three.js `ExtrudeGeometry` settings, including the vertex-budget reduction.
|
|
198
|
+
* This is the exact math used by both the client geometry pipeline and the
|
|
199
|
+
* server GLB export, so they never drift.
|
|
200
|
+
*/
|
|
201
|
+
export function buildExtrudeSettings(maxFlatDim, shapeCount, opts) {
|
|
202
|
+
const { depth, smoothness, bevelEnabled = true, bevelThickness: userThickness = 0.5, bevelSize: userSize = 0.5, vertexBudget = 600_000, } = opts;
|
|
203
|
+
const complexity = shapeCount;
|
|
204
|
+
const vertsBudgetPerShape = Math.max(Math.floor(vertexBudget / Math.max(complexity, 1)), 500);
|
|
205
|
+
const scaledDepth = (depth / 10) * maxFlatDim;
|
|
206
|
+
const bevelScale = Math.min(maxFlatDim * 0.05, 1);
|
|
207
|
+
const idealBevel = Math.round(4 + smoothness * 8);
|
|
208
|
+
const idealCurve = Math.round(32 + smoothness * 64);
|
|
209
|
+
const estimatedVerts = idealBevel * idealCurve * 6;
|
|
210
|
+
const reductionFactor = estimatedVerts > vertsBudgetPerShape ? Math.sqrt(vertsBudgetPerShape / estimatedVerts) : 1;
|
|
211
|
+
const bevelSegments = Math.max(2, Math.min(Math.round(idealBevel * reductionFactor), 64));
|
|
212
|
+
const curveSegments = Math.max(8, Math.min(Math.round(idealCurve * reductionFactor), 128));
|
|
213
|
+
const maxBevel = Math.max(0.01, scaledDepth * 0.5);
|
|
214
|
+
const bevelThickness = Math.min(bevelScale * userThickness, maxBevel);
|
|
215
|
+
const bevelSize = Math.min(bevelScale * userSize, maxBevel);
|
|
216
|
+
return {
|
|
217
|
+
depth: scaledDepth,
|
|
218
|
+
bevelEnabled,
|
|
219
|
+
bevelThickness,
|
|
220
|
+
bevelSize,
|
|
221
|
+
bevelSegments,
|
|
222
|
+
curveSegments,
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Measure the flat (un-extruded) bounding box of a set of shapes and return the
|
|
227
|
+
* larger of width/height (clamped to ≥ 1) — the `maxFlatDim` that drives depth
|
|
228
|
+
* and bevel scaling. Disposes the temporary geometry it builds.
|
|
229
|
+
*/
|
|
230
|
+
export function measureFlatMaxDim(shapes) {
|
|
231
|
+
const tempGeo = new THREE.ShapeGeometry(shapes);
|
|
232
|
+
tempGeo.computeBoundingBox();
|
|
233
|
+
const flatSize = new THREE.Vector3();
|
|
234
|
+
tempGeo.boundingBox.getSize(flatSize);
|
|
235
|
+
const maxFlatDim = Math.max(flatSize.x, flatSize.y, 1);
|
|
236
|
+
tempGeo.dispose();
|
|
237
|
+
return maxFlatDim;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* SVG (or pre-parsed shapes) → a single extruded, merged, normal-smoothed,
|
|
241
|
+
* triplanar-UV'd `BufferGeometry`, plus its center and a fit-to-4-units scale.
|
|
242
|
+
*
|
|
243
|
+
* This is the pure heart of the Studio3D engine's `useExtrudedGeometry` hook
|
|
244
|
+
* (which keeps React state, progress, cancellation and disposal). The geometry
|
|
245
|
+
* math here is identical to the legacy hook — extracting it changes nothing.
|
|
246
|
+
*
|
|
247
|
+
* The caller owns the returned `geometry` and must `dispose()` it.
|
|
248
|
+
*/
|
|
249
|
+
export function buildExtrudedGeometry(svg, opts) {
|
|
250
|
+
const allShapes = typeof svg === 'string' ? parseShapesFromSVG(svg) : svg;
|
|
251
|
+
if (allShapes.length === 0)
|
|
252
|
+
return null;
|
|
253
|
+
const maxFlatDim = measureFlatMaxDim(allShapes);
|
|
254
|
+
const extrudeSettings = buildExtrudeSettings(maxFlatDim, allShapes.length, opts);
|
|
255
|
+
const individualGeos = [];
|
|
256
|
+
for (let i = 0; i < allShapes.length; i++) {
|
|
257
|
+
individualGeos.push(new THREE.ExtrudeGeometry(allShapes[i], extrudeSettings));
|
|
258
|
+
}
|
|
259
|
+
let merged = BufferGeometryUtils.mergeGeometries(individualGeos, false);
|
|
260
|
+
individualGeos.forEach((g) => g.dispose());
|
|
261
|
+
if (!merged)
|
|
262
|
+
return null;
|
|
263
|
+
const creaseAngle = opts.creaseAngle === undefined ? Math.PI / 6 : opts.creaseAngle;
|
|
264
|
+
if (creaseAngle != null) {
|
|
265
|
+
const smoothed = smoothCreaseNormals(merged, creaseAngle);
|
|
266
|
+
merged.dispose();
|
|
267
|
+
merged = smoothed;
|
|
268
|
+
}
|
|
269
|
+
else {
|
|
270
|
+
merged.computeVertexNormals();
|
|
271
|
+
}
|
|
272
|
+
merged.computeBoundingBox();
|
|
273
|
+
recomputeTriplanarUVs(merged, merged.boundingBox);
|
|
274
|
+
const bb = merged.boundingBox;
|
|
275
|
+
const ctr = new THREE.Vector3();
|
|
276
|
+
bb.getCenter(ctr);
|
|
277
|
+
const size = new THREE.Vector3();
|
|
278
|
+
bb.getSize(size);
|
|
279
|
+
const maxDim = Math.max(size.x, size.y, size.z);
|
|
280
|
+
const s = maxDim > 0 ? 4 / maxDim : 1;
|
|
281
|
+
return { geometry: merged, center: ctr, baseScale: s, shapeCount: allShapes.length };
|
|
282
|
+
}
|
|
283
|
+
//# sourceMappingURL=geometry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"geometry.js","sourceRoot":"","sources":["../src/geometry.ts"],"names":[],"mappings":"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"}
|
package/dist/glb.d.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Options for {@link svgToGlb}. All optional; defaults match the Studio3D
|
|
3
|
+
* server export. `depth`/`smoothness`/bevel drive the same extrude math the
|
|
4
|
+
* client uses; `color`/`metalness`/`roughness` become the GLB PBR material.
|
|
5
|
+
*/
|
|
6
|
+
export interface SvgToGlbOptions {
|
|
7
|
+
depth?: number;
|
|
8
|
+
smoothness?: number;
|
|
9
|
+
bevelEnabled?: boolean;
|
|
10
|
+
bevelThickness?: number;
|
|
11
|
+
bevelSize?: number;
|
|
12
|
+
color?: string;
|
|
13
|
+
metalness?: number;
|
|
14
|
+
roughness?: number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Output of {@link svgToGlb}: the raw GLB bytes plus the interleaved geometry
|
|
18
|
+
* arrays they were built from (handy for tests / further processing).
|
|
19
|
+
*/
|
|
20
|
+
export interface GlbResult {
|
|
21
|
+
/** Raw `.glb` bytes (`glTF` magic header). */
|
|
22
|
+
glb: Uint8Array;
|
|
23
|
+
positions: Float32Array;
|
|
24
|
+
normals: Float32Array;
|
|
25
|
+
indices: Uint32Array;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* SVG string → extruded, merged, centered geometry serialized as a binary glTF
|
|
29
|
+
* (`.glb`). This is the pure core of the Studio3D server export
|
|
30
|
+
* (`studio3dExportService.svgToGlb`); the service keeps only the DOMParser/JSDOM
|
|
31
|
+
* environment shim and storage. Geometry math (extrude settings, vertex budget)
|
|
32
|
+
* matches the legacy server path exactly — plain `computeVertexNormals`, no
|
|
33
|
+
* crease smoothing, no bevel clamp.
|
|
34
|
+
*
|
|
35
|
+
* Returns the GLB bytes plus the geometry arrays. Throws if the SVG yields no
|
|
36
|
+
* shapes or the merge fails. Requires a DOM `DOMParser` to be available (the
|
|
37
|
+
* server installs one via JSDOM before calling).
|
|
38
|
+
*/
|
|
39
|
+
export declare function svgToGlb(svgString: string, opts?: SvgToGlbOptions): GlbResult;
|
|
40
|
+
//# sourceMappingURL=glb.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"glb.d.ts","sourceRoot":"","sources":["../src/glb.ts"],"names":[],"mappings":"AAIA;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAaD;;;GAGG;AACH,MAAM,WAAW,SAAS;IACxB,8CAA8C;IAC9C,GAAG,EAAE,UAAU,CAAC;IAChB,SAAS,EAAE,YAAY,CAAC;IACxB,OAAO,EAAE,YAAY,CAAC;IACtB,OAAO,EAAE,WAAW,CAAC;CACtB;AAyGD;;;;;;;;;;;GAWG;AACH,wBAAgB,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,GAAE,eAAoB,GAAG,SAAS,CA4DjF"}
|