three-text 0.5.2 → 0.6.1

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.
Files changed (52) hide show
  1. package/LICENSE_THIRD_PARTY +15 -0
  2. package/README.md +73 -42
  3. package/dist/index.cjs +1 -1
  4. package/dist/index.d.ts +2 -0
  5. package/dist/index.js +1 -1
  6. package/dist/index.min.cjs +1 -1
  7. package/dist/index.min.js +1 -1
  8. package/dist/index.umd.js +1 -1
  9. package/dist/index.umd.min.js +1 -1
  10. package/dist/three/react.d.ts +2 -0
  11. package/dist/types/core/types.d.ts +2 -33
  12. package/dist/types/vector/{core.d.ts → core/index.d.ts} +8 -7
  13. package/dist/types/vector/index.d.ts +22 -25
  14. package/dist/types/vector/react.d.ts +4 -3
  15. package/dist/types/vector/slug/SlugPacker.d.ts +2 -0
  16. package/dist/types/vector/slug/curveUtils.d.ts +6 -0
  17. package/dist/types/vector/slug/index.d.ts +8 -0
  18. package/dist/types/vector/slug/shaderStrings.d.ts +4 -0
  19. package/dist/types/vector/slug/slugGLSL.d.ts +21 -0
  20. package/dist/types/vector/slug/slugTSL.d.ts +13 -0
  21. package/dist/types/vector/slug/types.d.ts +30 -0
  22. package/dist/types/vector/slug/unpackVertices.d.ts +11 -0
  23. package/dist/types/vector/webgl/index.d.ts +7 -3
  24. package/dist/types/vector/webgpu/index.d.ts +4 -4
  25. package/dist/vector/core/index.cjs +860 -0
  26. package/dist/vector/core/index.d.ts +63 -0
  27. package/dist/vector/core/index.js +858 -0
  28. package/dist/vector/core.cjs +4419 -240
  29. package/dist/vector/core.d.ts +361 -71
  30. package/dist/vector/core.js +4406 -226
  31. package/dist/vector/index.cjs +5 -229
  32. package/dist/vector/index.d.ts +45 -396
  33. package/dist/vector/index.js +3 -223
  34. package/dist/vector/index2.cjs +287 -0
  35. package/dist/vector/index2.js +264 -0
  36. package/dist/vector/react.cjs +37 -8
  37. package/dist/vector/react.d.ts +6 -3
  38. package/dist/vector/react.js +18 -8
  39. package/dist/vector/slugTSL.cjs +252 -0
  40. package/dist/vector/slugTSL.js +231 -0
  41. package/dist/vector/webgl/index.cjs +131 -201
  42. package/dist/vector/webgl/index.d.ts +19 -44
  43. package/dist/vector/webgl/index.js +131 -201
  44. package/dist/vector/webgpu/index.cjs +100 -283
  45. package/dist/vector/webgpu/index.d.ts +16 -45
  46. package/dist/vector/webgpu/index.js +100 -283
  47. package/package.json +7 -2
  48. package/dist/types/vector/GlyphVectorGeometryBuilder.d.ts +0 -26
  49. package/dist/types/vector/LoopBlinnGeometry.d.ts +0 -68
  50. package/dist/types/vector/loopBlinnTSL.d.ts +0 -22
  51. package/dist/vector/loopBlinnTSL.cjs +0 -229
  52. package/dist/vector/loopBlinnTSL.js +0 -207
@@ -107,6 +107,21 @@ complete source and licensing details: https://github.com/hyphenation/tex-hyphen
107
107
 
108
108
  ---
109
109
 
110
+ 4. Slug Library
111
+
112
+ This software includes a port of the Slug library
113
+ (https://github.com/EricLengyel/Slug), a GPU-based vector graphics
114
+ rendering algorithm for text and shapes by Eric Lengyel. The original
115
+ GLSL shaders were ported to Three.js TSL (Three.js Shading Language)
116
+ with added adaptive supersampling
117
+
118
+ Copyright (c) 2017 Eric Lengyel
119
+
120
+ Slug is distributed under the MIT License.
121
+ (See Appendix A for MIT License text)
122
+
123
+ ---
124
+
110
125
  Appendix A: MIT License text
111
126
 
112
127
  Permission is hereby granted, free of charge, to any person obtaining a copy
package/README.md CHANGED
@@ -15,11 +15,11 @@ High fidelity 3D text rendering and layout for the web
15
15
  > [!CAUTION]
16
16
  > three-text is in alpha release and the API may break rapidly. This warning will likely last until summer of 2026. If API stability is important to you, consider pinning your version. Community feedback is encouraged; please open an issue if you have any suggestions or feedback, thank you
17
17
 
18
- **three-text** is a 3D font rendering and text layout library for the web. It supports TTF, OTF, WOFF, and WOFF2 font files and uses [TeX](https://en.wikipedia.org/wiki/TeX)-based parameters for layout, with support for CJK and RTL scripts. Two rendering pipelines share the same core: **mesh** (extruded, lit, deformable geometry) and **vector** (resolution-independent Loop-Blinn outlines). Contours and geometries are cached per glyph for low CPU overhead in text-heavy scenes. Variable fonts are supported
18
+ **three-text** is a 3D font rendering and text layout library for the web. It supports TTF, OTF, WOFF, and WOFF2 font files and uses [TeX](https://en.wikipedia.org/wiki/TeX)-based parameters for layout, with support for CJK and RTL scripts. Two rendering pipelines share the same core: **mesh** (extrudable, deformable geometry) and **vector** (resolution-independent GPU outlines). Contours and geometries are cached per glyph for low CPU overhead in text-heavy scenes. Variable fonts are supported
19
19
 
20
20
  The library has a framework-agnostic core with lightweight adapters for [Three.js](https://threejs.org), [React Three Fiber](https://docs.pmnd.rs/react-three-fiber), [p5.js](https://p5js.org), [WebGL](https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API), and [WebGPU](https://developer.mozilla.org/en-US/docs/Web/API/WebGPU_API)
21
21
 
22
- Under the hood, three-text relies on a core of [harfbuzzjs](https://github.com/harfbuzz/harfbuzzjs) (based on [HarfBuzz](https://github.com/harfbuzz/harfbuzz) by Behdad Esfahbod et al) for text shaping, [Knuth-Plass](http://www.eprg.org/G53DOC/pdfs/knuth-plass-breaking.pdf) line breaking (with [SILE](https://github.com/sile-typesetter/sile/blob/master/core/break.lua) and LuaTex being the closest modern references), [Liang](https://tug.org/docs/liang/liang-thesis.pdf) hyphenation and the [TeX hyphenation patterns](https://github.com/hyphenation/tex-hyphen), and [woff-lib](https://github.com/countertype/woff-lib) for optional WOFF2 support. The mesh text pipeline uses [libtess-ts](https://github.com/countertype/libtess-ts) (a port of the [GLU tessellator](https://www.songho.ca/opengl/gl_tessellation.html) by Eric Veach) for removing overlaps and triangulation, adaptive curve polygonization from Maxim Shemanarev's [Anti-Grain Geometry](https://web.archive.org/web/20060128212843/http://www.antigrain.com/research/adaptive_bezier/index.html), and [Visvalingam-Whyatt](https://hull-repository.worktribe.com/preview/376364/000870493786962263.pdf) [line simplification](https://bost.ocks.org/mike/simplify/). The vector pipeline uses [Loop-Blinn](https://www.microsoft.com/en-us/research/wp-content/uploads/2005/01/p1000-loop.pdf) resolution-independent curve rendering with [Kokojima et al.](https://dl.acm.org/doi/10.1145/1179849.1179997) stencil filling
22
+ Under the hood, three-text relies on a core of [harfbuzzjs](https://github.com/harfbuzz/harfbuzzjs) (based on [HarfBuzz](https://github.com/harfbuzz/harfbuzz) by Behdad Esfahbod et al) for text shaping, [Knuth-Plass](http://www.eprg.org/G53DOC/pdfs/knuth-plass-breaking.pdf) line breaking (with [SILE](https://github.com/sile-typesetter/sile/blob/master/core/break.lua) and LuaTex being the closest modern references), [Liang](https://tug.org/docs/liang/liang-thesis.pdf) hyphenation and the [TeX hyphenation patterns](https://github.com/hyphenation/tex-hyphen), and [woff-lib](https://github.com/countertype/woff-lib) for optional WOFF2 support. The mesh text pipeline uses [libtess-ts](https://github.com/countertype/libtess-ts) (a port of the [GLU tessellator](https://www.songho.ca/opengl/gl_tessellation.html) by Eric Veach) for removing overlaps and triangulation, adaptive curve polygonization from Maxim Shemanarev's [Anti-Grain Geometry](https://web.archive.org/web/20060128212843/http://www.antigrain.com/research/adaptive_bezier/index.html), and [Visvalingam-Whyatt](https://hull-repository.worktribe.com/preview/376364/000870493786962263.pdf) [line simplification](https://bost.ocks.org/mike/simplify/). The vector pipeline uses [Slug](https://github.com/EricLengyel/Slug) by Eric Lengyel for resolution-independent curve rendering
23
23
 
24
24
  ## Table of contents
25
25
 
@@ -71,8 +71,9 @@ three-text has a framework-agnostic core that processes fonts and generates geom
71
71
  - **`three-text/mesh/webgpu`** - WebGPU mesh buffer utility
72
72
  - **`three-text/mesh/p5`** - p5.js adapter
73
73
  - **`three-text/core`** - Framework-agnostic core (returns raw arrays)
74
- - **`three-text/vector`** - Vector rendering (Loop-Blinn curve eval + Kokojima stencil fill), `Text.create()` returns a `THREE.Group` ready for `scene.add()`
74
+ - **`three-text/vector`** - Vector rendering (Slug per-fragment curve evaluation), `Text.create()` returns a `THREE.Group`
75
75
  - **`three-text/vector/react`** - React Three Fiber component for vector text
76
+ - **`three-text/vector/core`** - Framework-agnostic vector core (returns raw `SlugGPUData`, no Three.js dependency)
76
77
  - **`three-text/vector/webgl`** - Raw WebGL2 vector renderer (no Three.js dependency)
77
78
  - **`three-text/vector/webgpu`** - Raw WebGPU vector renderer (no Three.js dependency)
78
79
  - **`three-text/webgl`** - Deprecated, use `three-text/mesh/webgl`
@@ -86,7 +87,7 @@ Most users will just `import { Text } from 'three-text'` for Three.js projects w
86
87
  The library offers two rendering modes that share the same core (HarfBuzz shaping, Knuth-Plass justification, glyph caching):
87
88
 
88
89
  - **Mesh** (`three-text` (default) / `three-text/mesh`): triangulated geometry you can extrude, light, and shade. Use for 3D text, text in a scene graph, or anywhere you need depth
89
- - **Vector** (`three-text/vector`): resolution-independent rendering on the GPU without tessellation. Use for text that needs to stay sharp at arbitrary zoom
90
+ - **Vector** (`three-text/vector`): resolution-independent rendering on the GPU via per-fragment curve evaluation. No tessellation, no stencil buffer. Use for text that needs to stay sharp at arbitrary zoom
90
91
 
91
92
  Both can be used in the same project from separate entry points
92
93
 
@@ -96,7 +97,7 @@ Both can be used in the same project from separate entry points
96
97
 
97
98
  #### Mesh (Three.js)
98
99
 
99
- Extruded `BufferGeometry` light, shade, and deform as a normal mesh:
100
+ Extruded `BufferGeometry` that you can light, shade, and deform like any mesh:
100
101
 
101
102
  ```javascript
102
103
  import { Text } from 'three-text';
@@ -117,7 +118,7 @@ scene.add(mesh);
117
118
 
118
119
  #### Vector (Three.js)
119
120
 
120
- Resolution-independent outlines via Loop-Blinn stencil passes (see [Vector rendering](#vector-rendering)):
121
+ Resolution-independent outlines via per-fragment curve evaluation (see [Vector rendering](#vector-rendering)):
121
122
 
122
123
  ```javascript
123
124
  import { Text } from 'three-text/vector';
@@ -127,16 +128,16 @@ const result = await Text.create({
127
128
  text: 'Hello Vector',
128
129
  font: '/fonts/Font.woff2',
129
130
  size: 72,
130
- color: '#ffffff'
131
+ color: [1, 1, 1]
131
132
  });
132
133
  scene.add(result.group);
133
134
  ```
134
135
 
135
- `Text.create()` handles stencil setup, render ordering, and geometry centering internally. Pass `positionNode` and `colorNode` for TSL animation/styling. For raw WebGL2 or WebGPU without Three.js, see [Vector rendering](#vector-rendering)
136
+ `Text.create()` returns a `THREE.Group` ready for `scene.add()`. Uses TSL node materials when the renderer supports them (Three.js r170+), otherwise falls back to a GLSL `RawShaderMaterial` that works with any WebGL2 renderer
136
137
 
137
138
  #### Mesh + vector in one scene
138
139
 
139
- Alias one import to avoid the name collision between `Text` components. The entry points share a core (shaping, layout, font cache) so you can mix them freely fonts load once regardless of which entry point requests them:
140
+ Alias one import to avoid the name collision between `Text` components. The entry points share a core (shaping, layout, font cache) so you can mix them freely; fonts load once regardless of which entry point requests them:
140
141
 
141
142
  ```javascript
142
143
  import { Text as MeshText } from 'three-text';
@@ -155,7 +156,7 @@ const caption = await VectorText.create({
155
156
  text: 'Caption text',
156
157
  font: '/fonts/Font.woff2',
157
158
  size: 24,
158
- color: '#ffffff'
159
+ color: [1, 1, 1]
159
160
  });
160
161
  scene.add(caption.group);
161
162
  ```
@@ -182,7 +183,7 @@ function App() {
182
183
 
183
184
  #### React Three Fiber — vector
184
185
 
185
- The vector `Text` builds three internal meshes (interior / curve / fill) with TSL stencil materials. Requires a renderer that supports `MeshBasicNodeMaterial` (Three.js r170+). With WebGPU, pass a `WebGPURenderer` with `stencil: true` and await `init()` (see the [three.js WebGPU examples](https://threejs.org/examples/?q=webgpu)):
186
+ The vector `Text` component creates a single mesh with a TSL node material that evaluates curve coverage per fragment. Requires a renderer that supports `MeshBasicNodeMaterial` (Three.js r170+). With WebGPU, pass a `WebGPURenderer` and await `init()` (see the [three.js WebGPU examples](https://threejs.org/examples/?q=webgpu)):
186
187
 
187
188
  ```jsx
188
189
  import { Canvas } from '@react-three/fiber';
@@ -197,7 +198,7 @@ function App() {
197
198
  gl={async (props) => {
198
199
  const renderer = new THREE.WebGPURenderer({
199
200
  canvas: props.canvas,
200
- stencil: true
201
+ antialias: true
201
202
  });
202
203
  await renderer.init();
203
204
  return renderer;
@@ -229,7 +230,7 @@ function App() {
229
230
  gl={async (props) => {
230
231
  const renderer = new THREE.WebGPURenderer({
231
232
  canvas: props.canvas,
232
- stencil: true
233
+ antialias: true
233
234
  });
234
235
  await renderer.init();
235
236
  return renderer;
@@ -291,12 +292,13 @@ import { woff2Decode } from 'woff-lib/woff2/decode';
291
292
  Text.setHarfBuzzPath('/hb/hb.wasm');
292
293
  Text.enableWoff2(woff2Decode);
293
294
 
294
- const gl = canvas.getContext('webgl2', { antialias: true, stencil: true });
295
- const renderer = createWebGLVectorRenderer(gl);
295
+ const gl = canvas.getContext('webgl2', { antialias: true });
296
+ const renderer = createWebGLVectorRenderer(gl, {
297
+ adaptiveSupersampling: true
298
+ });
296
299
 
297
300
  const result = await Text.create({ text: 'Hello', font: '/fonts/Font.woff2', size: 72 });
298
- const vectorData = result.geometryData;
299
- renderer.setGeometry(vectorData);
301
+ renderer.setGeometry(result.gpuData);
300
302
 
301
303
  // In render loop:
302
304
  renderer.render(mvpMatrix, new Float32Array([1, 1, 1, 1]));
@@ -313,19 +315,52 @@ Text.setHarfBuzzPath('/hb/hb.wasm');
313
315
  Text.enableWoff2(woff2Decode);
314
316
 
315
317
  const renderer = createWebGPUVectorRenderer(device, format, {
316
- depthStencilFormat: 'depth24plus-stencil8',
317
318
  sampleCount: 4
318
319
  });
319
320
 
320
321
  const result = await Text.create({ text: 'Hello', font: '/fonts/Font.woff2', size: 72 });
321
- const vectorData = result.geometryData;
322
- renderer.setGeometry(vectorData);
322
+ renderer.setGeometry(result.gpuData);
323
323
 
324
324
  // In render pass:
325
325
  renderer.render(passEncoder, mvpMatrix, new Float32Array([1, 1, 1, 1]));
326
326
  ```
327
327
 
328
- See `examples/webgl-vector.html` and `examples/webgpu-vector.html` for raw WebGL/WebGPU demos. The main interactive demo (`examples/index.html`) uses `WebGPURenderer` with TSL node materials for both mesh and Loop-Blinn vector paths. A `WebGLRenderer` variant is available at `examples/index-webgl.html`
328
+ **Adaptive supersampling** is available on the GLSL code path (raw WebGL2 renderer, `createSlugGLSLMesh`, and Three.js vector when TSL is unavailable). It uses rotated-grid supersampling (RGSS, 4 samples per pixel) with a per-fragment rotation angle derived from interleaved gradient noise. This converts structured aliasing shimmer into uncorrelated grain that the eye naturally filters out. Pass `adaptiveSupersampling: true` to enable it. The TSL and WebGPU paths do not currently support this option
329
+
330
+ #### GLSL animation injection
331
+
332
+ For custom vertex animations, `createSlugGLSLMesh` gives direct access to the GLSL vertex shader. This is exported from `three-text/vector` and builds a Three.js `Mesh` backed by a `RawShaderMaterial`:
333
+
334
+ ```javascript
335
+ import { Text, createSlugGLSLMesh } from 'three-text/vector';
336
+
337
+ const result = await Text.create({
338
+ text: 'Hello',
339
+ font: '/fonts/Font.woff2',
340
+ size: 72,
341
+ perGlyphAttributes: true
342
+ });
343
+
344
+ const { mesh, uniforms } = createSlugGLSLMesh(result.gpuData, {
345
+ color: { r: 1, g: 1, b: 1 },
346
+ adaptiveSupersampling: true,
347
+ animationDeclarations: `
348
+ uniform float waveAmplitude;
349
+ `,
350
+ animationBody: `
351
+ vec3 outPos = position;
352
+ outPos.y += sin(glyphIndex * 0.5 + time) * waveAmplitude;
353
+ `,
354
+ uniforms: {
355
+ waveAmplitude: { value: 10.0 }
356
+ }
357
+ });
358
+ scene.add(mesh);
359
+ ```
360
+
361
+ The vertex shader provides `position` (vec3), `glyphCenter` (vec3), `glyphIndex` (float), and `time` (float) for use in your animation body. Your code must write a `vec3 outPos` that becomes the final vertex position
362
+
363
+ The main interactive demo (`examples/index.html`) uses `WebGPURenderer` with TSL node materials for both mesh and vector paths. A `WebGLRenderer` variant for mesh mode is available at `examples/index-webgl.html`
329
364
 
330
365
  ### Coordinate systems
331
366
 
@@ -435,8 +470,8 @@ Then navigate to `http://localhost:3000`
435
470
 
436
471
  three-text renders text from real font files (TTF, OTF, WOFF, WOFF2) with two pipelines:
437
472
 
438
- - **Mesh** tessellated 3D geometry that can be extruded, lit, and shaded like any model
439
- - **Vector** resolution-independent outlines rendered directly from curve data on the GPU, sharp at any zoom or angle
473
+ - **Mesh**: tessellated 3D geometry that can be extruded, lit, and shaded like any model
474
+ - **Vector**: resolution-independent outlines rendered directly from curve data on the GPU, sharp at any zoom or angle
440
475
 
441
476
  Both share the same layout engine (HarfBuzz shaping, Knuth-Plass line breaking) and glyph cache, so a paragraph of 1000 words might only require 50 unique glyphs to be processed
442
477
 
@@ -448,9 +483,9 @@ Existing solutions take different approaches:
448
483
 
449
484
  three-text produces actual geometry from font files, sharper at close distances than bitmap approaches, with control over typesetting and paragraph justification via TeX-based parameters
450
485
 
451
- ### Why Loop-Blinn
486
+ ### Why Slug
452
487
 
453
- The vector path uses Loop-Blinn curve rendering, where each quadratic Bezier segment becomes a triangle whose fragment shader evaluates the implicit equation `u² - v = 0` to resolve inside/outside analytically, while interior regions are filled separately. We also evaluated Eric Lengyel's [Slug](https://github.com/EricLengyel/Slug) algorithm, which renders glyphs by casting rays against banded curve data over a dilated bounding polygon, and tested several antialiasing configurations including adaptive supersampling and alpha-to-coverage. Loop-Blinn produced cleaner results under strong perspective and oblique viewing angles, so that is what we use
488
+ The vector path uses the Slug algorithm by Eric Lengyel. Each glyph is a single quad; the fragment shader evaluates curve coverage analytically to compute a winding number. Because it operates on winding rather than geometry, it naturally handles the self-intersecting contours that variable fonts produce when axes like weight push outlines into each other
454
489
 
455
490
  ## Library structure
456
491
 
@@ -474,15 +509,12 @@ three-text/
474
509
  │ │ ├── index.ts # BufferGeometry wrapper
475
510
  │ │ ├── react.tsx # React component export
476
511
  │ │ └── ThreeText.tsx # React Three Fiber component
477
- │ ├── vector/ # Vector rendering (Loop-Blinn)
478
- │ │ ├── index.ts # Main entry point (wraps core + Three.js integration)
479
- │ │ ├── core.ts # Three.js-free layout engine (used by raw WebGL/WebGPU)
480
- │ │ ├── loopBlinnTSL.ts # TSL stencil materials and mesh construction
481
- │ │ ├── LoopBlinnGeometry.ts # Fan triangulation + curve extraction
482
- │ │ ├── GlyphVectorGeometryBuilder.ts # Outline collection and geometry packing
483
- │ │ ├── GlyphOutlineCollector.ts # Collects draw callbacks for vector path
484
- │ │ ├── webgl/ # WebGL2 stencil-based renderer
485
- │ │ └── webgpu/ # WebGPU stencil-based renderer
512
+ │ ├── vector/ # Vector rendering (Slug)
513
+ │ │ ├── index.ts # Main entry point (Text.create result.group)
514
+ │ │ ├── react.tsx # React Three Fiber component
515
+ │ │ ├── slug/ # Slug per-fragment curve evaluation
516
+ │ │ ├── core/ # Outline collection and Slug packing
517
+ │ │ └── GlyphOutlineCollector.ts # Collects draw callbacks for vector path
486
518
  │ ├── webgl/ # WebGL mesh buffer utility
487
519
  │ ├── webgpu/ # WebGPU mesh buffer utility
488
520
  │ ├── p5/ # p5.js adapter
@@ -534,11 +566,9 @@ The multi-stage geometry approach (curve polygonization followed by cleanup, the
534
566
 
535
567
  ### Vector rendering
536
568
 
537
- The vector pipeline (`three-text/vector`) renders glyphs directly from their mathematical outlines without tessellation or curve flattening. Text stays sharp at any zoom level and the geometry footprint is small -- just the control points of each curve
538
-
539
- Curves use the [Loop-Blinn](https://www.microsoft.com/en-us/research/wp-content/uploads/2005/01/p1000-loop.pdf) technique: each quadratic curve is rendered as a triangle whose fragment shader evaluates `u² - v` to resolve inside/outside, with screen-space derivatives producing a signed distance that feeds alpha-to-coverage for smooth MSAA edges. Glyph interiors use [Kokojima et al.](https://dl.acm.org/doi/10.1145/1179849.1179997) stencil filling with a nonzero winding rule (`INCR_WRAP`/`DECR_WRAP`), which correctly handles overlapping contours in variable fonts and connected scripts
569
+ The vector pipeline (`three-text/vector`) renders glyphs directly from their mathematical outlines without tessellation or curve flattening. Text stays sharp at any zoom level and the geometry footprint is small: one quad per glyph
540
570
 
541
- An alternative for this sort of resolution-independent rendering is [Slug](https://github.com/EricLengyel/Slug) by Eric Lengyel, which casts rays against all curves per fragment to compute winding numbers. Loop-Blinn was chosen here because it integrates with hardware MSAA and alpha-to-coverage directly, without the overhead of adaptive supersampling that Slug requires for comparable antialiasing
571
+ Each quad's fragment shader casts rays against band-indexed curve data to compute a winding number, resolving inside/outside analytically. This is the [Slug](https://jcgt.org/published/0006/02/02/) algorithm by Eric Lengyel. Because winding is evaluated per fragment from the original curves, self-intersecting contours in variable fonts are handled correctly without any special-case geometry processing
542
572
 
543
573
  #### Glyph caching
544
574
 
@@ -764,7 +794,7 @@ const text = await Text.create({
764
794
  // - glyphBaselineY (float): Y coordinate of glyph baseline
765
795
  ```
766
796
 
767
- **Mesh:** attributes live on the extruded `geometry`. **Vector:** the same attributes are emitted on interior, curve, and fill buffer geometries. When you need per-glyph draw ranges (for example stencil passes that must not XOR across overlapping glyphs), use `geometryData.glyphRanges`: each entry lists index/vertex ranges for that glyph’s interior, curve, and fill quads
797
+ **Mesh:** attributes live on the extruded `geometry`. **Vector:** the same attributes are present on the Slug quad mesh, where each glyph is a single quad with `glyphIndex` and `glyphCenter` attributes
768
798
 
769
799
  This option bypasses overlap-based clustering and adds vertex attributes suitable for per-character manipulation in vertex shaders (or TSL `positionNode` displacements). Each unique glyph is still tessellated only once and cached for reuse. The tradeoff is potential visual artifacts where glyphs actually overlap (tight kerning, cursive scripts)
770
800
 
@@ -1239,12 +1269,13 @@ The build generates multiple module formats for core and all adapters:
1239
1269
  **Adapters:**
1240
1270
  - `dist/three/` - Three.js adapter
1241
1271
  - `dist/three/react.js` - React component
1242
- - `dist/vector/` - Vector rendering (Loop-Blinn, Three.js adapter)
1272
+ - `dist/vector/` - Vector rendering (Slug per-fragment curve evaluation)
1243
1273
  - `dist/vector/react.js` - React Three Fiber vector component
1274
+ - `dist/vector/core/` - Framework-agnostic vector core (raw SlugGPUData)
1275
+ - `dist/vector/webgl/` - Raw WebGL2 vector renderer
1276
+ - `dist/vector/webgpu/` - Raw WebGPU vector renderer
1244
1277
  - `dist/webgl/` - WebGL mesh buffer utility
1245
- - `dist/vector/webgl/` - WebGL vector renderer
1246
1278
  - `dist/webgpu/` - WebGPU mesh buffer utility
1247
- - `dist/vector/webgpu/` - WebGPU vector renderer
1248
1279
  - `dist/p5/` - p5.js adapter
1249
1280
 
1250
1281
  **Patterns:**
package/dist/index.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  /*!
2
2
  * @license
3
- * three-text v0.5.2
3
+ * three-text v0.6.1
4
4
  * Copyright © 2025-2026 Jeremy Tribby, Countertype LLC
5
5
  * SPDX-License-Identifier: MIT
6
6
  */
package/dist/index.d.ts CHANGED
@@ -410,6 +410,8 @@ interface TextOptions {
410
410
  geometryOptimization?: GeometryOptimizationOptions;
411
411
  layout?: LayoutOptions;
412
412
  color?: [number, number, number] | ColorOptions;
413
+ /** Enable rotated RGSS-4 adaptive supersampling (4 samples per pixel). Takes effect when the GLSL rendering path is active. */
414
+ adaptiveSupersampling?: boolean;
413
415
  }
414
416
  interface HyphenationPatternsMap {
415
417
  [language: string]: HyphenationTrieNode;
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /*!
2
2
  * @license
3
- * three-text v0.5.2
3
+ * three-text v0.6.1
4
4
  * Copyright © 2025-2026 Jeremy Tribby, Countertype LLC
5
5
  * SPDX-License-Identifier: MIT
6
6
  */
@@ -1,6 +1,6 @@
1
1
  /*!
2
2
  * @license
3
- * three-text v0.5.2
3
+ * three-text v0.6.1
4
4
  * Copyright © 2025-2026 Jeremy Tribby, Countertype LLC
5
5
  * SPDX-License-Identifier: MIT
6
6
  */
package/dist/index.min.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /*!
2
2
  * @license
3
- * three-text v0.5.2
3
+ * three-text v0.6.1
4
4
  * Copyright © 2025-2026 Jeremy Tribby, Countertype LLC
5
5
  * SPDX-License-Identifier: MIT
6
6
  */
package/dist/index.umd.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /*!
2
2
  * @license
3
- * three-text v0.5.2
3
+ * three-text v0.6.1
4
4
  * Copyright © 2025-2026 Jeremy Tribby, Countertype LLC
5
5
  * SPDX-License-Identifier: MIT
6
6
  */
@@ -1,6 +1,6 @@
1
1
  /*!
2
2
  * @license
3
- * three-text v0.5.2
3
+ * three-text v0.6.1
4
4
  * Copyright © 2025-2026 Jeremy Tribby, Countertype LLC
5
5
  * SPDX-License-Identifier: MIT
6
6
  */
@@ -208,6 +208,8 @@ interface TextOptions {
208
208
  geometryOptimization?: GeometryOptimizationOptions;
209
209
  layout?: LayoutOptions;
210
210
  color?: [number, number, number] | ColorOptions;
211
+ /** Enable rotated RGSS-4 adaptive supersampling (4 samples per pixel). Takes effect when the GLSL rendering path is active. */
212
+ adaptiveSupersampling?: boolean;
211
213
  }
212
214
  interface HyphenationPatternsMap {
213
215
  [language: string]: HyphenationTrieNode;
@@ -294,39 +294,6 @@ export interface VectorGlyphInfo extends GlyphGeometryInfo {
294
294
  segmentStart: number;
295
295
  segmentCount: number;
296
296
  }
297
- export interface PackedFloatTexture {
298
- width: number;
299
- height: number;
300
- data: Float32Array;
301
- }
302
- export interface VectorTextGeometryInfo {
303
- quadVertices: Float32Array;
304
- quadIndices: Uint16Array;
305
- instances: {
306
- position: Float32Array;
307
- bounds: Float32Array;
308
- segmentRange: Uint32Array;
309
- glyphDataIndex: Uint32Array;
310
- glyphIndex: Uint32Array;
311
- textIndex: Uint32Array;
312
- lineIndex: Uint32Array;
313
- };
314
- segmentTexelsPerSegment: number;
315
- segments: PackedFloatTexture;
316
- segmentBounds?: PackedFloatTexture;
317
- bandCount?: number;
318
- bandRanges?: PackedFloatTexture;
319
- bandIndices?: PackedFloatTexture;
320
- xBandCount?: number;
321
- xBandRanges?: PackedFloatTexture;
322
- xBandIndices?: PackedFloatTexture;
323
- tileCountX?: number;
324
- tileCountY?: number;
325
- tileRanges?: PackedFloatTexture;
326
- tileIndices?: PackedFloatTexture;
327
- glyphs: VectorGlyphInfo[];
328
- planeBounds: BoundingBox;
329
- }
330
297
  export interface TextLayoutData {
331
298
  lines: LineInfo[];
332
299
  scaledLineHeight: number;
@@ -403,6 +370,8 @@ export interface TextOptions {
403
370
  geometryOptimization?: GeometryOptimizationOptions;
404
371
  layout?: LayoutOptions;
405
372
  color?: [number, number, number] | ColorOptions;
373
+ /** Enable rotated RGSS-4 adaptive supersampling (4 samples per pixel). Takes effect when the GLSL rendering path is active. */
374
+ adaptiveSupersampling?: boolean;
406
375
  }
407
376
  export interface HyphenationPatternsMap {
408
377
  [language: string]: HyphenationTrieNode;
@@ -1,10 +1,12 @@
1
- import { Text as TextCore } from '../core/Text';
2
- import type { TextOptions, VectorGlyphInfo, LoadedFont, TextQueryOptions, TextRange } from '../core/types';
3
- import type { VectorGeometryData } from './LoopBlinnGeometry';
4
- import type { HyphenationTrieNode } from '../hyphenation';
1
+ import { Text as TextCore } from '../../core/Text';
2
+ import type { TextOptions, VectorGlyphInfo, LoadedFont, TextQueryOptions, TextRange } from '../../core/types';
3
+ import type { BoundingBox } from '../../utils/vectors';
4
+ import type { SlugGPUData } from '../slug/types';
5
+ import type { HyphenationTrieNode } from '../../hyphenation';
5
6
  export interface VectorTextResult {
7
+ gpuData: SlugGPUData;
6
8
  glyphs: VectorGlyphInfo[];
7
- geometryData: VectorGeometryData;
9
+ planeBounds: BoundingBox;
8
10
  query(options: TextQueryOptions): TextRange[];
9
11
  getLoadedFont(): LoadedFont | undefined;
10
12
  measureTextWidth(text: string, letterSpacing?: number): number;
@@ -23,5 +25,4 @@ export declare class Text {
23
25
  }
24
26
  export type { TextOptions, VectorTextResult as TextGeometryInfo, VectorGlyphInfo, LoadedFont };
25
27
  export type { HyphenationTrieNode };
26
- export { buildVectorGeometry, extractContours } from './LoopBlinnGeometry';
27
- export type { VectorGeometryData, VectorGlyphAttributes, GlyphRange, VectorContour, LoopBlinnInput, LoopBlinnGlyphInput, QuadraticSegment } from './LoopBlinnGeometry';
28
+ export type { SlugGPUData } from '../slug/types';
@@ -1,19 +1,19 @@
1
- import { type VectorTextResult } from './core';
2
- import { createVectorMeshes, loopBlinnFragment, type VectorMeshes, type VectorMeshOptions } from './loopBlinnTSL';
3
- import type { TextOptions } from '../core/types';
4
- export interface VectorTextOptions extends TextOptions {
5
- color?: any;
6
- positionNode?: any;
7
- colorNode?: any;
8
- center?: boolean;
9
- }
10
- export interface VectorResult extends VectorTextResult {
11
- group: import('three').Group;
12
- interiorGeometry: import('three').BufferGeometry;
13
- curveGeometry: import('three').BufferGeometry;
14
- fillGeometry: import('three').BufferGeometry;
15
- updateMaterials(options?: VectorMeshOptions): void;
16
- update(newOptions: Partial<VectorTextOptions>): Promise<VectorResult>;
1
+ import type { TextOptions, VectorGlyphInfo, LoadedFont, TextQueryOptions, TextRange } from '../core/types';
2
+ import type { BoundingBox } from '../utils/vectors';
3
+ import type { SlugGPUData } from './slug/types';
4
+ import type { HyphenationTrieNode } from '../hyphenation';
5
+ import * as THREE from 'three';
6
+ export interface VectorTextResult {
7
+ group: THREE.Group;
8
+ mesh: THREE.Mesh;
9
+ gpuData: SlugGPUData;
10
+ glyphs: VectorGlyphInfo[];
11
+ planeBounds: BoundingBox;
12
+ query(options: TextQueryOptions): TextRange[];
13
+ getLoadedFont(): LoadedFont | undefined;
14
+ measureTextWidth(text: string, letterSpacing?: number): number;
15
+ update(options: Partial<TextOptions>): Promise<VectorTextResult>;
16
+ dispose(): void;
17
17
  }
18
18
  export declare class Text {
19
19
  static setHarfBuzzPath: typeof import("..").Text.setHarfBuzzPath;
@@ -23,13 +23,10 @@ export declare class Text {
23
23
  static preloadPatterns: typeof import("..").Text.preloadPatterns;
24
24
  static setMaxFontCacheMemoryMB: typeof import("..").Text.setMaxFontCacheMemoryMB;
25
25
  static enableWoff2: typeof import("..").Text.enableWoff2;
26
- static create(options: VectorTextOptions): Promise<VectorResult>;
26
+ static create(options: TextOptions): Promise<VectorTextResult>;
27
27
  }
28
- export { createVectorMeshes, loopBlinnFragment };
29
- export type { VectorMeshes, VectorMeshOptions };
30
- export type { VectorTextResult } from './core';
31
- export { buildVectorGeometry, extractContours } from './LoopBlinnGeometry';
32
- export type { TextOptions } from '../core/types';
33
- export type { VectorGeometryData, VectorGlyphAttributes, GlyphRange, VectorContour, LoopBlinnInput, LoopBlinnGlyphInput, QuadraticSegment } from './LoopBlinnGeometry';
34
- export type { VectorGlyphInfo, LoadedFont } from '../core/types';
35
- export type { HyphenationTrieNode } from '../hyphenation';
28
+ export type { TextOptions, VectorTextResult as TextGeometryInfo, VectorGlyphInfo, LoadedFont };
29
+ export type { HyphenationTrieNode };
30
+ export { createSlugGLSLMesh } from './slug/slugGLSL';
31
+ export type { SlugGLSLMesh, SlugGLSLMeshOptions } from './slug/slugGLSL';
32
+ export type { SlugGPUData } from './slug/types';
@@ -1,13 +1,14 @@
1
1
  import * as THREE from 'three';
2
- import { Text as VectorText, type VectorResult, type VectorTextOptions } from './index';
3
- export interface TextProps extends Omit<VectorTextOptions, 'text' | 'color'> {
2
+ import { Text as VectorText, type VectorTextResult, type TextOptions } from './index';
3
+ export interface TextProps extends Omit<TextOptions, 'text' | 'color'> {
4
4
  children: string;
5
5
  font: string | ArrayBuffer;
6
6
  fillColor?: THREE.ColorRepresentation;
7
7
  position?: [number, number, number];
8
8
  rotation?: [number, number, number];
9
9
  scale?: [number, number, number];
10
- onLoad?: (result: VectorResult) => void;
10
+ positionNode?: any;
11
+ onLoad?: (result: VectorTextResult) => void;
11
12
  onError?: (error: Error) => void;
12
13
  }
13
14
  export declare const Text: import("react").ForwardRefExoticComponent<TextProps & import("react").RefAttributes<THREE.Group<THREE.Object3DEventMap>>> & {
@@ -0,0 +1,2 @@
1
+ import type { SlugShape, SlugGPUData, SlugPackOptions } from './types';
2
+ export declare function packSlugData(shapes: SlugShape[], options?: SlugPackOptions): SlugGPUData;
@@ -0,0 +1,6 @@
1
+ import type { QuadCurve, SlugVec2 } from './types';
2
+ export declare function lineToQuadratic(p0: SlugVec2, p1: SlugVec2): QuadCurve;
3
+ export declare function cubicToQuadratics(p0: SlugVec2, p1: SlugVec2, p2: SlugVec2, p3: SlugVec2, options?: {
4
+ maxError?: number;
5
+ maxDepth?: number;
6
+ }): QuadCurve[];
@@ -0,0 +1,8 @@
1
+ export { packSlugData } from './SlugPacker';
2
+ export { lineToQuadratic, cubicToQuadratics } from './curveUtils';
3
+ export { unpackSlugVertices } from './unpackVertices';
4
+ export { vertexShaderGLSL300, fragmentShaderGLSL300, vertexShaderWGSL, fragmentShaderWGSL } from './shaderStrings';
5
+ export type { QuadCurve, SlugVec2, SlugShape, SlugPackedTexture, SlugGPUData, SlugPackOptions } from './types';
6
+ export type { SlugVertexArrays } from './unpackVertices';
7
+ export { createSlugTSLMesh } from './slugTSL';
8
+ export type { SlugTSLMesh } from './slugTSL';
@@ -0,0 +1,4 @@
1
+ export declare const vertexShaderGLSL300: string;
2
+ export declare const fragmentShaderGLSL300: string;
3
+ export declare const vertexShaderWGSL: string;
4
+ export declare const fragmentShaderWGSL: string;
@@ -0,0 +1,21 @@
1
+ import * as THREE from 'three';
2
+ import type { SlugGPUData } from './types';
3
+ export interface SlugGLSLMeshOptions {
4
+ color?: {
5
+ r: number;
6
+ g: number;
7
+ b: number;
8
+ };
9
+ animationDeclarations?: string;
10
+ animationBody?: string;
11
+ uniforms?: Record<string, THREE.IUniform>;
12
+ adaptiveSupersampling?: boolean;
13
+ }
14
+ export interface SlugGLSLMesh {
15
+ mesh: THREE.Mesh;
16
+ uniforms: Record<string, THREE.IUniform>;
17
+ setOffset(x: number, y: number, z?: number): void;
18
+ setColor(r: number, g: number, b: number): void;
19
+ dispose(): void;
20
+ }
21
+ export declare function createSlugGLSLMesh(gpuData: SlugGPUData, options?: SlugGLSLMeshOptions): SlugGLSLMesh;
@@ -0,0 +1,13 @@
1
+ import * as THREE from 'three';
2
+ import type { SlugGPUData } from './types';
3
+ export interface SlugTSLMesh {
4
+ mesh: THREE.Mesh;
5
+ setOffset(x: number, y: number, z?: number): void;
6
+ setColor(r: number, g: number, b: number): void;
7
+ dispose(): void;
8
+ }
9
+ export declare function createSlugTSLMesh(gpuData: SlugGPUData, color?: {
10
+ r: number;
11
+ g: number;
12
+ b: number;
13
+ }): SlugTSLMesh;
@@ -0,0 +1,30 @@
1
+ export interface QuadCurve {
2
+ p1: [number, number];
3
+ p2: [number, number];
4
+ p3: [number, number];
5
+ }
6
+ export type SlugVec2 = [number, number];
7
+ export interface SlugShape {
8
+ curves: QuadCurve[];
9
+ bounds: [number, number, number, number];
10
+ }
11
+ export interface SlugPackedTexture {
12
+ data: Float32Array | Uint32Array;
13
+ width: number;
14
+ height: number;
15
+ }
16
+ export interface SlugGPUData {
17
+ curveTexture: SlugPackedTexture & {
18
+ data: Float32Array;
19
+ };
20
+ bandTexture: SlugPackedTexture & {
21
+ data: Uint32Array;
22
+ };
23
+ vertices: Float32Array;
24
+ indices: Uint16Array;
25
+ shapeCount: number;
26
+ }
27
+ export interface SlugPackOptions {
28
+ bandCount?: number;
29
+ evenOdd?: boolean;
30
+ }
@@ -0,0 +1,11 @@
1
+ import type { SlugGPUData } from './types';
2
+ export interface SlugVertexArrays {
3
+ positions: Float32Array;
4
+ texcoords: Float32Array;
5
+ bandings: Float32Array;
6
+ glyphData: Float32Array;
7
+ colors: Float32Array;
8
+ glyphCenters: Float32Array;
9
+ glyphIndices: Float32Array;
10
+ }
11
+ export declare function unpackSlugVertices(gpuData: SlugGPUData): SlugVertexArrays;