@deck.gl-community/infovis-layers 9.3.5 → 9.3.7

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 (126) hide show
  1. package/dist/index.cjs +2998 -114
  2. package/dist/index.cjs.map +4 -4
  3. package/dist/index.d.ts +11 -2
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +9 -1
  6. package/dist/index.js.map +1 -1
  7. package/dist/layers/animation-layer/animation-layer.d.ts +30 -0
  8. package/dist/layers/animation-layer/animation-layer.d.ts.map +1 -0
  9. package/dist/layers/animation-layer/animation-layer.js +53 -0
  10. package/dist/layers/animation-layer/animation-layer.js.map +1 -0
  11. package/dist/layers/animation-layer/animation.d.ts +86 -0
  12. package/dist/layers/animation-layer/animation.d.ts.map +1 -0
  13. package/dist/layers/animation-layer/animation.js +214 -0
  14. package/dist/layers/animation-layer/animation.js.map +1 -0
  15. package/dist/layers/block-layer/block-layer-fragment.glsl.d.ts +3 -0
  16. package/dist/layers/block-layer/block-layer-fragment.glsl.d.ts.map +1 -0
  17. package/dist/layers/block-layer/block-layer-fragment.glsl.js +41 -0
  18. package/dist/layers/block-layer/block-layer-fragment.glsl.js.map +1 -0
  19. package/dist/layers/block-layer/block-layer-uniforms.d.ts +27 -0
  20. package/dist/layers/block-layer/block-layer-uniforms.d.ts.map +1 -0
  21. package/dist/layers/block-layer/block-layer-uniforms.js +26 -0
  22. package/dist/layers/block-layer/block-layer-uniforms.js.map +1 -0
  23. package/dist/layers/block-layer/block-layer-vertex.glsl.d.ts +3 -0
  24. package/dist/layers/block-layer/block-layer-vertex.glsl.d.ts.map +1 -0
  25. package/dist/layers/block-layer/block-layer-vertex.glsl.js +67 -0
  26. package/dist/layers/block-layer/block-layer-vertex.glsl.js.map +1 -0
  27. package/dist/layers/block-layer/block-layer.d.ts +71 -0
  28. package/dist/layers/block-layer/block-layer.d.ts.map +1 -0
  29. package/dist/layers/block-layer/block-layer.js +108 -0
  30. package/dist/layers/block-layer/block-layer.js.map +1 -0
  31. package/dist/layers/fast-text-layer/fast-text-layer.d.ts +148 -0
  32. package/dist/layers/fast-text-layer/fast-text-layer.d.ts.map +1 -0
  33. package/dist/layers/fast-text-layer/fast-text-layer.js +862 -0
  34. package/dist/layers/fast-text-layer/fast-text-layer.js.map +1 -0
  35. package/dist/layers/fast-text-layer/fast-text-layer.test.d.ts +2 -0
  36. package/dist/layers/fast-text-layer/fast-text-layer.test.d.ts.map +1 -0
  37. package/dist/layers/fast-text-layer/fast-text-layer.test.js +300 -0
  38. package/dist/layers/fast-text-layer/fast-text-layer.test.js.map +1 -0
  39. package/dist/layers/fast-text-layer/fast-text-layout.d.ts +179 -0
  40. package/dist/layers/fast-text-layer/fast-text-layout.d.ts.map +1 -0
  41. package/dist/layers/fast-text-layer/fast-text-layout.js +665 -0
  42. package/dist/layers/fast-text-layer/fast-text-layout.js.map +1 -0
  43. package/dist/layers/fast-text-layer/font-atlas.d.ts +98 -0
  44. package/dist/layers/fast-text-layer/font-atlas.d.ts.map +1 -0
  45. package/dist/layers/fast-text-layer/font-atlas.js +337 -0
  46. package/dist/layers/fast-text-layer/font-atlas.js.map +1 -0
  47. package/dist/layers/fast-text-layer/index.d.ts +4 -0
  48. package/dist/layers/fast-text-layer/index.d.ts.map +1 -0
  49. package/dist/layers/fast-text-layer/index.js +4 -0
  50. package/dist/layers/fast-text-layer/index.js.map +1 -0
  51. package/dist/layers/time-delta-layer.d.ts +22 -60
  52. package/dist/layers/time-delta-layer.d.ts.map +1 -1
  53. package/dist/layers/time-delta-layer.js +23 -45
  54. package/dist/layers/time-delta-layer.js.map +1 -1
  55. package/dist/synchronized-views/synchronized-views.js +1 -1
  56. package/dist/synchronized-views/synchronized-views.js.map +1 -1
  57. package/dist/utils/format-utils.d.ts +27 -3
  58. package/dist/utils/format-utils.d.ts.map +1 -1
  59. package/dist/utils/format-utils.js +72 -40
  60. package/dist/utils/format-utils.js.map +1 -1
  61. package/dist/utils/utf8-string-view.d.ts +53 -0
  62. package/dist/utils/utf8-string-view.d.ts.map +1 -0
  63. package/dist/utils/utf8-string-view.js +169 -0
  64. package/dist/utils/utf8-string-view.js.map +1 -0
  65. package/dist/utils/utf8-string-view.test.d.ts +2 -0
  66. package/dist/utils/utf8-string-view.test.d.ts.map +1 -0
  67. package/dist/utils/utf8-string-view.test.js +88 -0
  68. package/dist/utils/utf8-string-view.test.js.map +1 -0
  69. package/dist/views/bounds-utils.d.ts +23 -0
  70. package/dist/views/bounds-utils.d.ts.map +1 -0
  71. package/dist/views/bounds-utils.js +65 -0
  72. package/dist/views/bounds-utils.js.map +1 -0
  73. package/dist/views/layer-bounds-filter.d.ts +48 -0
  74. package/dist/views/layer-bounds-filter.d.ts.map +1 -0
  75. package/dist/views/layer-bounds-filter.js +99 -0
  76. package/dist/views/layer-bounds-filter.js.map +1 -0
  77. package/dist/views/orthographic-utils.d.ts +30 -2
  78. package/dist/views/orthographic-utils.d.ts.map +1 -1
  79. package/dist/views/orthographic-utils.js +33 -14
  80. package/dist/views/orthographic-utils.js.map +1 -1
  81. package/dist/views/view-layout/build-views-from-view-layout.d.ts +37 -0
  82. package/dist/views/view-layout/build-views-from-view-layout.d.ts.map +1 -0
  83. package/dist/views/view-layout/build-views-from-view-layout.js +413 -0
  84. package/dist/views/view-layout/build-views-from-view-layout.js.map +1 -0
  85. package/dist/views/view-layout/index.d.ts +3 -0
  86. package/dist/views/view-layout/index.d.ts.map +1 -0
  87. package/dist/views/view-layout/index.js +3 -0
  88. package/dist/views/view-layout/index.js.map +1 -0
  89. package/dist/views/view-layout/view-layout-item.d.ts +74 -0
  90. package/dist/views/view-layout/view-layout-item.d.ts.map +1 -0
  91. package/dist/views/view-layout/view-layout-item.js +48 -0
  92. package/dist/views/view-layout/view-layout-item.js.map +1 -0
  93. package/dist/views/view-layout/view-layout.test.d.ts +2 -0
  94. package/dist/views/view-layout/view-layout.test.d.ts.map +1 -0
  95. package/dist/views/view-layout/view-layout.test.js +112 -0
  96. package/dist/views/view-layout/view-layout.test.js.map +1 -0
  97. package/dist/views/viewport-bounds-utils.d.ts +55 -0
  98. package/dist/views/viewport-bounds-utils.d.ts.map +1 -0
  99. package/dist/views/viewport-bounds-utils.js +70 -0
  100. package/dist/views/viewport-bounds-utils.js.map +1 -0
  101. package/package.json +11 -4
  102. package/src/index.ts +91 -2
  103. package/src/layers/animation-layer/animation-layer.ts +92 -0
  104. package/src/layers/animation-layer/animation.ts +328 -0
  105. package/src/layers/block-layer/block-layer-fragment.glsl.ts +41 -0
  106. package/src/layers/block-layer/block-layer-uniforms.ts +43 -0
  107. package/src/layers/block-layer/block-layer-vertex.glsl.ts +67 -0
  108. package/src/layers/block-layer/block-layer.ts +199 -0
  109. package/src/layers/fast-text-layer/fast-text-layer.test.ts +359 -0
  110. package/src/layers/fast-text-layer/fast-text-layer.ts +1208 -0
  111. package/src/layers/fast-text-layer/fast-text-layout.ts +1124 -0
  112. package/src/layers/fast-text-layer/font-atlas.ts +574 -0
  113. package/src/layers/fast-text-layer/index.ts +37 -0
  114. package/src/layers/time-delta-layer.ts +47 -62
  115. package/src/synchronized-views/synchronized-views.ts +1 -1
  116. package/src/utils/format-utils.ts +90 -40
  117. package/src/utils/utf8-string-view.test.ts +119 -0
  118. package/src/utils/utf8-string-view.ts +273 -0
  119. package/src/views/bounds-utils.ts +88 -0
  120. package/src/views/layer-bounds-filter.ts +160 -0
  121. package/src/views/orthographic-utils.ts +65 -18
  122. package/src/views/view-layout/build-views-from-view-layout.ts +547 -0
  123. package/src/views/view-layout/index.ts +18 -0
  124. package/src/views/view-layout/view-layout-item.ts +125 -0
  125. package/src/views/view-layout/view-layout.test.ts +129 -0
  126. package/src/views/viewport-bounds-utils.ts +104 -0
@@ -0,0 +1,665 @@
1
+ // Minimal WebGL attribute text layout ported from WIP luma.gl text utilities.
2
+ // SPDX-License-Identifier: MIT
3
+ import { createIterable } from '@deck.gl/core';
4
+ import { getArrowUtf8ColumnSource, getUtf8ColumnSourceRowView } from "../../utils/utf8-string-view.js";
5
+ /**
6
+ * Bytes per generated glyph vertex record.
7
+ *
8
+ * The record stores `sint16x2 glyphOffsets`, `uint16x4 glyphFrames`, `sint16x4 clipRect`,
9
+ * and `unorm8x4 color`.
10
+ */
11
+ export const FAST_TEXT_GLYPH_VERTEX_BYTE_STRIDE = Int16Array.BYTES_PER_ELEMENT * 10 + Uint8Array.BYTES_PER_ELEMENT * 4;
12
+ /** Build a direct-buffer UTF-8 column source without copying Arrow string bytes. */
13
+ export function buildFastTextUtf8ColumnSource(utf8Column) {
14
+ return getArrowUtf8ColumnSource(utf8Column);
15
+ }
16
+ /** Collect all renderable characters reached by a text accessor. */
17
+ export function collectFastTextCharacterSet(props) {
18
+ const characterSet = new Set();
19
+ const { iterable, objectInfo } = createIterable(props.data);
20
+ for (const object of iterable) {
21
+ objectInfo.index++;
22
+ const text = resolveAccessor(props.getText, object, objectInfo) ?? '';
23
+ for (const character of Array.from(text)) {
24
+ if (character !== '\n') {
25
+ characterSet.add(character);
26
+ }
27
+ }
28
+ }
29
+ return characterSet;
30
+ }
31
+ /** Build packed per-glyph attributes for one fast text layer update. */
32
+ export function buildFastTextGlyphData(props) {
33
+ const buildStartTime = performance.now();
34
+ const columnNormalizeStartTime = performance.now();
35
+ const textUtf8Column = resolveFastTextUtf8ColumnSource(props.textUtf8Column);
36
+ const columnNormalizeDurationMs = performance.now() - columnNormalizeStartTime;
37
+ if (props.textUtf8Column && !textUtf8Column) {
38
+ throw new Error('FastTextLayer textUtf8Column must expose Arrow Utf8 buffers');
39
+ }
40
+ if ((textUtf8Column || props.getTextUtf8) && !props.singleLine) {
41
+ throw new Error('FastTextLayer UTF-8 text sources require singleLine text layout');
42
+ }
43
+ const glyphData = props.singleLine
44
+ ? buildSingleLineGlyphData({
45
+ ...props,
46
+ textUtf8Column
47
+ })
48
+ : buildMultiLineGlyphData(props);
49
+ return withFastTextGlyphBuildStats(glyphData, {
50
+ columnNormalizeDurationMs,
51
+ totalDurationMs: performance.now() - buildStartTime
52
+ });
53
+ }
54
+ /** Build packed per-glyph attributes for source rows that are expected to contain no newlines. */
55
+ export function buildSingleLineGlyphData(props) {
56
+ const buildStartTime = performance.now();
57
+ const countStartTime = performance.now();
58
+ const countResult = countSingleLineFastTextGlyphs(props);
59
+ const countDurationMs = performance.now() - countStartTime;
60
+ const allocateStartTime = performance.now();
61
+ const attributes = allocateFastTextGlyphAttributes(countResult.glyphCount);
62
+ const startIndices = new Uint32Array(countResult.rowCount + 1);
63
+ const allocateDurationMs = performance.now() - allocateStartTime;
64
+ const writeState = {
65
+ glyphIndex: 0,
66
+ startIndices,
67
+ attributes,
68
+ glyphRecordViews: createFastTextGlyphRecordViews(attributes.instanceGlyphData)
69
+ };
70
+ const writeTimings = createFastTextGlyphWriteTimings();
71
+ const byteLookup = props.textUtf8Column || props.getTextUtf8 ? buildFastTextByteGlyphLookup(props.mapping) : null;
72
+ const scratchUtf8View = { data: EMPTY_BYTES, start: 0, end: 0 };
73
+ const { iterable, objectInfo } = createIterable(props.data);
74
+ const writeStartTime = performance.now();
75
+ for (const object of iterable) {
76
+ objectInfo.index++;
77
+ startIndices[objectInfo.index] = writeState.glyphIndex;
78
+ if (props.textUtf8Column && byteLookup) {
79
+ writeSingleLineUtf8LabelGlyphs(props, props.textUtf8Column, byteLookup, scratchUtf8View, object, objectInfo, writeState, writeTimings);
80
+ }
81
+ else if (props.getTextUtf8 && byteLookup) {
82
+ writeSingleLineUtf8ViewLabelGlyphs(props, props.getTextUtf8, byteLookup, scratchUtf8View, object, objectInfo, writeState, writeTimings);
83
+ }
84
+ else {
85
+ writeSingleLineStringLabelGlyphs(props, object, objectInfo, writeState, writeTimings);
86
+ }
87
+ }
88
+ startIndices[countResult.rowCount] = writeState.glyphIndex;
89
+ const writeDurationMs = performance.now() - writeStartTime;
90
+ return createFastTextGlyphData(countResult, startIndices, attributes, {
91
+ sourceMode: props.textUtf8Column ? 'utf8-column' : props.getTextUtf8 ? 'utf8-view' : 'string',
92
+ layoutMode: 'single-line',
93
+ columnNormalizeDurationMs: 0,
94
+ countDurationMs,
95
+ allocateDurationMs,
96
+ writeDurationMs,
97
+ ...writeTimings,
98
+ totalDurationMs: performance.now() - buildStartTime
99
+ });
100
+ }
101
+ /** Build packed per-glyph attributes for source rows that may contain newlines. */
102
+ export function buildMultiLineGlyphData(props) {
103
+ const buildStartTime = performance.now();
104
+ const countStartTime = performance.now();
105
+ const countResult = countMultiLineFastTextGlyphs(props);
106
+ const countDurationMs = performance.now() - countStartTime;
107
+ const allocateStartTime = performance.now();
108
+ const attributes = allocateFastTextGlyphAttributes(countResult.glyphCount);
109
+ const startIndices = new Uint32Array(countResult.rowCount + 1);
110
+ const allocateDurationMs = performance.now() - allocateStartTime;
111
+ const writeState = {
112
+ glyphIndex: 0,
113
+ startIndices,
114
+ attributes,
115
+ glyphRecordViews: createFastTextGlyphRecordViews(attributes.instanceGlyphData)
116
+ };
117
+ const writeTimings = createFastTextGlyphWriteTimings();
118
+ const { iterable, objectInfo } = createIterable(props.data);
119
+ const writeStartTime = performance.now();
120
+ for (const object of iterable) {
121
+ objectInfo.index++;
122
+ startIndices[objectInfo.index] = writeState.glyphIndex;
123
+ writeMultiLineLabelGlyphs(props, object, objectInfo, writeState, writeTimings);
124
+ }
125
+ startIndices[countResult.rowCount] = writeState.glyphIndex;
126
+ const writeDurationMs = performance.now() - writeStartTime;
127
+ return createFastTextGlyphData(countResult, startIndices, attributes, {
128
+ sourceMode: 'string',
129
+ layoutMode: 'multi-line',
130
+ columnNormalizeDurationMs: 0,
131
+ countDurationMs,
132
+ allocateDurationMs,
133
+ writeDurationMs,
134
+ ...writeTimings,
135
+ totalDurationMs: performance.now() - buildStartTime
136
+ });
137
+ }
138
+ /** Rewrite dynamic per-row glyph attributes while preserving generated text layout attributes. */
139
+ export function updateFastTextDynamicGlyphAttributes(props) {
140
+ const updateStartTime = performance.now();
141
+ const glyphRecordViews = createFastTextGlyphRecordViews(props.glyphData.attributes.instanceGlyphData);
142
+ const { iterable, objectInfo } = createIterable(props.data);
143
+ let rowCount = 0;
144
+ let glyphCount = 0;
145
+ let positionAccessorDurationMs = 0;
146
+ let colorAccessorDurationMs = 0;
147
+ let clipRectAccessorDurationMs = 0;
148
+ let writeDurationMs = 0;
149
+ for (const object of iterable) {
150
+ objectInfo.index++;
151
+ rowCount++;
152
+ const rowGlyphStart = props.glyphData.startIndices[objectInfo.index] ?? 0;
153
+ const rowGlyphEnd = props.glyphData.startIndices[objectInfo.index + 1] ?? rowGlyphStart;
154
+ if (rowGlyphEnd <= rowGlyphStart) {
155
+ continue;
156
+ }
157
+ let position = DEFAULT_POSITION;
158
+ let color = DEFAULT_COLOR;
159
+ let clipRect = DEFAULT_CLIP_RECT;
160
+ if (props.updatePositions) {
161
+ const accessorStartTime = performance.now();
162
+ position = resolveAccessor(props.getPosition, object, objectInfo) ?? DEFAULT_POSITION;
163
+ positionAccessorDurationMs += performance.now() - accessorStartTime;
164
+ }
165
+ if (props.updateColors) {
166
+ const accessorStartTime = performance.now();
167
+ color = resolveAccessor(props.getColor, object, objectInfo) ?? DEFAULT_COLOR;
168
+ colorAccessorDurationMs += performance.now() - accessorStartTime;
169
+ }
170
+ if (props.updateClipRects) {
171
+ const accessorStartTime = performance.now();
172
+ clipRect = resolveAccessor(props.getClipRect, object, objectInfo) ?? DEFAULT_CLIP_RECT;
173
+ clipRectAccessorDurationMs += performance.now() - accessorStartTime;
174
+ }
175
+ const writeStartTime = performance.now();
176
+ for (let glyphIndex = rowGlyphStart; glyphIndex < rowGlyphEnd; glyphIndex += 1) {
177
+ if (props.updatePositions) {
178
+ writeFastTextGlyphPosition(props.glyphData.attributes, glyphIndex, position);
179
+ }
180
+ if (props.updateClipRects) {
181
+ writeFastTextGlyphClipRect(glyphRecordViews, glyphIndex, clipRect);
182
+ }
183
+ if (props.updateColors) {
184
+ writeFastTextGlyphColor(glyphRecordViews, glyphIndex, color);
185
+ }
186
+ }
187
+ writeDurationMs += performance.now() - writeStartTime;
188
+ glyphCount += rowGlyphEnd - rowGlyphStart;
189
+ }
190
+ return {
191
+ rowCount,
192
+ glyphCount,
193
+ attributeByteLength: props.glyphData.byteLength,
194
+ positionAccessorDurationMs,
195
+ colorAccessorDurationMs,
196
+ clipRectAccessorDurationMs,
197
+ writeDurationMs,
198
+ totalDurationMs: performance.now() - updateStartTime
199
+ };
200
+ }
201
+ const MISSING_CHARACTER_ADVANCE = 32;
202
+ const FAST_TEXT_BYTE_LOOKUP_SIZE = 256;
203
+ const EMPTY_BYTES = new Uint8Array();
204
+ const DEFAULT_COLOR = [0, 0, 0, 255];
205
+ const DEFAULT_POSITION = [0, 0, 0];
206
+ const DEFAULT_CLIP_RECT = [0, 0, -1, -1];
207
+ const ANCHOR_OFFSET_MULTIPLIER = {
208
+ start: 0,
209
+ middle: -0.5,
210
+ end: -1
211
+ };
212
+ const BASELINE_OFFSET_MULTIPLIER = {
213
+ top: 0,
214
+ center: -0.5,
215
+ bottom: -1
216
+ };
217
+ /** Count single-line rendered glyphs and rows before allocating fixed-size typed arrays. */
218
+ function countSingleLineFastTextGlyphs(props) {
219
+ if (props.textUtf8Column) {
220
+ return countSingleLineUtf8FastTextGlyphs(props, props.textUtf8Column);
221
+ }
222
+ if (props.getTextUtf8) {
223
+ return countSingleLineUtf8ViewFastTextGlyphs(props, props.getTextUtf8);
224
+ }
225
+ return countSingleLineStringFastTextGlyphs(props);
226
+ }
227
+ /** Count single-line UTF-8 bytes as glyph records without decoding strings. */
228
+ function countSingleLineUtf8FastTextGlyphs(props, textUtf8Column) {
229
+ let glyphCount = 0;
230
+ let rowCount = 0;
231
+ const characterSet = new Set();
232
+ const textView = { data: EMPTY_BYTES, start: 0, end: 0 };
233
+ const { iterable, objectInfo } = createIterable(props.data);
234
+ for (const object of iterable) {
235
+ objectInfo.index++;
236
+ rowCount++;
237
+ const rowIndex = resolveTextUtf8Row(props.getTextUtf8Row, object, objectInfo);
238
+ if (rowIndex == null || !getUtf8ColumnSourceRowView(textUtf8Column, rowIndex, textView)) {
239
+ continue;
240
+ }
241
+ glyphCount += textView.end - textView.start;
242
+ }
243
+ return { glyphCount, rowCount, characterSet };
244
+ }
245
+ /** Count single-line UTF-8 byte-view labels without decoding strings. */
246
+ function countSingleLineUtf8ViewFastTextGlyphs(props, getTextUtf8) {
247
+ let glyphCount = 0;
248
+ let rowCount = 0;
249
+ const characterSet = new Set();
250
+ const textView = { data: EMPTY_BYTES, start: 0, end: 0 };
251
+ const { iterable, objectInfo } = createIterable(props.data);
252
+ for (const object of iterable) {
253
+ objectInfo.index++;
254
+ rowCount++;
255
+ if (getTextUtf8(object, textView, objectInfo)) {
256
+ glyphCount += Math.max(0, textView.end - textView.start);
257
+ }
258
+ }
259
+ return { glyphCount, rowCount, characterSet };
260
+ }
261
+ /** Count single-line JavaScript string code units without scanning for newline separators. */
262
+ function countSingleLineStringFastTextGlyphs(props) {
263
+ let glyphCount = 0;
264
+ let rowCount = 0;
265
+ const characterSet = new Set();
266
+ const { iterable, objectInfo } = createIterable(props.data);
267
+ for (const object of iterable) {
268
+ objectInfo.index++;
269
+ rowCount++;
270
+ const text = resolveAccessor(props.getText, object, objectInfo) ?? '';
271
+ glyphCount += text.length;
272
+ for (let index = 0; index < text.length; index += 1) {
273
+ characterSet.add(text[index]);
274
+ }
275
+ }
276
+ return { glyphCount, rowCount, characterSet };
277
+ }
278
+ /** Count multiline rendered glyphs and rows before allocating fixed-size typed arrays. */
279
+ function countMultiLineFastTextGlyphs(props) {
280
+ let glyphCount = 0;
281
+ let rowCount = 0;
282
+ const characterSet = new Set();
283
+ const { iterable, objectInfo } = createIterable(props.data);
284
+ for (const object of iterable) {
285
+ objectInfo.index++;
286
+ rowCount++;
287
+ const text = resolveAccessor(props.getText, object, objectInfo) ?? '';
288
+ for (const character of Array.from(text)) {
289
+ if (character === '\n') {
290
+ continue;
291
+ }
292
+ characterSet.add(character);
293
+ glyphCount++;
294
+ }
295
+ }
296
+ return { glyphCount, rowCount, characterSet };
297
+ }
298
+ /** Allocate all per-glyph typed arrays for one glyph count. */
299
+ function allocateFastTextGlyphAttributes(glyphCount) {
300
+ return {
301
+ instanceGlyphData: new Uint8Array(glyphCount * FAST_TEXT_GLYPH_VERTEX_BYTE_STRIDE),
302
+ instancePositions: new Float32Array(glyphCount * 2)
303
+ };
304
+ }
305
+ /** Build typed record views over the packed generated glyph data. */
306
+ function createFastTextGlyphRecordViews(instanceGlyphData) {
307
+ return {
308
+ int16Values: new Int16Array(instanceGlyphData.buffer),
309
+ uint16Values: new Uint16Array(instanceGlyphData.buffer),
310
+ uint8Values: instanceGlyphData
311
+ };
312
+ }
313
+ /** Create the public glyph data wrapper around generated typed arrays. */
314
+ function createFastTextGlyphData(countResult, startIndices, attributes, stats) {
315
+ const byteLength = startIndices.byteLength +
316
+ attributes.instanceGlyphData.byteLength +
317
+ attributes.instancePositions.byteLength;
318
+ return {
319
+ length: countResult.glyphCount,
320
+ startIndices,
321
+ attributes,
322
+ characterSet: countResult.characterSet,
323
+ byteLength,
324
+ buildStats: {
325
+ ...stats,
326
+ rowCount: countResult.rowCount,
327
+ glyphCount: countResult.glyphCount,
328
+ attributeByteLength: byteLength
329
+ }
330
+ };
331
+ }
332
+ function createFastTextGlyphWriteTimings() {
333
+ return {
334
+ textResolveDurationMs: 0,
335
+ styleAccessorDurationMs: 0,
336
+ layoutDurationMs: 0,
337
+ glyphWriteDurationMs: 0
338
+ };
339
+ }
340
+ /** Return one glyph-data object with caller-supplied timing overrides. */
341
+ function withFastTextGlyphBuildStats(glyphData, stats) {
342
+ return {
343
+ ...glyphData,
344
+ buildStats: {
345
+ ...glyphData.buildStats,
346
+ ...stats
347
+ }
348
+ };
349
+ }
350
+ /** Write all generated glyph records for one single-line JavaScript string label row. */
351
+ function writeSingleLineStringLabelGlyphs(props, object, objectInfo, writeState, timings) {
352
+ const textResolveStartTime = performance.now();
353
+ const text = resolveAccessor(props.getText, object, objectInfo) ?? '';
354
+ timings.textResolveDurationMs += performance.now() - textResolveStartTime;
355
+ if (!text) {
356
+ return;
357
+ }
358
+ const styleAccessorStartTime = performance.now();
359
+ const position = resolveAccessor(props.getPosition, object, objectInfo) ?? DEFAULT_POSITION;
360
+ const color = resolveAccessor(props.getColor, object, objectInfo) ?? DEFAULT_COLOR;
361
+ const clipRect = resolveAccessor(props.getClipRect, object, objectInfo) ?? DEFAULT_CLIP_RECT;
362
+ timings.styleAccessorDurationMs += performance.now() - styleAccessorStartTime;
363
+ const layoutStartTime = performance.now();
364
+ const width = getSingleLineStringWidth(text, props.mapping);
365
+ const lineOffsetX = ANCHOR_OFFSET_MULTIPLIER[props.textAnchor] * width;
366
+ const rowBaselineY = getSingleLineBaselineY(props);
367
+ timings.layoutDurationMs += performance.now() - layoutStartTime;
368
+ let cursorX = 0;
369
+ const glyphWriteStartTime = performance.now();
370
+ for (let index = 0; index < text.length; index += 1) {
371
+ const character = text[index];
372
+ const frame = props.mapping[character];
373
+ writeFastTextGlyphRecord({
374
+ frame,
375
+ lineOffsetX,
376
+ cursorX,
377
+ rowBaselineY,
378
+ position,
379
+ color,
380
+ clipRect,
381
+ writeState
382
+ });
383
+ cursorX += frame?.advance ?? MISSING_CHARACTER_ADVANCE;
384
+ }
385
+ timings.glyphWriteDurationMs += performance.now() - glyphWriteStartTime;
386
+ }
387
+ /** Write all generated glyph records for one single-line UTF-8 byte label row. */
388
+ function writeSingleLineUtf8LabelGlyphs(props, textUtf8Column, byteLookup, textView, object, objectInfo, writeState, timings) {
389
+ const textResolveStartTime = performance.now();
390
+ const rowIndex = resolveTextUtf8Row(props.getTextUtf8Row, object, objectInfo);
391
+ const hasTextView = rowIndex != null && getUtf8ColumnSourceRowView(textUtf8Column, rowIndex, textView);
392
+ timings.textResolveDurationMs += performance.now() - textResolveStartTime;
393
+ if (!hasTextView) {
394
+ return;
395
+ }
396
+ if (textView.end <= textView.start) {
397
+ return;
398
+ }
399
+ const styleAccessorStartTime = performance.now();
400
+ const position = resolveAccessor(props.getPosition, object, objectInfo) ?? DEFAULT_POSITION;
401
+ const color = resolveAccessor(props.getColor, object, objectInfo) ?? DEFAULT_COLOR;
402
+ const clipRect = resolveAccessor(props.getClipRect, object, objectInfo) ?? DEFAULT_CLIP_RECT;
403
+ timings.styleAccessorDurationMs += performance.now() - styleAccessorStartTime;
404
+ const layoutStartTime = performance.now();
405
+ const width = getSingleLineUtf8Width(textView, byteLookup);
406
+ const lineOffsetX = ANCHOR_OFFSET_MULTIPLIER[props.textAnchor] * width;
407
+ const rowBaselineY = getSingleLineBaselineY(props);
408
+ timings.layoutDurationMs += performance.now() - layoutStartTime;
409
+ let cursorX = 0;
410
+ const glyphWriteStartTime = performance.now();
411
+ for (let byteIndex = textView.start; byteIndex < textView.end; byteIndex += 1) {
412
+ const frame = byteLookup[textView.data[byteIndex] ?? -1];
413
+ writeFastTextGlyphRecord({
414
+ frame,
415
+ lineOffsetX,
416
+ cursorX,
417
+ rowBaselineY,
418
+ position,
419
+ color,
420
+ clipRect,
421
+ writeState
422
+ });
423
+ cursorX += frame?.advance ?? MISSING_CHARACTER_ADVANCE;
424
+ }
425
+ timings.glyphWriteDurationMs += performance.now() - glyphWriteStartTime;
426
+ }
427
+ /** Write all generated glyph records for one accessor-provided UTF-8 byte label row. */
428
+ function writeSingleLineUtf8ViewLabelGlyphs(props, getTextUtf8, byteLookup, textView, object, objectInfo, writeState, timings) {
429
+ const textResolveStartTime = performance.now();
430
+ const hasTextView = getTextUtf8(object, textView, objectInfo);
431
+ timings.textResolveDurationMs += performance.now() - textResolveStartTime;
432
+ if (!hasTextView || textView.end <= textView.start) {
433
+ return;
434
+ }
435
+ const styleAccessorStartTime = performance.now();
436
+ const position = resolveAccessor(props.getPosition, object, objectInfo) ?? DEFAULT_POSITION;
437
+ const color = resolveAccessor(props.getColor, object, objectInfo) ?? DEFAULT_COLOR;
438
+ const clipRect = resolveAccessor(props.getClipRect, object, objectInfo) ?? DEFAULT_CLIP_RECT;
439
+ timings.styleAccessorDurationMs += performance.now() - styleAccessorStartTime;
440
+ const layoutStartTime = performance.now();
441
+ const width = getSingleLineUtf8Width(textView, byteLookup);
442
+ const lineOffsetX = ANCHOR_OFFSET_MULTIPLIER[props.textAnchor] * width;
443
+ const rowBaselineY = getSingleLineBaselineY(props);
444
+ timings.layoutDurationMs += performance.now() - layoutStartTime;
445
+ let cursorX = 0;
446
+ const glyphWriteStartTime = performance.now();
447
+ for (let byteIndex = textView.start; byteIndex < textView.end; byteIndex += 1) {
448
+ const frame = byteLookup[textView.data[byteIndex] ?? -1];
449
+ writeFastTextGlyphRecord({
450
+ frame,
451
+ lineOffsetX,
452
+ cursorX,
453
+ rowBaselineY,
454
+ position,
455
+ color,
456
+ clipRect,
457
+ writeState
458
+ });
459
+ cursorX += frame?.advance ?? MISSING_CHARACTER_ADVANCE;
460
+ }
461
+ timings.glyphWriteDurationMs += performance.now() - glyphWriteStartTime;
462
+ }
463
+ /** Write all generated glyph records for one multiline source label row. */
464
+ function writeMultiLineLabelGlyphs(props, object, objectInfo, writeState, timings) {
465
+ const textResolveStartTime = performance.now();
466
+ const text = resolveAccessor(props.getText, object, objectInfo) ?? '';
467
+ timings.textResolveDurationMs += performance.now() - textResolveStartTime;
468
+ if (!text) {
469
+ return;
470
+ }
471
+ const styleAccessorStartTime = performance.now();
472
+ const position = resolveAccessor(props.getPosition, object, objectInfo) ?? DEFAULT_POSITION;
473
+ const color = resolveAccessor(props.getColor, object, objectInfo) ?? DEFAULT_COLOR;
474
+ const clipRect = resolveAccessor(props.getClipRect, object, objectInfo) ?? DEFAULT_CLIP_RECT;
475
+ timings.styleAccessorDurationMs += performance.now() - styleAccessorStartTime;
476
+ const layoutStartTime = performance.now();
477
+ const lines = buildFastTextLineLayouts(text, props.mapping);
478
+ const lineHeightPixels = props.lineHeight * props.fontSize;
479
+ const labelHeight = lines.length * lineHeightPixels;
480
+ const blockOffsetY = BASELINE_OFFSET_MULTIPLIER[props.alignmentBaseline] * labelHeight;
481
+ timings.layoutDurationMs += performance.now() - layoutStartTime;
482
+ const glyphWriteStartTime = performance.now();
483
+ for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
484
+ const line = lines[lineIndex];
485
+ const lineOffsetX = ANCHOR_OFFSET_MULTIPLIER[props.textAnchor] * line.width;
486
+ const rowBaselineY = blockOffsetY + props.baselineOffset + lineHeightPixels / 2 + lineIndex * lineHeightPixels;
487
+ let cursorX = 0;
488
+ for (const character of line.characters) {
489
+ const frame = props.mapping[character];
490
+ writeFastTextGlyphRecord({
491
+ frame,
492
+ lineOffsetX,
493
+ cursorX,
494
+ rowBaselineY,
495
+ position,
496
+ color,
497
+ clipRect,
498
+ writeState
499
+ });
500
+ cursorX += frame?.advance ?? MISSING_CHARACTER_ADVANCE;
501
+ }
502
+ }
503
+ timings.glyphWriteDurationMs += performance.now() - glyphWriteStartTime;
504
+ }
505
+ /** Convert one source text into line widths and per-line characters. */
506
+ function buildFastTextLineLayouts(text, mapping) {
507
+ const lines = [];
508
+ let currentCharacters = [];
509
+ let currentWidth = 0;
510
+ let currentGlyphCount = 0;
511
+ for (const character of Array.from(text)) {
512
+ if (character === '\n') {
513
+ lines.push({
514
+ characters: currentCharacters,
515
+ width: currentWidth,
516
+ glyphCount: currentGlyphCount
517
+ });
518
+ currentCharacters = [];
519
+ currentWidth = 0;
520
+ currentGlyphCount = 0;
521
+ continue;
522
+ }
523
+ const frame = mapping[character];
524
+ currentCharacters.push(character);
525
+ currentWidth += frame?.advance ?? MISSING_CHARACTER_ADVANCE;
526
+ if (frame) {
527
+ currentGlyphCount++;
528
+ }
529
+ }
530
+ lines.push({
531
+ characters: currentCharacters,
532
+ width: currentWidth,
533
+ glyphCount: currentGlyphCount
534
+ });
535
+ return lines;
536
+ }
537
+ /** Resolve a caller-supplied Arrow vector or pre-normalized source into a direct UTF-8 source. */
538
+ function resolveFastTextUtf8ColumnSource(textUtf8Column) {
539
+ if (!textUtf8Column) {
540
+ return null;
541
+ }
542
+ return isFastTextUtf8ColumnSource(textUtf8Column)
543
+ ? textUtf8Column
544
+ : buildFastTextUtf8ColumnSource(textUtf8Column);
545
+ }
546
+ /** Return whether an object already has the normalized UTF-8 column-source shape. */
547
+ function isFastTextUtf8ColumnSource(textUtf8Column) {
548
+ return 'rowCount' in textUtf8Column && 'chunks' in textUtf8Column;
549
+ }
550
+ /** Build an ASCII byte-to-glyph lookup from the string-keyed atlas mapping. */
551
+ function buildFastTextByteGlyphLookup(mapping) {
552
+ const lookup = new Array(FAST_TEXT_BYTE_LOOKUP_SIZE);
553
+ for (let byte = 0; byte < 128; byte += 1) {
554
+ lookup[byte] = mapping[String.fromCharCode(byte)];
555
+ }
556
+ return lookup;
557
+ }
558
+ /** Resolve the source-row index inside a UTF-8 column. */
559
+ function resolveTextUtf8Row(getTextUtf8Row, object, objectInfo) {
560
+ return getTextUtf8Row === undefined
561
+ ? objectInfo.index
562
+ : resolveAccessor(getTextUtf8Row, object, objectInfo);
563
+ }
564
+ /** Compute one single-line string label width without looking for newline separators. */
565
+ function getSingleLineStringWidth(text, mapping) {
566
+ let width = 0;
567
+ for (let index = 0; index < text.length; index += 1) {
568
+ width += mapping[text[index]]?.advance ?? MISSING_CHARACTER_ADVANCE;
569
+ }
570
+ return width;
571
+ }
572
+ /** Compute one single-line UTF-8 byte label width without decoding bytes to characters. */
573
+ function getSingleLineUtf8Width(textView, byteLookup) {
574
+ let width = 0;
575
+ for (let byteIndex = textView.start; byteIndex < textView.end; byteIndex += 1) {
576
+ width += byteLookup[textView.data[byteIndex] ?? -1]?.advance ?? MISSING_CHARACTER_ADVANCE;
577
+ }
578
+ return width;
579
+ }
580
+ /** Compute the baseline Y position for a one-line label. */
581
+ function getSingleLineBaselineY(props) {
582
+ const lineHeightPixels = props.lineHeight * props.fontSize;
583
+ const blockOffsetY = BASELINE_OFFSET_MULTIPLIER[props.alignmentBaseline] * lineHeightPixels;
584
+ return blockOffsetY + props.baselineOffset + lineHeightPixels / 2;
585
+ }
586
+ /** Write one packed glyph record into the generated attribute arrays. */
587
+ function writeFastTextGlyphRecord(params) {
588
+ const frame = params.frame;
589
+ const glyphIndex = params.writeState.glyphIndex;
590
+ const recordInt16Index = (glyphIndex * FAST_TEXT_GLYPH_VERTEX_BYTE_STRIDE) / Int16Array.BYTES_PER_ELEMENT;
591
+ const glyphX = params.lineOffsetX + params.cursorX + (frame?.anchorX ?? 0) - (frame?.width ?? 0) / 2;
592
+ const glyphY = params.rowBaselineY - (frame?.anchorY ?? 0);
593
+ const writeState = params.writeState;
594
+ writeFastTextGlyphPosition(writeState.attributes, glyphIndex, params.position);
595
+ writeState.glyphRecordViews.int16Values[recordInt16Index] = toInt16(glyphX);
596
+ writeState.glyphRecordViews.int16Values[recordInt16Index + 1] = toInt16(glyphY);
597
+ writeState.glyphRecordViews.uint16Values[recordInt16Index + 2] = toUint16(frame?.x ?? 0);
598
+ writeState.glyphRecordViews.uint16Values[recordInt16Index + 3] = toUint16(frame?.y ?? 0);
599
+ writeState.glyphRecordViews.uint16Values[recordInt16Index + 4] = toUint16(frame?.width ?? 0);
600
+ writeState.glyphRecordViews.uint16Values[recordInt16Index + 5] = toUint16(frame?.height ?? 0);
601
+ writeFastTextGlyphClipRect(writeState.glyphRecordViews, glyphIndex, params.clipRect);
602
+ writeFastTextGlyphColor(writeState.glyphRecordViews, glyphIndex, params.color);
603
+ writeState.glyphIndex++;
604
+ }
605
+ /** Write one source anchor position into the repeated per-glyph position array. */
606
+ function writeFastTextGlyphPosition(attributes, glyphIndex, position) {
607
+ const positionOffset = glyphIndex * 2;
608
+ attributes.instancePositions[positionOffset] = position[0] ?? 0;
609
+ attributes.instancePositions[positionOffset + 1] = position[1] ?? 0;
610
+ }
611
+ /** Write one source clip rectangle into the packed generated glyph record. */
612
+ function writeFastTextGlyphClipRect(glyphRecordViews, glyphIndex, clipRect) {
613
+ const recordInt16Index = (glyphIndex * FAST_TEXT_GLYPH_VERTEX_BYTE_STRIDE) / Int16Array.BYTES_PER_ELEMENT;
614
+ glyphRecordViews.int16Values[recordInt16Index + 6] = toInt16(clipRect[0] ?? 0);
615
+ glyphRecordViews.int16Values[recordInt16Index + 7] = toInt16(clipRect[1] ?? 0);
616
+ glyphRecordViews.int16Values[recordInt16Index + 8] = toInt16ClipDimension(clipRect[2] ?? -1);
617
+ glyphRecordViews.int16Values[recordInt16Index + 9] = toInt16ClipDimension(clipRect[3] ?? -1);
618
+ }
619
+ /** Write one source color into the packed generated glyph record. */
620
+ function writeFastTextGlyphColor(glyphRecordViews, glyphIndex, color) {
621
+ const recordByteIndex = glyphIndex * FAST_TEXT_GLYPH_VERTEX_BYTE_STRIDE;
622
+ writeColor(glyphRecordViews.uint8Values, recordByteIndex + Int16Array.BYTES_PER_ELEMENT * 10, color);
623
+ }
624
+ /** Resolve deck accessors that may be constants or functions. */
625
+ function resolveAccessor(accessor, object, objectInfo) {
626
+ return typeof accessor === 'function'
627
+ ? accessor(object, objectInfo)
628
+ : accessor;
629
+ }
630
+ /** Write one deck color into a byte array with a default opaque alpha. */
631
+ function writeColor(target, offset, color) {
632
+ target[offset] = clampByte(color[0] ?? DEFAULT_COLOR[0]);
633
+ target[offset + 1] = clampByte(color[1] ?? DEFAULT_COLOR[1]);
634
+ target[offset + 2] = clampByte(color[2] ?? DEFAULT_COLOR[2]);
635
+ target[offset + 3] = clampByte(color[3] ?? DEFAULT_COLOR[3]);
636
+ }
637
+ /** Clamp one color channel into the unsigned byte range. */
638
+ function clampByte(value) {
639
+ return Math.max(0, Math.min(255, Math.round(value)));
640
+ }
641
+ /** Round and validate one value for the signed 16-bit generated attribute record. */
642
+ function toInt16(value) {
643
+ const integerValue = Math.round(value);
644
+ if (integerValue < -32768 || integerValue > 32767) {
645
+ throw new Error(`FastTextLayer value ${value} is outside the signed 16-bit range`);
646
+ }
647
+ return integerValue;
648
+ }
649
+ /** Round one clip dimension while preserving positive non-zero values. */
650
+ function toInt16ClipDimension(value) {
651
+ const integerValue = value > 0 ? Math.max(1, Math.round(value)) : Math.round(value);
652
+ if (integerValue < -32768 || integerValue > 32767) {
653
+ throw new Error(`FastTextLayer value ${value} is outside the signed 16-bit range`);
654
+ }
655
+ return integerValue;
656
+ }
657
+ /** Round and validate one atlas frame value for the unsigned 16-bit generated attribute record. */
658
+ function toUint16(value) {
659
+ const integerValue = Math.round(value);
660
+ if (integerValue < 0 || integerValue > 65535) {
661
+ throw new Error(`FastTextLayer glyph frame value ${value} is outside the unsigned 16-bit range`);
662
+ }
663
+ return integerValue;
664
+ }
665
+ //# sourceMappingURL=fast-text-layout.js.map