three-text 0.2.15 → 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 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 with normals pointing toward the camera (positive Z direction), reducing triangles by approximately 50% compared to extruded 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.15
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 (each segment emits 4 vertices + 6 indices)
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
- // Each contour is a flat [x0,y0,x1,y1,...] array; side walls connect consecutive points
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
- // Flat faces only
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];
@@ -2818,15 +2817,16 @@ class Extruder {
2818
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
- // Front/back faces
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
- // Fill front vertices/normals (0..numPoints-1)
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];
@@ -2834,9 +2834,9 @@ class Extruder {
2834
2834
  vertices[base + 2] = 0;
2835
2835
  normals[base] = 0;
2836
2836
  normals[base + 1] = 0;
2837
- normals[base + 2] = 1;
2837
+ normals[base + 2] = -1;
2838
2838
  }
2839
- // Fill back vertices/normals (numPoints..2*numPoints-1)
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];
@@ -2844,16 +2844,16 @@ class Extruder {
2844
2844
  vertices[base + 2] = backZ;
2845
2845
  normals[base] = 0;
2846
2846
  normals[base + 1] = 0;
2847
- normals[base + 2] = -1;
2847
+ normals[base + 2] = 1;
2848
2848
  }
2849
- // Front indices
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
- // Back indices (reverse winding + offset)
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
- // Unit normal for the wall quad (per-edge)
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
- // 4 vertices (two at z=0, two at z=depth)
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
- // Normals (same for all 4 wall vertices)
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
- // Indices (two triangles)
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.15
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 (each segment emits 4 vertices + 6 indices)
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
- // Each contour is a flat [x0,y0,x1,y1,...] array; side walls connect consecutive points
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
- // Flat faces only
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];
@@ -2815,15 +2814,16 @@ class Extruder {
2815
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
- // Front/back faces
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
- // Fill front vertices/normals (0..numPoints-1)
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];
@@ -2831,9 +2831,9 @@ class Extruder {
2831
2831
  vertices[base + 2] = 0;
2832
2832
  normals[base] = 0;
2833
2833
  normals[base + 1] = 0;
2834
- normals[base + 2] = 1;
2834
+ normals[base + 2] = -1;
2835
2835
  }
2836
- // Fill back vertices/normals (numPoints..2*numPoints-1)
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];
@@ -2841,16 +2841,16 @@ class Extruder {
2841
2841
  vertices[base + 2] = backZ;
2842
2842
  normals[base] = 0;
2843
2843
  normals[base + 1] = 0;
2844
- normals[base + 2] = -1;
2844
+ normals[base + 2] = 1;
2845
2845
  }
2846
- // Front indices
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
- // Back indices (reverse winding + offset)
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
- // Unit normal for the wall quad (per-edge)
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
- // 4 vertices (two at z=0, two at z=depth)
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
- // Normals (same for all 4 wall vertices)
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
- // Indices (two triangles)
2903
+ // Two triangles per wall segment
2904
2904
  indices[idxPos++] = baseVertex;
2905
2905
  indices[idxPos++] = baseVertex + 1;
2906
2906
  indices[idxPos++] = baseVertex + 2;
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * three-text v0.2.15
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
@@ -479,9 +479,9 @@ 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
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
- 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[n.length-1-t]+r
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[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.15
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
@@ -478,9 +478,9 @@ 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
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
- 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[n.length-1-t]+r
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[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.15
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 (each segment emits 4 vertices + 6 indices)
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
- // Each contour is a flat [x0,y0,x1,y1,...] array; side walls connect consecutive points
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
- // Flat faces only
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];
@@ -2822,15 +2821,16 @@
2822
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
- // Front/back faces
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
- // Fill front vertices/normals (0..numPoints-1)
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];
@@ -2838,9 +2838,9 @@
2838
2838
  vertices[base + 2] = 0;
2839
2839
  normals[base] = 0;
2840
2840
  normals[base + 1] = 0;
2841
- normals[base + 2] = 1;
2841
+ normals[base + 2] = -1;
2842
2842
  }
2843
- // Fill back vertices/normals (numPoints..2*numPoints-1)
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];
@@ -2848,16 +2848,16 @@
2848
2848
  vertices[base + 2] = backZ;
2849
2849
  normals[base] = 0;
2850
2850
  normals[base + 1] = 0;
2851
- normals[base + 2] = -1;
2851
+ normals[base + 2] = 1;
2852
2852
  }
2853
- // Front indices
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
- // Back indices (reverse winding + offset)
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
- // Unit normal for the wall quad (per-edge)
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
- // 4 vertices (two at z=0, two at z=depth)
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
- // Normals (same for all 4 wall vertices)
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
- // Indices (two triangles)
2910
+ // Two triangles per wall segment
2911
2911
  indices[idxPos++] = baseVertex;
2912
2912
  indices[idxPos++] = baseVertex + 1;
2913
2913
  indices[idxPos++] = baseVertex + 2;
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * three-text v0.2.15
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
@@ -480,9 +480,9 @@ 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
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
- 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[n.length-1-t]+r
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[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;
@@ -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 (NO FLIP - pass through)
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 (NO FLIP - pass through)
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();
@@ -11,6 +11,7 @@ interface WebGPUBufferSet {
11
11
  color?: GPUVertexBufferLayout;
12
12
  };
13
13
  indexCount: number;
14
+ indexFormat: GPUIndexFormat;
14
15
  dispose(): void;
15
16
  }
16
17
  declare function createWebGPUBuffers(device: GPUDevice, textGeometry: TextGeometryInfo): WebGPUBufferSet;
@@ -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 (NO FLIP - pass through)
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 (NO FLIP - pass through)
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();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "three-text",
3
- "version": "0.2.15",
3
+ "version": "0.2.16",
4
4
  "description": "3D font rendering and text layout engine for the web",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.js",