@blorkfield/overlay-core 0.5.0 → 0.5.1
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 +71 -2
- package/dist/index.cjs +234 -29
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +76 -46
- package/dist/index.d.ts +76 -46
- package/dist/index.js +234 -29
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -464,7 +464,7 @@ function createBodyFromVertices(id, x, y, vertices, renderOptions) {
|
|
|
464
464
|
function createBoundariesWithFloorConfig(bounds, floorConfig) {
|
|
465
465
|
const width = bounds.right - bounds.left;
|
|
466
466
|
const height = bounds.bottom - bounds.top;
|
|
467
|
-
const
|
|
467
|
+
const wallOptions = { isStatic: true, render: { visible: false } };
|
|
468
468
|
const walls = [
|
|
469
469
|
// Left wall
|
|
470
470
|
Matter2.Bodies.rectangle(
|
|
@@ -472,7 +472,7 @@ function createBoundariesWithFloorConfig(bounds, floorConfig) {
|
|
|
472
472
|
bounds.top + height / 2,
|
|
473
473
|
BOUNDARY_THICKNESS,
|
|
474
474
|
height,
|
|
475
|
-
{ ...
|
|
475
|
+
{ ...wallOptions, label: "leftWall" }
|
|
476
476
|
),
|
|
477
477
|
// Right wall
|
|
478
478
|
Matter2.Bodies.rectangle(
|
|
@@ -480,23 +480,44 @@ function createBoundariesWithFloorConfig(bounds, floorConfig) {
|
|
|
480
480
|
bounds.top + height / 2,
|
|
481
481
|
BOUNDARY_THICKNESS,
|
|
482
482
|
height,
|
|
483
|
-
{ ...
|
|
483
|
+
{ ...wallOptions, label: "rightWall" }
|
|
484
484
|
)
|
|
485
485
|
];
|
|
486
486
|
const segmentCount = floorConfig?.segments ?? 1;
|
|
487
|
-
const segmentWidth = width / segmentCount;
|
|
488
487
|
const floorSegments = [];
|
|
488
|
+
let segmentWidths;
|
|
489
|
+
if (floorConfig?.segmentWidths && floorConfig.segmentWidths.length === segmentCount) {
|
|
490
|
+
const sum = floorConfig.segmentWidths.reduce((a, b) => a + b, 0);
|
|
491
|
+
segmentWidths = floorConfig.segmentWidths.map((w) => w / sum * width);
|
|
492
|
+
} else {
|
|
493
|
+
const equalWidth = width / segmentCount;
|
|
494
|
+
segmentWidths = Array(segmentCount).fill(equalWidth);
|
|
495
|
+
}
|
|
496
|
+
let currentX = bounds.left;
|
|
489
497
|
for (let i = 0; i < segmentCount; i++) {
|
|
490
|
-
const
|
|
498
|
+
const segmentWidth = segmentWidths[i];
|
|
499
|
+
const thickness = floorConfig?.thickness !== void 0 ? Array.isArray(floorConfig.thickness) ? floorConfig.thickness[i] ?? BOUNDARY_THICKNESS : floorConfig.thickness : BOUNDARY_THICKNESS;
|
|
500
|
+
const color = floorConfig?.color !== void 0 ? Array.isArray(floorConfig.color) ? floorConfig.color[i] : floorConfig.color : void 0;
|
|
501
|
+
const segmentX = currentX + segmentWidth / 2;
|
|
502
|
+
const segmentY = bounds.bottom - thickness / 2;
|
|
503
|
+
const segmentOptions = {
|
|
504
|
+
isStatic: true,
|
|
505
|
+
label: `floor-segment-${i}`,
|
|
506
|
+
render: {
|
|
507
|
+
visible: color !== void 0,
|
|
508
|
+
fillStyle: color ?? "#888888"
|
|
509
|
+
}
|
|
510
|
+
};
|
|
491
511
|
floorSegments.push(
|
|
492
512
|
Matter2.Bodies.rectangle(
|
|
493
513
|
segmentX,
|
|
494
|
-
|
|
514
|
+
segmentY,
|
|
495
515
|
segmentWidth,
|
|
496
|
-
|
|
497
|
-
|
|
516
|
+
thickness,
|
|
517
|
+
segmentOptions
|
|
498
518
|
)
|
|
499
519
|
);
|
|
520
|
+
currentX += segmentWidth;
|
|
500
521
|
}
|
|
501
522
|
return { walls, floorSegments };
|
|
502
523
|
}
|
|
@@ -1188,6 +1209,7 @@ var OverlayScene = class {
|
|
|
1188
1209
|
this.boundaries = [...boundariesResult.walls, ...boundariesResult.floorSegments];
|
|
1189
1210
|
this.floorSegments = boundariesResult.floorSegments;
|
|
1190
1211
|
Matter5.Composite.add(this.engine.world, this.boundaries);
|
|
1212
|
+
this.checkInitialFloorIntegrity();
|
|
1191
1213
|
this.mouse = Matter5.Mouse.create(canvas);
|
|
1192
1214
|
this.mouseConstraint = Matter5.MouseConstraint.create(this.engine, {
|
|
1193
1215
|
mouse: this.mouse,
|
|
@@ -1306,7 +1328,8 @@ var OverlayScene = class {
|
|
|
1306
1328
|
if (onObstacles.has(dyn.id)) continue;
|
|
1307
1329
|
const dynBounds = dyn.body.bounds;
|
|
1308
1330
|
const horizontalOverlap = dynBounds.max.x > segmentBounds.min.x && dynBounds.min.x < segmentBounds.max.x;
|
|
1309
|
-
|
|
1331
|
+
const nearFloor = dynBounds.max.y >= segmentBounds.min.y - 10;
|
|
1332
|
+
if (horizontalOverlap && nearFloor) {
|
|
1310
1333
|
resting.add(dyn.id);
|
|
1311
1334
|
}
|
|
1312
1335
|
}
|
|
@@ -1332,17 +1355,50 @@ var OverlayScene = class {
|
|
|
1332
1355
|
const objectIds = this.floorSegmentPressure.get(i);
|
|
1333
1356
|
const pressure = objectIds ? this.calculateWeightedPressure(objectIds) : 0;
|
|
1334
1357
|
if (pressure >= threshold) {
|
|
1335
|
-
this.collapseFloorSegment(i, pressure
|
|
1358
|
+
this.collapseFloorSegment(i, `pressure ${pressure} >= threshold ${threshold}`);
|
|
1336
1359
|
}
|
|
1337
1360
|
}
|
|
1338
1361
|
}
|
|
1339
1362
|
/** Collapse a single floor segment */
|
|
1340
|
-
collapseFloorSegment(index,
|
|
1363
|
+
collapseFloorSegment(index, reason) {
|
|
1341
1364
|
if (this.collapsedSegments.has(index)) return;
|
|
1342
1365
|
this.collapsedSegments.add(index);
|
|
1343
1366
|
const segment = this.floorSegments[index];
|
|
1344
1367
|
Matter5.Composite.remove(this.engine.world, segment);
|
|
1345
|
-
|
|
1368
|
+
logger.debug("OverlayScene", `Floor segment ${index} collapsed: ${reason}`);
|
|
1369
|
+
this.checkFloorIntegrity();
|
|
1370
|
+
}
|
|
1371
|
+
/** Check if floor integrity requirement is violated and collapse all remaining if so */
|
|
1372
|
+
checkFloorIntegrity() {
|
|
1373
|
+
const minIntegrity = this.config.floorConfig?.minIntegrity;
|
|
1374
|
+
if (minIntegrity === void 0) return;
|
|
1375
|
+
const totalSegments = this.floorSegments.length;
|
|
1376
|
+
const remainingSegments = totalSegments - this.collapsedSegments.size;
|
|
1377
|
+
if (remainingSegments < minIntegrity && remainingSegments > 0) {
|
|
1378
|
+
logger.debug("OverlayScene", `Floor integrity failed: ${remainingSegments} remaining < ${minIntegrity} required. Collapsing all.`);
|
|
1379
|
+
for (let i = 0; i < totalSegments; i++) {
|
|
1380
|
+
if (!this.collapsedSegments.has(i)) {
|
|
1381
|
+
this.collapsedSegments.add(i);
|
|
1382
|
+
const segment = this.floorSegments[i];
|
|
1383
|
+
Matter5.Composite.remove(this.engine.world, segment);
|
|
1384
|
+
logger.debug("OverlayScene", `Floor segment ${i} collapsed: integrity failure cascade`);
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
/** Check floor integrity on initialization (handles minIntegrity > segments) */
|
|
1390
|
+
checkInitialFloorIntegrity() {
|
|
1391
|
+
const minIntegrity = this.config.floorConfig?.minIntegrity;
|
|
1392
|
+
if (minIntegrity === void 0) return;
|
|
1393
|
+
const totalSegments = this.floorSegments.length;
|
|
1394
|
+
if (totalSegments < minIntegrity) {
|
|
1395
|
+
logger.debug("OverlayScene", `Floor integrity impossible: ${totalSegments} segments < ${minIntegrity} required. Collapsing all immediately.`);
|
|
1396
|
+
for (let i = 0; i < totalSegments; i++) {
|
|
1397
|
+
this.collapsedSegments.add(i);
|
|
1398
|
+
const segment = this.floorSegments[i];
|
|
1399
|
+
Matter5.Composite.remove(this.engine.world, segment);
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1346
1402
|
}
|
|
1347
1403
|
/** Log a summary of pressure on all obstacles, grouped by word */
|
|
1348
1404
|
logPressureSummary() {
|
|
@@ -1598,6 +1654,7 @@ var OverlayScene = class {
|
|
|
1598
1654
|
this.collapsedSegments.clear();
|
|
1599
1655
|
this.floorSegmentPressure.clear();
|
|
1600
1656
|
Matter5.Composite.add(this.engine.world, this.boundaries);
|
|
1657
|
+
this.checkInitialFloorIntegrity();
|
|
1601
1658
|
this.render.options.width = width;
|
|
1602
1659
|
this.render.options.height = height;
|
|
1603
1660
|
this.render.canvas.width = width;
|
|
@@ -1614,6 +1671,21 @@ var OverlayScene = class {
|
|
|
1614
1671
|
* Without 'falling' tag, object is static.
|
|
1615
1672
|
*/
|
|
1616
1673
|
spawnObject(config) {
|
|
1674
|
+
if (config.element) {
|
|
1675
|
+
const result = this.addDOMObstacleInternal({
|
|
1676
|
+
element: config.element,
|
|
1677
|
+
x: config.x,
|
|
1678
|
+
y: config.y,
|
|
1679
|
+
width: config.width,
|
|
1680
|
+
height: config.height,
|
|
1681
|
+
tags: config.tags,
|
|
1682
|
+
pressureThreshold: config.pressureThreshold,
|
|
1683
|
+
weight: config.weight,
|
|
1684
|
+
shadow: config.shadow === true ? { opacity: 0.3 } : config.shadow || void 0,
|
|
1685
|
+
clickToFall: config.clickToFall
|
|
1686
|
+
});
|
|
1687
|
+
return result.id;
|
|
1688
|
+
}
|
|
1617
1689
|
const id = crypto.randomUUID();
|
|
1618
1690
|
const tags = config.tags ?? [];
|
|
1619
1691
|
const isStatic = !tags.includes("falling");
|
|
@@ -1633,6 +1705,17 @@ var OverlayScene = class {
|
|
|
1633
1705
|
} else {
|
|
1634
1706
|
body = createObstacle(id, config, isStatic);
|
|
1635
1707
|
}
|
|
1708
|
+
let pressureThreshold;
|
|
1709
|
+
if (config.pressureThreshold) {
|
|
1710
|
+
pressureThreshold = typeof config.pressureThreshold.value === "number" ? config.pressureThreshold.value : config.pressureThreshold.value[0];
|
|
1711
|
+
}
|
|
1712
|
+
let shadow;
|
|
1713
|
+
if (config.shadow === true) {
|
|
1714
|
+
shadow = { opacity: 0.3 };
|
|
1715
|
+
} else if (config.shadow && typeof config.shadow === "object") {
|
|
1716
|
+
shadow = { opacity: config.shadow.opacity ?? 0.3 };
|
|
1717
|
+
}
|
|
1718
|
+
const clicksRemaining = config.clickToFall?.clicks;
|
|
1636
1719
|
const entry = {
|
|
1637
1720
|
id,
|
|
1638
1721
|
body,
|
|
@@ -1640,10 +1723,17 @@ var OverlayScene = class {
|
|
|
1640
1723
|
spawnTime: performance.now(),
|
|
1641
1724
|
ttl: config.ttl,
|
|
1642
1725
|
despawnEffect: config.despawnEffect,
|
|
1643
|
-
weight: config.weight ?? 1
|
|
1726
|
+
weight: config.weight ?? 1,
|
|
1727
|
+
pressureThreshold,
|
|
1728
|
+
shadow,
|
|
1729
|
+
originalPosition: shadow || clicksRemaining !== void 0 ? { x: config.x, y: config.y } : void 0,
|
|
1730
|
+
clicksRemaining
|
|
1644
1731
|
};
|
|
1645
1732
|
this.objects.set(id, entry);
|
|
1646
1733
|
Matter5.Composite.add(this.engine.world, body);
|
|
1734
|
+
if (isStatic && pressureThreshold !== void 0) {
|
|
1735
|
+
this.obstaclePressure.set(id, /* @__PURE__ */ new Set());
|
|
1736
|
+
}
|
|
1647
1737
|
return id;
|
|
1648
1738
|
}
|
|
1649
1739
|
/**
|
|
@@ -1670,6 +1760,17 @@ var OverlayScene = class {
|
|
|
1670
1760
|
} else {
|
|
1671
1761
|
body = await createObstacleAsync(id, config, isStatic);
|
|
1672
1762
|
}
|
|
1763
|
+
let pressureThreshold;
|
|
1764
|
+
if (config.pressureThreshold) {
|
|
1765
|
+
pressureThreshold = typeof config.pressureThreshold.value === "number" ? config.pressureThreshold.value : config.pressureThreshold.value[0];
|
|
1766
|
+
}
|
|
1767
|
+
let shadow;
|
|
1768
|
+
if (config.shadow === true) {
|
|
1769
|
+
shadow = { opacity: 0.3 };
|
|
1770
|
+
} else if (config.shadow && typeof config.shadow === "object") {
|
|
1771
|
+
shadow = { opacity: config.shadow.opacity ?? 0.3 };
|
|
1772
|
+
}
|
|
1773
|
+
const clicksRemaining = config.clickToFall?.clicks;
|
|
1673
1774
|
const entry = {
|
|
1674
1775
|
id,
|
|
1675
1776
|
body,
|
|
@@ -1677,10 +1778,17 @@ var OverlayScene = class {
|
|
|
1677
1778
|
spawnTime: performance.now(),
|
|
1678
1779
|
ttl: config.ttl,
|
|
1679
1780
|
despawnEffect: config.despawnEffect,
|
|
1680
|
-
weight: config.weight ?? 1
|
|
1781
|
+
weight: config.weight ?? 1,
|
|
1782
|
+
pressureThreshold,
|
|
1783
|
+
shadow,
|
|
1784
|
+
originalPosition: shadow || clicksRemaining !== void 0 ? { x: config.x, y: config.y } : void 0,
|
|
1785
|
+
clicksRemaining
|
|
1681
1786
|
};
|
|
1682
1787
|
this.objects.set(id, entry);
|
|
1683
1788
|
Matter5.Composite.add(this.engine.world, body);
|
|
1789
|
+
if (isStatic && pressureThreshold !== void 0) {
|
|
1790
|
+
this.obstaclePressure.set(id, /* @__PURE__ */ new Set());
|
|
1791
|
+
}
|
|
1684
1792
|
return id;
|
|
1685
1793
|
}
|
|
1686
1794
|
/**
|
|
@@ -1939,17 +2047,12 @@ var OverlayScene = class {
|
|
|
1939
2047
|
areFontsInitialized() {
|
|
1940
2048
|
return this.fontsInitialized;
|
|
1941
2049
|
}
|
|
1942
|
-
// ==================== DOM OBSTACLE METHODS ====================
|
|
2050
|
+
// ==================== DOM OBSTACLE METHODS (INTERNAL) ====================
|
|
1943
2051
|
/**
|
|
1944
|
-
* Attach a DOM element to physics.
|
|
1945
|
-
*
|
|
1946
|
-
*
|
|
1947
|
-
* When the element collapses (becomes dynamic), its CSS transform will be
|
|
1948
|
-
* updated each frame to match the physics body position and rotation.
|
|
1949
|
-
*
|
|
1950
|
-
* Shadow creates a cloned DOM element that stays at the original position.
|
|
2052
|
+
* Internal: Attach a DOM element to physics.
|
|
2053
|
+
* Called by spawnObject when element is provided.
|
|
1951
2054
|
*/
|
|
1952
|
-
|
|
2055
|
+
addDOMObstacleInternal(config) {
|
|
1953
2056
|
const { element, x, y } = config;
|
|
1954
2057
|
const width = config.width ?? element.offsetWidth;
|
|
1955
2058
|
const height = config.height ?? element.offsetHeight;
|
|
@@ -1986,6 +2089,7 @@ var OverlayScene = class {
|
|
|
1986
2089
|
};
|
|
1987
2090
|
this.objects.set(id, entry);
|
|
1988
2091
|
Matter5.Composite.add(this.engine.world, body);
|
|
2092
|
+
this.updateDOMElementTransform(entry);
|
|
1989
2093
|
if (isStatic && pressureThreshold !== void 0) {
|
|
1990
2094
|
this.obstaclePressure.set(id, /* @__PURE__ */ new Set());
|
|
1991
2095
|
}
|
|
@@ -2092,11 +2196,61 @@ var OverlayScene = class {
|
|
|
2092
2196
|
maxDimension = Math.max(maxDimension, dims.width, dims.height);
|
|
2093
2197
|
}
|
|
2094
2198
|
if (maxDimension === 0) maxDimension = 100;
|
|
2199
|
+
const calculateLineWidth = (line) => {
|
|
2200
|
+
let width = 0;
|
|
2201
|
+
for (const char of line) {
|
|
2202
|
+
if (char === " ") {
|
|
2203
|
+
width += 20;
|
|
2204
|
+
} else if (/^[A-Za-z0-9]$/.test(char)) {
|
|
2205
|
+
const dims = charDimensions.get(char);
|
|
2206
|
+
if (dims) {
|
|
2207
|
+
const scale = letterSize / Math.max(dims.width, dims.height);
|
|
2208
|
+
const scaledWidth = dims.width * scale;
|
|
2209
|
+
const extraSpacing = config.letterSpacing !== void 0 ? config.letterSpacing - scaledWidth : 0;
|
|
2210
|
+
width += scaledWidth + Math.max(0, extraSpacing);
|
|
2211
|
+
}
|
|
2212
|
+
}
|
|
2213
|
+
}
|
|
2214
|
+
return width;
|
|
2215
|
+
};
|
|
2216
|
+
const lineWidths = lines.map((line) => calculateLineWidth(line));
|
|
2217
|
+
const align = config.align ?? "left";
|
|
2218
|
+
const maxLineWidth = Math.max(...lineWidths, 0);
|
|
2219
|
+
let boundsLeft;
|
|
2220
|
+
let boundsRight;
|
|
2221
|
+
switch (align) {
|
|
2222
|
+
case "center":
|
|
2223
|
+
boundsLeft = config.x - maxLineWidth / 2;
|
|
2224
|
+
boundsRight = config.x + maxLineWidth / 2;
|
|
2225
|
+
break;
|
|
2226
|
+
case "right":
|
|
2227
|
+
boundsLeft = config.x - maxLineWidth;
|
|
2228
|
+
boundsRight = config.x;
|
|
2229
|
+
break;
|
|
2230
|
+
default:
|
|
2231
|
+
boundsLeft = config.x;
|
|
2232
|
+
boundsRight = config.x + maxLineWidth;
|
|
2233
|
+
}
|
|
2234
|
+
const boundsTop = config.y - letterSize / 2;
|
|
2235
|
+
const totalHeight = lines.length > 0 ? (lines.length - 1) * lineHeight + letterSize : 0;
|
|
2236
|
+
const boundsBottom = boundsTop + totalHeight;
|
|
2095
2237
|
let currentY = config.y;
|
|
2096
2238
|
let globalCharIndex = 0;
|
|
2097
|
-
for (
|
|
2239
|
+
for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
|
|
2240
|
+
const line = lines[lineIndex];
|
|
2098
2241
|
const chars = line.split("");
|
|
2099
|
-
|
|
2242
|
+
const lineWidth = lineWidths[lineIndex];
|
|
2243
|
+
let currentX;
|
|
2244
|
+
switch (align) {
|
|
2245
|
+
case "center":
|
|
2246
|
+
currentX = config.x - lineWidth / 2;
|
|
2247
|
+
break;
|
|
2248
|
+
case "right":
|
|
2249
|
+
currentX = config.x - lineWidth;
|
|
2250
|
+
break;
|
|
2251
|
+
default:
|
|
2252
|
+
currentX = config.x;
|
|
2253
|
+
}
|
|
2100
2254
|
if (inWord) {
|
|
2101
2255
|
currentWordIndex++;
|
|
2102
2256
|
inWord = false;
|
|
@@ -2212,12 +2366,21 @@ var OverlayScene = class {
|
|
|
2212
2366
|
letterColor,
|
|
2213
2367
|
lineCount: lines.length
|
|
2214
2368
|
});
|
|
2369
|
+
const bounds = {
|
|
2370
|
+
left: boundsLeft,
|
|
2371
|
+
right: boundsRight,
|
|
2372
|
+
top: boundsTop,
|
|
2373
|
+
bottom: boundsBottom,
|
|
2374
|
+
width: boundsRight - boundsLeft,
|
|
2375
|
+
height: boundsBottom - boundsTop
|
|
2376
|
+
};
|
|
2215
2377
|
return {
|
|
2216
2378
|
letterIds,
|
|
2217
2379
|
stringTag,
|
|
2218
2380
|
wordTags,
|
|
2219
2381
|
letterMap,
|
|
2220
|
-
letterDebugInfo: debugInfo
|
|
2382
|
+
letterDebugInfo: debugInfo,
|
|
2383
|
+
bounds
|
|
2221
2384
|
};
|
|
2222
2385
|
}
|
|
2223
2386
|
/**
|
|
@@ -2290,10 +2453,43 @@ var OverlayScene = class {
|
|
|
2290
2453
|
const fontInfo = this.fonts.find((f) => f.fontUrl === fontUrl);
|
|
2291
2454
|
const fontFamily = fontInfo?.name ?? "sans-serif";
|
|
2292
2455
|
const lines = text.split("\n");
|
|
2456
|
+
const lineWidths = lines.map((line) => measureText(loadedFont, line, fontSize));
|
|
2457
|
+
const align = config.align ?? "left";
|
|
2458
|
+
const maxLineWidth = Math.max(...lineWidths, 0);
|
|
2459
|
+
let boundsLeft;
|
|
2460
|
+
let boundsRight;
|
|
2461
|
+
switch (align) {
|
|
2462
|
+
case "center":
|
|
2463
|
+
boundsLeft = x - maxLineWidth / 2;
|
|
2464
|
+
boundsRight = x + maxLineWidth / 2;
|
|
2465
|
+
break;
|
|
2466
|
+
case "right":
|
|
2467
|
+
boundsLeft = x - maxLineWidth;
|
|
2468
|
+
boundsRight = x;
|
|
2469
|
+
break;
|
|
2470
|
+
default:
|
|
2471
|
+
boundsLeft = x;
|
|
2472
|
+
boundsRight = x + maxLineWidth;
|
|
2473
|
+
}
|
|
2474
|
+
const boundsTop = y - fontSize * 0.8;
|
|
2475
|
+
const totalHeight = lines.length > 0 ? (lines.length - 1) * lineHeight + fontSize : 0;
|
|
2476
|
+
const boundsBottom = boundsTop + totalHeight;
|
|
2293
2477
|
let currentY = y;
|
|
2294
2478
|
let globalCharIndex = 0;
|
|
2295
|
-
for (
|
|
2296
|
-
|
|
2479
|
+
for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
|
|
2480
|
+
const line = lines[lineIndex];
|
|
2481
|
+
const lineWidth = lineWidths[lineIndex];
|
|
2482
|
+
let currentX;
|
|
2483
|
+
switch (align) {
|
|
2484
|
+
case "center":
|
|
2485
|
+
currentX = x - lineWidth / 2;
|
|
2486
|
+
break;
|
|
2487
|
+
case "right":
|
|
2488
|
+
currentX = x - lineWidth;
|
|
2489
|
+
break;
|
|
2490
|
+
default:
|
|
2491
|
+
currentX = x;
|
|
2492
|
+
}
|
|
2297
2493
|
if (inWord) {
|
|
2298
2494
|
currentWordIndex++;
|
|
2299
2495
|
inWord = false;
|
|
@@ -2403,12 +2599,21 @@ var OverlayScene = class {
|
|
|
2403
2599
|
wordTags,
|
|
2404
2600
|
lineCount: lines.length
|
|
2405
2601
|
});
|
|
2602
|
+
const bounds = {
|
|
2603
|
+
left: boundsLeft,
|
|
2604
|
+
right: boundsRight,
|
|
2605
|
+
top: boundsTop,
|
|
2606
|
+
bottom: boundsBottom,
|
|
2607
|
+
width: boundsRight - boundsLeft,
|
|
2608
|
+
height: boundsBottom - boundsTop
|
|
2609
|
+
};
|
|
2406
2610
|
return {
|
|
2407
2611
|
letterIds,
|
|
2408
2612
|
stringTag,
|
|
2409
2613
|
wordTags,
|
|
2410
2614
|
letterMap,
|
|
2411
|
-
letterDebugInfo: []
|
|
2615
|
+
letterDebugInfo: [],
|
|
2616
|
+
bounds
|
|
2412
2617
|
};
|
|
2413
2618
|
}
|
|
2414
2619
|
/**
|