pa_font 0.2.4 → 0.2.5
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/paFont.cjs +448 -247
- package/dist/paFont.cjs.map +1 -1
- package/dist/paFont.js +448 -247
- package/dist/paFont.js.map +1 -1
- package/package.json +1 -1
package/dist/paFont.js
CHANGED
|
@@ -3,52 +3,127 @@ import { load, parse } from "opentype.js";
|
|
|
3
3
|
function toScreenPoint(point, scale) {
|
|
4
4
|
return [point.x * scale, -point.y * scale];
|
|
5
5
|
}
|
|
6
|
-
function cloneRawPoint(point) {
|
|
7
|
-
return {
|
|
8
|
-
x: point.x,
|
|
9
|
-
y: point.y
|
|
10
|
-
};
|
|
11
|
-
}
|
|
12
6
|
function pushUniquePoint(points, point) {
|
|
13
|
-
if (points.length === 0 || !pointsEqual2D(points[points.length - 1], point)) points.push(point);
|
|
14
|
-
}
|
|
15
|
-
function flattenQuadratic(p0, p1, p2, tolerance, out
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
7
|
+
if (points.length === 0 || !pointsEqual2D$1(points[points.length - 1], point)) points.push(point);
|
|
8
|
+
}
|
|
9
|
+
function flattenQuadratic(p0, p1, p2, tolerance, out) {
|
|
10
|
+
let sp = 0;
|
|
11
|
+
const stack = _quadStack;
|
|
12
|
+
stack[sp++] = p0[0];
|
|
13
|
+
stack[sp++] = p0[1];
|
|
14
|
+
stack[sp++] = p1[0];
|
|
15
|
+
stack[sp++] = p1[1];
|
|
16
|
+
stack[sp++] = p2[0];
|
|
17
|
+
stack[sp++] = p2[1];
|
|
18
|
+
stack[sp++] = 0;
|
|
19
|
+
while (sp > 0) {
|
|
20
|
+
const depth = stack[--sp];
|
|
21
|
+
const ax2 = stack[--sp];
|
|
22
|
+
const ay2 = stack[--sp];
|
|
23
|
+
const ax1 = stack[--sp];
|
|
24
|
+
const ay1 = stack[--sp];
|
|
25
|
+
const ax0 = stack[--sp];
|
|
26
|
+
const ay0 = stack[--sp];
|
|
27
|
+
if (depth >= 12 || _quadFlatnessInline(ax0, ay0, ax1, ay1, ax2, ay2) <= tolerance) {
|
|
28
|
+
pushUniquePoint(out, [ax2, ay2]);
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
const m01x = (ax0 + ax1) * .5, m01y = (ay0 + ay1) * .5;
|
|
32
|
+
const m12x = (ax1 + ax2) * .5, m12y = (ay1 + ay2) * .5;
|
|
33
|
+
const mx = (m01x + m12x) * .5, my = (m01y + m12y) * .5;
|
|
34
|
+
const d1 = depth + 1;
|
|
35
|
+
stack[sp++] = mx;
|
|
36
|
+
stack[sp++] = my;
|
|
37
|
+
stack[sp++] = m12x;
|
|
38
|
+
stack[sp++] = m12y;
|
|
39
|
+
stack[sp++] = ax2;
|
|
40
|
+
stack[sp++] = ay2;
|
|
41
|
+
stack[sp++] = d1;
|
|
42
|
+
stack[sp++] = ax0;
|
|
43
|
+
stack[sp++] = ay0;
|
|
44
|
+
stack[sp++] = m01x;
|
|
45
|
+
stack[sp++] = m01y;
|
|
46
|
+
stack[sp++] = mx;
|
|
47
|
+
stack[sp++] = my;
|
|
48
|
+
stack[sp++] = d1;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function flattenCubic(p0, p1, p2, p3, tolerance, out) {
|
|
52
|
+
let sp = 0;
|
|
53
|
+
const stack = _cubicStack;
|
|
54
|
+
stack[sp++] = p0[0];
|
|
55
|
+
stack[sp++] = p0[1];
|
|
56
|
+
stack[sp++] = p1[0];
|
|
57
|
+
stack[sp++] = p1[1];
|
|
58
|
+
stack[sp++] = p2[0];
|
|
59
|
+
stack[sp++] = p2[1];
|
|
60
|
+
stack[sp++] = p3[0];
|
|
61
|
+
stack[sp++] = p3[1];
|
|
62
|
+
stack[sp++] = 0;
|
|
63
|
+
while (sp > 0) {
|
|
64
|
+
const depth = stack[--sp];
|
|
65
|
+
const bx3 = stack[--sp];
|
|
66
|
+
const by3 = stack[--sp];
|
|
67
|
+
const bx2 = stack[--sp];
|
|
68
|
+
const by2 = stack[--sp];
|
|
69
|
+
const bx1 = stack[--sp];
|
|
70
|
+
const by1 = stack[--sp];
|
|
71
|
+
const bx0 = stack[--sp];
|
|
72
|
+
const by0 = stack[--sp];
|
|
73
|
+
if (depth >= 12 || _cubicFlatnessInline(bx0, by0, bx1, by1, bx2, by2, bx3, by3) <= tolerance) {
|
|
74
|
+
pushUniquePoint(out, [bx3, by3]);
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
const m01x = (bx0 + bx1) * .5, m01y = (by0 + by1) * .5;
|
|
78
|
+
const m12x = (bx1 + bx2) * .5, m12y = (by1 + by2) * .5;
|
|
79
|
+
const m23x = (bx2 + bx3) * .5, m23y = (by2 + by3) * .5;
|
|
80
|
+
const m012x = (m01x + m12x) * .5, m012y = (m01y + m12y) * .5;
|
|
81
|
+
const m123x = (m12x + m23x) * .5, m123y = (m12y + m23y) * .5;
|
|
82
|
+
const mx = (m012x + m123x) * .5, my = (m012y + m123y) * .5;
|
|
83
|
+
const d1 = depth + 1;
|
|
84
|
+
stack[sp++] = mx;
|
|
85
|
+
stack[sp++] = my;
|
|
86
|
+
stack[sp++] = m123x;
|
|
87
|
+
stack[sp++] = m123y;
|
|
88
|
+
stack[sp++] = m23x;
|
|
89
|
+
stack[sp++] = m23y;
|
|
90
|
+
stack[sp++] = bx3;
|
|
91
|
+
stack[sp++] = by3;
|
|
92
|
+
stack[sp++] = d1;
|
|
93
|
+
stack[sp++] = bx0;
|
|
94
|
+
stack[sp++] = by0;
|
|
95
|
+
stack[sp++] = m01x;
|
|
96
|
+
stack[sp++] = m01y;
|
|
97
|
+
stack[sp++] = m012x;
|
|
98
|
+
stack[sp++] = m012y;
|
|
99
|
+
stack[sp++] = mx;
|
|
100
|
+
stack[sp++] = my;
|
|
101
|
+
stack[sp++] = d1;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
var _quadStack = new Float64Array(896);
|
|
105
|
+
var _cubicStack = new Float64Array(1152);
|
|
106
|
+
function _quadFlatnessInline(x0, y0, x1, y1, x2, y2) {
|
|
107
|
+
const dx = x2 - x0, dy = y2 - y0;
|
|
108
|
+
const lenSq = dx * dx + dy * dy;
|
|
109
|
+
if (lenSq === 0) {
|
|
110
|
+
const ex = x1 - x0, ey = y1 - y0;
|
|
111
|
+
return Math.sqrt(ex * ex + ey * ey);
|
|
112
|
+
}
|
|
113
|
+
return Math.abs(dx * (y0 - y1) - (x0 - x1) * dy) / Math.sqrt(lenSq);
|
|
114
|
+
}
|
|
115
|
+
function _cubicFlatnessInline(x0, y0, x1, y1, x2, y2, x3, y3) {
|
|
116
|
+
const dx = x3 - x0, dy = y3 - y0;
|
|
117
|
+
const lenSq = dx * dx + dy * dy;
|
|
118
|
+
if (lenSq === 0) {
|
|
119
|
+
const e1x = x1 - x0, e1y = y1 - y0;
|
|
120
|
+
const e2x = x2 - x0, e2y = y2 - y0;
|
|
121
|
+
return Math.max(Math.sqrt(e1x * e1x + e1y * e1y), Math.sqrt(e2x * e2x + e2y * e2y));
|
|
122
|
+
}
|
|
123
|
+
const invLen = 1 / Math.sqrt(lenSq);
|
|
124
|
+
const a1 = Math.abs(dx * (y0 - y1) - (x0 - x1) * dy);
|
|
125
|
+
const a2 = Math.abs(dx * (y0 - y2) - (x0 - x2) * dy);
|
|
126
|
+
return Math.max(a1, a2) * invLen;
|
|
52
127
|
}
|
|
53
128
|
function midpoint(a, b) {
|
|
54
129
|
return [(a[0] + b[0]) * .5, (a[1] + b[1]) * .5];
|
|
@@ -106,28 +181,37 @@ function distance(a, b) {
|
|
|
106
181
|
return Math.sqrt(dx * dx + dy * dy);
|
|
107
182
|
}
|
|
108
183
|
function sampleRing(ring, step, callback) {
|
|
109
|
-
|
|
184
|
+
const len = ring.length;
|
|
185
|
+
for (let index = 0; index < len; index++) {
|
|
110
186
|
const a = ring[index];
|
|
111
|
-
const b = ring[(index + 1) %
|
|
112
|
-
const
|
|
187
|
+
const b = ring[(index + 1) % len];
|
|
188
|
+
const dx = b[0] - a[0], dy = b[1] - a[1];
|
|
189
|
+
const segmentLength = Math.sqrt(dx * dx + dy * dy);
|
|
113
190
|
const divisions = Math.max(1, Math.ceil(segmentLength / step));
|
|
114
|
-
|
|
191
|
+
if (index === 0) {
|
|
192
|
+
callback([a[0], a[1]]);
|
|
193
|
+
for (let offset = 1; offset < divisions; offset++) {
|
|
194
|
+
const t = offset / divisions;
|
|
195
|
+
callback([a[0] + dx * t, a[1] + dy * t]);
|
|
196
|
+
}
|
|
197
|
+
} else for (let offset = 0; offset < divisions; offset++) {
|
|
115
198
|
const t = offset / divisions;
|
|
116
|
-
callback([a[0] +
|
|
117
|
-
}
|
|
199
|
+
callback([a[0] + dx * t, a[1] + dy * t]);
|
|
200
|
+
}
|
|
118
201
|
}
|
|
119
202
|
}
|
|
120
203
|
function ringBounds(ring) {
|
|
121
|
-
let minX =
|
|
122
|
-
let minY =
|
|
123
|
-
let maxX =
|
|
124
|
-
let maxY =
|
|
125
|
-
ring.
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
204
|
+
let minX = ring[0][0];
|
|
205
|
+
let minY = ring[0][1];
|
|
206
|
+
let maxX = minX;
|
|
207
|
+
let maxY = minY;
|
|
208
|
+
for (let i = 1, len = ring.length; i < len; i++) {
|
|
209
|
+
const x = ring[i][0], y = ring[i][1];
|
|
210
|
+
if (x < minX) minX = x;
|
|
211
|
+
else if (x > maxX) maxX = x;
|
|
212
|
+
if (y < minY) minY = y;
|
|
213
|
+
else if (y > maxY) maxY = y;
|
|
214
|
+
}
|
|
131
215
|
return {
|
|
132
216
|
x: minX,
|
|
133
217
|
y: minY,
|
|
@@ -136,7 +220,10 @@ function ringBounds(ring) {
|
|
|
136
220
|
};
|
|
137
221
|
}
|
|
138
222
|
function translateRing(ring, tx, ty) {
|
|
139
|
-
|
|
223
|
+
const len = ring.length;
|
|
224
|
+
const result = new Array(len);
|
|
225
|
+
for (let i = 0; i < len; i++) result[i] = [ring[i][0] + tx, ring[i][1] + ty];
|
|
226
|
+
return result;
|
|
140
227
|
}
|
|
141
228
|
function translateRect(rect, tx, ty) {
|
|
142
229
|
return {
|
|
@@ -146,21 +233,29 @@ function translateRect(rect, tx, ty) {
|
|
|
146
233
|
h: rect.h
|
|
147
234
|
};
|
|
148
235
|
}
|
|
149
|
-
function copyRing(ring) {
|
|
150
|
-
|
|
236
|
+
function copyRing$1(ring) {
|
|
237
|
+
const len = ring.length;
|
|
238
|
+
const result = new Array(len);
|
|
239
|
+
for (let i = 0; i < len; i++) result[i] = [ring[i][0], ring[i][1]];
|
|
240
|
+
return result;
|
|
151
241
|
}
|
|
152
242
|
function combineRects(rects) {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
let
|
|
157
|
-
let
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
243
|
+
const len = rects.length;
|
|
244
|
+
if (len === 0) return null;
|
|
245
|
+
const first = rects[0];
|
|
246
|
+
let minX = first.x;
|
|
247
|
+
let minY = first.y;
|
|
248
|
+
let maxX = first.x + first.w;
|
|
249
|
+
let maxY = first.y + first.h;
|
|
250
|
+
for (let i = 1; i < len; i++) {
|
|
251
|
+
const r = rects[i];
|
|
252
|
+
const rx = r.x, ry = r.y;
|
|
253
|
+
const rx2 = rx + r.w, ry2 = ry + r.h;
|
|
254
|
+
if (rx < minX) minX = rx;
|
|
255
|
+
if (ry < minY) minY = ry;
|
|
256
|
+
if (rx2 > maxX) maxX = rx2;
|
|
257
|
+
if (ry2 > maxY) maxY = ry2;
|
|
258
|
+
}
|
|
164
259
|
return {
|
|
165
260
|
x: minX,
|
|
166
261
|
y: minY,
|
|
@@ -191,7 +286,7 @@ function normalizePositive(value, fallback) {
|
|
|
191
286
|
function samePoint(a, b) {
|
|
192
287
|
return a.x === b.x && a.y === b.y;
|
|
193
288
|
}
|
|
194
|
-
function pointsEqual2D(a, b) {
|
|
289
|
+
function pointsEqual2D$1(a, b) {
|
|
195
290
|
return a[0] === b[0] && a[1] === b[1];
|
|
196
291
|
}
|
|
197
292
|
function pointsAlmostEqual2D(a, b, epsilon = 1e-6) {
|
|
@@ -202,43 +297,64 @@ function ringPerimeter(ring) {
|
|
|
202
297
|
for (let index = 0; index < ring.length; index += 1) total += distance(ring[index], ring[(index + 1) % ring.length]);
|
|
203
298
|
return total;
|
|
204
299
|
}
|
|
205
|
-
function
|
|
300
|
+
function buildCumulativeDistances(ring) {
|
|
301
|
+
const len = ring.length;
|
|
302
|
+
const cumulative = new Float64Array(len + 1);
|
|
206
303
|
let total = 0;
|
|
207
|
-
for (let
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
304
|
+
for (let i = 0; i < len; i++) {
|
|
305
|
+
cumulative[i] = total;
|
|
306
|
+
const next = (i + 1) % len;
|
|
307
|
+
const dx = ring[next][0] - ring[i][0];
|
|
308
|
+
const dy = ring[next][1] - ring[i][1];
|
|
309
|
+
total += Math.sqrt(dx * dx + dy * dy);
|
|
310
|
+
}
|
|
311
|
+
cumulative[len] = total;
|
|
312
|
+
return cumulative;
|
|
313
|
+
}
|
|
314
|
+
function buildOpenCumulativeDistances(path) {
|
|
315
|
+
const len = path.length;
|
|
316
|
+
const cumulative = new Float64Array(len);
|
|
317
|
+
let total = 0;
|
|
318
|
+
cumulative[0] = 0;
|
|
319
|
+
for (let i = 1; i < len; i++) {
|
|
320
|
+
const dx = path[i][0] - path[i - 1][0];
|
|
321
|
+
const dy = path[i][1] - path[i - 1][1];
|
|
322
|
+
total += Math.sqrt(dx * dx + dy * dy);
|
|
323
|
+
cumulative[i] = total;
|
|
324
|
+
}
|
|
325
|
+
return cumulative;
|
|
326
|
+
}
|
|
327
|
+
function binarySearchSegment(cumulative, d) {
|
|
328
|
+
let lo = 0, hi = cumulative.length - 2;
|
|
329
|
+
while (lo < hi) {
|
|
330
|
+
const mid = lo + hi + 1 >>> 1;
|
|
331
|
+
if (cumulative[mid] <= d) lo = mid;
|
|
332
|
+
else hi = mid - 1;
|
|
333
|
+
}
|
|
334
|
+
return lo;
|
|
335
|
+
}
|
|
336
|
+
function pointAtClosedPathDistanceFast(ring, cumulative, distanceAlong) {
|
|
337
|
+
const perimeter = cumulative[ring.length];
|
|
212
338
|
if (perimeter <= 1e-9) return [ring[0][0], ring[0][1]];
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
remaining -= segmentLength;
|
|
224
|
-
}
|
|
225
|
-
return [ring[ring.length - 1][0], ring[ring.length - 1][1]];
|
|
226
|
-
}
|
|
227
|
-
function pointAtOpenPathDistance(path, distanceAlong) {
|
|
339
|
+
const d = mod(distanceAlong, perimeter);
|
|
340
|
+
const idx = binarySearchSegment(cumulative, d);
|
|
341
|
+
const segLen = cumulative[idx + 1] - cumulative[idx];
|
|
342
|
+
if (segLen <= 1e-9) return [ring[idx][0], ring[idx][1]];
|
|
343
|
+
const t = (d - cumulative[idx]) / segLen;
|
|
344
|
+
const start = ring[idx];
|
|
345
|
+
const end = ring[(idx + 1) % ring.length];
|
|
346
|
+
return [start[0] + (end[0] - start[0]) * t, start[1] + (end[1] - start[1]) * t];
|
|
347
|
+
}
|
|
348
|
+
function pointAtOpenPathDistanceFast(path, cumulative, distanceAlong) {
|
|
228
349
|
if (distanceAlong <= 0) return [path[0][0], path[0][1]];
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
return [start[0] + (end[0] - start[0]) * t, start[1] + (end[1] - start[1]) * t];
|
|
238
|
-
}
|
|
239
|
-
remaining -= segmentLength;
|
|
240
|
-
}
|
|
241
|
-
return [path[path.length - 1][0], path[path.length - 1][1]];
|
|
350
|
+
if (distanceAlong >= cumulative[path.length - 1]) return [path[path.length - 1][0], path[path.length - 1][1]];
|
|
351
|
+
const idx = binarySearchSegment(cumulative, distanceAlong);
|
|
352
|
+
const segLen = cumulative[idx + 1] - cumulative[idx];
|
|
353
|
+
if (segLen <= 1e-9) return [path[idx][0], path[idx][1]];
|
|
354
|
+
const t = (distanceAlong - cumulative[idx]) / segLen;
|
|
355
|
+
const start = path[idx];
|
|
356
|
+
const end = path[idx + 1];
|
|
357
|
+
return [start[0] + (end[0] - start[0]) * t, start[1] + (end[1] - start[1]) * t];
|
|
242
358
|
}
|
|
243
359
|
function segmentIntersectsRing(a, b, ring, ignoredEdges, epsilon) {
|
|
244
360
|
for (let index = 0; index < ring.length; index += 1) {
|
|
@@ -320,23 +436,28 @@ function buildGlyphTopology(glyph, fallbackUnitsPerEm) {
|
|
|
320
436
|
current = null;
|
|
321
437
|
return;
|
|
322
438
|
}
|
|
323
|
-
if (!samePoint(current.cursor, current.start)) {
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
|
|
439
|
+
if (!samePoint(current.cursor, current.start)) current.segments.push({
|
|
440
|
+
type: "L",
|
|
441
|
+
from: {
|
|
442
|
+
x: current.cursor.x,
|
|
443
|
+
y: current.cursor.y
|
|
444
|
+
},
|
|
445
|
+
to: {
|
|
446
|
+
x: current.start.x,
|
|
447
|
+
y: current.start.y
|
|
448
|
+
}
|
|
449
|
+
});
|
|
331
450
|
contours.push({
|
|
332
451
|
id: contourId++,
|
|
333
|
-
start:
|
|
452
|
+
start: current.start,
|
|
334
453
|
segments: current.segments
|
|
335
454
|
});
|
|
336
455
|
current = null;
|
|
337
456
|
};
|
|
338
|
-
commands.
|
|
339
|
-
|
|
457
|
+
for (let i = 0, len = commands.length; i < len; i++) {
|
|
458
|
+
const cmd = commands[i];
|
|
459
|
+
const type = cmd.type;
|
|
460
|
+
if (type === "M") {
|
|
340
461
|
finishCurrentContour();
|
|
341
462
|
current = {
|
|
342
463
|
start: {
|
|
@@ -349,47 +470,49 @@ function buildGlyphTopology(glyph, fallbackUnitsPerEm) {
|
|
|
349
470
|
},
|
|
350
471
|
segments: []
|
|
351
472
|
};
|
|
352
|
-
|
|
473
|
+
continue;
|
|
353
474
|
}
|
|
354
|
-
if (!current)
|
|
355
|
-
if (
|
|
475
|
+
if (!current) continue;
|
|
476
|
+
if (type === "L") {
|
|
356
477
|
const to = {
|
|
357
478
|
x: cmd.x,
|
|
358
479
|
y: cmd.y
|
|
359
480
|
};
|
|
360
481
|
current.segments.push({
|
|
361
482
|
type: "L",
|
|
362
|
-
from:
|
|
483
|
+
from: current.cursor,
|
|
363
484
|
to
|
|
364
485
|
});
|
|
365
|
-
current.cursor =
|
|
366
|
-
|
|
486
|
+
current.cursor = to;
|
|
487
|
+
continue;
|
|
367
488
|
}
|
|
368
|
-
if (
|
|
489
|
+
if (type === "Q") {
|
|
490
|
+
const from = current.cursor;
|
|
369
491
|
const to = {
|
|
370
492
|
x: cmd.x,
|
|
371
493
|
y: cmd.y
|
|
372
494
|
};
|
|
373
495
|
current.segments.push({
|
|
374
496
|
type: "Q",
|
|
375
|
-
from
|
|
497
|
+
from,
|
|
376
498
|
c1: {
|
|
377
499
|
x: cmd.x1,
|
|
378
500
|
y: cmd.y1
|
|
379
501
|
},
|
|
380
502
|
to
|
|
381
503
|
});
|
|
382
|
-
current.cursor =
|
|
383
|
-
|
|
504
|
+
current.cursor = to;
|
|
505
|
+
continue;
|
|
384
506
|
}
|
|
385
|
-
if (
|
|
507
|
+
if (type === "C") {
|
|
508
|
+
const from = current.cursor;
|
|
386
509
|
const to = {
|
|
387
510
|
x: cmd.x,
|
|
388
511
|
y: cmd.y
|
|
389
512
|
};
|
|
390
513
|
current.segments.push({
|
|
391
514
|
type: "C",
|
|
392
|
-
from
|
|
515
|
+
from,
|
|
393
516
|
c1: {
|
|
394
517
|
x: cmd.x1,
|
|
395
518
|
y: cmd.y1
|
|
@@ -400,11 +523,11 @@ function buildGlyphTopology(glyph, fallbackUnitsPerEm) {
|
|
|
400
523
|
},
|
|
401
524
|
to
|
|
402
525
|
});
|
|
403
|
-
current.cursor =
|
|
404
|
-
|
|
526
|
+
current.cursor = to;
|
|
527
|
+
continue;
|
|
405
528
|
}
|
|
406
|
-
if (
|
|
407
|
-
}
|
|
529
|
+
if (type === "Z") finishCurrentContour();
|
|
530
|
+
}
|
|
408
531
|
finishCurrentContour();
|
|
409
532
|
return {
|
|
410
533
|
glyphIndex: glyph.index,
|
|
@@ -428,18 +551,25 @@ function flattenGlyphTopology(topology, options) {
|
|
|
428
551
|
}
|
|
429
552
|
function flattenContour(contour, scale, tolerance) {
|
|
430
553
|
const ring = [];
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
554
|
+
let cursor = toScreenPoint(contour.start, scale);
|
|
555
|
+
pushUniquePoint(ring, cursor);
|
|
556
|
+
const segments = contour.segments;
|
|
557
|
+
for (let i = 0, len = segments.length; i < len; i++) {
|
|
558
|
+
const segment = segments[i];
|
|
559
|
+
const type = segment.type;
|
|
560
|
+
if (type === "L") {
|
|
561
|
+
cursor = toScreenPoint(segment.to, scale);
|
|
562
|
+
pushUniquePoint(ring, cursor);
|
|
563
|
+
} else if (type === "Q") {
|
|
564
|
+
const to = toScreenPoint(segment.to, scale);
|
|
565
|
+
flattenQuadratic(cursor, toScreenPoint(segment.c1, scale), to, tolerance, ring);
|
|
566
|
+
cursor = to;
|
|
567
|
+
} else if (type === "C") {
|
|
568
|
+
const to = toScreenPoint(segment.to, scale);
|
|
569
|
+
flattenCubic(cursor, toScreenPoint(segment.c1, scale), toScreenPoint(segment.c2, scale), to, tolerance, ring);
|
|
570
|
+
cursor = to;
|
|
436
571
|
}
|
|
437
|
-
|
|
438
|
-
flattenQuadratic(toScreenPoint(segment.from, scale), toScreenPoint(segment.c1, scale), toScreenPoint(segment.to, scale), tolerance, ring);
|
|
439
|
-
return;
|
|
440
|
-
}
|
|
441
|
-
if (segment.type === "C") flattenCubic(toScreenPoint(segment.from, scale), toScreenPoint(segment.c1, scale), toScreenPoint(segment.c2, scale), toScreenPoint(segment.to, scale), tolerance, ring);
|
|
442
|
-
});
|
|
572
|
+
}
|
|
443
573
|
if (ring.length > 1 && pointsEqual2D(ring[0], ring[ring.length - 1])) ring.pop();
|
|
444
574
|
if (ring.length < 3) return null;
|
|
445
575
|
const bbox = ringBounds(ring);
|
|
@@ -452,37 +582,45 @@ function flattenContour(contour, scale, tolerance) {
|
|
|
452
582
|
};
|
|
453
583
|
}
|
|
454
584
|
function classifyContours(contours) {
|
|
455
|
-
const
|
|
456
|
-
|
|
585
|
+
const len = contours.length;
|
|
586
|
+
const result = new Array(len);
|
|
587
|
+
for (let i = 0; i < len; i++) result[i] = {
|
|
588
|
+
...contours[i],
|
|
457
589
|
parentId: null,
|
|
458
590
|
depth: 0,
|
|
459
591
|
role: "outer"
|
|
460
|
-
}
|
|
461
|
-
result.
|
|
592
|
+
};
|
|
593
|
+
const sortedByArea = result.slice().sort((a, b) => Math.abs(b.area) - Math.abs(a.area));
|
|
594
|
+
for (let ci = 0; ci < len; ci++) {
|
|
595
|
+
const child = result[ci];
|
|
596
|
+
const childAbsArea = Math.abs(child.area);
|
|
462
597
|
let parent = null;
|
|
463
598
|
let parentArea = Number.POSITIVE_INFINITY;
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
if (
|
|
468
|
-
if (
|
|
469
|
-
|
|
470
|
-
if (
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
}
|
|
599
|
+
for (let si = 0; si < len; si++) {
|
|
600
|
+
const candidate = sortedByArea[si];
|
|
601
|
+
const candidateAbsArea = Math.abs(candidate.area);
|
|
602
|
+
if (candidateAbsArea <= childAbsArea) break;
|
|
603
|
+
if (candidate.id === child.id) continue;
|
|
604
|
+
if (candidateAbsArea >= parentArea) continue;
|
|
605
|
+
if (!rectContainsRect(candidate.bbox, child.bbox)) continue;
|
|
606
|
+
if (!pointInRing(child.ring[0], candidate.ring)) continue;
|
|
607
|
+
parent = candidate;
|
|
608
|
+
parentArea = candidateAbsArea;
|
|
609
|
+
}
|
|
475
610
|
if (parent) child.parentId = parent.id;
|
|
476
|
-
}
|
|
611
|
+
}
|
|
477
612
|
const byId = new Map(result.map((contour) => [contour.id, contour]));
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
613
|
+
for (let i = 0; i < len; i++) {
|
|
614
|
+
const contour = result[i];
|
|
615
|
+
let depth = 0;
|
|
616
|
+
let current = contour;
|
|
617
|
+
while (current.parentId != null) {
|
|
618
|
+
depth++;
|
|
619
|
+
current = byId.get(current.parentId);
|
|
620
|
+
}
|
|
621
|
+
contour.depth = depth;
|
|
622
|
+
contour.role = depth % 2 === 0 ? "outer" : "hole";
|
|
623
|
+
}
|
|
486
624
|
return result;
|
|
487
625
|
}
|
|
488
626
|
function buildParts(contours) {
|
|
@@ -497,16 +635,26 @@ function buildParts(contours) {
|
|
|
497
635
|
});
|
|
498
636
|
}
|
|
499
637
|
function translateGlyphGeometry(geometry, tx, ty, glyphPosition) {
|
|
500
|
-
|
|
501
|
-
|
|
638
|
+
const srcParts = geometry.parts;
|
|
639
|
+
const srcContours = geometry.contours;
|
|
640
|
+
const partsLen = srcParts.length;
|
|
641
|
+
const contoursLen = srcContours.length;
|
|
642
|
+
const parts = new Array(partsLen);
|
|
643
|
+
const contours = new Array(contoursLen);
|
|
644
|
+
for (let i = 0; i < partsLen; i++) {
|
|
645
|
+
const part = srcParts[i];
|
|
646
|
+
parts[i] = {
|
|
502
647
|
outer: translateRing(part.outer, tx, ty),
|
|
503
648
|
holes: part.holes.map((hole) => translateRing(hole, tx, ty)),
|
|
504
649
|
bbox: translateRect(part.bbox, tx, ty),
|
|
505
650
|
area: part.area,
|
|
506
651
|
glyphPosition,
|
|
507
|
-
partIndex
|
|
508
|
-
}
|
|
509
|
-
|
|
652
|
+
partIndex: i
|
|
653
|
+
};
|
|
654
|
+
}
|
|
655
|
+
for (let i = 0; i < contoursLen; i++) {
|
|
656
|
+
const contour = srcContours[i];
|
|
657
|
+
contours[i] = {
|
|
510
658
|
id: `${glyphPosition}:${contour.id}`,
|
|
511
659
|
sourceId: contour.id,
|
|
512
660
|
glyphPosition,
|
|
@@ -516,7 +664,11 @@ function translateGlyphGeometry(geometry, tx, ty, glyphPosition) {
|
|
|
516
664
|
area: contour.area,
|
|
517
665
|
bbox: translateRect(contour.bbox, tx, ty),
|
|
518
666
|
ring: translateRing(contour.ring, tx, ty)
|
|
519
|
-
}
|
|
667
|
+
};
|
|
668
|
+
}
|
|
669
|
+
return {
|
|
670
|
+
parts,
|
|
671
|
+
contours,
|
|
520
672
|
bbox: translateRect(geometry.bbox, tx, ty)
|
|
521
673
|
};
|
|
522
674
|
}
|
|
@@ -692,26 +844,31 @@ var PAShape = class {
|
|
|
692
844
|
function createTextShape(layout, opts, fontInstance) {
|
|
693
845
|
const parts = [];
|
|
694
846
|
const contours = [];
|
|
695
|
-
const
|
|
696
|
-
const
|
|
847
|
+
const layoutGlyphs = layout.glyphs;
|
|
848
|
+
const glyphCount = layoutGlyphs.length;
|
|
849
|
+
const glyphs = new Array(glyphCount);
|
|
850
|
+
const sourceLayoutGlyphs = Array.isArray(layoutGlyphs) ? layoutGlyphs.slice() : [];
|
|
697
851
|
const variantOptions = {
|
|
698
852
|
openWidth: normalizePositive(opts.openWidth, 0),
|
|
699
853
|
step: normalizePositive(opts.step, 0)
|
|
700
854
|
};
|
|
701
|
-
|
|
855
|
+
for (let glyphPosition = 0; glyphPosition < glyphCount; glyphPosition++) {
|
|
856
|
+
const item = layoutGlyphs[glyphPosition];
|
|
702
857
|
const translated = translateGlyphGeometry(fontInstance._getGlyphGeometryVariant(item.glyph, opts), item.x, item.y, glyphPosition);
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
858
|
+
const tParts = translated.parts;
|
|
859
|
+
const tContours = translated.contours;
|
|
860
|
+
for (let j = 0; j < tParts.length; j++) parts.push(tParts[j]);
|
|
861
|
+
for (let j = 0; j < tContours.length; j++) contours.push(tContours[j]);
|
|
862
|
+
glyphs[glyphPosition] = {
|
|
706
863
|
position: glyphPosition,
|
|
707
864
|
glyphIndex: item.glyph.index,
|
|
708
865
|
x: item.x,
|
|
709
866
|
y: item.y,
|
|
710
867
|
size: item.size,
|
|
711
868
|
bbox: translated.bbox,
|
|
712
|
-
partCount:
|
|
713
|
-
}
|
|
714
|
-
}
|
|
869
|
+
partCount: tParts.length
|
|
870
|
+
};
|
|
871
|
+
}
|
|
715
872
|
const bbox = combineRects(parts.map((part) => part.bbox)) ?? emptyRect();
|
|
716
873
|
return new PAShape({
|
|
717
874
|
text: layout.text,
|
|
@@ -805,8 +962,8 @@ function createDerivedShape(shape, parts) {
|
|
|
805
962
|
}
|
|
806
963
|
function createRegionCollection(shape) {
|
|
807
964
|
return shape.parts.map((part) => ({
|
|
808
|
-
outer: copyRing(part.outer),
|
|
809
|
-
holes: part.holes.map((hole) => copyRing(hole)),
|
|
965
|
+
outer: copyRing$1(part.outer),
|
|
966
|
+
holes: part.holes.map((hole) => copyRing$1(hole)),
|
|
810
967
|
bbox: { ...part.bbox }
|
|
811
968
|
}));
|
|
812
969
|
}
|
|
@@ -816,8 +973,8 @@ function copyParts(parts) {
|
|
|
816
973
|
function copyPart(part) {
|
|
817
974
|
return {
|
|
818
975
|
...part,
|
|
819
|
-
outer: copyRing(part.outer),
|
|
820
|
-
holes: part.holes.map((hole) => copyRing(hole)),
|
|
976
|
+
outer: copyRing$1(part.outer),
|
|
977
|
+
holes: part.holes.map((hole) => copyRing$1(hole)),
|
|
821
978
|
anchors: part.anchors ? part.anchors.map((point) => [point[0], point[1]]) : void 0,
|
|
822
979
|
bbox: { ...part.bbox }
|
|
823
980
|
};
|
|
@@ -826,7 +983,7 @@ function copyContours(contours) {
|
|
|
826
983
|
return contours.map((contour) => ({
|
|
827
984
|
...contour,
|
|
828
985
|
bbox: { ...contour.bbox },
|
|
829
|
-
ring: copyRing(contour.ring)
|
|
986
|
+
ring: copyRing$1(contour.ring)
|
|
830
987
|
}));
|
|
831
988
|
}
|
|
832
989
|
function groupPartsByGlyphPosition(parts) {
|
|
@@ -942,76 +1099,108 @@ function resolveGeometryEpsilon(width) {
|
|
|
942
1099
|
}
|
|
943
1100
|
function openPartWithSlit(part, width, epsilon) {
|
|
944
1101
|
if (!part.holes || part.holes.length === 0) return copyPart(part);
|
|
945
|
-
let outer = copyRing(part.outer);
|
|
946
|
-
const holes = part.holes.map((hole) => copyRing(hole));
|
|
1102
|
+
let outer = copyRing$1(part.outer);
|
|
1103
|
+
const holes = part.holes.map((hole) => copyRing$1(hole));
|
|
947
1104
|
const anchors = [];
|
|
1105
|
+
const blockers = [];
|
|
948
1106
|
while (holes.length > 0) {
|
|
949
1107
|
let selectedHoleIndex = -1;
|
|
950
1108
|
let selectedBridge = null;
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
1109
|
+
let selectedDist = Number.POSITIVE_INFINITY;
|
|
1110
|
+
for (let hi = 0; hi < holes.length; hi++) {
|
|
1111
|
+
blockers.length = 0;
|
|
1112
|
+
for (let bi = 0; bi < holes.length; bi++) if (bi !== hi) blockers.push(holes[bi]);
|
|
1113
|
+
const candidate = findBestHoleBridge(outer, holes[hi], blockers, epsilon);
|
|
1114
|
+
if (candidate && candidate.distance < selectedDist) {
|
|
955
1115
|
selectedBridge = candidate;
|
|
956
|
-
selectedHoleIndex =
|
|
1116
|
+
selectedHoleIndex = hi;
|
|
1117
|
+
selectedDist = candidate.distance;
|
|
957
1118
|
}
|
|
958
|
-
}
|
|
1119
|
+
}
|
|
959
1120
|
if (!selectedBridge || selectedHoleIndex === -1) break;
|
|
960
1121
|
const [hole] = holes.splice(selectedHoleIndex, 1);
|
|
961
1122
|
const merged = mergeHoleIntoOuter(outer, hole, selectedBridge, width, epsilon);
|
|
962
1123
|
outer = merged.ring;
|
|
963
|
-
|
|
1124
|
+
const mergedAnchors = merged.anchors;
|
|
1125
|
+
for (let i = 0; i < mergedAnchors.length; i++) anchors.push(mergedAnchors[i]);
|
|
964
1126
|
}
|
|
1127
|
+
let holeArea = 0;
|
|
1128
|
+
for (let i = 0; i < holes.length; i++) holeArea += Math.abs(signedArea(holes[i]));
|
|
965
1129
|
return {
|
|
966
1130
|
...part,
|
|
967
1131
|
outer,
|
|
968
1132
|
holes,
|
|
969
1133
|
anchors,
|
|
970
1134
|
bbox: ringBounds(outer),
|
|
971
|
-
area: Math.abs(signedArea(outer)) -
|
|
1135
|
+
area: Math.abs(signedArea(outer)) - holeArea
|
|
972
1136
|
};
|
|
973
1137
|
}
|
|
974
1138
|
function resamplePart(part, step) {
|
|
975
1139
|
const outer = resampleRing(part.outer, step, part.anchors ?? []);
|
|
976
|
-
const
|
|
1140
|
+
const srcHoles = part.holes;
|
|
1141
|
+
const holes = new Array(srcHoles.length);
|
|
1142
|
+
let holeArea = 0;
|
|
1143
|
+
for (let i = 0; i < srcHoles.length; i++) {
|
|
1144
|
+
holes[i] = resampleRing(srcHoles[i], step);
|
|
1145
|
+
holeArea += Math.abs(signedArea(holes[i]));
|
|
1146
|
+
}
|
|
977
1147
|
return {
|
|
978
1148
|
...part,
|
|
979
1149
|
outer,
|
|
980
1150
|
holes,
|
|
981
1151
|
anchors: part.anchors ? part.anchors.map((point) => [point[0], point[1]]) : void 0,
|
|
982
1152
|
bbox: ringBounds(outer),
|
|
983
|
-
area: Math.abs(signedArea(outer)) -
|
|
1153
|
+
area: Math.abs(signedArea(outer)) - holeArea
|
|
984
1154
|
};
|
|
985
1155
|
}
|
|
986
1156
|
function findBestHoleBridge(outer, hole, blockers, epsilon) {
|
|
987
1157
|
let best = null;
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
1158
|
+
let bestDist = Number.POSITIVE_INFINITY;
|
|
1159
|
+
const outerLen = outer.length;
|
|
1160
|
+
const holeLen = hole.length;
|
|
1161
|
+
for (let oi = 0; oi < outerLen; oi++) {
|
|
1162
|
+
const op = outer[oi];
|
|
1163
|
+
for (let hi = 0; hi < holeLen; hi++) {
|
|
1164
|
+
const hp = hole[hi];
|
|
1165
|
+
const dx = op[0] - hp[0], dy = op[1] - hp[1];
|
|
1166
|
+
const dist = Math.sqrt(dx * dx + dy * dy);
|
|
1167
|
+
if (dist >= bestDist) continue;
|
|
1168
|
+
if (!isVisibleBridge(outer, hole, blockers, op, hp, oi, hi, epsilon)) continue;
|
|
1169
|
+
best = {
|
|
1170
|
+
outerIndex: oi,
|
|
1171
|
+
holeIndex: hi,
|
|
1172
|
+
distance: dist
|
|
995
1173
|
};
|
|
996
|
-
|
|
997
|
-
}
|
|
998
|
-
}
|
|
1174
|
+
bestDist = dist;
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
999
1177
|
if (best) return best;
|
|
1000
1178
|
return findNearestBridge(outer, hole);
|
|
1001
1179
|
}
|
|
1002
1180
|
function findNearestBridge(outer, hole) {
|
|
1003
|
-
let
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1181
|
+
let bestOi = 0, bestHi = 0;
|
|
1182
|
+
let bestDistSq = Number.POSITIVE_INFINITY;
|
|
1183
|
+
const outerLen = outer.length;
|
|
1184
|
+
const holeLen = hole.length;
|
|
1185
|
+
for (let oi = 0; oi < outerLen; oi++) {
|
|
1186
|
+
const op = outer[oi];
|
|
1187
|
+
const ox = op[0], oy = op[1];
|
|
1188
|
+
for (let hi = 0; hi < holeLen; hi++) {
|
|
1189
|
+
const hp = hole[hi];
|
|
1190
|
+
const dx = ox - hp[0], dy = oy - hp[1];
|
|
1191
|
+
const distSq = dx * dx + dy * dy;
|
|
1192
|
+
if (distSq < bestDistSq) {
|
|
1193
|
+
bestDistSq = distSq;
|
|
1194
|
+
bestOi = oi;
|
|
1195
|
+
bestHi = hi;
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
return {
|
|
1200
|
+
outerIndex: bestOi,
|
|
1201
|
+
holeIndex: bestHi,
|
|
1202
|
+
distance: Math.sqrt(bestDistSq)
|
|
1203
|
+
};
|
|
1015
1204
|
}
|
|
1016
1205
|
function isVisibleBridge(outer, hole, blockers, outerPoint, holePoint, outerIndex, holeIndex, epsilon) {
|
|
1017
1206
|
if (distance(outerPoint, holePoint) <= epsilon) return false;
|
|
@@ -1138,7 +1327,7 @@ function normalizeCutLocation(name, ring, cut, epsilon) {
|
|
|
1138
1327
|
};
|
|
1139
1328
|
}
|
|
1140
1329
|
function ringPath(ring, startIndex, endIndex) {
|
|
1141
|
-
if (startIndex == null || endIndex == null) return copyRing(ring);
|
|
1330
|
+
if (startIndex == null || endIndex == null) return copyRing$1(ring);
|
|
1142
1331
|
const path = [ring[startIndex]];
|
|
1143
1332
|
let cursor = startIndex;
|
|
1144
1333
|
while (cursor !== endIndex) {
|
|
@@ -1153,7 +1342,7 @@ function appendPath(target, path, epsilon) {
|
|
|
1153
1342
|
});
|
|
1154
1343
|
}
|
|
1155
1344
|
function resampleRing(ring, step, anchors = []) {
|
|
1156
|
-
if (ring.length < 3) return copyRing(ring);
|
|
1345
|
+
if (ring.length < 3) return copyRing$1(ring);
|
|
1157
1346
|
const normalizedAnchors = normalizeAnchorPoints(ring, anchors);
|
|
1158
1347
|
if (normalizedAnchors.length >= 2) return resampleRingWithAnchors(ring, step, normalizedAnchors);
|
|
1159
1348
|
return resampleClosedPath(ring, step);
|
|
@@ -1174,30 +1363,32 @@ function resampleRingWithAnchors(ring, step, anchorIndices) {
|
|
|
1174
1363
|
appendPath(result, resampleOpenPath(ringPath(ring, startIndex, endIndex), step), 1e-6);
|
|
1175
1364
|
}
|
|
1176
1365
|
if (result.length > 1 && pointsAlmostEqual2D(result[0], result[result.length - 1], 1e-6)) result.pop();
|
|
1177
|
-
if (result.length < 3 || result.length >= ring.length) return copyRing(ring);
|
|
1366
|
+
if (result.length < 3 || result.length >= ring.length) return copyRing$1(ring);
|
|
1178
1367
|
return result;
|
|
1179
1368
|
}
|
|
1180
1369
|
function resampleClosedPath(ring, step) {
|
|
1181
|
-
const
|
|
1182
|
-
|
|
1370
|
+
const cumulative = buildCumulativeDistances(ring);
|
|
1371
|
+
const perimeter = cumulative[ring.length];
|
|
1372
|
+
if (perimeter <= 1e-9) return copyRing$1(ring);
|
|
1183
1373
|
const pointCount = Math.max(3, Math.round(perimeter / step));
|
|
1184
|
-
if (pointCount >= ring.length) return copyRing(ring);
|
|
1374
|
+
if (pointCount >= ring.length) return copyRing$1(ring);
|
|
1185
1375
|
const sampled = [];
|
|
1186
|
-
for (let index = 0; index < pointCount; index
|
|
1187
|
-
const point =
|
|
1376
|
+
for (let index = 0; index < pointCount; index++) {
|
|
1377
|
+
const point = pointAtClosedPathDistanceFast(ring, cumulative, perimeter * index / pointCount);
|
|
1188
1378
|
if (sampled.length === 0 || !pointsAlmostEqual2D(sampled[sampled.length - 1], point, 1e-6)) sampled.push(point);
|
|
1189
1379
|
}
|
|
1190
|
-
if (sampled.length < 3 || sampled.length >= ring.length) return copyRing(ring);
|
|
1380
|
+
if (sampled.length < 3 || sampled.length >= ring.length) return copyRing$1(ring);
|
|
1191
1381
|
return sampled;
|
|
1192
1382
|
}
|
|
1193
1383
|
function resampleOpenPath(path, step) {
|
|
1194
1384
|
if (path.length <= 2) return path.map((point) => [point[0], point[1]]);
|
|
1195
|
-
const
|
|
1385
|
+
const cumulative = buildOpenCumulativeDistances(path);
|
|
1386
|
+
const length = cumulative[path.length - 1];
|
|
1196
1387
|
if (length <= step) return [[path[0][0], path[0][1]], [path[path.length - 1][0], path[path.length - 1][1]]];
|
|
1197
1388
|
const divisionCount = Math.max(1, Math.round(length / step));
|
|
1198
1389
|
const sampled = [];
|
|
1199
|
-
for (let index = 0; index <= divisionCount; index
|
|
1200
|
-
const point =
|
|
1390
|
+
for (let index = 0; index <= divisionCount; index++) {
|
|
1391
|
+
const point = pointAtOpenPathDistanceFast(path, cumulative, length * index / divisionCount);
|
|
1201
1392
|
if (sampled.length === 0 || !pointsAlmostEqual2D(sampled[sampled.length - 1], point, 1e-6)) sampled.push(point);
|
|
1202
1393
|
}
|
|
1203
1394
|
return sampled;
|
|
@@ -3569,9 +3760,25 @@ function truncateLineToWidth(line, maxWidth, measureWidth, suffix) {
|
|
|
3569
3760
|
width: 0,
|
|
3570
3761
|
hardBreak: false
|
|
3571
3762
|
};
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3763
|
+
const trimmed = trimTrailingWhitespace(line.text);
|
|
3764
|
+
if (measureWidth(`${trimmed}${suffixText}`) <= maxWidth + JUSTIFY_EPSILON) {
|
|
3765
|
+
const text = `${trimmed}${suffixText}`;
|
|
3766
|
+
return {
|
|
3767
|
+
...line,
|
|
3768
|
+
text,
|
|
3769
|
+
width: measureWidth(text),
|
|
3770
|
+
hardBreak: false
|
|
3771
|
+
};
|
|
3772
|
+
}
|
|
3773
|
+
const segmenter = getGraphemeSegmenter();
|
|
3774
|
+
const graphemes = Array.from(segmenter.segment(trimmed), (seg) => seg.segment);
|
|
3775
|
+
let lo = 0, hi = graphemes.length;
|
|
3776
|
+
while (lo < hi) {
|
|
3777
|
+
const mid = lo + hi + 1 >>> 1;
|
|
3778
|
+
if (measureWidth(graphemes.slice(0, mid).join("") + suffixText) <= maxWidth + JUSTIFY_EPSILON) lo = mid;
|
|
3779
|
+
else hi = mid - 1;
|
|
3780
|
+
}
|
|
3781
|
+
const text = (lo > 0 ? graphemes.slice(0, lo).join("") : "") + suffixText;
|
|
3575
3782
|
return {
|
|
3576
3783
|
...line,
|
|
3577
3784
|
text,
|
|
@@ -4108,12 +4315,6 @@ function getGraphemeSegmenter() {
|
|
|
4108
4315
|
function trimTrailingWhitespace(value) {
|
|
4109
4316
|
return value.replace(/\s+$/u, "");
|
|
4110
4317
|
}
|
|
4111
|
-
function trimLastGrapheme(value) {
|
|
4112
|
-
const segmenter = getGraphemeSegmenter();
|
|
4113
|
-
const graphemes = Array.from(segmenter.segment(value), (segment) => segment.segment);
|
|
4114
|
-
graphemes.pop();
|
|
4115
|
-
return graphemes.join("");
|
|
4116
|
-
}
|
|
4117
4318
|
function splitPreservingWhitespace(value) {
|
|
4118
4319
|
return value.split(/(\s+)/u).filter((token) => token.length > 0);
|
|
4119
4320
|
}
|