three-text 0.2.17 → 0.2.18
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 +100 -84
- package/dist/index.js +100 -84
- package/dist/index.min.cjs +259 -251
- package/dist/index.min.js +319 -311
- package/dist/index.umd.js +100 -84
- package/dist/index.umd.min.js +295 -287
- package/dist/types/core/geometry/Extruder.d.ts +1 -0
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* three-text v0.2.
|
|
2
|
+
* three-text v0.2.18
|
|
3
3
|
* Copyright (C) 2025 Countertype LLC
|
|
4
4
|
*
|
|
5
5
|
* This program is free software: you can redistribute it and/or modify
|
|
@@ -2727,7 +2727,9 @@ class Tessellator {
|
|
|
2727
2727
|
tessContours = originalContours;
|
|
2728
2728
|
}
|
|
2729
2729
|
let extrusionContours = needsExtrusionContours
|
|
2730
|
-
?
|
|
2730
|
+
? needsWindingReversal
|
|
2731
|
+
? tessContours
|
|
2732
|
+
: originalContours ?? this.pathsToContours(paths)
|
|
2731
2733
|
: [];
|
|
2732
2734
|
if (removeOverlaps) {
|
|
2733
2735
|
logger.log('Two-pass: boundary extraction then triangulation');
|
|
@@ -2749,24 +2751,6 @@ class Tessellator {
|
|
|
2749
2751
|
}
|
|
2750
2752
|
else {
|
|
2751
2753
|
logger.log(`Single-pass triangulation for ${isCFF ? 'CFF' : 'TTF'}`);
|
|
2752
|
-
// TTF contours may have inconsistent winding; check if we need normalization
|
|
2753
|
-
if (needsExtrusionContours && !isCFF) {
|
|
2754
|
-
const needsNormalization = this.needsWindingNormalization(extrusionContours);
|
|
2755
|
-
if (needsNormalization) {
|
|
2756
|
-
logger.log('Complex topology detected, running boundary pass for winding normalization');
|
|
2757
|
-
perfLogger.start('Tessellator.windingNormalization', {
|
|
2758
|
-
contourCount: extrusionContours.length
|
|
2759
|
-
});
|
|
2760
|
-
const boundaryResult = this.performTessellation(extrusionContours, 'boundary');
|
|
2761
|
-
perfLogger.end('Tessellator.windingNormalization');
|
|
2762
|
-
if (boundaryResult) {
|
|
2763
|
-
extrusionContours = this.boundaryToContours(boundaryResult);
|
|
2764
|
-
}
|
|
2765
|
-
}
|
|
2766
|
-
else {
|
|
2767
|
-
logger.log('Simple topology, skipping winding normalization');
|
|
2768
|
-
}
|
|
2769
|
-
}
|
|
2770
2754
|
}
|
|
2771
2755
|
perfLogger.start('Tessellator.triangulationPass', {
|
|
2772
2756
|
contourCount: tessContours.length
|
|
@@ -2948,28 +2932,58 @@ class Tessellator {
|
|
|
2948
2932
|
|
|
2949
2933
|
class Extruder {
|
|
2950
2934
|
constructor() { }
|
|
2935
|
+
packEdge(a, b) {
|
|
2936
|
+
const lo = a < b ? a : b;
|
|
2937
|
+
const hi = a < b ? b : a;
|
|
2938
|
+
return lo * 0x100000000 + hi;
|
|
2939
|
+
}
|
|
2951
2940
|
extrude(geometry, depth = 0, unitsPerEm) {
|
|
2952
2941
|
const points = geometry.triangles.vertices;
|
|
2953
2942
|
const triangleIndices = geometry.triangles.indices;
|
|
2954
2943
|
const numPoints = points.length / 2;
|
|
2955
|
-
// Count side
|
|
2956
|
-
let
|
|
2944
|
+
// Count boundary edges for side walls (4 vertices + 6 indices per edge)
|
|
2945
|
+
let boundaryEdges = [];
|
|
2957
2946
|
if (depth !== 0) {
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2947
|
+
const counts = new Map();
|
|
2948
|
+
const oriented = new Map();
|
|
2949
|
+
for (let i = 0; i < triangleIndices.length; i += 3) {
|
|
2950
|
+
const a = triangleIndices[i];
|
|
2951
|
+
const b = triangleIndices[i + 1];
|
|
2952
|
+
const c = triangleIndices[i + 2];
|
|
2953
|
+
const k0 = this.packEdge(a, b);
|
|
2954
|
+
const n0 = (counts.get(k0) ?? 0) + 1;
|
|
2955
|
+
counts.set(k0, n0);
|
|
2956
|
+
if (n0 === 1)
|
|
2957
|
+
oriented.set(k0, [a, b]);
|
|
2958
|
+
const k1 = this.packEdge(b, c);
|
|
2959
|
+
const n1 = (counts.get(k1) ?? 0) + 1;
|
|
2960
|
+
counts.set(k1, n1);
|
|
2961
|
+
if (n1 === 1)
|
|
2962
|
+
oriented.set(k1, [b, c]);
|
|
2963
|
+
const k2 = this.packEdge(c, a);
|
|
2964
|
+
const n2 = (counts.get(k2) ?? 0) + 1;
|
|
2965
|
+
counts.set(k2, n2);
|
|
2966
|
+
if (n2 === 1)
|
|
2967
|
+
oriented.set(k2, [c, a]);
|
|
2968
|
+
}
|
|
2969
|
+
boundaryEdges = [];
|
|
2970
|
+
for (const [key, count] of counts) {
|
|
2971
|
+
if (count !== 1)
|
|
2972
|
+
continue;
|
|
2973
|
+
const edge = oriented.get(key);
|
|
2974
|
+
if (edge)
|
|
2975
|
+
boundaryEdges.push(edge);
|
|
2963
2976
|
}
|
|
2964
2977
|
}
|
|
2965
|
-
const
|
|
2978
|
+
const sideEdgeCount = depth === 0 ? 0 : boundaryEdges.length;
|
|
2979
|
+
const sideVertexCount = depth === 0 ? 0 : sideEdgeCount * 4;
|
|
2966
2980
|
const baseVertexCount = depth === 0 ? numPoints : numPoints * 2;
|
|
2967
2981
|
const vertexCount = baseVertexCount + sideVertexCount;
|
|
2968
2982
|
const vertices = new Float32Array(vertexCount * 3);
|
|
2969
2983
|
const normals = new Float32Array(vertexCount * 3);
|
|
2970
2984
|
const indexCount = depth === 0
|
|
2971
2985
|
? triangleIndices.length
|
|
2972
|
-
: triangleIndices.length * 2 +
|
|
2986
|
+
: triangleIndices.length * 2 + sideEdgeCount * 6;
|
|
2973
2987
|
const indices = new Uint32Array(indexCount);
|
|
2974
2988
|
if (depth === 0) {
|
|
2975
2989
|
// Single-sided flat geometry at z=0
|
|
@@ -3025,60 +3039,62 @@ class Extruder {
|
|
|
3025
3039
|
// Side walls
|
|
3026
3040
|
let nextVertex = numPoints * 2;
|
|
3027
3041
|
let idxPos = triangleIndices.length * 2;
|
|
3028
|
-
for (
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3042
|
+
for (let e = 0; e < boundaryEdges.length; e++) {
|
|
3043
|
+
const [u, v] = boundaryEdges[e];
|
|
3044
|
+
const u2 = u * 2;
|
|
3045
|
+
const v2 = v * 2;
|
|
3046
|
+
const p0x = points[u2];
|
|
3047
|
+
const p0y = points[u2 + 1];
|
|
3048
|
+
const p1x = points[v2];
|
|
3049
|
+
const p1y = points[v2 + 1];
|
|
3050
|
+
// Perpendicular normal for this wall segment
|
|
3051
|
+
// Uses the edge direction from the cap triangulation so winding does not depend on contour direction
|
|
3052
|
+
const ex = p1x - p0x;
|
|
3053
|
+
const ey = p1y - p0y;
|
|
3054
|
+
const lenSq = ex * ex + ey * ey;
|
|
3055
|
+
let nx = 0;
|
|
3056
|
+
let ny = 0;
|
|
3057
|
+
if (lenSq > 0) {
|
|
3058
|
+
const invLen = 1 / Math.sqrt(lenSq);
|
|
3059
|
+
nx = ey * invLen;
|
|
3060
|
+
ny = -ex * invLen;
|
|
3061
|
+
}
|
|
3062
|
+
const baseVertex = nextVertex;
|
|
3063
|
+
const base = baseVertex * 3;
|
|
3064
|
+
// Wall quad: front edge at z=0, back edge at z=depth
|
|
3065
|
+
vertices[base] = p0x;
|
|
3066
|
+
vertices[base + 1] = p0y;
|
|
3067
|
+
vertices[base + 2] = 0;
|
|
3068
|
+
vertices[base + 3] = p1x;
|
|
3069
|
+
vertices[base + 4] = p1y;
|
|
3070
|
+
vertices[base + 5] = 0;
|
|
3071
|
+
vertices[base + 6] = p0x;
|
|
3072
|
+
vertices[base + 7] = p0y;
|
|
3073
|
+
vertices[base + 8] = backZ;
|
|
3074
|
+
vertices[base + 9] = p1x;
|
|
3075
|
+
vertices[base + 10] = p1y;
|
|
3076
|
+
vertices[base + 11] = backZ;
|
|
3077
|
+
// Wall normals point perpendicular to edge
|
|
3078
|
+
normals[base] = nx;
|
|
3079
|
+
normals[base + 1] = ny;
|
|
3080
|
+
normals[base + 2] = 0;
|
|
3081
|
+
normals[base + 3] = nx;
|
|
3082
|
+
normals[base + 4] = ny;
|
|
3083
|
+
normals[base + 5] = 0;
|
|
3084
|
+
normals[base + 6] = nx;
|
|
3085
|
+
normals[base + 7] = ny;
|
|
3086
|
+
normals[base + 8] = 0;
|
|
3087
|
+
normals[base + 9] = nx;
|
|
3088
|
+
normals[base + 10] = ny;
|
|
3089
|
+
normals[base + 11] = 0;
|
|
3090
|
+
// Two triangles per wall segment
|
|
3091
|
+
indices[idxPos++] = baseVertex;
|
|
3092
|
+
indices[idxPos++] = baseVertex + 1;
|
|
3093
|
+
indices[idxPos++] = baseVertex + 2;
|
|
3094
|
+
indices[idxPos++] = baseVertex + 1;
|
|
3095
|
+
indices[idxPos++] = baseVertex + 3;
|
|
3096
|
+
indices[idxPos++] = baseVertex + 2;
|
|
3097
|
+
nextVertex += 4;
|
|
3082
3098
|
}
|
|
3083
3099
|
return { vertices, normals, indices };
|
|
3084
3100
|
}
|
|
@@ -4177,7 +4193,7 @@ class GlyphGeometryBuilder {
|
|
|
4177
4193
|
// Use glyph-level caching when separateGlyphs is set or when cluster contains colored text
|
|
4178
4194
|
const forceSeparate = separateGlyphs || clusterHasColoredGlyphs;
|
|
4179
4195
|
// Iterate over the geometric groups identified by BoundaryClusterer
|
|
4180
|
-
// logical groups (words) split into geometric sub-groups
|
|
4196
|
+
// logical groups (words) split into geometric sub-groups
|
|
4181
4197
|
for (const groupIndices of boundaryGroups) {
|
|
4182
4198
|
const isOverlappingGroup = groupIndices.length > 1;
|
|
4183
4199
|
const shouldCluster = isOverlappingGroup && !forceSeparate;
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* three-text v0.2.
|
|
2
|
+
* three-text v0.2.18
|
|
3
3
|
* Copyright (C) 2025 Countertype LLC
|
|
4
4
|
*
|
|
5
5
|
* This program is free software: you can redistribute it and/or modify
|
|
@@ -2724,7 +2724,9 @@ class Tessellator {
|
|
|
2724
2724
|
tessContours = originalContours;
|
|
2725
2725
|
}
|
|
2726
2726
|
let extrusionContours = needsExtrusionContours
|
|
2727
|
-
?
|
|
2727
|
+
? needsWindingReversal
|
|
2728
|
+
? tessContours
|
|
2729
|
+
: originalContours ?? this.pathsToContours(paths)
|
|
2728
2730
|
: [];
|
|
2729
2731
|
if (removeOverlaps) {
|
|
2730
2732
|
logger.log('Two-pass: boundary extraction then triangulation');
|
|
@@ -2746,24 +2748,6 @@ class Tessellator {
|
|
|
2746
2748
|
}
|
|
2747
2749
|
else {
|
|
2748
2750
|
logger.log(`Single-pass triangulation for ${isCFF ? 'CFF' : 'TTF'}`);
|
|
2749
|
-
// TTF contours may have inconsistent winding; check if we need normalization
|
|
2750
|
-
if (needsExtrusionContours && !isCFF) {
|
|
2751
|
-
const needsNormalization = this.needsWindingNormalization(extrusionContours);
|
|
2752
|
-
if (needsNormalization) {
|
|
2753
|
-
logger.log('Complex topology detected, running boundary pass for winding normalization');
|
|
2754
|
-
perfLogger.start('Tessellator.windingNormalization', {
|
|
2755
|
-
contourCount: extrusionContours.length
|
|
2756
|
-
});
|
|
2757
|
-
const boundaryResult = this.performTessellation(extrusionContours, 'boundary');
|
|
2758
|
-
perfLogger.end('Tessellator.windingNormalization');
|
|
2759
|
-
if (boundaryResult) {
|
|
2760
|
-
extrusionContours = this.boundaryToContours(boundaryResult);
|
|
2761
|
-
}
|
|
2762
|
-
}
|
|
2763
|
-
else {
|
|
2764
|
-
logger.log('Simple topology, skipping winding normalization');
|
|
2765
|
-
}
|
|
2766
|
-
}
|
|
2767
2751
|
}
|
|
2768
2752
|
perfLogger.start('Tessellator.triangulationPass', {
|
|
2769
2753
|
contourCount: tessContours.length
|
|
@@ -2945,28 +2929,58 @@ class Tessellator {
|
|
|
2945
2929
|
|
|
2946
2930
|
class Extruder {
|
|
2947
2931
|
constructor() { }
|
|
2932
|
+
packEdge(a, b) {
|
|
2933
|
+
const lo = a < b ? a : b;
|
|
2934
|
+
const hi = a < b ? b : a;
|
|
2935
|
+
return lo * 0x100000000 + hi;
|
|
2936
|
+
}
|
|
2948
2937
|
extrude(geometry, depth = 0, unitsPerEm) {
|
|
2949
2938
|
const points = geometry.triangles.vertices;
|
|
2950
2939
|
const triangleIndices = geometry.triangles.indices;
|
|
2951
2940
|
const numPoints = points.length / 2;
|
|
2952
|
-
// Count side
|
|
2953
|
-
let
|
|
2941
|
+
// Count boundary edges for side walls (4 vertices + 6 indices per edge)
|
|
2942
|
+
let boundaryEdges = [];
|
|
2954
2943
|
if (depth !== 0) {
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2944
|
+
const counts = new Map();
|
|
2945
|
+
const oriented = new Map();
|
|
2946
|
+
for (let i = 0; i < triangleIndices.length; i += 3) {
|
|
2947
|
+
const a = triangleIndices[i];
|
|
2948
|
+
const b = triangleIndices[i + 1];
|
|
2949
|
+
const c = triangleIndices[i + 2];
|
|
2950
|
+
const k0 = this.packEdge(a, b);
|
|
2951
|
+
const n0 = (counts.get(k0) ?? 0) + 1;
|
|
2952
|
+
counts.set(k0, n0);
|
|
2953
|
+
if (n0 === 1)
|
|
2954
|
+
oriented.set(k0, [a, b]);
|
|
2955
|
+
const k1 = this.packEdge(b, c);
|
|
2956
|
+
const n1 = (counts.get(k1) ?? 0) + 1;
|
|
2957
|
+
counts.set(k1, n1);
|
|
2958
|
+
if (n1 === 1)
|
|
2959
|
+
oriented.set(k1, [b, c]);
|
|
2960
|
+
const k2 = this.packEdge(c, a);
|
|
2961
|
+
const n2 = (counts.get(k2) ?? 0) + 1;
|
|
2962
|
+
counts.set(k2, n2);
|
|
2963
|
+
if (n2 === 1)
|
|
2964
|
+
oriented.set(k2, [c, a]);
|
|
2965
|
+
}
|
|
2966
|
+
boundaryEdges = [];
|
|
2967
|
+
for (const [key, count] of counts) {
|
|
2968
|
+
if (count !== 1)
|
|
2969
|
+
continue;
|
|
2970
|
+
const edge = oriented.get(key);
|
|
2971
|
+
if (edge)
|
|
2972
|
+
boundaryEdges.push(edge);
|
|
2960
2973
|
}
|
|
2961
2974
|
}
|
|
2962
|
-
const
|
|
2975
|
+
const sideEdgeCount = depth === 0 ? 0 : boundaryEdges.length;
|
|
2976
|
+
const sideVertexCount = depth === 0 ? 0 : sideEdgeCount * 4;
|
|
2963
2977
|
const baseVertexCount = depth === 0 ? numPoints : numPoints * 2;
|
|
2964
2978
|
const vertexCount = baseVertexCount + sideVertexCount;
|
|
2965
2979
|
const vertices = new Float32Array(vertexCount * 3);
|
|
2966
2980
|
const normals = new Float32Array(vertexCount * 3);
|
|
2967
2981
|
const indexCount = depth === 0
|
|
2968
2982
|
? triangleIndices.length
|
|
2969
|
-
: triangleIndices.length * 2 +
|
|
2983
|
+
: triangleIndices.length * 2 + sideEdgeCount * 6;
|
|
2970
2984
|
const indices = new Uint32Array(indexCount);
|
|
2971
2985
|
if (depth === 0) {
|
|
2972
2986
|
// Single-sided flat geometry at z=0
|
|
@@ -3022,60 +3036,62 @@ class Extruder {
|
|
|
3022
3036
|
// Side walls
|
|
3023
3037
|
let nextVertex = numPoints * 2;
|
|
3024
3038
|
let idxPos = triangleIndices.length * 2;
|
|
3025
|
-
for (
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3039
|
+
for (let e = 0; e < boundaryEdges.length; e++) {
|
|
3040
|
+
const [u, v] = boundaryEdges[e];
|
|
3041
|
+
const u2 = u * 2;
|
|
3042
|
+
const v2 = v * 2;
|
|
3043
|
+
const p0x = points[u2];
|
|
3044
|
+
const p0y = points[u2 + 1];
|
|
3045
|
+
const p1x = points[v2];
|
|
3046
|
+
const p1y = points[v2 + 1];
|
|
3047
|
+
// Perpendicular normal for this wall segment
|
|
3048
|
+
// Uses the edge direction from the cap triangulation so winding does not depend on contour direction
|
|
3049
|
+
const ex = p1x - p0x;
|
|
3050
|
+
const ey = p1y - p0y;
|
|
3051
|
+
const lenSq = ex * ex + ey * ey;
|
|
3052
|
+
let nx = 0;
|
|
3053
|
+
let ny = 0;
|
|
3054
|
+
if (lenSq > 0) {
|
|
3055
|
+
const invLen = 1 / Math.sqrt(lenSq);
|
|
3056
|
+
nx = ey * invLen;
|
|
3057
|
+
ny = -ex * invLen;
|
|
3058
|
+
}
|
|
3059
|
+
const baseVertex = nextVertex;
|
|
3060
|
+
const base = baseVertex * 3;
|
|
3061
|
+
// Wall quad: front edge at z=0, back edge at z=depth
|
|
3062
|
+
vertices[base] = p0x;
|
|
3063
|
+
vertices[base + 1] = p0y;
|
|
3064
|
+
vertices[base + 2] = 0;
|
|
3065
|
+
vertices[base + 3] = p1x;
|
|
3066
|
+
vertices[base + 4] = p1y;
|
|
3067
|
+
vertices[base + 5] = 0;
|
|
3068
|
+
vertices[base + 6] = p0x;
|
|
3069
|
+
vertices[base + 7] = p0y;
|
|
3070
|
+
vertices[base + 8] = backZ;
|
|
3071
|
+
vertices[base + 9] = p1x;
|
|
3072
|
+
vertices[base + 10] = p1y;
|
|
3073
|
+
vertices[base + 11] = backZ;
|
|
3074
|
+
// Wall normals point perpendicular to edge
|
|
3075
|
+
normals[base] = nx;
|
|
3076
|
+
normals[base + 1] = ny;
|
|
3077
|
+
normals[base + 2] = 0;
|
|
3078
|
+
normals[base + 3] = nx;
|
|
3079
|
+
normals[base + 4] = ny;
|
|
3080
|
+
normals[base + 5] = 0;
|
|
3081
|
+
normals[base + 6] = nx;
|
|
3082
|
+
normals[base + 7] = ny;
|
|
3083
|
+
normals[base + 8] = 0;
|
|
3084
|
+
normals[base + 9] = nx;
|
|
3085
|
+
normals[base + 10] = ny;
|
|
3086
|
+
normals[base + 11] = 0;
|
|
3087
|
+
// Two triangles per wall segment
|
|
3088
|
+
indices[idxPos++] = baseVertex;
|
|
3089
|
+
indices[idxPos++] = baseVertex + 1;
|
|
3090
|
+
indices[idxPos++] = baseVertex + 2;
|
|
3091
|
+
indices[idxPos++] = baseVertex + 1;
|
|
3092
|
+
indices[idxPos++] = baseVertex + 3;
|
|
3093
|
+
indices[idxPos++] = baseVertex + 2;
|
|
3094
|
+
nextVertex += 4;
|
|
3079
3095
|
}
|
|
3080
3096
|
return { vertices, normals, indices };
|
|
3081
3097
|
}
|
|
@@ -4174,7 +4190,7 @@ class GlyphGeometryBuilder {
|
|
|
4174
4190
|
// Use glyph-level caching when separateGlyphs is set or when cluster contains colored text
|
|
4175
4191
|
const forceSeparate = separateGlyphs || clusterHasColoredGlyphs;
|
|
4176
4192
|
// Iterate over the geometric groups identified by BoundaryClusterer
|
|
4177
|
-
// logical groups (words) split into geometric sub-groups
|
|
4193
|
+
// logical groups (words) split into geometric sub-groups
|
|
4178
4194
|
for (const groupIndices of boundaryGroups) {
|
|
4179
4195
|
const isOverlappingGroup = groupIndices.length > 1;
|
|
4180
4196
|
const shouldCluster = isOverlappingGroup && !forceSeparate;
|