three-text 0.2.1 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -17,7 +17,7 @@ A high fidelity 3D font renderer and text layout engine for the web
17
17
 
18
18
  **three-text** renders and formats text from TTF, OTF, and WOFF font files as 3D geometry. It uses [TeX](https://en.wikipedia.org/wiki/TeX)-based parameters for breaking text into paragraphs across multiple lines, and turns font outlines into 3D shapes on the fly, caching their geometries for low CPU overhead in languages with lots of repeating glyphs. Variable fonts are supported as static instances at a given axis coordinate
19
19
 
20
- The library has a framework-agnostic core that returns raw vertex data, with lightweight adapters for Three.js, React Three Fiber, p5.js, WebGL, and WebGPU
20
+ The library has a framework-agnostic core that returns raw vertex data, 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
22
  Under the hood, three-text relies on [HarfBuzz](https://github.com/harfbuzz/harfbuzzjs) for text shaping, [Knuth-Plass](http://www.eprg.org/G53DOC/pdfs/knuth-plass-breaking.pdf) line breaking, [Liang](https://tug.org/docs/liang/liang-thesis.pdf) hyphenation, [libtess2](https://github.com/memononen/libtess2) (based on the [OpenGL Utility Library (GLU) tessellator](https://www.songho.ca/opengl/gl_tessellation.html) by Eric Veach) for removing overlaps and triangulation, bezier 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/).
23
23
 
@@ -25,12 +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
- - [Three.js](#threejs-usage)
29
- - [React Three Fiber](#react-three-fiber-usage)
30
- - [WebGL](#webgl-usage)
31
- - [WebGPU](#webgpu-usage)
32
- - [p5.js](#p5js-usage)
33
- - [Core (framework-agnostic)](#core-usage)
28
+ - [Three.js](#threejs)
29
+ - [p5.js](#p5js)
30
+ - [React Three Fiber](#react-three-fiber)
31
+ - [WebGL](#webgl)
32
+ - [WebGPU](#webgpu)
33
+ - [Core](#core)
34
34
  - [Development and examples](#development-and-examples)
35
35
  - [Architecture](#architecture)
36
36
  - [Why three-text?](#why-three-text)
@@ -70,7 +70,7 @@ three-text has a framework-agnostic core that processes fonts and generates geom
70
70
  - **`three-text/three/react`** - React Three Fiber component
71
71
  - **`three-text/webgl`** - WebGL buffer utility
72
72
  - **`three-text/webgpu`** - WebGPU buffer utility
73
- - **`three-text/p5`** - p5.js geometry converter
73
+ - **`three-text/p5`** - p5.js adapter
74
74
 
75
75
  Choose the import that matches your stack. Most users will use `three-text/three` or `three-text/p5`
76
76
 
@@ -97,28 +97,29 @@ scene.add(mesh);
97
97
  #### p5.js
98
98
 
99
99
  ```javascript
100
- import { Text } from 'three-text';
101
- import { createP5Geometry } from 'three-text/p5';
100
+ import 'three-text/p5';
102
101
 
103
- let textGeom;
102
+ let font;
103
+ let textResult;
104
+
105
+ function preload() {
106
+ loadThreeTextShaper('/hb/hb.wasm');
107
+ font = loadThreeTextFont('/fonts/Font.woff');
108
+ }
104
109
 
105
110
  async function setup() {
106
111
  createCanvas(400, 400, WEBGL);
107
- Text.setHarfBuzzPath('/hb/hb.wasm');
108
-
109
- const data = await Text.create({
110
- text: 'Hello p5!',
111
- font: '/fonts/Font.woff',
112
- size: 72
112
+ textResult = await createThreeTextGeometry('Hello p5!', {
113
+ font: font,
114
+ size: 72,
115
+ depth: 30
113
116
  });
114
-
115
- textGeom = createP5Geometry(window, data);
116
117
  }
117
118
 
118
119
  function draw() {
119
120
  background(200);
120
121
  lights();
121
- model(textGeom);
122
+ if (textResult) model(textResult.geometry);
122
123
  }
123
124
  ```
124
125
 
@@ -244,7 +245,7 @@ three-text/
244
245
  │ │ └── ThreeText.tsx # React Three Fiber component
245
246
  │ ├── webgl/ # WebGL buffer utility
246
247
  │ ├── webgpu/ # WebGPU buffer utility
247
- │ ├── p5/ # p5.js geometry converter
248
+ │ ├── p5/ # p5.js adapter
248
249
  │ ├── hyphenation/ # Language-specific hyphenation patterns
249
250
  │ └── utils/ # Performance logging, data structures
250
251
  ├── examples/ # Demos for all adapters
@@ -961,7 +962,7 @@ The build generates multiple module formats for core and all adapters:
961
962
  - `dist/three/react.js` - React component
962
963
  - `dist/webgl/` - WebGL utility
963
964
  - `dist/webgpu/` - WebGPU utility
964
- - `dist/p5/` - p5.js converter
965
+ - `dist/p5/` - p5.js adapter
965
966
 
966
967
  **Patterns:**
967
968
  - `dist/patterns/` - Hyphenation patterns (ESM and UMD)
package/dist/index.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * three-text v0.2.1
2
+ * three-text v0.2.2
3
3
  * Copyright (C) 2025 Countertype LLC
4
4
  *
5
5
  * This program is free software: you can redistribute it and/or modify
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * three-text v0.2.1
2
+ * three-text v0.2.2
3
3
  * Copyright (C) 2025 Countertype LLC
4
4
  *
5
5
  * This program is free software: you can redistribute it and/or modify
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * three-text v0.2.1
2
+ * three-text v0.2.2
3
3
  * Copyright (C) 2025 Countertype LLC
4
4
  *
5
5
  * This program is free software: you can redistribute it and/or modify
package/dist/index.min.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * three-text v0.2.1
2
+ * three-text v0.2.2
3
3
  * Copyright (C) 2025 Countertype LLC
4
4
  *
5
5
  * This program is free software: you can redistribute it and/or modify
package/dist/index.umd.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * three-text v0.2.1
2
+ * three-text v0.2.2
3
3
  * Copyright (C) 2025 Countertype LLC
4
4
  *
5
5
  * This program is free software: you can redistribute it and/or modify
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * three-text v0.2.1
2
+ * three-text v0.2.2
3
3
  * Copyright (C) 2025 Countertype LLC
4
4
  *
5
5
  * This program is free software: you can redistribute it and/or modify
package/dist/p5/index.cjs CHANGED
@@ -1,21 +1,30 @@
1
1
  'use strict';
2
2
 
3
- // p5.js adapter - converts core geometry to p5.Geometry
4
- function createP5Geometry(p5Instance, textGeometry) {
5
- // In global mode, p5.Geometry exists but createVector is global
6
- // In instance mode, both are on the instance
7
- const P5Geometry = p5Instance.Geometry || window.p5?.Geometry;
8
- const createVec = p5Instance.createVector || window.createVector;
9
- if (!P5Geometry || !createVec) {
10
- throw new Error('p5.js not found. Make sure p5.js is loaded before calling this function.');
11
- }
12
- const geom = new P5Geometry();
3
+ var Text = require('../index.cjs');
4
+
5
+ // p5.js adapter
6
+ function convertToP5Geometry(p5Instance, textGeometry) {
13
7
  const { vertices, normals, indices } = textGeometry;
14
- // Convert vertices (flip Y for p5.js coordinate system)
8
+ const P5GeometryClass = p5Instance.constructor?.Geometry ||
9
+ (typeof window !== 'undefined' && window.p5?.Geometry);
10
+ if (!P5GeometryClass) {
11
+ throw new Error('p5.Geometry not found. Ensure p5.js is loaded.');
12
+ }
13
+ const geom = new P5GeometryClass();
14
+ const createVec = (x, y, z) => {
15
+ if (typeof p5Instance.createVector === 'function') {
16
+ return p5Instance.createVector(x, y, z);
17
+ }
18
+ const globalCreateVector = (typeof window !== 'undefined' && window.createVector);
19
+ if (globalCreateVector) {
20
+ return globalCreateVector(x, y, z);
21
+ }
22
+ throw new Error('createVector not found');
23
+ };
24
+ // p5 uses +Y up, we use +Y down
15
25
  for (let i = 0; i < vertices.length; i += 3) {
16
26
  geom.vertices.push(createVec(vertices[i], -vertices[i + 1], vertices[i + 2]));
17
27
  }
18
- // Convert normals (flip Y)
19
28
  for (let i = 0; i < normals.length; i += 3) {
20
29
  geom.vertexNormals.push(createVec(normals[i], -normals[i + 1], normals[i + 2]));
21
30
  }
@@ -29,5 +38,75 @@ function createP5Geometry(p5Instance, textGeometry) {
29
38
  }
30
39
  return geom;
31
40
  }
41
+ let shaperInitialized = false;
42
+ if (typeof window !== 'undefined' && window.p5) {
43
+ const p5 = window.p5;
44
+ p5.prototype.loadThreeTextShaper = function (wasmPath) {
45
+ if (shaperInitialized) {
46
+ return;
47
+ }
48
+ Text.Text.setHarfBuzzPath(wasmPath);
49
+ shaperInitialized = true;
50
+ Text.Text.init()
51
+ .then(() => {
52
+ this._decrementPreload();
53
+ })
54
+ .catch((err) => {
55
+ console.error('Failed to load text shaper:', err);
56
+ this._decrementPreload();
57
+ });
58
+ };
59
+ p5.prototype.loadThreeTextFont = function (fontPath, fontVariations) {
60
+ const fontRef = {
61
+ buffer: null,
62
+ path: fontPath,
63
+ variations: fontVariations
64
+ };
65
+ fetch(fontPath)
66
+ .then(res => {
67
+ if (!res.ok) {
68
+ throw new Error(`Failed to load font: HTTP ${res.status}`);
69
+ }
70
+ return res.arrayBuffer();
71
+ })
72
+ .then(buffer => {
73
+ fontRef.buffer = buffer;
74
+ this._decrementPreload();
75
+ })
76
+ .catch((err) => {
77
+ console.error(`Failed to load font ${fontPath}:`, err);
78
+ this._decrementPreload();
79
+ });
80
+ return fontRef;
81
+ };
82
+ p5.prototype.createThreeTextGeometry = async function (text, options) {
83
+ if (!options.font || !options.font.buffer) {
84
+ console.error('Font not loaded. Use loadThreeTextFont() in preload().');
85
+ return null;
86
+ }
87
+ const { font, ...coreOptions } = options;
88
+ try {
89
+ const result = await Text.Text.create({
90
+ text,
91
+ font: font.buffer,
92
+ fontVariations: font.variations,
93
+ ...coreOptions
94
+ });
95
+ const p5Instance = this;
96
+ const geometry = convertToP5Geometry(p5Instance, result);
97
+ return {
98
+ geometry,
99
+ planeBounds: result.planeBounds,
100
+ glyphs: result.glyphs
101
+ };
102
+ }
103
+ catch (err) {
104
+ console.error('Failed to create text geometry:', err);
105
+ return null;
106
+ }
107
+ };
108
+ p5.prototype.registerPreloadMethod('loadThreeTextShaper', p5.prototype);
109
+ p5.prototype.registerPreloadMethod('loadThreeTextFont', p5.prototype);
110
+ }
32
111
 
33
- exports.createP5Geometry = createP5Geometry;
112
+ exports.createP5Geometry = convertToP5Geometry;
@@ -13,7 +13,13 @@ interface P5Geometry {
13
13
  interface P5Instance {
14
14
  Geometry: new () => P5Geometry;
15
15
  createVector(x: number, y: number, z: number): P5Vector;
16
+ _decrementPreload(): void;
16
17
  }
17
- declare function createP5Geometry(p5Instance: P5Instance | any, textGeometry: TextGeometryInfo): P5Geometry;
18
+ declare global {
19
+ interface Window {
20
+ p5?: any;
21
+ }
22
+ }
23
+ declare function convertToP5Geometry(p5Instance: P5Instance, textGeometry: TextGeometryInfo): P5Geometry;
18
24
 
19
- export { createP5Geometry };
25
+ export { convertToP5Geometry as createP5Geometry };
package/dist/p5/index.js CHANGED
@@ -1,19 +1,28 @@
1
- // p5.js adapter - converts core geometry to p5.Geometry
2
- function createP5Geometry(p5Instance, textGeometry) {
3
- // In global mode, p5.Geometry exists but createVector is global
4
- // In instance mode, both are on the instance
5
- const P5Geometry = p5Instance.Geometry || window.p5?.Geometry;
6
- const createVec = p5Instance.createVector || window.createVector;
7
- if (!P5Geometry || !createVec) {
8
- throw new Error('p5.js not found. Make sure p5.js is loaded before calling this function.');
9
- }
10
- const geom = new P5Geometry();
1
+ import { Text } from '../index.js';
2
+
3
+ // p5.js adapter
4
+ function convertToP5Geometry(p5Instance, textGeometry) {
11
5
  const { vertices, normals, indices } = textGeometry;
12
- // Convert vertices (flip Y for p5.js coordinate system)
6
+ const P5GeometryClass = p5Instance.constructor?.Geometry ||
7
+ (typeof window !== 'undefined' && window.p5?.Geometry);
8
+ if (!P5GeometryClass) {
9
+ throw new Error('p5.Geometry not found. Ensure p5.js is loaded.');
10
+ }
11
+ const geom = new P5GeometryClass();
12
+ const createVec = (x, y, z) => {
13
+ if (typeof p5Instance.createVector === 'function') {
14
+ return p5Instance.createVector(x, y, z);
15
+ }
16
+ const globalCreateVector = (typeof window !== 'undefined' && window.createVector);
17
+ if (globalCreateVector) {
18
+ return globalCreateVector(x, y, z);
19
+ }
20
+ throw new Error('createVector not found');
21
+ };
22
+ // p5 uses +Y up, we use +Y down
13
23
  for (let i = 0; i < vertices.length; i += 3) {
14
24
  geom.vertices.push(createVec(vertices[i], -vertices[i + 1], vertices[i + 2]));
15
25
  }
16
- // Convert normals (flip Y)
17
26
  for (let i = 0; i < normals.length; i += 3) {
18
27
  geom.vertexNormals.push(createVec(normals[i], -normals[i + 1], normals[i + 2]));
19
28
  }
@@ -27,5 +36,75 @@ function createP5Geometry(p5Instance, textGeometry) {
27
36
  }
28
37
  return geom;
29
38
  }
39
+ let shaperInitialized = false;
40
+ if (typeof window !== 'undefined' && window.p5) {
41
+ const p5 = window.p5;
42
+ p5.prototype.loadThreeTextShaper = function (wasmPath) {
43
+ if (shaperInitialized) {
44
+ return;
45
+ }
46
+ Text.setHarfBuzzPath(wasmPath);
47
+ shaperInitialized = true;
48
+ Text.init()
49
+ .then(() => {
50
+ this._decrementPreload();
51
+ })
52
+ .catch((err) => {
53
+ console.error('Failed to load text shaper:', err);
54
+ this._decrementPreload();
55
+ });
56
+ };
57
+ p5.prototype.loadThreeTextFont = function (fontPath, fontVariations) {
58
+ const fontRef = {
59
+ buffer: null,
60
+ path: fontPath,
61
+ variations: fontVariations
62
+ };
63
+ fetch(fontPath)
64
+ .then(res => {
65
+ if (!res.ok) {
66
+ throw new Error(`Failed to load font: HTTP ${res.status}`);
67
+ }
68
+ return res.arrayBuffer();
69
+ })
70
+ .then(buffer => {
71
+ fontRef.buffer = buffer;
72
+ this._decrementPreload();
73
+ })
74
+ .catch((err) => {
75
+ console.error(`Failed to load font ${fontPath}:`, err);
76
+ this._decrementPreload();
77
+ });
78
+ return fontRef;
79
+ };
80
+ p5.prototype.createThreeTextGeometry = async function (text, options) {
81
+ if (!options.font || !options.font.buffer) {
82
+ console.error('Font not loaded. Use loadThreeTextFont() in preload().');
83
+ return null;
84
+ }
85
+ const { font, ...coreOptions } = options;
86
+ try {
87
+ const result = await Text.create({
88
+ text,
89
+ font: font.buffer,
90
+ fontVariations: font.variations,
91
+ ...coreOptions
92
+ });
93
+ const p5Instance = this;
94
+ const geometry = convertToP5Geometry(p5Instance, result);
95
+ return {
96
+ geometry,
97
+ planeBounds: result.planeBounds,
98
+ glyphs: result.glyphs
99
+ };
100
+ }
101
+ catch (err) {
102
+ console.error('Failed to create text geometry:', err);
103
+ return null;
104
+ }
105
+ };
106
+ p5.prototype.registerPreloadMethod('loadThreeTextShaper', p5.prototype);
107
+ p5.prototype.registerPreloadMethod('loadThreeTextFont', p5.prototype);
108
+ }
30
109
 
31
- export { createP5Geometry };
110
+ export { convertToP5Geometry as createP5Geometry };
@@ -12,6 +12,12 @@ interface P5Geometry {
12
12
  interface P5Instance {
13
13
  Geometry: new () => P5Geometry;
14
14
  createVector(x: number, y: number, z: number): P5Vector;
15
+ _decrementPreload(): void;
15
16
  }
16
- export declare function createP5Geometry(p5Instance: P5Instance | any, textGeometry: TextGeometryInfo): P5Geometry;
17
- export {};
17
+ declare global {
18
+ interface Window {
19
+ p5?: any;
20
+ }
21
+ }
22
+ declare function convertToP5Geometry(p5Instance: P5Instance, textGeometry: TextGeometryInfo): P5Geometry;
23
+ export { convertToP5Geometry as createP5Geometry };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "three-text",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
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",