three-text 0.1.1 → 0.2.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.
- package/README.md +166 -156
- package/dist/index.cjs +370 -81
- package/dist/index.d.ts +46 -17
- package/dist/index.js +369 -80
- package/dist/index.min.cjs +2 -2
- package/dist/index.min.js +2 -2
- package/dist/index.umd.js +374 -83
- package/dist/index.umd.min.js +2 -2
- package/dist/p5/index.cjs +33 -0
- package/dist/p5/index.d.ts +19 -0
- package/dist/p5/index.js +31 -0
- package/dist/three/index.cjs +50 -0
- package/dist/three/index.d.ts +29 -0
- package/dist/three/index.js +48 -0
- package/dist/{react/index.cjs → three/react.cjs} +14 -4
- package/dist/three/react.d.ts +346 -0
- package/dist/{react/index.js → three/react.js} +14 -4
- package/dist/types/core/Text.d.ts +1 -1
- package/dist/types/core/cache/GlyphCache.d.ts +4 -4
- package/dist/types/core/cache/GlyphContourCollector.d.ts +2 -2
- package/dist/types/core/cache/GlyphGeometryBuilder.d.ts +3 -2
- package/dist/types/core/geometry/BoundaryClusterer.d.ts +2 -2
- package/dist/types/core/geometry/Polygonizer.d.ts +3 -3
- package/dist/types/core/layout/TextLayout.d.ts +1 -2
- package/dist/types/core/shaping/TextShaper.d.ts +1 -2
- package/dist/types/core/types.d.ts +13 -16
- package/dist/types/core/vectors.d.ts +75 -0
- package/dist/types/p5/index.d.ts +17 -0
- package/dist/types/{react → three}/ThreeText.d.ts +2 -2
- package/dist/types/three/index.d.ts +21 -0
- package/dist/types/three/react.d.ts +10 -0
- package/dist/types/webgl/index.d.ts +48 -0
- package/dist/types/webgpu/index.d.ts +16 -0
- package/dist/webgl/index.cjs +88 -0
- package/dist/webgl/index.d.ts +51 -0
- package/dist/webgl/index.js +86 -0
- package/dist/webgpu/index.cjs +99 -0
- package/dist/webgpu/index.d.ts +19 -0
- package/dist/webgpu/index.js +97 -0
- package/package.json +22 -6
- package/dist/react/index.d.ts +0 -18
- package/dist/types/react/index.d.ts +0 -2
package/dist/index.umd.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* three-text v0.
|
|
2
|
+
* three-text v0.2.0
|
|
3
3
|
* Copyright (C) 2025 Countertype LLC
|
|
4
4
|
*
|
|
5
5
|
* This program is free software: you can redistribute it and/or modify
|
|
@@ -12,10 +12,10 @@
|
|
|
12
12
|
* This software includes third-party code - see LICENSE_THIRD_PARTY for details.
|
|
13
13
|
*/
|
|
14
14
|
(function (global, factory) {
|
|
15
|
-
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports
|
|
16
|
-
typeof define === 'function' && define.amd ? define(['exports'
|
|
17
|
-
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.ThreeText = {}
|
|
18
|
-
})(this, (function (exports
|
|
15
|
+
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
|
16
|
+
typeof define === 'function' && define.amd ? define(['exports'], factory) :
|
|
17
|
+
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.ThreeText = {}));
|
|
18
|
+
})(this, (function (exports) {
|
|
19
19
|
// Cached flag check at module load time for zero-cost logging
|
|
20
20
|
const isLogEnabled = (() => {
|
|
21
21
|
if (typeof window !== 'undefined' && window.THREE_TEXT_LOG) {
|
|
@@ -205,7 +205,7 @@
|
|
|
205
205
|
this.allNodes = new Set();
|
|
206
206
|
}
|
|
207
207
|
getKey(position, fitness) {
|
|
208
|
-
return
|
|
208
|
+
return (position << 2) | fitness;
|
|
209
209
|
}
|
|
210
210
|
insert(node) {
|
|
211
211
|
const key = this.getKey(node.position, node.fitness);
|
|
@@ -1257,7 +1257,7 @@
|
|
|
1257
1257
|
}
|
|
1258
1258
|
return { lines };
|
|
1259
1259
|
}
|
|
1260
|
-
applyAlignment(
|
|
1260
|
+
applyAlignment(vertices, options) {
|
|
1261
1261
|
const { width, align, planeBounds } = options;
|
|
1262
1262
|
let offset = 0;
|
|
1263
1263
|
const adjustedBounds = {
|
|
@@ -1273,7 +1273,10 @@
|
|
|
1273
1273
|
offset = width - planeBounds.max.x;
|
|
1274
1274
|
}
|
|
1275
1275
|
if (offset !== 0) {
|
|
1276
|
-
|
|
1276
|
+
// Translate vertices directly
|
|
1277
|
+
for (let i = 0; i < vertices.length; i += 3) {
|
|
1278
|
+
vertices[i] += offset;
|
|
1279
|
+
}
|
|
1277
1280
|
adjustedBounds.min.x += offset;
|
|
1278
1281
|
adjustedBounds.max.x += offset;
|
|
1279
1282
|
}
|
|
@@ -1728,6 +1731,268 @@
|
|
|
1728
1731
|
}
|
|
1729
1732
|
}
|
|
1730
1733
|
|
|
1734
|
+
// Bector and bounding box types for core
|
|
1735
|
+
// 2D Vector
|
|
1736
|
+
class Vec2 {
|
|
1737
|
+
constructor(x = 0, y = 0) {
|
|
1738
|
+
this.x = x;
|
|
1739
|
+
this.y = y;
|
|
1740
|
+
}
|
|
1741
|
+
set(x, y) {
|
|
1742
|
+
this.x = x;
|
|
1743
|
+
this.y = y;
|
|
1744
|
+
return this;
|
|
1745
|
+
}
|
|
1746
|
+
clone() {
|
|
1747
|
+
return new Vec2(this.x, this.y);
|
|
1748
|
+
}
|
|
1749
|
+
copy(v) {
|
|
1750
|
+
this.x = v.x;
|
|
1751
|
+
this.y = v.y;
|
|
1752
|
+
return this;
|
|
1753
|
+
}
|
|
1754
|
+
add(v) {
|
|
1755
|
+
this.x += v.x;
|
|
1756
|
+
this.y += v.y;
|
|
1757
|
+
return this;
|
|
1758
|
+
}
|
|
1759
|
+
sub(v) {
|
|
1760
|
+
this.x -= v.x;
|
|
1761
|
+
this.y -= v.y;
|
|
1762
|
+
return this;
|
|
1763
|
+
}
|
|
1764
|
+
multiply(scalar) {
|
|
1765
|
+
this.x *= scalar;
|
|
1766
|
+
this.y *= scalar;
|
|
1767
|
+
return this;
|
|
1768
|
+
}
|
|
1769
|
+
divide(scalar) {
|
|
1770
|
+
this.x /= scalar;
|
|
1771
|
+
this.y /= scalar;
|
|
1772
|
+
return this;
|
|
1773
|
+
}
|
|
1774
|
+
length() {
|
|
1775
|
+
return Math.sqrt(this.x * this.x + this.y * this.y);
|
|
1776
|
+
}
|
|
1777
|
+
lengthSq() {
|
|
1778
|
+
return this.x * this.x + this.y * this.y;
|
|
1779
|
+
}
|
|
1780
|
+
normalize() {
|
|
1781
|
+
const len = this.length();
|
|
1782
|
+
if (len > 0) {
|
|
1783
|
+
this.divide(len);
|
|
1784
|
+
}
|
|
1785
|
+
return this;
|
|
1786
|
+
}
|
|
1787
|
+
dot(v) {
|
|
1788
|
+
return this.x * v.x + this.y * v.y;
|
|
1789
|
+
}
|
|
1790
|
+
distanceTo(v) {
|
|
1791
|
+
const dx = this.x - v.x;
|
|
1792
|
+
const dy = this.y - v.y;
|
|
1793
|
+
return Math.sqrt(dx * dx + dy * dy);
|
|
1794
|
+
}
|
|
1795
|
+
distanceToSquared(v) {
|
|
1796
|
+
const dx = this.x - v.x;
|
|
1797
|
+
const dy = this.y - v.y;
|
|
1798
|
+
return dx * dx + dy * dy;
|
|
1799
|
+
}
|
|
1800
|
+
equals(v) {
|
|
1801
|
+
return this.x === v.x && this.y === v.y;
|
|
1802
|
+
}
|
|
1803
|
+
angle() {
|
|
1804
|
+
return Math.atan2(this.y, this.x);
|
|
1805
|
+
}
|
|
1806
|
+
}
|
|
1807
|
+
// 3D Vector
|
|
1808
|
+
class Vec3 {
|
|
1809
|
+
constructor(x = 0, y = 0, z = 0) {
|
|
1810
|
+
this.x = x;
|
|
1811
|
+
this.y = y;
|
|
1812
|
+
this.z = z;
|
|
1813
|
+
}
|
|
1814
|
+
set(x, y, z) {
|
|
1815
|
+
this.x = x;
|
|
1816
|
+
this.y = y;
|
|
1817
|
+
this.z = z;
|
|
1818
|
+
return this;
|
|
1819
|
+
}
|
|
1820
|
+
clone() {
|
|
1821
|
+
return new Vec3(this.x, this.y, this.z);
|
|
1822
|
+
}
|
|
1823
|
+
copy(v) {
|
|
1824
|
+
this.x = v.x;
|
|
1825
|
+
this.y = v.y;
|
|
1826
|
+
this.z = v.z;
|
|
1827
|
+
return this;
|
|
1828
|
+
}
|
|
1829
|
+
add(v) {
|
|
1830
|
+
this.x += v.x;
|
|
1831
|
+
this.y += v.y;
|
|
1832
|
+
this.z += v.z;
|
|
1833
|
+
return this;
|
|
1834
|
+
}
|
|
1835
|
+
sub(v) {
|
|
1836
|
+
this.x -= v.x;
|
|
1837
|
+
this.y -= v.y;
|
|
1838
|
+
this.z -= v.z;
|
|
1839
|
+
return this;
|
|
1840
|
+
}
|
|
1841
|
+
multiply(scalar) {
|
|
1842
|
+
this.x *= scalar;
|
|
1843
|
+
this.y *= scalar;
|
|
1844
|
+
this.z *= scalar;
|
|
1845
|
+
return this;
|
|
1846
|
+
}
|
|
1847
|
+
divide(scalar) {
|
|
1848
|
+
this.x /= scalar;
|
|
1849
|
+
this.y /= scalar;
|
|
1850
|
+
this.z /= scalar;
|
|
1851
|
+
return this;
|
|
1852
|
+
}
|
|
1853
|
+
length() {
|
|
1854
|
+
return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
|
|
1855
|
+
}
|
|
1856
|
+
lengthSq() {
|
|
1857
|
+
return this.x * this.x + this.y * this.y + this.z * this.z;
|
|
1858
|
+
}
|
|
1859
|
+
normalize() {
|
|
1860
|
+
const len = this.length();
|
|
1861
|
+
if (len > 0) {
|
|
1862
|
+
this.divide(len);
|
|
1863
|
+
}
|
|
1864
|
+
return this;
|
|
1865
|
+
}
|
|
1866
|
+
dot(v) {
|
|
1867
|
+
return this.x * v.x + this.y * v.y + this.z * v.z;
|
|
1868
|
+
}
|
|
1869
|
+
cross(v) {
|
|
1870
|
+
const x = this.y * v.z - this.z * v.y;
|
|
1871
|
+
const y = this.z * v.x - this.x * v.z;
|
|
1872
|
+
const z = this.x * v.y - this.y * v.x;
|
|
1873
|
+
this.x = x;
|
|
1874
|
+
this.y = y;
|
|
1875
|
+
this.z = z;
|
|
1876
|
+
return this;
|
|
1877
|
+
}
|
|
1878
|
+
distanceTo(v) {
|
|
1879
|
+
const dx = this.x - v.x;
|
|
1880
|
+
const dy = this.y - v.y;
|
|
1881
|
+
const dz = this.z - v.z;
|
|
1882
|
+
return Math.sqrt(dx * dx + dy * dy + dz * dz);
|
|
1883
|
+
}
|
|
1884
|
+
distanceToSquared(v) {
|
|
1885
|
+
const dx = this.x - v.x;
|
|
1886
|
+
const dy = this.y - v.y;
|
|
1887
|
+
const dz = this.z - v.z;
|
|
1888
|
+
return dx * dx + dy * dy + dz * dz;
|
|
1889
|
+
}
|
|
1890
|
+
equals(v) {
|
|
1891
|
+
return this.x === v.x && this.y === v.y && this.z === v.z;
|
|
1892
|
+
}
|
|
1893
|
+
}
|
|
1894
|
+
// 3D Bounding Box
|
|
1895
|
+
class Box3 {
|
|
1896
|
+
constructor(min = new Vec3(Infinity, Infinity, Infinity), max = new Vec3(-Infinity, -Infinity, -Infinity)) {
|
|
1897
|
+
this.min = min;
|
|
1898
|
+
this.max = max;
|
|
1899
|
+
}
|
|
1900
|
+
set(min, max) {
|
|
1901
|
+
this.min.copy(min);
|
|
1902
|
+
this.max.copy(max);
|
|
1903
|
+
return this;
|
|
1904
|
+
}
|
|
1905
|
+
setFromPoints(points) {
|
|
1906
|
+
this.makeEmpty();
|
|
1907
|
+
for (let i = 0; i < points.length; i++) {
|
|
1908
|
+
this.expandByPoint(points[i]);
|
|
1909
|
+
}
|
|
1910
|
+
return this;
|
|
1911
|
+
}
|
|
1912
|
+
makeEmpty() {
|
|
1913
|
+
this.min.x = this.min.y = this.min.z = Infinity;
|
|
1914
|
+
this.max.x = this.max.y = this.max.z = -Infinity;
|
|
1915
|
+
return this;
|
|
1916
|
+
}
|
|
1917
|
+
isEmpty() {
|
|
1918
|
+
return (this.max.x < this.min.x ||
|
|
1919
|
+
this.max.y < this.min.y ||
|
|
1920
|
+
this.max.z < this.min.z);
|
|
1921
|
+
}
|
|
1922
|
+
expandByPoint(point) {
|
|
1923
|
+
this.min.x = Math.min(this.min.x, point.x);
|
|
1924
|
+
this.min.y = Math.min(this.min.y, point.y);
|
|
1925
|
+
this.min.z = Math.min(this.min.z, point.z);
|
|
1926
|
+
this.max.x = Math.max(this.max.x, point.x);
|
|
1927
|
+
this.max.y = Math.max(this.max.y, point.y);
|
|
1928
|
+
this.max.z = Math.max(this.max.z, point.z);
|
|
1929
|
+
return this;
|
|
1930
|
+
}
|
|
1931
|
+
expandByScalar(scalar) {
|
|
1932
|
+
this.min.x -= scalar;
|
|
1933
|
+
this.min.y -= scalar;
|
|
1934
|
+
this.min.z -= scalar;
|
|
1935
|
+
this.max.x += scalar;
|
|
1936
|
+
this.max.y += scalar;
|
|
1937
|
+
this.max.z += scalar;
|
|
1938
|
+
return this;
|
|
1939
|
+
}
|
|
1940
|
+
containsPoint(point) {
|
|
1941
|
+
return (point.x >= this.min.x &&
|
|
1942
|
+
point.x <= this.max.x &&
|
|
1943
|
+
point.y >= this.min.y &&
|
|
1944
|
+
point.y <= this.max.y &&
|
|
1945
|
+
point.z >= this.min.z &&
|
|
1946
|
+
point.z <= this.max.z);
|
|
1947
|
+
}
|
|
1948
|
+
containsBox(box) {
|
|
1949
|
+
return (this.min.x <= box.min.x &&
|
|
1950
|
+
box.max.x <= this.max.x &&
|
|
1951
|
+
this.min.y <= box.min.y &&
|
|
1952
|
+
box.max.y <= this.max.y &&
|
|
1953
|
+
this.min.z <= box.min.z &&
|
|
1954
|
+
box.max.z <= this.max.z);
|
|
1955
|
+
}
|
|
1956
|
+
intersectsBox(box) {
|
|
1957
|
+
return (box.max.x >= this.min.x &&
|
|
1958
|
+
box.min.x <= this.max.x &&
|
|
1959
|
+
box.max.y >= this.min.y &&
|
|
1960
|
+
box.min.y <= this.max.y &&
|
|
1961
|
+
box.max.z >= this.min.z &&
|
|
1962
|
+
box.min.z <= this.max.z);
|
|
1963
|
+
}
|
|
1964
|
+
getCenter(target = new Vec3()) {
|
|
1965
|
+
return this.isEmpty()
|
|
1966
|
+
? target.set(0, 0, 0)
|
|
1967
|
+
: target.set((this.min.x + this.max.x) * 0.5, (this.min.y + this.max.y) * 0.5, (this.min.z + this.max.z) * 0.5);
|
|
1968
|
+
}
|
|
1969
|
+
getSize(target = new Vec3()) {
|
|
1970
|
+
return this.isEmpty()
|
|
1971
|
+
? target.set(0, 0, 0)
|
|
1972
|
+
: target.set(this.max.x - this.min.x, this.max.y - this.min.y, this.max.z - this.min.z);
|
|
1973
|
+
}
|
|
1974
|
+
clone() {
|
|
1975
|
+
return new Box3(this.min.clone(), this.max.clone());
|
|
1976
|
+
}
|
|
1977
|
+
copy(box) {
|
|
1978
|
+
this.min.copy(box.min);
|
|
1979
|
+
this.max.copy(box.max);
|
|
1980
|
+
return this;
|
|
1981
|
+
}
|
|
1982
|
+
union(box) {
|
|
1983
|
+
this.min.x = Math.min(this.min.x, box.min.x);
|
|
1984
|
+
this.min.y = Math.min(this.min.y, box.min.y);
|
|
1985
|
+
this.min.z = Math.min(this.min.z, box.min.z);
|
|
1986
|
+
this.max.x = Math.max(this.max.x, box.max.x);
|
|
1987
|
+
this.max.y = Math.max(this.max.y, box.max.y);
|
|
1988
|
+
this.max.z = Math.max(this.max.z, box.max.z);
|
|
1989
|
+
return this;
|
|
1990
|
+
}
|
|
1991
|
+
equals(box) {
|
|
1992
|
+
return box.min.equals(this.min) && box.max.equals(this.max);
|
|
1993
|
+
}
|
|
1994
|
+
}
|
|
1995
|
+
|
|
1731
1996
|
var WINDING;
|
|
1732
1997
|
(function (WINDING) {
|
|
1733
1998
|
WINDING[WINDING["ODD"] = 0] = "ODD";
|
|
@@ -3948,8 +4213,8 @@
|
|
|
3948
4213
|
const p0y = points[i + 1];
|
|
3949
4214
|
const p1x = points[i + 2];
|
|
3950
4215
|
const p1y = points[i + 3];
|
|
3951
|
-
const edge = new
|
|
3952
|
-
const normal = new
|
|
4216
|
+
const edge = new Vec2(p1x - p0x, p1y - p0y);
|
|
4217
|
+
const normal = new Vec2(edge.y, -edge.x).normalize();
|
|
3953
4218
|
const wallBaseIndex = vertices.length / 3;
|
|
3954
4219
|
vertices.push(p0x, p0y, 0, p1x, p1y, 0, p0x, p0y, depth, p1x, p1y, depth);
|
|
3955
4220
|
normals.push(normal.x, normal.y, 0, normal.x, normal.y, 0, normal.x, normal.y, 0, normal.x, normal.y, 0);
|
|
@@ -4259,8 +4524,8 @@
|
|
|
4259
4524
|
const prev = points[i - 1];
|
|
4260
4525
|
const current = points[i];
|
|
4261
4526
|
const next = points[i + 1];
|
|
4262
|
-
const v1 = new
|
|
4263
|
-
const v2 = new
|
|
4527
|
+
const v1 = new Vec2(current.x - prev.x, current.y - prev.y);
|
|
4528
|
+
const v2 = new Vec2(next.x - current.x, next.y - current.y);
|
|
4264
4529
|
const angle = Math.abs(v1.angle() - v2.angle());
|
|
4265
4530
|
const normalizedAngle = Math.min(angle, 2 * Math.PI - angle);
|
|
4266
4531
|
if (normalizedAngle > threshold ||
|
|
@@ -4374,12 +4639,9 @@
|
|
|
4374
4639
|
const dx = x3 - x1;
|
|
4375
4640
|
const dy = y3 - y1;
|
|
4376
4641
|
const d = Math.abs((x2 - x3) * dy - (y2 - y3) * dx);
|
|
4377
|
-
const curveLength = Math.sqrt(dx * dx + dy * dy);
|
|
4378
4642
|
const baseTolerance = this.curveFidelityConfig.distanceTolerance ??
|
|
4379
4643
|
DEFAULT_CURVE_FIDELITY.distanceTolerance;
|
|
4380
|
-
const distanceTolerance =
|
|
4381
|
-
? baseTolerance * baseTolerance
|
|
4382
|
-
: baseTolerance * baseTolerance;
|
|
4644
|
+
const distanceTolerance = baseTolerance * baseTolerance;
|
|
4383
4645
|
if (d > COLLINEARITY_EPSILON) {
|
|
4384
4646
|
// Regular case
|
|
4385
4647
|
// Recursion terminates when the curve is flat enough (deviation from straight line is within tolerance)
|
|
@@ -4444,12 +4706,9 @@
|
|
|
4444
4706
|
const dy = y4 - y1;
|
|
4445
4707
|
const d2 = Math.abs((x2 - x4) * dy - (y2 - y4) * dx);
|
|
4446
4708
|
const d3 = Math.abs((x3 - x4) * dy - (y3 - y4) * dx);
|
|
4447
|
-
const curveLength = Math.sqrt(dx * dx + dy * dy);
|
|
4448
4709
|
const baseTolerance = this.curveFidelityConfig.distanceTolerance ??
|
|
4449
4710
|
DEFAULT_CURVE_FIDELITY.distanceTolerance;
|
|
4450
|
-
const distanceTolerance =
|
|
4451
|
-
? baseTolerance * baseTolerance
|
|
4452
|
-
: baseTolerance * baseTolerance;
|
|
4711
|
+
const distanceTolerance = baseTolerance * baseTolerance;
|
|
4453
4712
|
let switchCondition = 0;
|
|
4454
4713
|
if (d2 > COLLINEARITY_EPSILON)
|
|
4455
4714
|
switchCondition |= 1;
|
|
@@ -4557,7 +4816,7 @@
|
|
|
4557
4816
|
this.recursiveCubic(x1234, y1234, x234, y234, x34, y34, x4, y4, points, level + 1);
|
|
4558
4817
|
}
|
|
4559
4818
|
addPoint(x, y, points) {
|
|
4560
|
-
const newPoint = new
|
|
4819
|
+
const newPoint = new Vec2(x, y);
|
|
4561
4820
|
if (points.length === 0) {
|
|
4562
4821
|
points.push(newPoint);
|
|
4563
4822
|
return;
|
|
@@ -4580,13 +4839,13 @@
|
|
|
4580
4839
|
this.currentPath = null;
|
|
4581
4840
|
this.currentPoint = null;
|
|
4582
4841
|
this.currentGlyphBounds = {
|
|
4583
|
-
min: new
|
|
4584
|
-
max: new
|
|
4842
|
+
min: new Vec2(Infinity, Infinity),
|
|
4843
|
+
max: new Vec2(-Infinity, -Infinity)
|
|
4585
4844
|
};
|
|
4586
4845
|
this.collectedGlyphs = [];
|
|
4587
4846
|
this.glyphPositions = [];
|
|
4588
4847
|
this.glyphTextIndices = [];
|
|
4589
|
-
this.currentPosition = new
|
|
4848
|
+
this.currentPosition = new Vec2(0, 0);
|
|
4590
4849
|
this.polygonizer = new Polygonizer(curveFidelityConfig);
|
|
4591
4850
|
this.pathOptimizer = new PathOptimizer({
|
|
4592
4851
|
...DEFAULT_OPTIMIZATION_CONFIG,
|
|
@@ -4641,7 +4900,7 @@
|
|
|
4641
4900
|
if (this.currentPath) {
|
|
4642
4901
|
this.finishPath();
|
|
4643
4902
|
}
|
|
4644
|
-
this.currentPoint = new
|
|
4903
|
+
this.currentPoint = new Vec2(x, y);
|
|
4645
4904
|
this.updateBounds(this.currentPoint);
|
|
4646
4905
|
this.currentPath = {
|
|
4647
4906
|
points: [this.currentPoint],
|
|
@@ -4651,7 +4910,7 @@
|
|
|
4651
4910
|
onLineTo(x, y) {
|
|
4652
4911
|
if (!this.currentPath || !this.currentPoint)
|
|
4653
4912
|
return;
|
|
4654
|
-
const point = new
|
|
4913
|
+
const point = new Vec2(x, y);
|
|
4655
4914
|
this.updateBounds(point);
|
|
4656
4915
|
this.currentPath.points.push(point);
|
|
4657
4916
|
this.currentPoint = point;
|
|
@@ -4660,8 +4919,8 @@
|
|
|
4660
4919
|
if (!this.currentPath || !this.currentPoint)
|
|
4661
4920
|
return;
|
|
4662
4921
|
const start = this.currentPoint;
|
|
4663
|
-
const control = new
|
|
4664
|
-
const end = new
|
|
4922
|
+
const control = new Vec2(cx, cy);
|
|
4923
|
+
const end = new Vec2(x, y);
|
|
4665
4924
|
const dx = end.x - start.x;
|
|
4666
4925
|
const dy = end.y - start.y;
|
|
4667
4926
|
const d = Math.abs((control.x - end.x) * dy - (control.y - end.y) * dx);
|
|
@@ -4682,9 +4941,9 @@
|
|
|
4682
4941
|
if (!this.currentPath || !this.currentPoint)
|
|
4683
4942
|
return;
|
|
4684
4943
|
const start = this.currentPoint;
|
|
4685
|
-
const control1 = new
|
|
4686
|
-
const control2 = new
|
|
4687
|
-
const end = new
|
|
4944
|
+
const control1 = new Vec2(c1x, c1y);
|
|
4945
|
+
const control2 = new Vec2(c2x, c2y);
|
|
4946
|
+
const end = new Vec2(x, y);
|
|
4688
4947
|
const dx = end.x - start.x;
|
|
4689
4948
|
const dy = end.y - start.y;
|
|
4690
4949
|
const d1 = Math.abs((control1.x - end.x) * dy - (control1.y - end.y) * dx);
|
|
@@ -4726,7 +4985,7 @@
|
|
|
4726
4985
|
this.currentGlyphBounds.max.y = Math.max(this.currentGlyphBounds.max.y, point.y);
|
|
4727
4986
|
}
|
|
4728
4987
|
getCollectedGlyphs() {
|
|
4729
|
-
//
|
|
4988
|
+
// Finish any pending glyph
|
|
4730
4989
|
if (this.currentGlyphPaths.length > 0) {
|
|
4731
4990
|
this.finishGlyph();
|
|
4732
4991
|
}
|
|
@@ -4749,8 +5008,8 @@
|
|
|
4749
5008
|
this.currentTextIndex = 0;
|
|
4750
5009
|
this.currentPosition.set(0, 0);
|
|
4751
5010
|
this.currentGlyphBounds = {
|
|
4752
|
-
min: new
|
|
4753
|
-
max: new
|
|
5011
|
+
min: new Vec2(Infinity, Infinity),
|
|
5012
|
+
max: new Vec2(-Infinity, -Infinity)
|
|
4754
5013
|
};
|
|
4755
5014
|
}
|
|
4756
5015
|
setCurveFidelityConfig(config) {
|
|
@@ -4919,7 +5178,7 @@
|
|
|
4919
5178
|
clusterGlyphContours.push(this.getContoursForGlyph(glyph.g));
|
|
4920
5179
|
}
|
|
4921
5180
|
// Step 2: Check for overlaps within the cluster
|
|
4922
|
-
const relativePositions = cluster.glyphs.map((g) => new
|
|
5181
|
+
const relativePositions = cluster.glyphs.map((g) => new Vec3(g.x, g.y, 0));
|
|
4923
5182
|
const boundaryGroups = this.clusterer.cluster(clusterGlyphContours, relativePositions);
|
|
4924
5183
|
const hasOverlaps = separateGlyphs
|
|
4925
5184
|
? false
|
|
@@ -4936,7 +5195,7 @@
|
|
|
4936
5195
|
for (const path of glyphContours.paths) {
|
|
4937
5196
|
clusterPaths.push({
|
|
4938
5197
|
...path,
|
|
4939
|
-
points: path.points.map((p) => new
|
|
5198
|
+
points: path.points.map((p) => new Vec2(p.x + (glyph.x ?? 0), p.y + (glyph.y ?? 0)))
|
|
4940
5199
|
});
|
|
4941
5200
|
}
|
|
4942
5201
|
}
|
|
@@ -4949,7 +5208,7 @@
|
|
|
4949
5208
|
for (let i = 0; i < cluster.glyphs.length; i++) {
|
|
4950
5209
|
const glyph = cluster.glyphs[i];
|
|
4951
5210
|
const glyphContours = clusterGlyphContours[i];
|
|
4952
|
-
const absoluteGlyphPosition = new
|
|
5211
|
+
const absoluteGlyphPosition = new Vec3(cluster.position.x + (glyph.x ?? 0), cluster.position.y + (glyph.y ?? 0), cluster.position.z);
|
|
4953
5212
|
const glyphInfo = this.createGlyphInfo(glyph, vertexOffset, clusterVertexCount, absoluteGlyphPosition, glyphContours, depth);
|
|
4954
5213
|
glyphInfos.push(glyphInfo);
|
|
4955
5214
|
this.updatePlaneBounds(glyphInfo.bounds, planeBounds);
|
|
@@ -4960,7 +5219,7 @@
|
|
|
4960
5219
|
for (let i = 0; i < cluster.glyphs.length; i++) {
|
|
4961
5220
|
const glyph = cluster.glyphs[i];
|
|
4962
5221
|
const glyphContours = clusterGlyphContours[i];
|
|
4963
|
-
const glyphPosition = new
|
|
5222
|
+
const glyphPosition = new Vec3(cluster.position.x + (glyph.x ?? 0), cluster.position.y + (glyph.y ?? 0), cluster.position.z);
|
|
4964
5223
|
// Skip glyphs with no paths (spaces, zero-width characters, etc.)
|
|
4965
5224
|
if (glyphContours.paths.length === 0) {
|
|
4966
5225
|
const glyphInfo = this.createGlyphInfo(glyph, 0, 0, glyphPosition, glyphContours, depth);
|
|
@@ -4981,15 +5240,17 @@
|
|
|
4981
5240
|
}
|
|
4982
5241
|
}
|
|
4983
5242
|
}
|
|
4984
|
-
const geometry = new three.BufferGeometry();
|
|
4985
5243
|
const vertexArray = new Float32Array(vertices);
|
|
4986
5244
|
const normalArray = new Float32Array(normals);
|
|
4987
|
-
|
|
4988
|
-
geometry.setAttribute('normal', new three.Float32BufferAttribute(normalArray, 3));
|
|
4989
|
-
geometry.setIndex(indices);
|
|
4990
|
-
geometry.computeBoundingBox();
|
|
5245
|
+
const indexArray = new Uint32Array(indices);
|
|
4991
5246
|
perfLogger.end('GlyphGeometryBuilder.buildInstancedGeometry');
|
|
4992
|
-
return {
|
|
5247
|
+
return {
|
|
5248
|
+
vertices: vertexArray,
|
|
5249
|
+
normals: normalArray,
|
|
5250
|
+
indices: indexArray,
|
|
5251
|
+
glyphInfos,
|
|
5252
|
+
planeBounds
|
|
5253
|
+
};
|
|
4993
5254
|
}
|
|
4994
5255
|
appendGeometry(vertices, normals, indices, data, position, offset) {
|
|
4995
5256
|
for (let j = 0; j < data.vertices.length; j += 3) {
|
|
@@ -5047,19 +5308,34 @@
|
|
|
5047
5308
|
}
|
|
5048
5309
|
extrudeAndPackage(processedGeometry, depth) {
|
|
5049
5310
|
const extrudedResult = this.extruder.extrude(processedGeometry, depth, this.loadedFont.upem);
|
|
5050
|
-
|
|
5051
|
-
const vertices =
|
|
5052
|
-
|
|
5053
|
-
|
|
5054
|
-
|
|
5055
|
-
|
|
5056
|
-
|
|
5057
|
-
|
|
5311
|
+
// Compute bounding box from vertices
|
|
5312
|
+
const vertices = extrudedResult.vertices;
|
|
5313
|
+
let minX = Infinity, minY = Infinity, minZ = Infinity;
|
|
5314
|
+
let maxX = -Infinity, maxY = -Infinity, maxZ = -Infinity;
|
|
5315
|
+
for (let i = 0; i < vertices.length; i += 3) {
|
|
5316
|
+
const x = vertices[i];
|
|
5317
|
+
const y = vertices[i + 1];
|
|
5318
|
+
const z = vertices[i + 2];
|
|
5319
|
+
if (x < minX)
|
|
5320
|
+
minX = x;
|
|
5321
|
+
if (x > maxX)
|
|
5322
|
+
maxX = x;
|
|
5323
|
+
if (y < minY)
|
|
5324
|
+
minY = y;
|
|
5325
|
+
if (y > maxY)
|
|
5326
|
+
maxY = y;
|
|
5327
|
+
if (z < minZ)
|
|
5328
|
+
minZ = z;
|
|
5329
|
+
if (z > maxZ)
|
|
5330
|
+
maxZ = z;
|
|
5331
|
+
}
|
|
5332
|
+
const boundsMin = new Vec3(minX, minY, minZ);
|
|
5333
|
+
const boundsMax = new Vec3(maxX, maxY, maxZ);
|
|
5058
5334
|
const vertexCount = extrudedResult.vertices.length / 3;
|
|
5059
5335
|
const IndexArray = vertexCount < 65536 ? Uint16Array : Uint32Array;
|
|
5060
5336
|
return {
|
|
5061
5337
|
geometry: processedGeometry,
|
|
5062
|
-
vertices: vertices,
|
|
5338
|
+
vertices: new Float32Array(extrudedResult.vertices),
|
|
5063
5339
|
normals: new Float32Array(extrudedResult.normals),
|
|
5064
5340
|
indices: new IndexArray(extrudedResult.indices),
|
|
5065
5341
|
bounds: { min: boundsMin, max: boundsMax },
|
|
@@ -5076,8 +5352,8 @@
|
|
|
5076
5352
|
return this.extrudeAndPackage(processedGeometry, depth);
|
|
5077
5353
|
}
|
|
5078
5354
|
updatePlaneBounds(glyphBounds, planeBounds) {
|
|
5079
|
-
const planeBox = new
|
|
5080
|
-
const glyphBox = new
|
|
5355
|
+
const planeBox = new Box3(new Vec3(planeBounds.min.x, planeBounds.min.y, planeBounds.min.z), new Vec3(planeBounds.max.x, planeBounds.max.y, planeBounds.max.z));
|
|
5356
|
+
const glyphBox = new Box3(new Vec3(glyphBounds.min.x, glyphBounds.min.y, glyphBounds.min.z), new Vec3(glyphBounds.max.x, glyphBounds.max.y, glyphBounds.max.z));
|
|
5081
5357
|
planeBox.union(glyphBox);
|
|
5082
5358
|
planeBounds.min.x = planeBox.min.x;
|
|
5083
5359
|
planeBounds.min.y = planeBox.min.y;
|
|
@@ -5142,8 +5418,8 @@
|
|
|
5142
5418
|
const clusters = [];
|
|
5143
5419
|
let currentClusterGlyphs = [];
|
|
5144
5420
|
let currentClusterText = '';
|
|
5145
|
-
let clusterStartPosition = new
|
|
5146
|
-
let cursor = new
|
|
5421
|
+
let clusterStartPosition = new Vec3();
|
|
5422
|
+
let cursor = new Vec3(lineInfo.xOffset, -lineIndex * scaledLineHeight, 0);
|
|
5147
5423
|
const letterSpacingFU = letterSpacing * this.loadedFont.upem;
|
|
5148
5424
|
const spaceAdjustment = this.calculateSpaceAdjustment(lineInfo, align, letterSpacing);
|
|
5149
5425
|
for (let i = 0; i < glyphInfos.length; i++) {
|
|
@@ -5173,7 +5449,7 @@
|
|
|
5173
5449
|
}
|
|
5174
5450
|
const absoluteGlyphPosition = cursor
|
|
5175
5451
|
.clone()
|
|
5176
|
-
.add(new
|
|
5452
|
+
.add(new Vec3(glyph.dx, glyph.dy, 0));
|
|
5177
5453
|
if (!isWhitespace) {
|
|
5178
5454
|
if (currentClusterGlyphs.length === 0) {
|
|
5179
5455
|
clusterStartPosition.copy(absoluteGlyphPosition);
|
|
@@ -5306,7 +5582,7 @@
|
|
|
5306
5582
|
size += glyph.normals.length * 4;
|
|
5307
5583
|
// Indices (Uint16Array or Uint32Array)
|
|
5308
5584
|
size += glyph.indices.length * glyph.indices.BYTES_PER_ELEMENT;
|
|
5309
|
-
// Bounds (2
|
|
5585
|
+
// Bounds (2 Vec3s = 6 floats * 4 bytes)
|
|
5310
5586
|
size += 24;
|
|
5311
5587
|
// Object overhead
|
|
5312
5588
|
size += 256;
|
|
@@ -6122,9 +6398,9 @@
|
|
|
6122
6398
|
max: { x: 0, y: 0, z: 0 }
|
|
6123
6399
|
};
|
|
6124
6400
|
}
|
|
6125
|
-
const box = new
|
|
6401
|
+
const box = new Box3();
|
|
6126
6402
|
for (const glyph of glyphs) {
|
|
6127
|
-
const glyphBox = new
|
|
6403
|
+
const glyphBox = new Box3(new Vec3(glyph.bounds.min.x, glyph.bounds.min.y, glyph.bounds.min.z), new Vec3(glyph.bounds.max.x, glyph.bounds.max.y, glyph.bounds.max.z));
|
|
6128
6404
|
box.union(glyphBox);
|
|
6129
6405
|
}
|
|
6130
6406
|
return {
|
|
@@ -6155,7 +6431,7 @@
|
|
|
6155
6431
|
HarfBuzzLoader.setWasmBuffer(wasmBuffer);
|
|
6156
6432
|
Text.hbInitPromise = null;
|
|
6157
6433
|
}
|
|
6158
|
-
// Initialize HarfBuzz WASM
|
|
6434
|
+
// Initialize HarfBuzz WASM (optional - create() calls this if needed)
|
|
6159
6435
|
static init() {
|
|
6160
6436
|
if (!Text.hbInitPromise) {
|
|
6161
6437
|
Text.hbInitPromise = HarfBuzzLoader.getHarfBuzz();
|
|
@@ -6289,9 +6565,10 @@
|
|
|
6289
6565
|
const clustersByLine = this.textShaper.shapeLines(layoutData.lines, layoutData.scaledLineHeight, layoutData.letterSpacing, layoutData.align, layoutData.direction, options.color, options.text);
|
|
6290
6566
|
const shapedResult = this.geometryBuilder.buildInstancedGeometry(clustersByLine, layoutData.depth, shouldRemoveOverlaps, this.loadedFont.metrics.isCFF, options.separateGlyphsWithAttributes || false);
|
|
6291
6567
|
const cacheStats = this.geometryBuilder.getCacheStats();
|
|
6292
|
-
const result = this.finalizeGeometry(shapedResult.
|
|
6568
|
+
const result = this.finalizeGeometry(shapedResult.vertices, shapedResult.normals, shapedResult.indices, shapedResult.glyphInfos, shapedResult.planeBounds, options, cacheStats, options.text);
|
|
6293
6569
|
if (options.separateGlyphsWithAttributes) {
|
|
6294
|
-
this.
|
|
6570
|
+
const glyphAttrs = this.createGlyphAttributes(result.vertices.length / 3, result.glyphs);
|
|
6571
|
+
result.glyphAttributes = glyphAttrs;
|
|
6295
6572
|
}
|
|
6296
6573
|
return result;
|
|
6297
6574
|
}
|
|
@@ -6403,8 +6680,8 @@
|
|
|
6403
6680
|
size
|
|
6404
6681
|
};
|
|
6405
6682
|
}
|
|
6406
|
-
applyColorSystem(
|
|
6407
|
-
const vertexCount =
|
|
6683
|
+
applyColorSystem(vertices, glyphInfoArray, color, originalText) {
|
|
6684
|
+
const vertexCount = vertices.length / 3;
|
|
6408
6685
|
const colors = new Float32Array(vertexCount * 3);
|
|
6409
6686
|
const coloredRanges = [];
|
|
6410
6687
|
// Simple case: array color for all text
|
|
@@ -6494,16 +6771,15 @@
|
|
|
6494
6771
|
});
|
|
6495
6772
|
}
|
|
6496
6773
|
}
|
|
6497
|
-
|
|
6498
|
-
return coloredRanges;
|
|
6774
|
+
return { colors, coloredRanges };
|
|
6499
6775
|
}
|
|
6500
|
-
finalizeGeometry(
|
|
6776
|
+
finalizeGeometry(vertices, normals, indices, glyphInfoArray, planeBounds, options, cacheStats, originalText) {
|
|
6501
6777
|
const { layout = {}, size = 72 } = options;
|
|
6502
6778
|
const { width, align = layout.direction === 'rtl' ? 'right' : 'left' } = layout;
|
|
6503
6779
|
if (!this.textLayout) {
|
|
6504
6780
|
this.textLayout = new TextLayout(this.loadedFont);
|
|
6505
6781
|
}
|
|
6506
|
-
const alignmentResult = this.textLayout.applyAlignment(
|
|
6782
|
+
const alignmentResult = this.textLayout.applyAlignment(vertices, {
|
|
6507
6783
|
width,
|
|
6508
6784
|
align,
|
|
6509
6785
|
planeBounds
|
|
@@ -6512,7 +6788,13 @@
|
|
|
6512
6788
|
planeBounds.min.x = alignmentResult.adjustedBounds.min.x;
|
|
6513
6789
|
planeBounds.max.x = alignmentResult.adjustedBounds.max.x;
|
|
6514
6790
|
const finalScale = size / this.loadedFont.upem;
|
|
6515
|
-
|
|
6791
|
+
// Scale vertices and normals directly
|
|
6792
|
+
for (let i = 0; i < vertices.length; i++) {
|
|
6793
|
+
vertices[i] *= finalScale;
|
|
6794
|
+
}
|
|
6795
|
+
for (let i = 0; i < normals.length; i++) {
|
|
6796
|
+
normals[i] *= finalScale;
|
|
6797
|
+
}
|
|
6516
6798
|
planeBounds.min.x *= finalScale;
|
|
6517
6799
|
planeBounds.min.y *= finalScale;
|
|
6518
6800
|
planeBounds.min.z *= finalScale;
|
|
@@ -6532,15 +6814,22 @@
|
|
|
6532
6814
|
glyphInfo.bounds.max.y *= finalScale;
|
|
6533
6815
|
glyphInfo.bounds.max.z *= finalScale;
|
|
6534
6816
|
}
|
|
6535
|
-
|
|
6536
|
-
|
|
6537
|
-
|
|
6817
|
+
let colors;
|
|
6818
|
+
let coloredRanges;
|
|
6819
|
+
if (options.color) {
|
|
6820
|
+
const colorResult = this.applyColorSystem(vertices, glyphInfoArray, options.color, options.text);
|
|
6821
|
+
colors = colorResult.colors;
|
|
6822
|
+
coloredRanges = colorResult.coloredRanges;
|
|
6823
|
+
}
|
|
6538
6824
|
// Collect optimization stats for return value
|
|
6539
6825
|
const optimizationStats = this.geometryBuilder.getOptimizationStats();
|
|
6540
|
-
const trianglesGenerated =
|
|
6541
|
-
const verticesGenerated =
|
|
6826
|
+
const trianglesGenerated = indices.length / 3;
|
|
6827
|
+
const verticesGenerated = vertices.length / 3;
|
|
6542
6828
|
return {
|
|
6543
|
-
|
|
6829
|
+
vertices,
|
|
6830
|
+
normals,
|
|
6831
|
+
indices,
|
|
6832
|
+
colors,
|
|
6544
6833
|
glyphs: glyphInfoArray,
|
|
6545
6834
|
planeBounds,
|
|
6546
6835
|
stats: {
|
|
@@ -6558,7 +6847,8 @@
|
|
|
6558
6847
|
const queryInstance = new TextRangeQuery(originalText, glyphInfoArray);
|
|
6559
6848
|
return queryInstance.execute(options);
|
|
6560
6849
|
},
|
|
6561
|
-
coloredRanges
|
|
6850
|
+
coloredRanges,
|
|
6851
|
+
glyphAttributes: undefined
|
|
6562
6852
|
};
|
|
6563
6853
|
}
|
|
6564
6854
|
getFontMetrics() {
|
|
@@ -6603,8 +6893,7 @@
|
|
|
6603
6893
|
this.geometryBuilder.clearCache();
|
|
6604
6894
|
}
|
|
6605
6895
|
}
|
|
6606
|
-
|
|
6607
|
-
const vertexCount = geometry.attributes.position.count;
|
|
6896
|
+
createGlyphAttributes(vertexCount, glyphs) {
|
|
6608
6897
|
const glyphCenters = new Float32Array(vertexCount * 3);
|
|
6609
6898
|
const glyphIndices = new Float32Array(vertexCount);
|
|
6610
6899
|
const glyphLineIndices = new Float32Array(vertexCount);
|
|
@@ -6623,9 +6912,11 @@
|
|
|
6623
6912
|
}
|
|
6624
6913
|
}
|
|
6625
6914
|
});
|
|
6626
|
-
|
|
6627
|
-
|
|
6628
|
-
|
|
6915
|
+
return {
|
|
6916
|
+
glyphCenter: glyphCenters,
|
|
6917
|
+
glyphIndex: glyphIndices,
|
|
6918
|
+
glyphLineIndex: glyphLineIndices
|
|
6919
|
+
};
|
|
6629
6920
|
}
|
|
6630
6921
|
destroy() {
|
|
6631
6922
|
if (!this.loadedFont) {
|