three-text 0.2.14 → 0.2.16
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 +13 -2
- package/dist/index.cjs +18 -18
- package/dist/index.js +18 -18
- package/dist/index.min.cjs +4 -4
- package/dist/index.min.js +4 -4
- package/dist/index.umd.js +18 -18
- package/dist/index.umd.min.js +4 -4
- package/dist/types/webgpu/index.d.ts +1 -0
- package/dist/webgpu/index.cjs +4 -2
- package/dist/webgpu/index.d.ts +1 -0
- package/dist/webgpu/index.js +4 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -25,11 +25,12 @@ Under the hood, three-text relies on [HarfBuzz](https://github.com/harfbuzz/harf
|
|
|
25
25
|
|
|
26
26
|
- [Overview](#overview)
|
|
27
27
|
- [Getting started](#getting-started)
|
|
28
|
+
- [Architecture](#architecture)
|
|
28
29
|
- [Three.js](#threejs)
|
|
29
30
|
- [React Three Fiber](#react-three-fiber)
|
|
30
31
|
- [p5.js](#p5js)
|
|
32
|
+
- [Coordinate systems](#coordinate-systems)
|
|
31
33
|
- [Development and examples](#development-and-examples)
|
|
32
|
-
- [Architecture](#architecture)
|
|
33
34
|
- [Why three-text?](#why-three-text)
|
|
34
35
|
- [Library structure](#library-structure)
|
|
35
36
|
- [Key concepts and methods](#key-concepts-and-methods)
|
|
@@ -142,6 +143,13 @@ function draw() {
|
|
|
142
143
|
|
|
143
144
|
`createThreeTextGeometry()` accepts all the same options as Three.js (`layout`, `fontVariations`, `depth`, etc.) and returns `{ geometry, planeBounds, glyphs }`. Use `planeBounds` to center the text
|
|
144
145
|
|
|
146
|
+
### Coordinate systems
|
|
147
|
+
|
|
148
|
+
The core library uses a right-handed coordinate system with +Y down. Text extrudes from z=0 toward positive Z
|
|
149
|
+
|
|
150
|
+
**Three.js, WebGL, WebGPU:** Geometry is used as-is. Front cap normals point +Z
|
|
151
|
+
|
|
152
|
+
**p5.js:** The adapter flips Y coordinates (p5 uses +Y up) but preserves Z. When using `directionalLight(r, g, b, x, y, z)`, note that p5 negates the direction vector internally
|
|
145
153
|
|
|
146
154
|
### Setup
|
|
147
155
|
|
|
@@ -322,7 +330,10 @@ For text with tight tracking, connected scripts, or complex kerning pairs, indiv
|
|
|
322
330
|
|
|
323
331
|
#### Flat geometry mode
|
|
324
332
|
|
|
325
|
-
When `depth` is 0, the library generates single-sided geometry
|
|
333
|
+
When `depth` is 0, the library generates single-sided geometry, reducing triangles by approximately 50%
|
|
334
|
+
|
|
335
|
+
- Use `THREE.DoubleSide` for flat text so it remains visible from both sides
|
|
336
|
+
- For extruded text, `THREE.FrontSide` is typical since front and back faces are separate geometry
|
|
326
337
|
|
|
327
338
|
|
|
328
339
|
## Configuration
|
package/dist/index.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* three-text v0.2.
|
|
2
|
+
* three-text v0.2.16
|
|
3
3
|
* Copyright (C) 2025 Countertype LLC
|
|
4
4
|
*
|
|
5
5
|
* This program is free software: you can redistribute it and/or modify
|
|
@@ -2786,12 +2786,11 @@ class Extruder {
|
|
|
2786
2786
|
const points = geometry.triangles.vertices;
|
|
2787
2787
|
const triangleIndices = geometry.triangles.indices;
|
|
2788
2788
|
const numPoints = points.length / 2;
|
|
2789
|
-
// Count side-wall segments (
|
|
2789
|
+
// Count side-wall segments (4 vertices + 6 indices per segment)
|
|
2790
2790
|
let sideSegments = 0;
|
|
2791
2791
|
if (depth !== 0) {
|
|
2792
2792
|
for (const contour of geometry.contours) {
|
|
2793
|
-
//
|
|
2794
|
-
// Contours are expected to be closed (last point repeats first), so segments = (nPoints - 1)
|
|
2793
|
+
// Contours are closed (last point repeats first)
|
|
2795
2794
|
const contourPoints = contour.length / 2;
|
|
2796
2795
|
if (contourPoints >= 2)
|
|
2797
2796
|
sideSegments += contourPoints - 1;
|
|
@@ -2807,7 +2806,7 @@ class Extruder {
|
|
|
2807
2806
|
: triangleIndices.length * 2 + sideSegments * 6;
|
|
2808
2807
|
const indices = new Uint32Array(indexCount);
|
|
2809
2808
|
if (depth === 0) {
|
|
2810
|
-
//
|
|
2809
|
+
// Single-sided flat geometry at z=0
|
|
2811
2810
|
let vPos = 0;
|
|
2812
2811
|
for (let i = 0; i < points.length; i += 2) {
|
|
2813
2812
|
vertices[vPos] = points[i];
|
|
@@ -2815,18 +2814,19 @@ class Extruder {
|
|
|
2815
2814
|
vertices[vPos + 2] = 0;
|
|
2816
2815
|
normals[vPos] = 0;
|
|
2817
2816
|
normals[vPos + 1] = 0;
|
|
2818
|
-
normals[vPos + 2] =
|
|
2817
|
+
normals[vPos + 2] = 1;
|
|
2819
2818
|
vPos += 3;
|
|
2820
2819
|
}
|
|
2820
|
+
// libtess outputs CCW, use as-is for +Z facing geometry
|
|
2821
2821
|
for (let i = 0; i < triangleIndices.length; i++) {
|
|
2822
2822
|
indices[i] = triangleIndices[i];
|
|
2823
2823
|
}
|
|
2824
2824
|
return { vertices, normals, indices };
|
|
2825
2825
|
}
|
|
2826
|
-
//
|
|
2826
|
+
// Extruded geometry: front at z=0, back at z=depth
|
|
2827
2827
|
const minBackOffset = unitsPerEm * 0.000025;
|
|
2828
2828
|
const backZ = depth <= minBackOffset ? minBackOffset : depth;
|
|
2829
|
-
//
|
|
2829
|
+
// Cap at z=0, back face
|
|
2830
2830
|
for (let p = 0, vi = 0; p < points.length; p += 2, vi++) {
|
|
2831
2831
|
const base = vi * 3;
|
|
2832
2832
|
vertices[base] = points[p];
|
|
@@ -2836,7 +2836,7 @@ class Extruder {
|
|
|
2836
2836
|
normals[base + 1] = 0;
|
|
2837
2837
|
normals[base + 2] = -1;
|
|
2838
2838
|
}
|
|
2839
|
-
//
|
|
2839
|
+
// Cap at z=depth, front face
|
|
2840
2840
|
for (let p = 0, vi = 0; p < points.length; p += 2, vi++) {
|
|
2841
2841
|
const base = (numPoints + vi) * 3;
|
|
2842
2842
|
vertices[base] = points[p];
|
|
@@ -2846,14 +2846,14 @@ class Extruder {
|
|
|
2846
2846
|
normals[base + 1] = 0;
|
|
2847
2847
|
normals[base + 2] = 1;
|
|
2848
2848
|
}
|
|
2849
|
-
//
|
|
2849
|
+
// libtess outputs CCW triangles (viewed from +Z)
|
|
2850
|
+
// Z=0 cap faces -Z, reverse winding
|
|
2850
2851
|
for (let i = 0; i < triangleIndices.length; i++) {
|
|
2851
|
-
indices[i] = triangleIndices[i];
|
|
2852
|
+
indices[i] = triangleIndices[triangleIndices.length - 1 - i];
|
|
2852
2853
|
}
|
|
2853
|
-
//
|
|
2854
|
+
// Z=depth cap faces +Z, use original winding
|
|
2854
2855
|
for (let i = 0; i < triangleIndices.length; i++) {
|
|
2855
|
-
indices[triangleIndices.length + i] =
|
|
2856
|
-
triangleIndices[triangleIndices.length - 1 - i] + numPoints;
|
|
2856
|
+
indices[triangleIndices.length + i] = triangleIndices[i] + numPoints;
|
|
2857
2857
|
}
|
|
2858
2858
|
// Side walls
|
|
2859
2859
|
let nextVertex = numPoints * 2;
|
|
@@ -2864,7 +2864,7 @@ class Extruder {
|
|
|
2864
2864
|
const p0y = contour[i + 1];
|
|
2865
2865
|
const p1x = contour[i + 2];
|
|
2866
2866
|
const p1y = contour[i + 3];
|
|
2867
|
-
//
|
|
2867
|
+
// Perpendicular normal for this wall segment
|
|
2868
2868
|
const ex = p1x - p0x;
|
|
2869
2869
|
const ey = p1y - p0y;
|
|
2870
2870
|
const lenSq = ex * ex + ey * ey;
|
|
@@ -2877,7 +2877,7 @@ class Extruder {
|
|
|
2877
2877
|
}
|
|
2878
2878
|
const baseVertex = nextVertex;
|
|
2879
2879
|
const base = baseVertex * 3;
|
|
2880
|
-
//
|
|
2880
|
+
// Wall quad: front edge at z=0, back edge at z=depth
|
|
2881
2881
|
vertices[base] = p0x;
|
|
2882
2882
|
vertices[base + 1] = p0y;
|
|
2883
2883
|
vertices[base + 2] = 0;
|
|
@@ -2890,7 +2890,7 @@ class Extruder {
|
|
|
2890
2890
|
vertices[base + 9] = p1x;
|
|
2891
2891
|
vertices[base + 10] = p1y;
|
|
2892
2892
|
vertices[base + 11] = backZ;
|
|
2893
|
-
//
|
|
2893
|
+
// Wall normals point perpendicular to edge
|
|
2894
2894
|
normals[base] = nx;
|
|
2895
2895
|
normals[base + 1] = ny;
|
|
2896
2896
|
normals[base + 2] = 0;
|
|
@@ -2903,7 +2903,7 @@ class Extruder {
|
|
|
2903
2903
|
normals[base + 9] = nx;
|
|
2904
2904
|
normals[base + 10] = ny;
|
|
2905
2905
|
normals[base + 11] = 0;
|
|
2906
|
-
//
|
|
2906
|
+
// Two triangles per wall segment
|
|
2907
2907
|
indices[idxPos++] = baseVertex;
|
|
2908
2908
|
indices[idxPos++] = baseVertex + 1;
|
|
2909
2909
|
indices[idxPos++] = baseVertex + 2;
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* three-text v0.2.
|
|
2
|
+
* three-text v0.2.16
|
|
3
3
|
* Copyright (C) 2025 Countertype LLC
|
|
4
4
|
*
|
|
5
5
|
* This program is free software: you can redistribute it and/or modify
|
|
@@ -2783,12 +2783,11 @@ class Extruder {
|
|
|
2783
2783
|
const points = geometry.triangles.vertices;
|
|
2784
2784
|
const triangleIndices = geometry.triangles.indices;
|
|
2785
2785
|
const numPoints = points.length / 2;
|
|
2786
|
-
// Count side-wall segments (
|
|
2786
|
+
// Count side-wall segments (4 vertices + 6 indices per segment)
|
|
2787
2787
|
let sideSegments = 0;
|
|
2788
2788
|
if (depth !== 0) {
|
|
2789
2789
|
for (const contour of geometry.contours) {
|
|
2790
|
-
//
|
|
2791
|
-
// Contours are expected to be closed (last point repeats first), so segments = (nPoints - 1)
|
|
2790
|
+
// Contours are closed (last point repeats first)
|
|
2792
2791
|
const contourPoints = contour.length / 2;
|
|
2793
2792
|
if (contourPoints >= 2)
|
|
2794
2793
|
sideSegments += contourPoints - 1;
|
|
@@ -2804,7 +2803,7 @@ class Extruder {
|
|
|
2804
2803
|
: triangleIndices.length * 2 + sideSegments * 6;
|
|
2805
2804
|
const indices = new Uint32Array(indexCount);
|
|
2806
2805
|
if (depth === 0) {
|
|
2807
|
-
//
|
|
2806
|
+
// Single-sided flat geometry at z=0
|
|
2808
2807
|
let vPos = 0;
|
|
2809
2808
|
for (let i = 0; i < points.length; i += 2) {
|
|
2810
2809
|
vertices[vPos] = points[i];
|
|
@@ -2812,18 +2811,19 @@ class Extruder {
|
|
|
2812
2811
|
vertices[vPos + 2] = 0;
|
|
2813
2812
|
normals[vPos] = 0;
|
|
2814
2813
|
normals[vPos + 1] = 0;
|
|
2815
|
-
normals[vPos + 2] =
|
|
2814
|
+
normals[vPos + 2] = 1;
|
|
2816
2815
|
vPos += 3;
|
|
2817
2816
|
}
|
|
2817
|
+
// libtess outputs CCW, use as-is for +Z facing geometry
|
|
2818
2818
|
for (let i = 0; i < triangleIndices.length; i++) {
|
|
2819
2819
|
indices[i] = triangleIndices[i];
|
|
2820
2820
|
}
|
|
2821
2821
|
return { vertices, normals, indices };
|
|
2822
2822
|
}
|
|
2823
|
-
//
|
|
2823
|
+
// Extruded geometry: front at z=0, back at z=depth
|
|
2824
2824
|
const minBackOffset = unitsPerEm * 0.000025;
|
|
2825
2825
|
const backZ = depth <= minBackOffset ? minBackOffset : depth;
|
|
2826
|
-
//
|
|
2826
|
+
// Cap at z=0, back face
|
|
2827
2827
|
for (let p = 0, vi = 0; p < points.length; p += 2, vi++) {
|
|
2828
2828
|
const base = vi * 3;
|
|
2829
2829
|
vertices[base] = points[p];
|
|
@@ -2833,7 +2833,7 @@ class Extruder {
|
|
|
2833
2833
|
normals[base + 1] = 0;
|
|
2834
2834
|
normals[base + 2] = -1;
|
|
2835
2835
|
}
|
|
2836
|
-
//
|
|
2836
|
+
// Cap at z=depth, front face
|
|
2837
2837
|
for (let p = 0, vi = 0; p < points.length; p += 2, vi++) {
|
|
2838
2838
|
const base = (numPoints + vi) * 3;
|
|
2839
2839
|
vertices[base] = points[p];
|
|
@@ -2843,14 +2843,14 @@ class Extruder {
|
|
|
2843
2843
|
normals[base + 1] = 0;
|
|
2844
2844
|
normals[base + 2] = 1;
|
|
2845
2845
|
}
|
|
2846
|
-
//
|
|
2846
|
+
// libtess outputs CCW triangles (viewed from +Z)
|
|
2847
|
+
// Z=0 cap faces -Z, reverse winding
|
|
2847
2848
|
for (let i = 0; i < triangleIndices.length; i++) {
|
|
2848
|
-
indices[i] = triangleIndices[i];
|
|
2849
|
+
indices[i] = triangleIndices[triangleIndices.length - 1 - i];
|
|
2849
2850
|
}
|
|
2850
|
-
//
|
|
2851
|
+
// Z=depth cap faces +Z, use original winding
|
|
2851
2852
|
for (let i = 0; i < triangleIndices.length; i++) {
|
|
2852
|
-
indices[triangleIndices.length + i] =
|
|
2853
|
-
triangleIndices[triangleIndices.length - 1 - i] + numPoints;
|
|
2853
|
+
indices[triangleIndices.length + i] = triangleIndices[i] + numPoints;
|
|
2854
2854
|
}
|
|
2855
2855
|
// Side walls
|
|
2856
2856
|
let nextVertex = numPoints * 2;
|
|
@@ -2861,7 +2861,7 @@ class Extruder {
|
|
|
2861
2861
|
const p0y = contour[i + 1];
|
|
2862
2862
|
const p1x = contour[i + 2];
|
|
2863
2863
|
const p1y = contour[i + 3];
|
|
2864
|
-
//
|
|
2864
|
+
// Perpendicular normal for this wall segment
|
|
2865
2865
|
const ex = p1x - p0x;
|
|
2866
2866
|
const ey = p1y - p0y;
|
|
2867
2867
|
const lenSq = ex * ex + ey * ey;
|
|
@@ -2874,7 +2874,7 @@ class Extruder {
|
|
|
2874
2874
|
}
|
|
2875
2875
|
const baseVertex = nextVertex;
|
|
2876
2876
|
const base = baseVertex * 3;
|
|
2877
|
-
//
|
|
2877
|
+
// Wall quad: front edge at z=0, back edge at z=depth
|
|
2878
2878
|
vertices[base] = p0x;
|
|
2879
2879
|
vertices[base + 1] = p0y;
|
|
2880
2880
|
vertices[base + 2] = 0;
|
|
@@ -2887,7 +2887,7 @@ class Extruder {
|
|
|
2887
2887
|
vertices[base + 9] = p1x;
|
|
2888
2888
|
vertices[base + 10] = p1y;
|
|
2889
2889
|
vertices[base + 11] = backZ;
|
|
2890
|
-
//
|
|
2890
|
+
// Wall normals point perpendicular to edge
|
|
2891
2891
|
normals[base] = nx;
|
|
2892
2892
|
normals[base + 1] = ny;
|
|
2893
2893
|
normals[base + 2] = 0;
|
|
@@ -2900,7 +2900,7 @@ class Extruder {
|
|
|
2900
2900
|
normals[base + 9] = nx;
|
|
2901
2901
|
normals[base + 10] = ny;
|
|
2902
2902
|
normals[base + 11] = 0;
|
|
2903
|
-
//
|
|
2903
|
+
// Two triangles per wall segment
|
|
2904
2904
|
indices[idxPos++] = baseVertex;
|
|
2905
2905
|
indices[idxPos++] = baseVertex + 1;
|
|
2906
2906
|
indices[idxPos++] = baseVertex + 2;
|
package/dist/index.min.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* three-text v0.2.
|
|
2
|
+
* three-text v0.2.16
|
|
3
3
|
* Copyright (C) 2025 Countertype LLC
|
|
4
4
|
*
|
|
5
5
|
* This program is free software: you can redistribute it and/or modify
|
|
@@ -475,13 +475,13 @@ let o=0
|
|
|
475
475
|
if(0!==e)for(const e of t.qi){const t=e.length/2
|
|
476
476
|
2>t||(o+=t-1)}const h=(0===e?r:2*r)+(0===e?0:4*o),a=new Float32Array(3*h),l=new Float32Array(3*h),c=new Uint32Array(0===e?n.length:2*n.length+6*o)
|
|
477
477
|
if(0===e){let t=0
|
|
478
|
-
for(let e=0;s.length>e;e+=2)a[t]=s[e],a[t+1]=s[e+1],a[t+2]=0,l[t]=0,l[t+1]=0,l[t+2]
|
|
478
|
+
for(let e=0;s.length>e;e+=2)a[t]=s[e],a[t+1]=s[e+1],a[t+2]=0,l[t]=0,l[t+1]=0,l[t+2]=1,t+=3
|
|
479
479
|
for(let t=0;n.length>t;t++)c[t]=n[t]
|
|
480
480
|
return{vertices:a,normals:l,indices:c}}const u=25e-6*i,f=e>u?e:u
|
|
481
481
|
for(let t=0,e=0;s.length>t;t+=2,e++){const i=3*e
|
|
482
482
|
a[i]=s[t],a[i+1]=s[t+1],a[i+2]=0,l[i]=0,l[i+1]=0,l[i+2]=-1}for(let t=0,e=0;s.length>t;t+=2,e++){const i=3*(r+e)
|
|
483
|
-
a[i]=s[t],a[i+1]=s[t+1],a[i+2]=f,l[i]=0,l[i+1]=0,l[i+2]=1}for(let t=0;n.length>t;t++)c[t]=n[t]
|
|
484
|
-
for(let t=0;n.length>t;t++)c[n.length+t]=n[
|
|
483
|
+
a[i]=s[t],a[i+1]=s[t+1],a[i+2]=f,l[i]=0,l[i+1]=0,l[i+2]=1}for(let t=0;n.length>t;t++)c[t]=n[n.length-1-t]
|
|
484
|
+
for(let t=0;n.length>t;t++)c[n.length+t]=n[t]+r
|
|
485
485
|
let d=2*r,y=2*n.length
|
|
486
486
|
for(const e of t.qi)for(let t=0;e.length-2>t;t+=2){const i=e[t],s=e[t+1],n=e[t+2],r=e[t+3],o=n-i,h=r-s,u=o*o+h*h
|
|
487
487
|
let p=0,m=0
|
package/dist/index.min.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* three-text v0.2.
|
|
2
|
+
* three-text v0.2.16
|
|
3
3
|
* Copyright (C) 2025 Countertype LLC
|
|
4
4
|
*
|
|
5
5
|
* This program is free software: you can redistribute it and/or modify
|
|
@@ -474,13 +474,13 @@ let o=0
|
|
|
474
474
|
if(0!==e)for(const e of t.qi){const t=e.length/2
|
|
475
475
|
2>t||(o+=t-1)}const h=(0===e?r:2*r)+(0===e?0:4*o),a=new Float32Array(3*h),l=new Float32Array(3*h),c=new Uint32Array(0===e?n.length:2*n.length+6*o)
|
|
476
476
|
if(0===e){let t=0
|
|
477
|
-
for(let e=0;s.length>e;e+=2)a[t]=s[e],a[t+1]=s[e+1],a[t+2]=0,l[t]=0,l[t+1]=0,l[t+2]
|
|
477
|
+
for(let e=0;s.length>e;e+=2)a[t]=s[e],a[t+1]=s[e+1],a[t+2]=0,l[t]=0,l[t+1]=0,l[t+2]=1,t+=3
|
|
478
478
|
for(let t=0;n.length>t;t++)c[t]=n[t]
|
|
479
479
|
return{vertices:a,normals:l,indices:c}}const u=25e-6*i,f=e>u?e:u
|
|
480
480
|
for(let t=0,e=0;s.length>t;t+=2,e++){const i=3*e
|
|
481
481
|
a[i]=s[t],a[i+1]=s[t+1],a[i+2]=0,l[i]=0,l[i+1]=0,l[i+2]=-1}for(let t=0,e=0;s.length>t;t+=2,e++){const i=3*(r+e)
|
|
482
|
-
a[i]=s[t],a[i+1]=s[t+1],a[i+2]=f,l[i]=0,l[i+1]=0,l[i+2]=1}for(let t=0;n.length>t;t++)c[t]=n[t]
|
|
483
|
-
for(let t=0;n.length>t;t++)c[n.length+t]=n[
|
|
482
|
+
a[i]=s[t],a[i+1]=s[t+1],a[i+2]=f,l[i]=0,l[i+1]=0,l[i+2]=1}for(let t=0;n.length>t;t++)c[t]=n[n.length-1-t]
|
|
483
|
+
for(let t=0;n.length>t;t++)c[n.length+t]=n[t]+r
|
|
484
484
|
let d=2*r,y=2*n.length
|
|
485
485
|
for(const e of t.qi)for(let t=0;e.length-2>t;t+=2){const i=e[t],s=e[t+1],n=e[t+2],r=e[t+3],o=n-i,h=r-s,u=o*o+h*h
|
|
486
486
|
let p=0,g=0
|
package/dist/index.umd.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* three-text v0.2.
|
|
2
|
+
* three-text v0.2.16
|
|
3
3
|
* Copyright (C) 2025 Countertype LLC
|
|
4
4
|
*
|
|
5
5
|
* This program is free software: you can redistribute it and/or modify
|
|
@@ -2790,12 +2790,11 @@
|
|
|
2790
2790
|
const points = geometry.triangles.vertices;
|
|
2791
2791
|
const triangleIndices = geometry.triangles.indices;
|
|
2792
2792
|
const numPoints = points.length / 2;
|
|
2793
|
-
// Count side-wall segments (
|
|
2793
|
+
// Count side-wall segments (4 vertices + 6 indices per segment)
|
|
2794
2794
|
let sideSegments = 0;
|
|
2795
2795
|
if (depth !== 0) {
|
|
2796
2796
|
for (const contour of geometry.contours) {
|
|
2797
|
-
//
|
|
2798
|
-
// Contours are expected to be closed (last point repeats first), so segments = (nPoints - 1)
|
|
2797
|
+
// Contours are closed (last point repeats first)
|
|
2799
2798
|
const contourPoints = contour.length / 2;
|
|
2800
2799
|
if (contourPoints >= 2)
|
|
2801
2800
|
sideSegments += contourPoints - 1;
|
|
@@ -2811,7 +2810,7 @@
|
|
|
2811
2810
|
: triangleIndices.length * 2 + sideSegments * 6;
|
|
2812
2811
|
const indices = new Uint32Array(indexCount);
|
|
2813
2812
|
if (depth === 0) {
|
|
2814
|
-
//
|
|
2813
|
+
// Single-sided flat geometry at z=0
|
|
2815
2814
|
let vPos = 0;
|
|
2816
2815
|
for (let i = 0; i < points.length; i += 2) {
|
|
2817
2816
|
vertices[vPos] = points[i];
|
|
@@ -2819,18 +2818,19 @@
|
|
|
2819
2818
|
vertices[vPos + 2] = 0;
|
|
2820
2819
|
normals[vPos] = 0;
|
|
2821
2820
|
normals[vPos + 1] = 0;
|
|
2822
|
-
normals[vPos + 2] =
|
|
2821
|
+
normals[vPos + 2] = 1;
|
|
2823
2822
|
vPos += 3;
|
|
2824
2823
|
}
|
|
2824
|
+
// libtess outputs CCW, use as-is for +Z facing geometry
|
|
2825
2825
|
for (let i = 0; i < triangleIndices.length; i++) {
|
|
2826
2826
|
indices[i] = triangleIndices[i];
|
|
2827
2827
|
}
|
|
2828
2828
|
return { vertices, normals, indices };
|
|
2829
2829
|
}
|
|
2830
|
-
//
|
|
2830
|
+
// Extruded geometry: front at z=0, back at z=depth
|
|
2831
2831
|
const minBackOffset = unitsPerEm * 0.000025;
|
|
2832
2832
|
const backZ = depth <= minBackOffset ? minBackOffset : depth;
|
|
2833
|
-
//
|
|
2833
|
+
// Cap at z=0, back face
|
|
2834
2834
|
for (let p = 0, vi = 0; p < points.length; p += 2, vi++) {
|
|
2835
2835
|
const base = vi * 3;
|
|
2836
2836
|
vertices[base] = points[p];
|
|
@@ -2840,7 +2840,7 @@
|
|
|
2840
2840
|
normals[base + 1] = 0;
|
|
2841
2841
|
normals[base + 2] = -1;
|
|
2842
2842
|
}
|
|
2843
|
-
//
|
|
2843
|
+
// Cap at z=depth, front face
|
|
2844
2844
|
for (let p = 0, vi = 0; p < points.length; p += 2, vi++) {
|
|
2845
2845
|
const base = (numPoints + vi) * 3;
|
|
2846
2846
|
vertices[base] = points[p];
|
|
@@ -2850,14 +2850,14 @@
|
|
|
2850
2850
|
normals[base + 1] = 0;
|
|
2851
2851
|
normals[base + 2] = 1;
|
|
2852
2852
|
}
|
|
2853
|
-
//
|
|
2853
|
+
// libtess outputs CCW triangles (viewed from +Z)
|
|
2854
|
+
// Z=0 cap faces -Z, reverse winding
|
|
2854
2855
|
for (let i = 0; i < triangleIndices.length; i++) {
|
|
2855
|
-
indices[i] = triangleIndices[i];
|
|
2856
|
+
indices[i] = triangleIndices[triangleIndices.length - 1 - i];
|
|
2856
2857
|
}
|
|
2857
|
-
//
|
|
2858
|
+
// Z=depth cap faces +Z, use original winding
|
|
2858
2859
|
for (let i = 0; i < triangleIndices.length; i++) {
|
|
2859
|
-
indices[triangleIndices.length + i] =
|
|
2860
|
-
triangleIndices[triangleIndices.length - 1 - i] + numPoints;
|
|
2860
|
+
indices[triangleIndices.length + i] = triangleIndices[i] + numPoints;
|
|
2861
2861
|
}
|
|
2862
2862
|
// Side walls
|
|
2863
2863
|
let nextVertex = numPoints * 2;
|
|
@@ -2868,7 +2868,7 @@
|
|
|
2868
2868
|
const p0y = contour[i + 1];
|
|
2869
2869
|
const p1x = contour[i + 2];
|
|
2870
2870
|
const p1y = contour[i + 3];
|
|
2871
|
-
//
|
|
2871
|
+
// Perpendicular normal for this wall segment
|
|
2872
2872
|
const ex = p1x - p0x;
|
|
2873
2873
|
const ey = p1y - p0y;
|
|
2874
2874
|
const lenSq = ex * ex + ey * ey;
|
|
@@ -2881,7 +2881,7 @@
|
|
|
2881
2881
|
}
|
|
2882
2882
|
const baseVertex = nextVertex;
|
|
2883
2883
|
const base = baseVertex * 3;
|
|
2884
|
-
//
|
|
2884
|
+
// Wall quad: front edge at z=0, back edge at z=depth
|
|
2885
2885
|
vertices[base] = p0x;
|
|
2886
2886
|
vertices[base + 1] = p0y;
|
|
2887
2887
|
vertices[base + 2] = 0;
|
|
@@ -2894,7 +2894,7 @@
|
|
|
2894
2894
|
vertices[base + 9] = p1x;
|
|
2895
2895
|
vertices[base + 10] = p1y;
|
|
2896
2896
|
vertices[base + 11] = backZ;
|
|
2897
|
-
//
|
|
2897
|
+
// Wall normals point perpendicular to edge
|
|
2898
2898
|
normals[base] = nx;
|
|
2899
2899
|
normals[base + 1] = ny;
|
|
2900
2900
|
normals[base + 2] = 0;
|
|
@@ -2907,7 +2907,7 @@
|
|
|
2907
2907
|
normals[base + 9] = nx;
|
|
2908
2908
|
normals[base + 10] = ny;
|
|
2909
2909
|
normals[base + 11] = 0;
|
|
2910
|
-
//
|
|
2910
|
+
// Two triangles per wall segment
|
|
2911
2911
|
indices[idxPos++] = baseVertex;
|
|
2912
2912
|
indices[idxPos++] = baseVertex + 1;
|
|
2913
2913
|
indices[idxPos++] = baseVertex + 2;
|
package/dist/index.umd.min.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* three-text v0.2.
|
|
2
|
+
* three-text v0.2.16
|
|
3
3
|
* Copyright (C) 2025 Countertype LLC
|
|
4
4
|
*
|
|
5
5
|
* This program is free software: you can redistribute it and/or modify
|
|
@@ -476,13 +476,13 @@ let o=0
|
|
|
476
476
|
if(0!==e)for(const e of t.qi){const t=e.length/2
|
|
477
477
|
2>t||(o+=t-1)}const h=(0===e?r:2*r)+(0===e?0:4*o),a=new Float32Array(3*h),l=new Float32Array(3*h),c=new Uint32Array(0===e?n.length:2*n.length+6*o)
|
|
478
478
|
if(0===e){let t=0
|
|
479
|
-
for(let e=0;s.length>e;e+=2)a[t]=s[e],a[t+1]=s[e+1],a[t+2]=0,l[t]=0,l[t+1]=0,l[t+2]
|
|
479
|
+
for(let e=0;s.length>e;e+=2)a[t]=s[e],a[t+1]=s[e+1],a[t+2]=0,l[t]=0,l[t+1]=0,l[t+2]=1,t+=3
|
|
480
480
|
for(let t=0;n.length>t;t++)c[t]=n[t]
|
|
481
481
|
return{vertices:a,normals:l,indices:c}}const u=25e-6*i,f=e>u?e:u
|
|
482
482
|
for(let t=0,e=0;s.length>t;t+=2,e++){const i=3*e
|
|
483
483
|
a[i]=s[t],a[i+1]=s[t+1],a[i+2]=0,l[i]=0,l[i+1]=0,l[i+2]=-1}for(let t=0,e=0;s.length>t;t+=2,e++){const i=3*(r+e)
|
|
484
|
-
a[i]=s[t],a[i+1]=s[t+1],a[i+2]=f,l[i]=0,l[i+1]=0,l[i+2]=1}for(let t=0;n.length>t;t++)c[t]=n[t]
|
|
485
|
-
for(let t=0;n.length>t;t++)c[n.length+t]=n[
|
|
484
|
+
a[i]=s[t],a[i+1]=s[t+1],a[i+2]=f,l[i]=0,l[i+1]=0,l[i+2]=1}for(let t=0;n.length>t;t++)c[t]=n[n.length-1-t]
|
|
485
|
+
for(let t=0;n.length>t;t++)c[n.length+t]=n[t]+r
|
|
486
486
|
let d=2*r,y=2*n.length
|
|
487
487
|
for(const e of t.qi)for(let t=0;e.length-2>t;t+=2){const i=e[t],s=e[t+1],n=e[t+2],r=e[t+3],o=n-i,h=r-s,u=o*o+h*h
|
|
488
488
|
let p=0,g=0
|
|
@@ -10,6 +10,7 @@ export interface WebGPUBufferSet {
|
|
|
10
10
|
color?: GPUVertexBufferLayout;
|
|
11
11
|
};
|
|
12
12
|
indexCount: number;
|
|
13
|
+
indexFormat: GPUIndexFormat;
|
|
13
14
|
dispose(): void;
|
|
14
15
|
}
|
|
15
16
|
export declare function createWebGPUBuffers(device: GPUDevice, textGeometry: TextGeometryInfo): WebGPUBufferSet;
|
package/dist/webgpu/index.cjs
CHANGED
|
@@ -4,17 +4,18 @@
|
|
|
4
4
|
function createWebGPUBuffers(device, textGeometry) {
|
|
5
5
|
const { vertices, normals, indices, colors } = textGeometry;
|
|
6
6
|
const indexCount = indices.length;
|
|
7
|
+
const indexFormat = indices instanceof Uint16Array ? 'uint16' : 'uint32';
|
|
7
8
|
// Interleave position and normal data for better cache coherency
|
|
8
9
|
// Layout: [px, py, pz, nx, ny, nz, px, py, pz, nx, ny, nz, ...]
|
|
9
10
|
const interleavedData = new Float32Array((vertices.length / 3) * 6);
|
|
10
11
|
for (let i = 0; i < vertices.length / 3; i++) {
|
|
11
12
|
const baseIndex = i * 6;
|
|
12
13
|
const vertIndex = i * 3;
|
|
13
|
-
// Position
|
|
14
|
+
// Position
|
|
14
15
|
interleavedData[baseIndex] = vertices[vertIndex];
|
|
15
16
|
interleavedData[baseIndex + 1] = vertices[vertIndex + 1];
|
|
16
17
|
interleavedData[baseIndex + 2] = vertices[vertIndex + 2];
|
|
17
|
-
// Normal
|
|
18
|
+
// Normal
|
|
18
19
|
interleavedData[baseIndex + 3] = normals[vertIndex];
|
|
19
20
|
interleavedData[baseIndex + 4] = normals[vertIndex + 1];
|
|
20
21
|
interleavedData[baseIndex + 5] = normals[vertIndex + 2];
|
|
@@ -86,6 +87,7 @@ function createWebGPUBuffers(device, textGeometry) {
|
|
|
86
87
|
buffers,
|
|
87
88
|
layout,
|
|
88
89
|
indexCount,
|
|
90
|
+
indexFormat,
|
|
89
91
|
dispose() {
|
|
90
92
|
vertexBuffer.destroy();
|
|
91
93
|
indexBuffer.destroy();
|
package/dist/webgpu/index.d.ts
CHANGED
package/dist/webgpu/index.js
CHANGED
|
@@ -2,17 +2,18 @@
|
|
|
2
2
|
function createWebGPUBuffers(device, textGeometry) {
|
|
3
3
|
const { vertices, normals, indices, colors } = textGeometry;
|
|
4
4
|
const indexCount = indices.length;
|
|
5
|
+
const indexFormat = indices instanceof Uint16Array ? 'uint16' : 'uint32';
|
|
5
6
|
// Interleave position and normal data for better cache coherency
|
|
6
7
|
// Layout: [px, py, pz, nx, ny, nz, px, py, pz, nx, ny, nz, ...]
|
|
7
8
|
const interleavedData = new Float32Array((vertices.length / 3) * 6);
|
|
8
9
|
for (let i = 0; i < vertices.length / 3; i++) {
|
|
9
10
|
const baseIndex = i * 6;
|
|
10
11
|
const vertIndex = i * 3;
|
|
11
|
-
// Position
|
|
12
|
+
// Position
|
|
12
13
|
interleavedData[baseIndex] = vertices[vertIndex];
|
|
13
14
|
interleavedData[baseIndex + 1] = vertices[vertIndex + 1];
|
|
14
15
|
interleavedData[baseIndex + 2] = vertices[vertIndex + 2];
|
|
15
|
-
// Normal
|
|
16
|
+
// Normal
|
|
16
17
|
interleavedData[baseIndex + 3] = normals[vertIndex];
|
|
17
18
|
interleavedData[baseIndex + 4] = normals[vertIndex + 1];
|
|
18
19
|
interleavedData[baseIndex + 5] = normals[vertIndex + 2];
|
|
@@ -84,6 +85,7 @@ function createWebGPUBuffers(device, textGeometry) {
|
|
|
84
85
|
buffers,
|
|
85
86
|
layout,
|
|
86
87
|
indexCount,
|
|
88
|
+
indexFormat,
|
|
87
89
|
dispose() {
|
|
88
90
|
vertexBuffer.destroy();
|
|
89
91
|
indexBuffer.destroy();
|