three-text 0.2.19 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -19,6 +19,8 @@ function convertToThree(result) {
19
19
  geometry.setAttribute('glyphCenter', new three.Float32BufferAttribute(result.glyphAttributes.glyphCenter, 3));
20
20
  geometry.setAttribute('glyphIndex', new three.Float32BufferAttribute(result.glyphAttributes.glyphIndex, 1));
21
21
  geometry.setAttribute('glyphLineIndex', new three.Float32BufferAttribute(result.glyphAttributes.glyphLineIndex, 1));
22
+ geometry.setAttribute('glyphProgress', new three.Float32BufferAttribute(result.glyphAttributes.glyphProgress, 1));
23
+ geometry.setAttribute('glyphBaselineY', new three.Float32BufferAttribute(result.glyphAttributes.glyphBaselineY, 1));
22
24
  }
23
25
  geometry.computeBoundingBox();
24
26
  // Return Three.js specific interface with utility methods
@@ -31,7 +33,7 @@ function convertToThree(result) {
31
33
  coloredRanges: result.coloredRanges,
32
34
  // Pass through utility methods from core
33
35
  getLoadedFont: result.getLoadedFont,
34
- getCacheStatistics: result.getCacheStatistics,
36
+ getCacheSize: result.getCacheSize,
35
37
  clearCache: result.clearCache,
36
38
  measureTextWidth: result.measureTextWidth,
37
39
  update: async (newOptions) => {
@@ -10,21 +10,10 @@ interface HyphenationTrieNode {
10
10
  };
11
11
  }
12
12
 
13
- interface CacheStats {
14
- hits: number;
15
- misses: number;
16
- evictions: number;
17
- size: number;
18
- memoryUsage: number;
19
- }
20
-
21
13
  interface ThreeTextGeometryInfo extends Omit<TextGeometryInfo, 'vertices' | 'normals' | 'indices' | 'colors' | 'glyphAttributes'> {
22
14
  geometry: BufferGeometry;
23
15
  getLoadedFont(): LoadedFont | undefined;
24
- getCacheStatistics(): (CacheStats & {
25
- hitRate: number;
26
- memoryUsageMB: number;
27
- }) | null;
16
+ getCacheSize(): number;
28
17
  clearCache(): void;
29
18
  measureTextWidth(text: string, letterSpacing?: number): number;
30
19
  update(options: Partial<TextOptions>): Promise<ThreeTextGeometryInfo>;
@@ -17,6 +17,8 @@ function convertToThree(result) {
17
17
  geometry.setAttribute('glyphCenter', new Float32BufferAttribute(result.glyphAttributes.glyphCenter, 3));
18
18
  geometry.setAttribute('glyphIndex', new Float32BufferAttribute(result.glyphAttributes.glyphIndex, 1));
19
19
  geometry.setAttribute('glyphLineIndex', new Float32BufferAttribute(result.glyphAttributes.glyphLineIndex, 1));
20
+ geometry.setAttribute('glyphProgress', new Float32BufferAttribute(result.glyphAttributes.glyphProgress, 1));
21
+ geometry.setAttribute('glyphBaselineY', new Float32BufferAttribute(result.glyphAttributes.glyphBaselineY, 1));
20
22
  }
21
23
  geometry.computeBoundingBox();
22
24
  // Return Three.js specific interface with utility methods
@@ -29,7 +31,7 @@ function convertToThree(result) {
29
31
  coloredRanges: result.coloredRanges,
30
32
  // Pass through utility methods from core
31
33
  getLoadedFont: result.getLoadedFont,
32
- getCacheStatistics: result.getCacheStatistics,
34
+ getCacheSize: result.getCacheSize,
33
35
  clearCache: result.clearCache,
34
36
  measureTextWidth: result.measureTextWidth,
35
37
  update: async (newOptions) => {
@@ -10,11 +10,7 @@ interface HyphenationTrieNode {
10
10
  }
11
11
 
12
12
  interface CacheStats {
13
- hits: number;
14
- misses: number;
15
- evictions: number;
16
13
  size: number;
17
- memoryUsage: number;
18
14
  }
19
15
 
20
16
  interface BoundingBox {
@@ -182,6 +178,8 @@ interface TextGeometryInfo {
182
178
  glyphCenter: Float32Array;
183
179
  glyphIndex: Float32Array;
184
180
  glyphLineIndex: Float32Array;
181
+ glyphProgress: Float32Array;
182
+ glyphBaselineY: Float32Array;
185
183
  };
186
184
  glyphs: GlyphGeometryInfo[];
187
185
  planeBounds: BoundingBox;
@@ -200,10 +198,7 @@ interface TextGeometryInfo {
200
198
  }
201
199
  interface TextHandle extends TextGeometryInfo {
202
200
  getLoadedFont(): LoadedFont | undefined;
203
- getCacheStatistics(): (CacheStats & {
204
- hitRate: number;
205
- memoryUsageMB: number;
206
- }) | null;
201
+ getCacheSize(): number;
207
202
  clearCache(): void;
208
203
  measureTextWidth(text: string, letterSpacing?: number): number;
209
204
  update(options: Partial<TextOptions>): Promise<TextHandle>;
@@ -247,7 +242,7 @@ interface TextOptions {
247
242
  depth?: number;
248
243
  lineHeight?: number;
249
244
  letterSpacing?: number;
250
- separateGlyphsWithAttributes?: boolean;
245
+ perGlyphAttributes?: boolean;
251
246
  fontVariations?: {
252
247
  [key: string]: number;
253
248
  };
@@ -346,10 +341,7 @@ declare class Text$1 {
346
341
  static setMaxFontCacheMemoryMB(limitMB: number): void;
347
342
  getLoadedFont(): LoadedFont | undefined;
348
343
  measureTextWidth(text: string, letterSpacing?: number): number;
349
- getCacheStatistics(): (CacheStats & {
350
- hitRate: number;
351
- memoryUsageMB: number;
352
- }) | null;
344
+ getCacheSize(): number;
353
345
  clearCache(): void;
354
346
  private createGlyphAttributes;
355
347
  private resetHelpers;
@@ -46,10 +46,7 @@ export declare class Text {
46
46
  static setMaxFontCacheMemoryMB(limitMB: number): void;
47
47
  getLoadedFont(): LoadedFont | undefined;
48
48
  measureTextWidth(text: string, letterSpacing?: number): number;
49
- getCacheStatistics(): (import("..").CacheStats & {
50
- hitRate: number;
51
- memoryUsageMB: number;
52
- }) | null;
49
+ getCacheSize(): number;
53
50
  clearCache(): void;
54
51
  private createGlyphAttributes;
55
52
  private resetHelpers;
@@ -1,6 +1,6 @@
1
1
  import { GlyphGeometryInfo, LoadedFont, GlyphCluster, GlyphData } from '../types';
2
2
  import { CurveFidelityConfig, GeometryOptimizationOptions } from '../types';
3
- import { LRUCache } from '../../utils/LRUCache';
3
+ import { Cache } from '../../utils/Cache';
4
4
  export interface InstancedTextGeometry {
5
5
  vertices: Float32Array;
6
6
  normals: Float32Array;
@@ -35,14 +35,14 @@ export declare class GlyphGeometryBuilder {
35
35
  private contourCache;
36
36
  private clusteringCache;
37
37
  private emptyGlyphs;
38
- constructor(cache: LRUCache<string, GlyphData>, loadedFont: LoadedFont);
38
+ constructor(cache: Cache<string, GlyphData>, loadedFont: LoadedFont);
39
39
  getOptimizationStats(): import("../geometry/PathOptimizer").OptimizationStats;
40
40
  setCurveFidelityConfig(config?: CurveFidelityConfig): void;
41
41
  setGeometryOptimization(options?: GeometryOptimizationOptions): void;
42
42
  setFontId(fontId: string): void;
43
43
  private updateCacheKeyPrefix;
44
44
  private getGeometryConfigSignature;
45
- buildInstancedGeometry(clustersByLine: GlyphCluster[][], depth: number, removeOverlaps: boolean, isCFF: boolean, separateGlyphs?: boolean, coloredTextIndices?: Set<number>): InstancedTextGeometry;
45
+ buildInstancedGeometry(clustersByLine: GlyphCluster[][], depth: number, removeOverlaps: boolean, isCFF: boolean, scale: number, separateGlyphs?: boolean, coloredTextIndices?: Set<number>): InstancedTextGeometry;
46
46
  private getClusterKey;
47
47
  private createGlyphInfo;
48
48
  private getContoursForGlyph;
@@ -50,9 +50,6 @@ export declare class GlyphGeometryBuilder {
50
50
  private extrudeAndPackage;
51
51
  private tessellateGlyph;
52
52
  private updatePlaneBounds;
53
- getCacheStats(): import("../../utils/LRUCache").CacheStats & {
54
- hitRate: number;
55
- memoryUsageMB: number;
56
- };
53
+ getCacheStats(): import("../../utils/Cache").CacheStats;
57
54
  clearCache(): void;
58
55
  }
@@ -1,12 +1,11 @@
1
- import { LRUCache } from '../../utils/LRUCache';
1
+ import { Cache } from '../../utils/Cache';
2
2
  import type { GlyphData, GlyphContours } from '../types';
3
3
  export declare function getGlyphCacheKey(fontId: string, glyphId: number, depth: number, removeOverlaps: boolean): string;
4
- export declare function calculateGlyphMemoryUsage(glyph: GlyphData): number;
5
- export declare const globalGlyphCache: LRUCache<string, GlyphData>;
6
- export declare function createGlyphCache(maxCacheSizeMB?: number): LRUCache<string, GlyphData>;
7
- export declare const globalContourCache: LRUCache<string, GlyphContours>;
8
- export declare const globalWordCache: LRUCache<string, GlyphData>;
9
- export declare const globalClusteringCache: LRUCache<string, {
4
+ export declare const globalGlyphCache: Cache<string, GlyphData>;
5
+ export declare function createGlyphCache(): Cache<string, GlyphData>;
6
+ export declare const globalContourCache: Cache<string, GlyphContours>;
7
+ export declare const globalWordCache: Cache<string, GlyphData>;
8
+ export declare const globalClusteringCache: Cache<string, {
10
9
  glyphIds: number[];
11
10
  groups: number[][];
12
11
  }>;
@@ -6,6 +6,5 @@ export interface ExtrusionResult {
6
6
  }
7
7
  export declare class Extruder {
8
8
  constructor();
9
- private packEdge;
10
9
  extrude(geometry: ProcessedGeometry, depth: number | undefined, unitsPerEm: number): ExtrusionResult;
11
10
  }
@@ -31,8 +31,5 @@ export declare class TextShaper {
31
31
  private calculateSpaceAdjustment;
32
32
  private calculateCJKAdjustment;
33
33
  clearCache(): void;
34
- getCacheStats(): import("../..").CacheStats & {
35
- hitRate: number;
36
- memoryUsageMB: number;
37
- };
34
+ getCacheStats(): import("../..").CacheStats;
38
35
  }
@@ -1,5 +1,5 @@
1
1
  import type { HyphenationTrieNode } from '../hyphenation';
2
- import type { CacheStats } from '../utils/LRUCache';
2
+ import type { CacheStats } from '../utils/Cache';
3
3
  import type { Vec2, Vec3, BoundingBox } from './vectors';
4
4
  export type { HyphenationTrieNode };
5
5
  export interface Path {
@@ -241,6 +241,8 @@ export interface TextGeometryInfo {
241
241
  glyphCenter: Float32Array;
242
242
  glyphIndex: Float32Array;
243
243
  glyphLineIndex: Float32Array;
244
+ glyphProgress: Float32Array;
245
+ glyphBaselineY: Float32Array;
244
246
  };
245
247
  glyphs: GlyphGeometryInfo[];
246
248
  planeBounds: BoundingBox;
@@ -259,10 +261,7 @@ export interface TextGeometryInfo {
259
261
  }
260
262
  export interface TextHandle extends TextGeometryInfo {
261
263
  getLoadedFont(): LoadedFont | undefined;
262
- getCacheStatistics(): (CacheStats & {
263
- hitRate: number;
264
- memoryUsageMB: number;
265
- }) | null;
264
+ getCacheSize(): number;
266
265
  clearCache(): void;
267
266
  measureTextWidth(text: string, letterSpacing?: number): number;
268
267
  update(options: Partial<TextOptions>): Promise<TextHandle>;
@@ -306,7 +305,7 @@ export interface TextOptions {
306
305
  depth?: number;
307
306
  lineHeight?: number;
308
307
  letterSpacing?: number;
309
- separateGlyphsWithAttributes?: boolean;
308
+ perGlyphAttributes?: boolean;
310
309
  fontVariations?: {
311
310
  [key: string]: number;
312
311
  };
@@ -2,6 +2,6 @@ export { Text } from './core/Text';
2
2
  export { DEFAULT_CURVE_FIDELITY } from './core/geometry/Polygonizer';
3
3
  export { FontMetadataExtractor } from './core/font/FontMetadata';
4
4
  export { globalGlyphCache, createGlyphCache } from './core/cache/sharedCaches';
5
- export type { CacheStats } from './utils/LRUCache';
5
+ export type { CacheStats } from './utils/Cache';
6
6
  export type { TextAlign, TextDirection, LineInfo, LoadedFont, HarfBuzzModule, HarfBuzzAPI, HarfBuzzBlob, HarfBuzzFace, HarfBuzzFont, HarfBuzzBuffer, HarfBuzzInstance, VariationAxis, ExtractedMetrics, VerticalMetrics, FontMetrics, ProcessedGeometry, Triangles, GlyphData, GlyphGeometryInfo, TextGeometryInfo, TextHandle, TextOptions, ColorOptions, ColorByRange, ColoredRange, PathInfo, HyphenationPatternsMap, CurveFidelityConfig, LayoutOptions, GeometryOptimizationOptions, TextRange, TextQueryOptions } from './core/types';
7
7
  export type { HyphenationTrieNode } from './hyphenation';
@@ -2,14 +2,10 @@ import { BufferGeometry } from 'three';
2
2
  import { Text as TextCore } from '../core/Text';
3
3
  import type { TextOptions, TextGeometryInfo as CoreTextGeometryInfo, LoadedFont } from '../core/types';
4
4
  import type { HyphenationTrieNode } from '../hyphenation';
5
- import type { CacheStats } from '../utils/LRUCache';
6
5
  export interface ThreeTextGeometryInfo extends Omit<CoreTextGeometryInfo, 'vertices' | 'normals' | 'indices' | 'colors' | 'glyphAttributes'> {
7
6
  geometry: BufferGeometry;
8
7
  getLoadedFont(): LoadedFont | undefined;
9
- getCacheStatistics(): (CacheStats & {
10
- hitRate: number;
11
- memoryUsageMB: number;
12
- }) | null;
8
+ getCacheSize(): number;
13
9
  clearCache(): void;
14
10
  measureTextWidth(text: string, letterSpacing?: number): number;
15
11
  update(options: Partial<TextOptions>): Promise<ThreeTextGeometryInfo>;
@@ -0,0 +1,14 @@
1
+ export interface CacheStats {
2
+ size: number;
3
+ }
4
+ export declare class Cache<K, V> {
5
+ private cache;
6
+ get(key: K): V | undefined;
7
+ has(key: K): boolean;
8
+ set(key: K, value: V): void;
9
+ delete(key: K): boolean;
10
+ clear(): void;
11
+ get size(): number;
12
+ keys(): K[];
13
+ getStats(): CacheStats;
14
+ }
@@ -8,6 +8,8 @@ export interface WebGLBufferSet {
8
8
  glyphCenter?: WebGLBuffer;
9
9
  glyphIndex?: WebGLBuffer;
10
10
  glyphLineIndex?: WebGLBuffer;
11
+ glyphProgress?: WebGLBuffer;
12
+ glyphBaselineY?: WebGLBuffer;
11
13
  };
12
14
  attributes: {
13
15
  position: {
@@ -40,6 +42,16 @@ export interface WebGLBufferSet {
40
42
  type: GLenum;
41
43
  normalized: boolean;
42
44
  };
45
+ glyphProgress?: {
46
+ size: number;
47
+ type: GLenum;
48
+ normalized: boolean;
49
+ };
50
+ glyphBaselineY?: {
51
+ size: number;
52
+ type: GLenum;
53
+ normalized: boolean;
54
+ };
43
55
  };
44
56
  drawCount: number;
45
57
  mode: GLenum;
@@ -4,10 +4,20 @@ export interface WebGPUBufferSet {
4
4
  vertex: GPUBuffer;
5
5
  color?: GPUBuffer;
6
6
  indices: GPUBuffer;
7
+ glyphCenter?: GPUBuffer;
8
+ glyphIndex?: GPUBuffer;
9
+ glyphLineIndex?: GPUBuffer;
10
+ glyphProgress?: GPUBuffer;
11
+ glyphBaselineY?: GPUBuffer;
7
12
  };
8
13
  layout: {
9
14
  vertex: GPUVertexBufferLayout;
10
15
  color?: GPUVertexBufferLayout;
16
+ glyphCenter?: GPUVertexBufferLayout;
17
+ glyphIndex?: GPUVertexBufferLayout;
18
+ glyphLineIndex?: GPUVertexBufferLayout;
19
+ glyphProgress?: GPUVertexBufferLayout;
20
+ glyphBaselineY?: GPUVertexBufferLayout;
11
21
  };
12
22
  indexCount: number;
13
23
  indexFormat: GPUIndexFormat;
@@ -63,6 +63,20 @@ function createWebGLBuffers(gl, textGeometry) {
63
63
  gl.bufferData(gl.ARRAY_BUFFER, glyphAttributes.glyphLineIndex, gl.STATIC_DRAW);
64
64
  buffers.glyphLineIndex = glyphLineIndexBuffer;
65
65
  attributes.glyphLineIndex = { size: 1, type: gl.FLOAT, normalized: false };
66
+ const glyphProgressBuffer = gl.createBuffer();
67
+ if (!glyphProgressBuffer)
68
+ throw new Error('Failed to create glyphProgress buffer');
69
+ gl.bindBuffer(gl.ARRAY_BUFFER, glyphProgressBuffer);
70
+ gl.bufferData(gl.ARRAY_BUFFER, glyphAttributes.glyphProgress, gl.STATIC_DRAW);
71
+ buffers.glyphProgress = glyphProgressBuffer;
72
+ attributes.glyphProgress = { size: 1, type: gl.FLOAT, normalized: false };
73
+ const glyphBaselineYBuffer = gl.createBuffer();
74
+ if (!glyphBaselineYBuffer)
75
+ throw new Error('Failed to create glyphBaselineY buffer');
76
+ gl.bindBuffer(gl.ARRAY_BUFFER, glyphBaselineYBuffer);
77
+ gl.bufferData(gl.ARRAY_BUFFER, glyphAttributes.glyphBaselineY, gl.STATIC_DRAW);
78
+ buffers.glyphBaselineY = glyphBaselineYBuffer;
79
+ attributes.glyphBaselineY = { size: 1, type: gl.FLOAT, normalized: false };
66
80
  }
67
81
  return {
68
82
  buffers,
@@ -81,6 +95,10 @@ function createWebGLBuffers(gl, textGeometry) {
81
95
  gl.deleteBuffer(buffers.glyphIndex);
82
96
  if (buffers.glyphLineIndex)
83
97
  gl.deleteBuffer(buffers.glyphLineIndex);
98
+ if (buffers.glyphProgress)
99
+ gl.deleteBuffer(buffers.glyphProgress);
100
+ if (buffers.glyphBaselineY)
101
+ gl.deleteBuffer(buffers.glyphBaselineY);
84
102
  }
85
103
  };
86
104
  }
@@ -9,6 +9,8 @@ interface WebGLBufferSet {
9
9
  glyphCenter?: WebGLBuffer;
10
10
  glyphIndex?: WebGLBuffer;
11
11
  glyphLineIndex?: WebGLBuffer;
12
+ glyphProgress?: WebGLBuffer;
13
+ glyphBaselineY?: WebGLBuffer;
12
14
  };
13
15
  attributes: {
14
16
  position: {
@@ -41,6 +43,16 @@ interface WebGLBufferSet {
41
43
  type: GLenum;
42
44
  normalized: boolean;
43
45
  };
46
+ glyphProgress?: {
47
+ size: number;
48
+ type: GLenum;
49
+ normalized: boolean;
50
+ };
51
+ glyphBaselineY?: {
52
+ size: number;
53
+ type: GLenum;
54
+ normalized: boolean;
55
+ };
44
56
  };
45
57
  drawCount: number;
46
58
  mode: GLenum;
@@ -61,6 +61,20 @@ function createWebGLBuffers(gl, textGeometry) {
61
61
  gl.bufferData(gl.ARRAY_BUFFER, glyphAttributes.glyphLineIndex, gl.STATIC_DRAW);
62
62
  buffers.glyphLineIndex = glyphLineIndexBuffer;
63
63
  attributes.glyphLineIndex = { size: 1, type: gl.FLOAT, normalized: false };
64
+ const glyphProgressBuffer = gl.createBuffer();
65
+ if (!glyphProgressBuffer)
66
+ throw new Error('Failed to create glyphProgress buffer');
67
+ gl.bindBuffer(gl.ARRAY_BUFFER, glyphProgressBuffer);
68
+ gl.bufferData(gl.ARRAY_BUFFER, glyphAttributes.glyphProgress, gl.STATIC_DRAW);
69
+ buffers.glyphProgress = glyphProgressBuffer;
70
+ attributes.glyphProgress = { size: 1, type: gl.FLOAT, normalized: false };
71
+ const glyphBaselineYBuffer = gl.createBuffer();
72
+ if (!glyphBaselineYBuffer)
73
+ throw new Error('Failed to create glyphBaselineY buffer');
74
+ gl.bindBuffer(gl.ARRAY_BUFFER, glyphBaselineYBuffer);
75
+ gl.bufferData(gl.ARRAY_BUFFER, glyphAttributes.glyphBaselineY, gl.STATIC_DRAW);
76
+ buffers.glyphBaselineY = glyphBaselineYBuffer;
77
+ attributes.glyphBaselineY = { size: 1, type: gl.FLOAT, normalized: false };
64
78
  }
65
79
  return {
66
80
  buffers,
@@ -79,6 +93,10 @@ function createWebGLBuffers(gl, textGeometry) {
79
93
  gl.deleteBuffer(buffers.glyphIndex);
80
94
  if (buffers.glyphLineIndex)
81
95
  gl.deleteBuffer(buffers.glyphLineIndex);
96
+ if (buffers.glyphProgress)
97
+ gl.deleteBuffer(buffers.glyphProgress);
98
+ if (buffers.glyphBaselineY)
99
+ gl.deleteBuffer(buffers.glyphBaselineY);
82
100
  }
83
101
  };
84
102
  }
@@ -2,7 +2,7 @@
2
2
 
3
3
  // WebGPU adapter - lightweight utility to create GPU buffers from core geometry
4
4
  function createWebGPUBuffers(device, textGeometry) {
5
- const { vertices, normals, indices, colors } = textGeometry;
5
+ const { vertices, normals, indices, colors, glyphAttributes } = textGeometry;
6
6
  const indexCount = indices.length;
7
7
  const indexFormat = indices instanceof Uint16Array ? 'uint16' : 'uint32';
8
8
  // Interleave position and normal data for better cache coherency
@@ -83,6 +83,75 @@ function createWebGPUBuffers(device, textGeometry) {
83
83
  buffers.color = colorBuffer;
84
84
  layout.color = colorLayout;
85
85
  }
86
+ // Optional glyph attribute buffers
87
+ let glyphCenterBuffer;
88
+ let glyphIndexBuffer;
89
+ let glyphLineIndexBuffer;
90
+ let glyphProgressBuffer;
91
+ let glyphBaselineYBuffer;
92
+ if (glyphAttributes) {
93
+ let nextShaderLocation = colors ? 3 : 2;
94
+ glyphCenterBuffer = device.createBuffer({
95
+ size: glyphAttributes.glyphCenter.byteLength,
96
+ usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
97
+ mappedAtCreation: true
98
+ });
99
+ new Float32Array(glyphCenterBuffer.getMappedRange()).set(glyphAttributes.glyphCenter);
100
+ glyphCenterBuffer.unmap();
101
+ buffers.glyphCenter = glyphCenterBuffer;
102
+ layout.glyphCenter = {
103
+ arrayStride: 12,
104
+ attributes: [{ shaderLocation: nextShaderLocation++, offset: 0, format: 'float32x3' }]
105
+ };
106
+ glyphIndexBuffer = device.createBuffer({
107
+ size: glyphAttributes.glyphIndex.byteLength,
108
+ usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
109
+ mappedAtCreation: true
110
+ });
111
+ new Float32Array(glyphIndexBuffer.getMappedRange()).set(glyphAttributes.glyphIndex);
112
+ glyphIndexBuffer.unmap();
113
+ buffers.glyphIndex = glyphIndexBuffer;
114
+ layout.glyphIndex = {
115
+ arrayStride: 4,
116
+ attributes: [{ shaderLocation: nextShaderLocation++, offset: 0, format: 'float32' }]
117
+ };
118
+ glyphLineIndexBuffer = device.createBuffer({
119
+ size: glyphAttributes.glyphLineIndex.byteLength,
120
+ usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
121
+ mappedAtCreation: true
122
+ });
123
+ new Float32Array(glyphLineIndexBuffer.getMappedRange()).set(glyphAttributes.glyphLineIndex);
124
+ glyphLineIndexBuffer.unmap();
125
+ buffers.glyphLineIndex = glyphLineIndexBuffer;
126
+ layout.glyphLineIndex = {
127
+ arrayStride: 4,
128
+ attributes: [{ shaderLocation: nextShaderLocation++, offset: 0, format: 'float32' }]
129
+ };
130
+ glyphProgressBuffer = device.createBuffer({
131
+ size: glyphAttributes.glyphProgress.byteLength,
132
+ usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
133
+ mappedAtCreation: true
134
+ });
135
+ new Float32Array(glyphProgressBuffer.getMappedRange()).set(glyphAttributes.glyphProgress);
136
+ glyphProgressBuffer.unmap();
137
+ buffers.glyphProgress = glyphProgressBuffer;
138
+ layout.glyphProgress = {
139
+ arrayStride: 4,
140
+ attributes: [{ shaderLocation: nextShaderLocation++, offset: 0, format: 'float32' }]
141
+ };
142
+ glyphBaselineYBuffer = device.createBuffer({
143
+ size: glyphAttributes.glyphBaselineY.byteLength,
144
+ usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
145
+ mappedAtCreation: true
146
+ });
147
+ new Float32Array(glyphBaselineYBuffer.getMappedRange()).set(glyphAttributes.glyphBaselineY);
148
+ glyphBaselineYBuffer.unmap();
149
+ buffers.glyphBaselineY = glyphBaselineYBuffer;
150
+ layout.glyphBaselineY = {
151
+ arrayStride: 4,
152
+ attributes: [{ shaderLocation: nextShaderLocation++, offset: 0, format: 'float32' }]
153
+ };
154
+ }
86
155
  return {
87
156
  buffers,
88
157
  layout,
@@ -93,6 +162,16 @@ function createWebGPUBuffers(device, textGeometry) {
93
162
  indexBuffer.destroy();
94
163
  if (colorBuffer)
95
164
  colorBuffer.destroy();
165
+ if (glyphCenterBuffer)
166
+ glyphCenterBuffer.destroy();
167
+ if (glyphIndexBuffer)
168
+ glyphIndexBuffer.destroy();
169
+ if (glyphLineIndexBuffer)
170
+ glyphLineIndexBuffer.destroy();
171
+ if (glyphProgressBuffer)
172
+ glyphProgressBuffer.destroy();
173
+ if (glyphBaselineYBuffer)
174
+ glyphBaselineYBuffer.destroy();
96
175
  }
97
176
  };
98
177
  }
@@ -5,10 +5,20 @@ interface WebGPUBufferSet {
5
5
  vertex: GPUBuffer;
6
6
  color?: GPUBuffer;
7
7
  indices: GPUBuffer;
8
+ glyphCenter?: GPUBuffer;
9
+ glyphIndex?: GPUBuffer;
10
+ glyphLineIndex?: GPUBuffer;
11
+ glyphProgress?: GPUBuffer;
12
+ glyphBaselineY?: GPUBuffer;
8
13
  };
9
14
  layout: {
10
15
  vertex: GPUVertexBufferLayout;
11
16
  color?: GPUVertexBufferLayout;
17
+ glyphCenter?: GPUVertexBufferLayout;
18
+ glyphIndex?: GPUVertexBufferLayout;
19
+ glyphLineIndex?: GPUVertexBufferLayout;
20
+ glyphProgress?: GPUVertexBufferLayout;
21
+ glyphBaselineY?: GPUVertexBufferLayout;
12
22
  };
13
23
  indexCount: number;
14
24
  indexFormat: GPUIndexFormat;
@@ -1,6 +1,6 @@
1
1
  // WebGPU adapter - lightweight utility to create GPU buffers from core geometry
2
2
  function createWebGPUBuffers(device, textGeometry) {
3
- const { vertices, normals, indices, colors } = textGeometry;
3
+ const { vertices, normals, indices, colors, glyphAttributes } = textGeometry;
4
4
  const indexCount = indices.length;
5
5
  const indexFormat = indices instanceof Uint16Array ? 'uint16' : 'uint32';
6
6
  // Interleave position and normal data for better cache coherency
@@ -81,6 +81,75 @@ function createWebGPUBuffers(device, textGeometry) {
81
81
  buffers.color = colorBuffer;
82
82
  layout.color = colorLayout;
83
83
  }
84
+ // Optional glyph attribute buffers
85
+ let glyphCenterBuffer;
86
+ let glyphIndexBuffer;
87
+ let glyphLineIndexBuffer;
88
+ let glyphProgressBuffer;
89
+ let glyphBaselineYBuffer;
90
+ if (glyphAttributes) {
91
+ let nextShaderLocation = colors ? 3 : 2;
92
+ glyphCenterBuffer = device.createBuffer({
93
+ size: glyphAttributes.glyphCenter.byteLength,
94
+ usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
95
+ mappedAtCreation: true
96
+ });
97
+ new Float32Array(glyphCenterBuffer.getMappedRange()).set(glyphAttributes.glyphCenter);
98
+ glyphCenterBuffer.unmap();
99
+ buffers.glyphCenter = glyphCenterBuffer;
100
+ layout.glyphCenter = {
101
+ arrayStride: 12,
102
+ attributes: [{ shaderLocation: nextShaderLocation++, offset: 0, format: 'float32x3' }]
103
+ };
104
+ glyphIndexBuffer = device.createBuffer({
105
+ size: glyphAttributes.glyphIndex.byteLength,
106
+ usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
107
+ mappedAtCreation: true
108
+ });
109
+ new Float32Array(glyphIndexBuffer.getMappedRange()).set(glyphAttributes.glyphIndex);
110
+ glyphIndexBuffer.unmap();
111
+ buffers.glyphIndex = glyphIndexBuffer;
112
+ layout.glyphIndex = {
113
+ arrayStride: 4,
114
+ attributes: [{ shaderLocation: nextShaderLocation++, offset: 0, format: 'float32' }]
115
+ };
116
+ glyphLineIndexBuffer = device.createBuffer({
117
+ size: glyphAttributes.glyphLineIndex.byteLength,
118
+ usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
119
+ mappedAtCreation: true
120
+ });
121
+ new Float32Array(glyphLineIndexBuffer.getMappedRange()).set(glyphAttributes.glyphLineIndex);
122
+ glyphLineIndexBuffer.unmap();
123
+ buffers.glyphLineIndex = glyphLineIndexBuffer;
124
+ layout.glyphLineIndex = {
125
+ arrayStride: 4,
126
+ attributes: [{ shaderLocation: nextShaderLocation++, offset: 0, format: 'float32' }]
127
+ };
128
+ glyphProgressBuffer = device.createBuffer({
129
+ size: glyphAttributes.glyphProgress.byteLength,
130
+ usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
131
+ mappedAtCreation: true
132
+ });
133
+ new Float32Array(glyphProgressBuffer.getMappedRange()).set(glyphAttributes.glyphProgress);
134
+ glyphProgressBuffer.unmap();
135
+ buffers.glyphProgress = glyphProgressBuffer;
136
+ layout.glyphProgress = {
137
+ arrayStride: 4,
138
+ attributes: [{ shaderLocation: nextShaderLocation++, offset: 0, format: 'float32' }]
139
+ };
140
+ glyphBaselineYBuffer = device.createBuffer({
141
+ size: glyphAttributes.glyphBaselineY.byteLength,
142
+ usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
143
+ mappedAtCreation: true
144
+ });
145
+ new Float32Array(glyphBaselineYBuffer.getMappedRange()).set(glyphAttributes.glyphBaselineY);
146
+ glyphBaselineYBuffer.unmap();
147
+ buffers.glyphBaselineY = glyphBaselineYBuffer;
148
+ layout.glyphBaselineY = {
149
+ arrayStride: 4,
150
+ attributes: [{ shaderLocation: nextShaderLocation++, offset: 0, format: 'float32' }]
151
+ };
152
+ }
84
153
  return {
85
154
  buffers,
86
155
  layout,
@@ -91,6 +160,16 @@ function createWebGPUBuffers(device, textGeometry) {
91
160
  indexBuffer.destroy();
92
161
  if (colorBuffer)
93
162
  colorBuffer.destroy();
163
+ if (glyphCenterBuffer)
164
+ glyphCenterBuffer.destroy();
165
+ if (glyphIndexBuffer)
166
+ glyphIndexBuffer.destroy();
167
+ if (glyphLineIndexBuffer)
168
+ glyphLineIndexBuffer.destroy();
169
+ if (glyphProgressBuffer)
170
+ glyphProgressBuffer.destroy();
171
+ if (glyphBaselineYBuffer)
172
+ glyphBaselineYBuffer.destroy();
94
173
  }
95
174
  };
96
175
  }