three-text 0.3.2 → 0.3.3
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/dist/index.cjs +74 -21
- package/dist/index.d.ts +1 -0
- package/dist/index.js +74 -21
- package/dist/index.min.cjs +268 -257
- package/dist/index.min.js +194 -183
- package/dist/index.umd.js +74 -21
- package/dist/index.umd.min.js +67 -56
- package/dist/three/react.d.ts +1 -0
- package/dist/types/core/Text.d.ts +1 -0
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* three-text v0.3.
|
|
2
|
+
* three-text v0.3.3
|
|
3
3
|
* Copyright (C) 2025 Countertype LLC
|
|
4
4
|
*
|
|
5
5
|
* This program is free software: you can redistribute it and/or modify
|
|
@@ -5867,8 +5867,10 @@ class Text {
|
|
|
5867
5867
|
colors[i + 1] = defaultColor[1];
|
|
5868
5868
|
colors[i + 2] = defaultColor[2];
|
|
5869
5869
|
}
|
|
5870
|
-
|
|
5871
|
-
|
|
5870
|
+
// Build glyph index once for both byText and byCharRange
|
|
5871
|
+
let glyphsByTextIndex;
|
|
5872
|
+
if ((color.byText && byTextMatches) || color.byCharRange) {
|
|
5873
|
+
glyphsByTextIndex = new Map();
|
|
5872
5874
|
for (const glyph of glyphInfoArray) {
|
|
5873
5875
|
const existing = glyphsByTextIndex.get(glyph.textIndex);
|
|
5874
5876
|
if (existing) {
|
|
@@ -5878,18 +5880,26 @@ class Text {
|
|
|
5878
5880
|
glyphsByTextIndex.set(glyph.textIndex, [glyph]);
|
|
5879
5881
|
}
|
|
5880
5882
|
}
|
|
5883
|
+
}
|
|
5884
|
+
if (color.byText && byTextMatches && glyphsByTextIndex) {
|
|
5881
5885
|
for (const match of byTextMatches) {
|
|
5882
5886
|
const targetColor = color.byText[match.pattern];
|
|
5883
5887
|
if (!targetColor)
|
|
5884
5888
|
continue;
|
|
5885
5889
|
const matchGlyphs = [];
|
|
5886
|
-
const
|
|
5890
|
+
const lineGroups = new Map();
|
|
5887
5891
|
for (let i = match.start; i < match.end; i++) {
|
|
5888
5892
|
const glyphs = glyphsByTextIndex.get(i);
|
|
5889
5893
|
if (glyphs) {
|
|
5890
5894
|
for (const glyph of glyphs) {
|
|
5891
5895
|
matchGlyphs.push(glyph);
|
|
5892
|
-
|
|
5896
|
+
const lineGlyphs = lineGroups.get(glyph.lineIndex);
|
|
5897
|
+
if (lineGlyphs) {
|
|
5898
|
+
lineGlyphs.push(glyph);
|
|
5899
|
+
}
|
|
5900
|
+
else {
|
|
5901
|
+
lineGroups.set(glyph.lineIndex, [glyph]);
|
|
5902
|
+
}
|
|
5893
5903
|
for (let v = 0; v < glyph.vertexCount; v++) {
|
|
5894
5904
|
const vertexIndex = (glyph.vertexStart + v) * 3;
|
|
5895
5905
|
if (vertexIndex >= 0 && vertexIndex < colors.length) {
|
|
@@ -5901,48 +5911,91 @@ class Text {
|
|
|
5901
5911
|
}
|
|
5902
5912
|
}
|
|
5903
5913
|
}
|
|
5914
|
+
// Calculate bounds per line for collision detection
|
|
5915
|
+
const bounds = Array.from(lineGroups.values()).map((lineGlyphs) => this.calculateGlyphBounds(lineGlyphs));
|
|
5904
5916
|
coloredRanges.push({
|
|
5905
5917
|
start: match.start,
|
|
5906
5918
|
end: match.end,
|
|
5907
5919
|
originalText: match.pattern,
|
|
5908
5920
|
color: targetColor,
|
|
5909
|
-
bounds
|
|
5921
|
+
bounds,
|
|
5910
5922
|
glyphs: matchGlyphs,
|
|
5911
|
-
lineIndices: Array.from(
|
|
5923
|
+
lineIndices: Array.from(lineGroups.keys()).sort((a, b) => a - b)
|
|
5912
5924
|
});
|
|
5913
5925
|
}
|
|
5914
5926
|
}
|
|
5915
5927
|
// Apply range coloring
|
|
5916
|
-
if (color.byCharRange) {
|
|
5917
|
-
color.byCharRange
|
|
5928
|
+
if (color.byCharRange && glyphsByTextIndex) {
|
|
5929
|
+
for (const range of color.byCharRange) {
|
|
5918
5930
|
const rangeGlyphs = [];
|
|
5919
|
-
|
|
5920
|
-
|
|
5921
|
-
|
|
5922
|
-
|
|
5923
|
-
|
|
5924
|
-
|
|
5925
|
-
|
|
5926
|
-
|
|
5927
|
-
|
|
5931
|
+
const lineGroups = new Map();
|
|
5932
|
+
for (let i = range.start; i < range.end; i++) {
|
|
5933
|
+
const glyphs = glyphsByTextIndex.get(i);
|
|
5934
|
+
if (glyphs) {
|
|
5935
|
+
for (const glyph of glyphs) {
|
|
5936
|
+
rangeGlyphs.push(glyph);
|
|
5937
|
+
const lineGlyphs = lineGroups.get(glyph.lineIndex);
|
|
5938
|
+
if (lineGlyphs) {
|
|
5939
|
+
lineGlyphs.push(glyph);
|
|
5940
|
+
}
|
|
5941
|
+
else {
|
|
5942
|
+
lineGroups.set(glyph.lineIndex, [glyph]);
|
|
5943
|
+
}
|
|
5944
|
+
for (let v = 0; v < glyph.vertexCount; v++) {
|
|
5945
|
+
const vertexIndex = (glyph.vertexStart + v) * 3;
|
|
5946
|
+
if (vertexIndex >= 0 && vertexIndex < colors.length) {
|
|
5947
|
+
colors[vertexIndex] = range.color[0];
|
|
5948
|
+
colors[vertexIndex + 1] = range.color[1];
|
|
5949
|
+
colors[vertexIndex + 2] = range.color[2];
|
|
5950
|
+
}
|
|
5928
5951
|
}
|
|
5929
5952
|
}
|
|
5930
5953
|
}
|
|
5931
5954
|
}
|
|
5955
|
+
// Calculate bounds per line for collision detection
|
|
5956
|
+
const bounds = Array.from(lineGroups.values()).map((lineGlyphs) => this.calculateGlyphBounds(lineGlyphs));
|
|
5932
5957
|
coloredRanges.push({
|
|
5933
5958
|
start: range.start,
|
|
5934
5959
|
end: range.end,
|
|
5935
5960
|
originalText: originalText.slice(range.start, range.end),
|
|
5936
5961
|
color: range.color,
|
|
5937
|
-
bounds
|
|
5962
|
+
bounds,
|
|
5938
5963
|
glyphs: rangeGlyphs,
|
|
5939
|
-
lineIndices:
|
|
5964
|
+
lineIndices: Array.from(lineGroups.keys()).sort((a, b) => a - b)
|
|
5940
5965
|
});
|
|
5941
|
-
}
|
|
5966
|
+
}
|
|
5942
5967
|
}
|
|
5943
5968
|
}
|
|
5944
5969
|
return { colors, coloredRanges };
|
|
5945
5970
|
}
|
|
5971
|
+
calculateGlyphBounds(glyphs) {
|
|
5972
|
+
if (glyphs.length === 0) {
|
|
5973
|
+
return {
|
|
5974
|
+
min: { x: 0, y: 0, z: 0 },
|
|
5975
|
+
max: { x: 0, y: 0, z: 0 }
|
|
5976
|
+
};
|
|
5977
|
+
}
|
|
5978
|
+
let minX = Infinity, minY = Infinity, minZ = Infinity;
|
|
5979
|
+
let maxX = -Infinity, maxY = -Infinity, maxZ = -Infinity;
|
|
5980
|
+
for (const glyph of glyphs) {
|
|
5981
|
+
if (glyph.bounds.min.x < minX)
|
|
5982
|
+
minX = glyph.bounds.min.x;
|
|
5983
|
+
if (glyph.bounds.min.y < minY)
|
|
5984
|
+
minY = glyph.bounds.min.y;
|
|
5985
|
+
if (glyph.bounds.min.z < minZ)
|
|
5986
|
+
minZ = glyph.bounds.min.z;
|
|
5987
|
+
if (glyph.bounds.max.x > maxX)
|
|
5988
|
+
maxX = glyph.bounds.max.x;
|
|
5989
|
+
if (glyph.bounds.max.y > maxY)
|
|
5990
|
+
maxY = glyph.bounds.max.y;
|
|
5991
|
+
if (glyph.bounds.max.z > maxZ)
|
|
5992
|
+
maxZ = glyph.bounds.max.z;
|
|
5993
|
+
}
|
|
5994
|
+
return {
|
|
5995
|
+
min: { x: minX, y: minY, z: minZ },
|
|
5996
|
+
max: { x: maxX, y: maxY, z: maxZ }
|
|
5997
|
+
};
|
|
5998
|
+
}
|
|
5946
5999
|
finalizeGeometry(vertices, normals, indices, glyphInfoArray, planeBounds, options, originalText, byTextMatches) {
|
|
5947
6000
|
const { layout = {} } = options;
|
|
5948
6001
|
const { width, align = layout.direction === 'rtl' ? 'right' : 'left' } = layout;
|
package/dist/index.d.ts
CHANGED
|
@@ -382,6 +382,7 @@ declare class Text {
|
|
|
382
382
|
private updateFontVariations;
|
|
383
383
|
private prepareLayout;
|
|
384
384
|
private applyColorSystem;
|
|
385
|
+
private calculateGlyphBounds;
|
|
385
386
|
private finalizeGeometry;
|
|
386
387
|
getFontMetrics(): FontMetrics;
|
|
387
388
|
static preloadPatterns(languages: string[], patternsPath?: string): Promise<void>;
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* three-text v0.3.
|
|
2
|
+
* three-text v0.3.3
|
|
3
3
|
* Copyright (C) 2025 Countertype LLC
|
|
4
4
|
*
|
|
5
5
|
* This program is free software: you can redistribute it and/or modify
|
|
@@ -5864,8 +5864,10 @@ class Text {
|
|
|
5864
5864
|
colors[i + 1] = defaultColor[1];
|
|
5865
5865
|
colors[i + 2] = defaultColor[2];
|
|
5866
5866
|
}
|
|
5867
|
-
|
|
5868
|
-
|
|
5867
|
+
// Build glyph index once for both byText and byCharRange
|
|
5868
|
+
let glyphsByTextIndex;
|
|
5869
|
+
if ((color.byText && byTextMatches) || color.byCharRange) {
|
|
5870
|
+
glyphsByTextIndex = new Map();
|
|
5869
5871
|
for (const glyph of glyphInfoArray) {
|
|
5870
5872
|
const existing = glyphsByTextIndex.get(glyph.textIndex);
|
|
5871
5873
|
if (existing) {
|
|
@@ -5875,18 +5877,26 @@ class Text {
|
|
|
5875
5877
|
glyphsByTextIndex.set(glyph.textIndex, [glyph]);
|
|
5876
5878
|
}
|
|
5877
5879
|
}
|
|
5880
|
+
}
|
|
5881
|
+
if (color.byText && byTextMatches && glyphsByTextIndex) {
|
|
5878
5882
|
for (const match of byTextMatches) {
|
|
5879
5883
|
const targetColor = color.byText[match.pattern];
|
|
5880
5884
|
if (!targetColor)
|
|
5881
5885
|
continue;
|
|
5882
5886
|
const matchGlyphs = [];
|
|
5883
|
-
const
|
|
5887
|
+
const lineGroups = new Map();
|
|
5884
5888
|
for (let i = match.start; i < match.end; i++) {
|
|
5885
5889
|
const glyphs = glyphsByTextIndex.get(i);
|
|
5886
5890
|
if (glyphs) {
|
|
5887
5891
|
for (const glyph of glyphs) {
|
|
5888
5892
|
matchGlyphs.push(glyph);
|
|
5889
|
-
|
|
5893
|
+
const lineGlyphs = lineGroups.get(glyph.lineIndex);
|
|
5894
|
+
if (lineGlyphs) {
|
|
5895
|
+
lineGlyphs.push(glyph);
|
|
5896
|
+
}
|
|
5897
|
+
else {
|
|
5898
|
+
lineGroups.set(glyph.lineIndex, [glyph]);
|
|
5899
|
+
}
|
|
5890
5900
|
for (let v = 0; v < glyph.vertexCount; v++) {
|
|
5891
5901
|
const vertexIndex = (glyph.vertexStart + v) * 3;
|
|
5892
5902
|
if (vertexIndex >= 0 && vertexIndex < colors.length) {
|
|
@@ -5898,48 +5908,91 @@ class Text {
|
|
|
5898
5908
|
}
|
|
5899
5909
|
}
|
|
5900
5910
|
}
|
|
5911
|
+
// Calculate bounds per line for collision detection
|
|
5912
|
+
const bounds = Array.from(lineGroups.values()).map((lineGlyphs) => this.calculateGlyphBounds(lineGlyphs));
|
|
5901
5913
|
coloredRanges.push({
|
|
5902
5914
|
start: match.start,
|
|
5903
5915
|
end: match.end,
|
|
5904
5916
|
originalText: match.pattern,
|
|
5905
5917
|
color: targetColor,
|
|
5906
|
-
bounds
|
|
5918
|
+
bounds,
|
|
5907
5919
|
glyphs: matchGlyphs,
|
|
5908
|
-
lineIndices: Array.from(
|
|
5920
|
+
lineIndices: Array.from(lineGroups.keys()).sort((a, b) => a - b)
|
|
5909
5921
|
});
|
|
5910
5922
|
}
|
|
5911
5923
|
}
|
|
5912
5924
|
// Apply range coloring
|
|
5913
|
-
if (color.byCharRange) {
|
|
5914
|
-
color.byCharRange
|
|
5925
|
+
if (color.byCharRange && glyphsByTextIndex) {
|
|
5926
|
+
for (const range of color.byCharRange) {
|
|
5915
5927
|
const rangeGlyphs = [];
|
|
5916
|
-
|
|
5917
|
-
|
|
5918
|
-
|
|
5919
|
-
|
|
5920
|
-
|
|
5921
|
-
|
|
5922
|
-
|
|
5923
|
-
|
|
5924
|
-
|
|
5928
|
+
const lineGroups = new Map();
|
|
5929
|
+
for (let i = range.start; i < range.end; i++) {
|
|
5930
|
+
const glyphs = glyphsByTextIndex.get(i);
|
|
5931
|
+
if (glyphs) {
|
|
5932
|
+
for (const glyph of glyphs) {
|
|
5933
|
+
rangeGlyphs.push(glyph);
|
|
5934
|
+
const lineGlyphs = lineGroups.get(glyph.lineIndex);
|
|
5935
|
+
if (lineGlyphs) {
|
|
5936
|
+
lineGlyphs.push(glyph);
|
|
5937
|
+
}
|
|
5938
|
+
else {
|
|
5939
|
+
lineGroups.set(glyph.lineIndex, [glyph]);
|
|
5940
|
+
}
|
|
5941
|
+
for (let v = 0; v < glyph.vertexCount; v++) {
|
|
5942
|
+
const vertexIndex = (glyph.vertexStart + v) * 3;
|
|
5943
|
+
if (vertexIndex >= 0 && vertexIndex < colors.length) {
|
|
5944
|
+
colors[vertexIndex] = range.color[0];
|
|
5945
|
+
colors[vertexIndex + 1] = range.color[1];
|
|
5946
|
+
colors[vertexIndex + 2] = range.color[2];
|
|
5947
|
+
}
|
|
5925
5948
|
}
|
|
5926
5949
|
}
|
|
5927
5950
|
}
|
|
5928
5951
|
}
|
|
5952
|
+
// Calculate bounds per line for collision detection
|
|
5953
|
+
const bounds = Array.from(lineGroups.values()).map((lineGlyphs) => this.calculateGlyphBounds(lineGlyphs));
|
|
5929
5954
|
coloredRanges.push({
|
|
5930
5955
|
start: range.start,
|
|
5931
5956
|
end: range.end,
|
|
5932
5957
|
originalText: originalText.slice(range.start, range.end),
|
|
5933
5958
|
color: range.color,
|
|
5934
|
-
bounds
|
|
5959
|
+
bounds,
|
|
5935
5960
|
glyphs: rangeGlyphs,
|
|
5936
|
-
lineIndices:
|
|
5961
|
+
lineIndices: Array.from(lineGroups.keys()).sort((a, b) => a - b)
|
|
5937
5962
|
});
|
|
5938
|
-
}
|
|
5963
|
+
}
|
|
5939
5964
|
}
|
|
5940
5965
|
}
|
|
5941
5966
|
return { colors, coloredRanges };
|
|
5942
5967
|
}
|
|
5968
|
+
calculateGlyphBounds(glyphs) {
|
|
5969
|
+
if (glyphs.length === 0) {
|
|
5970
|
+
return {
|
|
5971
|
+
min: { x: 0, y: 0, z: 0 },
|
|
5972
|
+
max: { x: 0, y: 0, z: 0 }
|
|
5973
|
+
};
|
|
5974
|
+
}
|
|
5975
|
+
let minX = Infinity, minY = Infinity, minZ = Infinity;
|
|
5976
|
+
let maxX = -Infinity, maxY = -Infinity, maxZ = -Infinity;
|
|
5977
|
+
for (const glyph of glyphs) {
|
|
5978
|
+
if (glyph.bounds.min.x < minX)
|
|
5979
|
+
minX = glyph.bounds.min.x;
|
|
5980
|
+
if (glyph.bounds.min.y < minY)
|
|
5981
|
+
minY = glyph.bounds.min.y;
|
|
5982
|
+
if (glyph.bounds.min.z < minZ)
|
|
5983
|
+
minZ = glyph.bounds.min.z;
|
|
5984
|
+
if (glyph.bounds.max.x > maxX)
|
|
5985
|
+
maxX = glyph.bounds.max.x;
|
|
5986
|
+
if (glyph.bounds.max.y > maxY)
|
|
5987
|
+
maxY = glyph.bounds.max.y;
|
|
5988
|
+
if (glyph.bounds.max.z > maxZ)
|
|
5989
|
+
maxZ = glyph.bounds.max.z;
|
|
5990
|
+
}
|
|
5991
|
+
return {
|
|
5992
|
+
min: { x: minX, y: minY, z: minZ },
|
|
5993
|
+
max: { x: maxX, y: maxY, z: maxZ }
|
|
5994
|
+
};
|
|
5995
|
+
}
|
|
5943
5996
|
finalizeGeometry(vertices, normals, indices, glyphInfoArray, planeBounds, options, originalText, byTextMatches) {
|
|
5944
5997
|
const { layout = {} } = options;
|
|
5945
5998
|
const { width, align = layout.direction === 'rtl' ? 'right' : 'left' } = layout;
|