@vectojs/core 0.2.7 → 0.2.9
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/{chunk-GTQRCY6W.js → chunk-BPMNCGU7.js} +87 -2
- package/dist/{chunk-SSOP3F5F.mjs → chunk-FNXOS7IA.mjs} +161 -15
- package/dist/{chunk-YVN3PJRC.mjs → chunk-PY4KGL5Y.mjs} +19 -7
- package/dist/{chunk-DWMQYHNC.js → chunk-U4UYRERW.js} +27 -15
- package/dist/{chunk-HVJHXIKW.mjs → chunk-WT2XZHJQ.mjs} +87 -2
- package/dist/{chunk-BWLNEJJW.js → chunk-XLZDOFSB.js} +175 -29
- package/dist/components/TextEntity.d.ts +11 -0
- package/dist/index.js +148 -115
- package/dist/index.mjs +55 -22
- package/dist/layout/LayoutEngine.d.ts +24 -0
- package/dist/layout.js +2 -2
- package/dist/layout.mjs +1 -1
- package/dist/renderer.js +2 -2
- package/dist/renderer.mjs +1 -1
- package/dist/text.js +2 -2
- package/dist/text.mjs +1 -1
- package/dist/tree/Entity.d.ts +13 -3
- package/dist/tree/Scene.d.ts +10 -2
- package/package.json +1 -1
|
@@ -701,6 +701,7 @@ var SVGRenderer = (_class2 = class {
|
|
|
701
701
|
}, _class2);
|
|
702
702
|
|
|
703
703
|
// src/renderer/colorParse.ts
|
|
704
|
+
var CACHE_MAX = 1e3;
|
|
704
705
|
var cache = /* @__PURE__ */ new Map();
|
|
705
706
|
var fallbackCtx;
|
|
706
707
|
function fromHex(hex) {
|
|
@@ -744,10 +745,17 @@ function fromCanvas(css) {
|
|
|
744
745
|
}
|
|
745
746
|
function parseColorToRGBA(css) {
|
|
746
747
|
const hit = cache.get(css);
|
|
747
|
-
if (hit)
|
|
748
|
+
if (hit) {
|
|
749
|
+
cache.delete(css);
|
|
750
|
+
cache.set(css, hit);
|
|
751
|
+
return hit;
|
|
752
|
+
}
|
|
748
753
|
const trimmed = css.trim();
|
|
749
754
|
const rgba = _nullishCoalesce(_nullishCoalesce(_nullishCoalesce((trimmed[0] === "#" ? fromHex(trimmed) : null), () => ( fromRgbFunc(trimmed))), () => ( fromCanvas(trimmed))), () => ( [0, 0, 0, 1]));
|
|
750
755
|
cache.set(css, rgba);
|
|
756
|
+
if (cache.size > CACHE_MAX) {
|
|
757
|
+
cache.delete(cache.keys().next().value);
|
|
758
|
+
}
|
|
751
759
|
return rgba;
|
|
752
760
|
}
|
|
753
761
|
|
|
@@ -825,6 +833,19 @@ void main() {
|
|
|
825
833
|
vec4 t = texture(u_tex, v_uv);
|
|
826
834
|
outColor = vec4(t.rgb * v_tint.rgb, t.a * v_tint.a);
|
|
827
835
|
}`;
|
|
836
|
+
var CIRCLE_QUAD_FRAG = `#version 300 es
|
|
837
|
+
precision mediump float;
|
|
838
|
+
in vec2 v_uv;
|
|
839
|
+
in vec4 v_tint;
|
|
840
|
+
out vec4 outColor;
|
|
841
|
+
void main() {
|
|
842
|
+
vec2 c = v_uv - 0.5;
|
|
843
|
+
float d = length(c);
|
|
844
|
+
float aa = fwidth(d);
|
|
845
|
+
float alpha = 1.0 - smoothstep(0.5 - aa, 0.5, d);
|
|
846
|
+
if (alpha <= 0.0) discard;
|
|
847
|
+
outColor = vec4(v_tint.rgb, v_tint.a * alpha);
|
|
848
|
+
}`;
|
|
828
849
|
var MSDF_FRAG = `#version 300 es
|
|
829
850
|
precision mediump float;
|
|
830
851
|
uniform sampler2D u_tex;
|
|
@@ -891,7 +912,9 @@ function createWebGLPointRenderer(canvas) {
|
|
|
891
912
|
const rectProgram = link(gl, RECT_VERT, RECT_FRAG);
|
|
892
913
|
const spriteProgram = link(gl, SPRITE_VERT, SPRITE_FRAG);
|
|
893
914
|
const msdfProgram = link(gl, SPRITE_VERT, MSDF_FRAG);
|
|
894
|
-
|
|
915
|
+
const circleQuadProgram = link(gl, SPRITE_VERT, CIRCLE_QUAD_FRAG);
|
|
916
|
+
if (!pointProgram || !rectProgram || !spriteProgram || !msdfProgram || !circleQuadProgram)
|
|
917
|
+
return null;
|
|
895
918
|
gl.enable(gl.BLEND);
|
|
896
919
|
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
|
897
920
|
const pAPos = gl.getAttribLocation(pointProgram, "a_pos");
|
|
@@ -951,6 +974,20 @@ function createWebGLPointRenderer(canvas) {
|
|
|
951
974
|
gl.vertexAttribPointer(gAUv, 2, gl.FLOAT, false, SPRITE_VERT_STRIDE, 8);
|
|
952
975
|
gl.enableVertexAttribArray(gATint);
|
|
953
976
|
gl.vertexAttribPointer(gATint, 4, gl.FLOAT, false, SPRITE_VERT_STRIDE, 16);
|
|
977
|
+
const cAPos = gl.getAttribLocation(circleQuadProgram, "a_pos");
|
|
978
|
+
const cAUv = gl.getAttribLocation(circleQuadProgram, "a_uv");
|
|
979
|
+
const cATint = gl.getAttribLocation(circleQuadProgram, "a_tint");
|
|
980
|
+
const cURes = gl.getUniformLocation(circleQuadProgram, "u_resolution");
|
|
981
|
+
const circleQuadBuffer = gl.createBuffer();
|
|
982
|
+
const circleQuadVAO = gl.createVertexArray();
|
|
983
|
+
gl.bindVertexArray(circleQuadVAO);
|
|
984
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, circleQuadBuffer);
|
|
985
|
+
gl.enableVertexAttribArray(cAPos);
|
|
986
|
+
gl.vertexAttribPointer(cAPos, 2, gl.FLOAT, false, SPRITE_VERT_STRIDE, 0);
|
|
987
|
+
gl.enableVertexAttribArray(cAUv);
|
|
988
|
+
gl.vertexAttribPointer(cAUv, 2, gl.FLOAT, false, SPRITE_VERT_STRIDE, 8);
|
|
989
|
+
gl.enableVertexAttribArray(cATint);
|
|
990
|
+
gl.vertexAttribPointer(cATint, 4, gl.FLOAT, false, SPRITE_VERT_STRIDE, 16);
|
|
954
991
|
gl.bindVertexArray(null);
|
|
955
992
|
let texture = null;
|
|
956
993
|
let textureSource = null;
|
|
@@ -965,10 +1002,16 @@ function createWebGLPointRenderer(canvas) {
|
|
|
965
1002
|
let spriteCount = 0;
|
|
966
1003
|
let glyphData = new Float32Array(FLOATS_PER_SPRITE_VERT * VERTS_PER_SPRITE * 1024);
|
|
967
1004
|
let glyphCount = 0;
|
|
1005
|
+
let circleQuadData = new Float32Array(
|
|
1006
|
+
FLOATS_PER_SPRITE_VERT * VERTS_PER_SPRITE * 64
|
|
1007
|
+
);
|
|
1008
|
+
let circleQuadCount = 0;
|
|
968
1009
|
let logicalW = 0;
|
|
969
1010
|
let logicalH = 0;
|
|
970
1011
|
let dpr = 1;
|
|
971
1012
|
let destroyed = false;
|
|
1013
|
+
const pointSizeRange = typeof gl.getParameter === "function" ? gl.getParameter(gl.ALIASED_POINT_SIZE_RANGE) : null;
|
|
1014
|
+
const maxPointSize = pointSizeRange ? pointSizeRange[1] : Infinity;
|
|
972
1015
|
const drawGlyphs = () => {
|
|
973
1016
|
if (glyphCount === 0 || !msdfTexture) return;
|
|
974
1017
|
const floats = glyphCount * VERTS_PER_SPRITE * FLOATS_PER_SPRITE_VERT;
|
|
@@ -1001,6 +1044,7 @@ function createWebGLPointRenderer(canvas) {
|
|
|
1001
1044
|
rectCount = 0;
|
|
1002
1045
|
spriteCount = 0;
|
|
1003
1046
|
glyphCount = 0;
|
|
1047
|
+
circleQuadCount = 0;
|
|
1004
1048
|
gl.clearColor(0, 0, 0, 0);
|
|
1005
1049
|
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
1006
1050
|
},
|
|
@@ -1108,6 +1152,35 @@ function createWebGLPointRenderer(canvas) {
|
|
|
1108
1152
|
glyphCount++;
|
|
1109
1153
|
},
|
|
1110
1154
|
addCircle(x, y, radius, color, alpha = 1) {
|
|
1155
|
+
const needsQuad = logicalW > 0 && (x < radius || y < radius || x > logicalW - radius || y > logicalH - radius) || radius * 2 * dpr > maxPointSize;
|
|
1156
|
+
if (needsQuad) {
|
|
1157
|
+
const stride = FLOATS_PER_SPRITE_VERT * VERTS_PER_SPRITE;
|
|
1158
|
+
circleQuadData = grow(circleQuadData, (circleQuadCount + 1) * stride);
|
|
1159
|
+
const [qr, qg, qb, qa] = parseColorToRGBA(color);
|
|
1160
|
+
const al = qa * alpha;
|
|
1161
|
+
const quad = [
|
|
1162
|
+
[x - radius, y - radius, 0, 0],
|
|
1163
|
+
[x + radius, y - radius, 1, 0],
|
|
1164
|
+
[x + radius, y + radius, 1, 1],
|
|
1165
|
+
[x - radius, y + radius, 0, 1]
|
|
1166
|
+
];
|
|
1167
|
+
const order = [0, 1, 2, 0, 2, 3];
|
|
1168
|
+
let o2 = circleQuadCount * stride;
|
|
1169
|
+
for (const i of order) {
|
|
1170
|
+
const [vx, vy, vu, vv] = quad[i];
|
|
1171
|
+
circleQuadData[o2] = vx;
|
|
1172
|
+
circleQuadData[o2 + 1] = vy;
|
|
1173
|
+
circleQuadData[o2 + 2] = vu;
|
|
1174
|
+
circleQuadData[o2 + 3] = vv;
|
|
1175
|
+
circleQuadData[o2 + 4] = qr;
|
|
1176
|
+
circleQuadData[o2 + 5] = qg;
|
|
1177
|
+
circleQuadData[o2 + 6] = qb;
|
|
1178
|
+
circleQuadData[o2 + 7] = al;
|
|
1179
|
+
o2 += FLOATS_PER_SPRITE_VERT;
|
|
1180
|
+
}
|
|
1181
|
+
circleQuadCount++;
|
|
1182
|
+
return;
|
|
1183
|
+
}
|
|
1111
1184
|
pointData = grow(pointData, (pointCount + 1) * FLOATS_PER_POINT);
|
|
1112
1185
|
const [r, g, b, a] = parseColorToRGBA(color);
|
|
1113
1186
|
const o = pointCount * FLOATS_PER_POINT;
|
|
@@ -1171,6 +1244,15 @@ function createWebGLPointRenderer(canvas) {
|
|
|
1171
1244
|
gl.uniform1f(pUDpr, dpr);
|
|
1172
1245
|
gl.drawArrays(gl.POINTS, 0, pointCount);
|
|
1173
1246
|
}
|
|
1247
|
+
if (circleQuadCount > 0) {
|
|
1248
|
+
const floats = circleQuadCount * VERTS_PER_SPRITE * FLOATS_PER_SPRITE_VERT;
|
|
1249
|
+
gl.useProgram(circleQuadProgram);
|
|
1250
|
+
gl.bindVertexArray(circleQuadVAO);
|
|
1251
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, circleQuadBuffer);
|
|
1252
|
+
gl.bufferData(gl.ARRAY_BUFFER, circleQuadData.subarray(0, floats), gl.DYNAMIC_DRAW);
|
|
1253
|
+
gl.uniform2f(cURes, logicalW, logicalH);
|
|
1254
|
+
gl.drawArrays(gl.TRIANGLES, 0, circleQuadCount * VERTS_PER_SPRITE);
|
|
1255
|
+
}
|
|
1174
1256
|
if (spriteCount > 0 && texture) {
|
|
1175
1257
|
const floats = spriteCount * VERTS_PER_SPRITE * FLOATS_PER_SPRITE_VERT;
|
|
1176
1258
|
gl.useProgram(spriteProgram);
|
|
@@ -1193,14 +1275,17 @@ function createWebGLPointRenderer(canvas) {
|
|
|
1193
1275
|
gl.deleteBuffer(rectBuffer);
|
|
1194
1276
|
gl.deleteBuffer(spriteBuffer);
|
|
1195
1277
|
gl.deleteBuffer(glyphBuffer);
|
|
1278
|
+
gl.deleteBuffer(circleQuadBuffer);
|
|
1196
1279
|
gl.deleteVertexArray(pointVAO);
|
|
1197
1280
|
gl.deleteVertexArray(rectVAO);
|
|
1198
1281
|
gl.deleteVertexArray(spriteVAO);
|
|
1199
1282
|
gl.deleteVertexArray(glyphVAO);
|
|
1283
|
+
gl.deleteVertexArray(circleQuadVAO);
|
|
1200
1284
|
gl.deleteProgram(pointProgram);
|
|
1201
1285
|
gl.deleteProgram(rectProgram);
|
|
1202
1286
|
gl.deleteProgram(spriteProgram);
|
|
1203
1287
|
gl.deleteProgram(msdfProgram);
|
|
1288
|
+
gl.deleteProgram(circleQuadProgram);
|
|
1204
1289
|
if (texture) gl.deleteTexture(texture);
|
|
1205
1290
|
if (msdfTexture) gl.deleteTexture(msdfTexture);
|
|
1206
1291
|
}
|
|
@@ -32,6 +32,13 @@ function computeLineSegments(top, bottom, maxWidth, exclusions) {
|
|
|
32
32
|
}
|
|
33
33
|
var LayoutEngine = class {
|
|
34
34
|
maxWidth;
|
|
35
|
+
/**
|
|
36
|
+
* Horizontal alignment. `'justify'` stretches inter-word spaces (or, for
|
|
37
|
+
* space-less CJK lines, inter-character gaps) so wrapped lines end flush at
|
|
38
|
+
* `maxWidth`; the last line of each paragraph stays ragged. Only applies to
|
|
39
|
+
* the object layout path without exclusion shapes.
|
|
40
|
+
*/
|
|
41
|
+
textAlign = "left";
|
|
35
42
|
maxHeight;
|
|
36
43
|
preserveLeadingSpaces = false;
|
|
37
44
|
wordSegmenter;
|
|
@@ -50,6 +57,22 @@ var LayoutEngine = class {
|
|
|
50
57
|
richParagraphCache = /* @__PURE__ */ new Map();
|
|
51
58
|
lastAtlas = null;
|
|
52
59
|
measurer;
|
|
60
|
+
_hyphenate = null;
|
|
61
|
+
/**
|
|
62
|
+
* Optional hyphenator: given a word, return its break parts (e.g.
|
|
63
|
+
* `['hyphen', 'ation']`). Used at wrap time when a word doesn't fit; a
|
|
64
|
+
* visible '-' is drawn at the chosen break. Soft hyphens (U+00AD) in the
|
|
65
|
+
* source work without any hyphenator. Setting this clears the prepared
|
|
66
|
+
* caches (break opportunities are baked in during prepare()).
|
|
67
|
+
*/
|
|
68
|
+
get hyphenate() {
|
|
69
|
+
return this._hyphenate;
|
|
70
|
+
}
|
|
71
|
+
set hyphenate(fn) {
|
|
72
|
+
this._hyphenate = fn;
|
|
73
|
+
this.paragraphCache.clear();
|
|
74
|
+
this.richParagraphCache.clear();
|
|
75
|
+
}
|
|
53
76
|
constructor(maxWidth, maxHeight, measurer) {
|
|
54
77
|
this.maxWidth = maxWidth;
|
|
55
78
|
this.maxHeight = maxHeight;
|
|
@@ -157,7 +180,13 @@ var LayoutEngine = class {
|
|
|
157
180
|
const word = segment.segment;
|
|
158
181
|
const glyphs = [];
|
|
159
182
|
let width = 0;
|
|
183
|
+
let breakPoints;
|
|
160
184
|
for (const char of this.getGraphemes(word)) {
|
|
185
|
+
if (char === "\xAD") {
|
|
186
|
+
(breakPoints ??= []).push(glyphs.length);
|
|
187
|
+
shapedCharIdx += char.length;
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
161
190
|
const visualStart = shapedCharIdx;
|
|
162
191
|
const visualEnd = shapedCharIdx + char.length;
|
|
163
192
|
const rawStart = indexMap[visualStart];
|
|
@@ -182,11 +211,23 @@ var LayoutEngine = class {
|
|
|
182
211
|
width += w;
|
|
183
212
|
shapedCharIdx += char.length;
|
|
184
213
|
}
|
|
214
|
+
if (!breakPoints && this._hyphenate && segment.isWordLike && glyphs.length > 3) {
|
|
215
|
+
const parts = this._hyphenate(word);
|
|
216
|
+
if (parts.length > 1) {
|
|
217
|
+
breakPoints = [];
|
|
218
|
+
let count = 0;
|
|
219
|
+
for (let pi = 0; pi < parts.length - 1; pi++) {
|
|
220
|
+
for (const _g of this.getGraphemes(parts[pi])) count++;
|
|
221
|
+
breakPoints.push(count);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
185
225
|
words.push({
|
|
186
226
|
glyphs,
|
|
187
227
|
width,
|
|
188
228
|
isWordLike: segment.isWordLike,
|
|
189
|
-
isWhitespace: word.trim().length === 0
|
|
229
|
+
isWhitespace: word.trim().length === 0,
|
|
230
|
+
breakPoints
|
|
190
231
|
});
|
|
191
232
|
}
|
|
192
233
|
const prepared = {
|
|
@@ -200,7 +241,12 @@ var LayoutEngine = class {
|
|
|
200
241
|
paragraphs.push(prepared);
|
|
201
242
|
offset += paragraph.length + 1;
|
|
202
243
|
}
|
|
203
|
-
return {
|
|
244
|
+
return {
|
|
245
|
+
paragraphs,
|
|
246
|
+
fontSize,
|
|
247
|
+
fallbackToCanvas: fallbackToCanvas || void 0,
|
|
248
|
+
hyphenWidth: this.glyphWidth(this.glyphKeyFor("-", fontAtlas), fontAtlas, fontSize)
|
|
249
|
+
};
|
|
204
250
|
}
|
|
205
251
|
/**
|
|
206
252
|
* **Cold pass for rich text.** Like {@link prepare}, but takes an array of
|
|
@@ -274,7 +320,13 @@ var LayoutEngine = class {
|
|
|
274
320
|
const word = segment.segment;
|
|
275
321
|
const glyphs = [];
|
|
276
322
|
let width = 0;
|
|
323
|
+
let breakPoints;
|
|
277
324
|
for (const char of this.getGraphemes(word)) {
|
|
325
|
+
if (char === "\xAD") {
|
|
326
|
+
(breakPoints ??= []).push(glyphs.length);
|
|
327
|
+
shapedCharIdx += char.length;
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
278
330
|
const visualStart = shapedCharIdx;
|
|
279
331
|
const visualEnd = shapedCharIdx + char.length;
|
|
280
332
|
const rawStart = indexMap[visualStart];
|
|
@@ -302,11 +354,23 @@ var LayoutEngine = class {
|
|
|
302
354
|
width += w;
|
|
303
355
|
shapedCharIdx += char.length;
|
|
304
356
|
}
|
|
357
|
+
if (!breakPoints && this._hyphenate && segment.isWordLike && glyphs.length > 3) {
|
|
358
|
+
const parts = this._hyphenate(word);
|
|
359
|
+
if (parts.length > 1) {
|
|
360
|
+
breakPoints = [];
|
|
361
|
+
let count = 0;
|
|
362
|
+
for (let pi = 0; pi < parts.length - 1; pi++) {
|
|
363
|
+
for (const _g of this.getGraphemes(parts[pi])) count++;
|
|
364
|
+
breakPoints.push(count);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
305
368
|
words.push({
|
|
306
369
|
glyphs,
|
|
307
370
|
width,
|
|
308
371
|
isWordLike: segment.isWordLike,
|
|
309
|
-
isWhitespace: word.trim().length === 0
|
|
372
|
+
isWhitespace: word.trim().length === 0,
|
|
373
|
+
breakPoints
|
|
310
374
|
});
|
|
311
375
|
}
|
|
312
376
|
const prepared = {
|
|
@@ -350,7 +414,7 @@ var LayoutEngine = class {
|
|
|
350
414
|
let si = 0;
|
|
351
415
|
let currentLineNodes = [];
|
|
352
416
|
let paragraphBaseLevel = 0;
|
|
353
|
-
const commitLine = () => {
|
|
417
|
+
const commitLine = (justifyTo) => {
|
|
354
418
|
if (currentLineNodes.length === 0) return;
|
|
355
419
|
const runs = [];
|
|
356
420
|
let currentRun = [];
|
|
@@ -375,6 +439,37 @@ var LayoutEngine = class {
|
|
|
375
439
|
node.isRTL = node.level % 2 === 1;
|
|
376
440
|
x += node.width;
|
|
377
441
|
}
|
|
442
|
+
if (justifyTo !== void 0 && runs.length === 1) {
|
|
443
|
+
let lastContent = run.length - 1;
|
|
444
|
+
while (lastContent >= 0 && run[lastContent].char.trim() === "") lastContent--;
|
|
445
|
+
if (lastContent > 0) {
|
|
446
|
+
const contentEnd = run[lastContent].x + run[lastContent].width;
|
|
447
|
+
const slack = justifyTo - contentEnd;
|
|
448
|
+
if (slack > 0 && slack <= (justifyTo - runStartX) * 0.5) {
|
|
449
|
+
const spaceIdx = [];
|
|
450
|
+
for (let k = 1; k < lastContent; k++) {
|
|
451
|
+
if (run[k].char.trim() === "") spaceIdx.push(k);
|
|
452
|
+
}
|
|
453
|
+
if (spaceIdx.length > 0) {
|
|
454
|
+
const extra = slack / spaceIdx.length;
|
|
455
|
+
let shift = 0;
|
|
456
|
+
let nextSpace = 0;
|
|
457
|
+
for (let k = 0; k <= lastContent; k++) {
|
|
458
|
+
run[k].x += shift;
|
|
459
|
+
if (nextSpace < spaceIdx.length && k === spaceIdx[nextSpace]) {
|
|
460
|
+
run[k].width += extra;
|
|
461
|
+
shift += extra;
|
|
462
|
+
nextSpace++;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
} else {
|
|
466
|
+
const extra = slack / lastContent;
|
|
467
|
+
for (let k = 1; k <= lastContent; k++) run[k].x += extra * k;
|
|
468
|
+
}
|
|
469
|
+
if (justifyTo > maxLineWidth) maxLineWidth = justifyTo;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
}
|
|
378
473
|
for (const node of run) {
|
|
379
474
|
layoutNodes.push(node);
|
|
380
475
|
}
|
|
@@ -394,6 +489,8 @@ var LayoutEngine = class {
|
|
|
394
489
|
}
|
|
395
490
|
return false;
|
|
396
491
|
};
|
|
492
|
+
const justifyTarget = this.textAlign === "justify" && !hasEx ? this.maxWidth : void 0;
|
|
493
|
+
const hyphenWidth = prepared.hyphenWidth ?? fontSize * 0.3;
|
|
397
494
|
for (const paragraph of prepared.paragraphs) {
|
|
398
495
|
if (paragraph.isEmpty) {
|
|
399
496
|
commitLine();
|
|
@@ -411,16 +508,65 @@ var LayoutEngine = class {
|
|
|
411
508
|
}
|
|
412
509
|
const lineHeight = pMax * 1.5;
|
|
413
510
|
if (!startLine(lineHeight)) break;
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
511
|
+
const wordQueue = paragraph.words.slice();
|
|
512
|
+
for (let qi = 0; qi < wordQueue.length; qi++) {
|
|
513
|
+
const word = wordQueue[qi];
|
|
514
|
+
if (currentX + word.width > segs[si].x1) {
|
|
515
|
+
if (!hasEx && word.breakPoints && word.breakPoints.length > 0) {
|
|
516
|
+
const avail = segs[si].x1 - currentX;
|
|
517
|
+
let chosen = -1;
|
|
518
|
+
let prefixWidth = 0;
|
|
519
|
+
let acc = 0;
|
|
520
|
+
let bpIdx = 0;
|
|
521
|
+
for (let g = 0; g < word.glyphs.length && bpIdx < word.breakPoints.length; g++) {
|
|
522
|
+
acc += word.glyphs[g].width;
|
|
523
|
+
if (g + 1 === word.breakPoints[bpIdx]) {
|
|
524
|
+
if (acc + hyphenWidth <= avail) {
|
|
525
|
+
chosen = word.breakPoints[bpIdx];
|
|
526
|
+
prefixWidth = acc;
|
|
527
|
+
}
|
|
528
|
+
bpIdx++;
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
if (chosen > 0) {
|
|
532
|
+
const anchorGlyph = word.glyphs[chosen - 1];
|
|
533
|
+
const prefix = {
|
|
534
|
+
glyphs: [
|
|
535
|
+
...word.glyphs.slice(0, chosen),
|
|
536
|
+
{
|
|
537
|
+
char: "-",
|
|
538
|
+
width: hyphenWidth,
|
|
539
|
+
level: anchorGlyph.level,
|
|
540
|
+
sourceIndex: anchorGlyph.sourceIndex,
|
|
541
|
+
sourceLength: 0
|
|
542
|
+
}
|
|
543
|
+
],
|
|
544
|
+
width: prefixWidth + hyphenWidth,
|
|
545
|
+
isWordLike: true,
|
|
546
|
+
isWhitespace: false
|
|
547
|
+
};
|
|
548
|
+
const rest = {
|
|
549
|
+
glyphs: word.glyphs.slice(chosen),
|
|
550
|
+
width: word.width - prefixWidth,
|
|
551
|
+
isWordLike: true,
|
|
552
|
+
isWhitespace: false,
|
|
553
|
+
breakPoints: word.breakPoints.filter((bp) => bp > chosen).map((bp) => bp - chosen)
|
|
554
|
+
};
|
|
555
|
+
wordQueue.splice(qi, 1, prefix, rest);
|
|
556
|
+
qi--;
|
|
557
|
+
continue;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
if (currentX > segs[si].x0) {
|
|
561
|
+
if (word.isWordLike === false && word.isWhitespace) continue;
|
|
562
|
+
if (si < segs.length - 1) {
|
|
563
|
+
si++;
|
|
564
|
+
currentX = segs[si].x0;
|
|
565
|
+
} else {
|
|
566
|
+
commitLine(justifyTarget);
|
|
567
|
+
currentY += lineHeight;
|
|
568
|
+
if (!startLine(lineHeight)) break;
|
|
569
|
+
}
|
|
424
570
|
}
|
|
425
571
|
}
|
|
426
572
|
for (const glyph of word.glyphs) {
|
|
@@ -433,7 +579,7 @@ var LayoutEngine = class {
|
|
|
433
579
|
si++;
|
|
434
580
|
currentX = segs[si].x0;
|
|
435
581
|
} else {
|
|
436
|
-
commitLine();
|
|
582
|
+
commitLine(justifyTarget);
|
|
437
583
|
currentY += lineHeight;
|
|
438
584
|
if (!startLine(lineHeight)) break;
|
|
439
585
|
}
|
|
@@ -360,10 +360,21 @@ var Entity = class {
|
|
|
360
360
|
/**
|
|
361
361
|
* Append a child entity to this node's children array.
|
|
362
362
|
*
|
|
363
|
+
* If `child` already has a parent (including `this`), it's detached from it
|
|
364
|
+
* first — otherwise re-adding an already-added child duplicates it in
|
|
365
|
+
* `children[]` (one `remove()` call only strips the first occurrence,
|
|
366
|
+
* leaving a stale entry that keeps rendering/updating despite
|
|
367
|
+
* `child.parent` reporting `null`), and re-parenting to a different entity
|
|
368
|
+
* without an explicit `remove()` first leaves the old parent holding a
|
|
369
|
+
* stale reference whose own `.parent` disagrees with where it now lives.
|
|
370
|
+
* `child.parent` is only ever `null` or `this`'s ultimate owner, so this
|
|
371
|
+
* check is O(1) for the overwhelming common case (a brand-new entity).
|
|
372
|
+
*
|
|
363
373
|
* @param child - The entity to add as a child.
|
|
364
374
|
* @returns `this` for method chaining.
|
|
365
375
|
*/
|
|
366
376
|
add(child) {
|
|
377
|
+
if (child.parent) child.parent.remove(child);
|
|
367
378
|
child.parent = this;
|
|
368
379
|
this.children.push(child);
|
|
369
380
|
const s = this.scene;
|
|
@@ -434,6 +445,7 @@ var Entity = class {
|
|
|
434
445
|
startTime: -1,
|
|
435
446
|
startProps: {}
|
|
436
447
|
});
|
|
448
|
+
this.scene?.markDirty();
|
|
437
449
|
return this;
|
|
438
450
|
}
|
|
439
451
|
/** Write a driver-computed value to a backing field without re-triggering the setter. */
|
|
@@ -715,9 +727,9 @@ var Entity = class {
|
|
|
715
727
|
* Return the exact accumulated Canvas `T * S * R` transform for this entity.
|
|
716
728
|
*/
|
|
717
729
|
getWorldTransform() {
|
|
718
|
-
const path =
|
|
730
|
+
const path = [this];
|
|
719
731
|
let ancestor = this.parent;
|
|
720
|
-
while (ancestor
|
|
732
|
+
while (ancestor) {
|
|
721
733
|
path.push(ancestor);
|
|
722
734
|
ancestor = ancestor.parent;
|
|
723
735
|
}
|
|
@@ -801,8 +813,8 @@ var Entity = class {
|
|
|
801
813
|
}
|
|
802
814
|
/**
|
|
803
815
|
* Accumulated world scale factors: this entity's own `scaleX`/`scaleY` times
|
|
804
|
-
* those of every ancestor
|
|
805
|
-
*
|
|
816
|
+
* those of every ancestor. Useful for mapping a world-space point back into
|
|
817
|
+
* local space for hit-testing.
|
|
806
818
|
*
|
|
807
819
|
* @returns The world scale `{ x, y }`.
|
|
808
820
|
*/
|
|
@@ -810,7 +822,7 @@ var Entity = class {
|
|
|
810
822
|
let sx = this.scaleX;
|
|
811
823
|
let sy = this.scaleY;
|
|
812
824
|
let curr = this.parent;
|
|
813
|
-
while (curr
|
|
825
|
+
while (curr) {
|
|
814
826
|
sx *= curr.scaleX;
|
|
815
827
|
sy *= curr.scaleY;
|
|
816
828
|
curr = curr.parent;
|
|
@@ -819,14 +831,14 @@ var Entity = class {
|
|
|
819
831
|
}
|
|
820
832
|
/**
|
|
821
833
|
* Accumulated world rotation: this entity's own `rotation` plus
|
|
822
|
-
* that of every ancestor
|
|
834
|
+
* that of every ancestor.
|
|
823
835
|
*
|
|
824
836
|
* @returns The accumulated world rotation in radians.
|
|
825
837
|
*/
|
|
826
838
|
getWorldRotation() {
|
|
827
839
|
let rot = this.rotation;
|
|
828
840
|
let curr = this.parent;
|
|
829
|
-
while (curr
|
|
841
|
+
while (curr) {
|
|
830
842
|
rot += curr.rotation;
|
|
831
843
|
curr = curr.parent;
|
|
832
844
|
}
|
|
@@ -360,10 +360,21 @@ var Entity = (_class4 = class {
|
|
|
360
360
|
/**
|
|
361
361
|
* Append a child entity to this node's children array.
|
|
362
362
|
*
|
|
363
|
+
* If `child` already has a parent (including `this`), it's detached from it
|
|
364
|
+
* first — otherwise re-adding an already-added child duplicates it in
|
|
365
|
+
* `children[]` (one `remove()` call only strips the first occurrence,
|
|
366
|
+
* leaving a stale entry that keeps rendering/updating despite
|
|
367
|
+
* `child.parent` reporting `null`), and re-parenting to a different entity
|
|
368
|
+
* without an explicit `remove()` first leaves the old parent holding a
|
|
369
|
+
* stale reference whose own `.parent` disagrees with where it now lives.
|
|
370
|
+
* `child.parent` is only ever `null` or `this`'s ultimate owner, so this
|
|
371
|
+
* check is O(1) for the overwhelming common case (a brand-new entity).
|
|
372
|
+
*
|
|
363
373
|
* @param child - The entity to add as a child.
|
|
364
374
|
* @returns `this` for method chaining.
|
|
365
375
|
*/
|
|
366
376
|
add(child) {
|
|
377
|
+
if (child.parent) child.parent.remove(child);
|
|
367
378
|
child.parent = this;
|
|
368
379
|
this.children.push(child);
|
|
369
380
|
const s = this.scene;
|
|
@@ -434,6 +445,7 @@ var Entity = (_class4 = class {
|
|
|
434
445
|
startTime: -1,
|
|
435
446
|
startProps: {}
|
|
436
447
|
});
|
|
448
|
+
_optionalChain([this, 'access', _40 => _40.scene, 'optionalAccess', _41 => _41.markDirty, 'call', _42 => _42()]);
|
|
437
449
|
return this;
|
|
438
450
|
}
|
|
439
451
|
/** Write a driver-computed value to a backing field without re-triggering the setter. */
|
|
@@ -489,10 +501,10 @@ var Entity = (_class4 = class {
|
|
|
489
501
|
const active = driver;
|
|
490
502
|
const onDone = active.onDone;
|
|
491
503
|
active.onDone = void 0;
|
|
492
|
-
_optionalChain([onDone, 'optionalCall',
|
|
504
|
+
_optionalChain([onDone, 'optionalCall', _43 => _43()]);
|
|
493
505
|
}
|
|
494
506
|
_spawnDriver(prop, to, cfg) {
|
|
495
|
-
if (prop !== "opacity" && _optionalChain([this, 'access',
|
|
507
|
+
if (prop !== "opacity" && _optionalChain([this, 'access', _44 => _44.scene, 'optionalAccess', _45 => _45.prefersReducedMotion])) {
|
|
496
508
|
const existing2 = this._drivers.get(prop);
|
|
497
509
|
if (existing2) this._settleDriver(existing2);
|
|
498
510
|
this._drivers.delete(prop);
|
|
@@ -508,11 +520,11 @@ var Entity = (_class4 = class {
|
|
|
508
520
|
const from = this._currentOf(prop);
|
|
509
521
|
const driver = isTweenConfig(cfg) ? new TweenDriver(from, to, cfg) : new SpringDriver(from, to, cfg === "spring" ? {} : cfg);
|
|
510
522
|
this._drivers.set(prop, driver);
|
|
511
|
-
_optionalChain([this, 'access',
|
|
523
|
+
_optionalChain([this, 'access', _46 => _46.scene, 'optionalAccess', _47 => _47.markDirty, 'call', _48 => _48()]);
|
|
512
524
|
}
|
|
513
525
|
/** Assignment path when a declarative transition is configured for `prop`. */
|
|
514
526
|
_animateProp(prop, to) {
|
|
515
|
-
const cfg = _optionalChain([this, 'access',
|
|
527
|
+
const cfg = _optionalChain([this, 'access', _49 => _49._transitions, 'optionalAccess', _50 => _50.get, 'call', _51 => _51(prop)]);
|
|
516
528
|
if (!cfg) {
|
|
517
529
|
this._applyAnimated(prop, to);
|
|
518
530
|
return;
|
|
@@ -562,7 +574,7 @@ var Entity = (_class4 = class {
|
|
|
562
574
|
this._applyAnimated(prop, driver.value);
|
|
563
575
|
}
|
|
564
576
|
}
|
|
565
|
-
_optionalChain([this, 'access',
|
|
577
|
+
_optionalChain([this, 'access', _52 => _52.scene, 'optionalAccess', _53 => _53.markDirty, 'call', _54 => _54()]);
|
|
566
578
|
}
|
|
567
579
|
/**
|
|
568
580
|
* Advance the entity's internal state for one frame.
|
|
@@ -616,7 +628,7 @@ var Entity = (_class4 = class {
|
|
|
616
628
|
* @example entity.on('click', (e) => console.log('clicked', e));
|
|
617
629
|
*/
|
|
618
630
|
on(event, callback, options) {
|
|
619
|
-
const map = _optionalChain([options, 'optionalAccess',
|
|
631
|
+
const map = _optionalChain([options, 'optionalAccess', _55 => _55.capture]) ? this.captureListeners : this.listeners;
|
|
620
632
|
if (!map.has(event)) {
|
|
621
633
|
map.set(event, []);
|
|
622
634
|
}
|
|
@@ -632,7 +644,7 @@ var Entity = (_class4 = class {
|
|
|
632
644
|
* @returns `this` for method chaining.
|
|
633
645
|
*/
|
|
634
646
|
off(event, callback, options) {
|
|
635
|
-
const handlers = (_optionalChain([options, 'optionalAccess',
|
|
647
|
+
const handlers = (_optionalChain([options, 'optionalAccess', _56 => _56.capture]) ? this.captureListeners : this.listeners).get(event);
|
|
636
648
|
if (handlers) {
|
|
637
649
|
const idx = handlers.indexOf(callback);
|
|
638
650
|
if (idx !== -1) handlers.splice(idx, 1);
|
|
@@ -715,9 +727,9 @@ var Entity = (_class4 = class {
|
|
|
715
727
|
* Return the exact accumulated Canvas `T * S * R` transform for this entity.
|
|
716
728
|
*/
|
|
717
729
|
getWorldTransform() {
|
|
718
|
-
const path =
|
|
730
|
+
const path = [this];
|
|
719
731
|
let ancestor = this.parent;
|
|
720
|
-
while (ancestor
|
|
732
|
+
while (ancestor) {
|
|
721
733
|
path.push(ancestor);
|
|
722
734
|
ancestor = ancestor.parent;
|
|
723
735
|
}
|
|
@@ -801,8 +813,8 @@ var Entity = (_class4 = class {
|
|
|
801
813
|
}
|
|
802
814
|
/**
|
|
803
815
|
* Accumulated world scale factors: this entity's own `scaleX`/`scaleY` times
|
|
804
|
-
* those of every ancestor
|
|
805
|
-
*
|
|
816
|
+
* those of every ancestor. Useful for mapping a world-space point back into
|
|
817
|
+
* local space for hit-testing.
|
|
806
818
|
*
|
|
807
819
|
* @returns The world scale `{ x, y }`.
|
|
808
820
|
*/
|
|
@@ -810,7 +822,7 @@ var Entity = (_class4 = class {
|
|
|
810
822
|
let sx = this.scaleX;
|
|
811
823
|
let sy = this.scaleY;
|
|
812
824
|
let curr = this.parent;
|
|
813
|
-
while (curr
|
|
825
|
+
while (curr) {
|
|
814
826
|
sx *= curr.scaleX;
|
|
815
827
|
sy *= curr.scaleY;
|
|
816
828
|
curr = curr.parent;
|
|
@@ -819,14 +831,14 @@ var Entity = (_class4 = class {
|
|
|
819
831
|
}
|
|
820
832
|
/**
|
|
821
833
|
* Accumulated world rotation: this entity's own `rotation` plus
|
|
822
|
-
* that of every ancestor
|
|
834
|
+
* that of every ancestor.
|
|
823
835
|
*
|
|
824
836
|
* @returns The accumulated world rotation in radians.
|
|
825
837
|
*/
|
|
826
838
|
getWorldRotation() {
|
|
827
839
|
let rot = this.rotation;
|
|
828
840
|
let curr = this.parent;
|
|
829
|
-
while (curr
|
|
841
|
+
while (curr) {
|
|
830
842
|
rot += curr.rotation;
|
|
831
843
|
curr = curr.parent;
|
|
832
844
|
}
|
|
@@ -1076,7 +1088,7 @@ var MSDFTextEntity = (_class6 = class extends Entity {
|
|
|
1076
1088
|
if (res.seqId < this.lastRenderedSeqId) return;
|
|
1077
1089
|
this.lastRenderedSeqId = res.seqId;
|
|
1078
1090
|
this.layoutResult = res;
|
|
1079
|
-
_optionalChain([this, 'access',
|
|
1091
|
+
_optionalChain([this, 'access', _57 => _57.scene, 'optionalAccess', _58 => _58.markDirty, 'call', _59 => _59()]);
|
|
1080
1092
|
}
|
|
1081
1093
|
});
|
|
1082
1094
|
}
|