@cardenelabs/cdl 0.30.1 → 0.30.3

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/CHANGELOG.md CHANGED
@@ -5,6 +5,62 @@ CDL (Chainome Diagram Language) の主要変更履歴。
5
5
 
6
6
  ## [Unreleased]
7
7
 
8
+ ## [0.30.3] - 2026-09-05
9
+
10
+ ### Fixed
11
+
12
+ - **線の終わりに潰れた角ができなくなった** (#663)
13
+
14
+ 同じ箱の同じ辺に入る線の終点をずらす工程が、 終点の手前に 28 の直線を作っていた。
15
+ 前後を角丸に挟まれて潰れた角に見え、 線がどこへ向かうのかが読めない。
16
+
17
+ ```text
18
+ 0.29.0 ... Q 588 1723, 602 1723 L 751 1723
19
+ 0.30.2 ... Q 588 1723, 602 1723 L 602 1695 L 751 1695
20
+ ```
21
+
22
+ この工程は最後の区間を助走ぶん戻った所に折れを 2 つ作って、 ずらした終点へ繋ぐ。
23
+ 助走が最後の区間いっぱいに達すると 1 つ目の折れが直前の折れと重なり、
24
+ 2 つ目の折れまでの区間がずらし量そのものになる。
25
+
26
+ 折れを足す代わりに、 **直前の折れごと** ずらすようにした。 ずらす向きと平行な区間まで
27
+ 手前へ辿り、 その区間の終わりだけを動かす。 平行な区間は伸び縮みするだけなので折れが増えない。
28
+
29
+ 戻す条件は 2 つ。 手前の区間が助走の下限を割る形と、 動かした先が箱に当たる形。
30
+ どちらも直す先が移るだけになるので、 従来の形に任せる。
31
+
32
+ `0.30.1` の #655 は同じ形を **起点** 側で直したもので、 終点側が残っていた。
33
+
34
+ 消費側の ER 図で 2 件あった 30 未満の直線が 0 件になった。 折れの数も交差の数も増えていない。
35
+
36
+ ## [0.30.2] - 2026-09-05
37
+
38
+ ### Fixed
39
+
40
+ - **関係の説明が箱や線に重ならなくなった** (#659)
41
+
42
+ 経路を動かす工程の後に、 説明 (札) を箱 ・ 説明どうし ・ 別の線から離す工程を足した。
43
+
44
+ 説明を箱から離す工程は、 経路を動かす 4 つの工程より **前** に走っていた。
45
+ 説明の初めの位置は経路から決まるため、 後の工程が経路を動かすと説明も一緒に動く。
46
+ 動いた後を見る者が居なかった。
47
+
48
+ `0.30.0` で経路を大きく動かす 2 件 (#649 / #651) が入り、 消費側の見本帳で説明が 11 件動いた
49
+ (最大 217)。 動いた先が箱の近くだと、 要求 (箱と説明で 32) を割ったまま残る。
50
+ 実測 = `0.29.0` で 0 件だった違反が `0.30.1` で 6 図 9 件。
51
+
52
+ 直した後は消費側の見本帳 12 群 431 図で 0 件になった。 既存の絵は 1 つも動いていない
53
+ (要求を満たしていない説明だけを動かすため)。
54
+
55
+ **動かすのは説明だけ**。 箱と経路は動かさない = 動かすと直前の工程が確定させた形
56
+ (根元の重なり分け / 起点の直進) を崩す。
57
+
58
+ 判定は視覚検査と同じ関数を引く。 別の式で近似すると、 検査は落ちるのに押し出しは動かない
59
+ 状態に戻る。 曲線は開いた形で測る = 弦のままだと膨らんだ弧が説明を貫いても見えない。
60
+
61
+ 行き先を確かめてから動かす。 一番安く済む向きへ出すだけだと、 上下の箱に挟まれた説明が
62
+ 往復して収まらない。
63
+
8
64
  ## [0.30.1] - 2026-09-05
9
65
 
10
66
  ### Fixed
@@ -1519,7 +1575,9 @@ Initial OSS release.
1519
1575
  使わない = annotated tag を push しても GitHub Release は作られず、 Release を作っていない版で
1520
1576
  行き止まりになる。
1521
1577
 
1522
- [Unreleased]: https://github.com/cardene777/cdl/compare/v0.30.1...HEAD
1578
+ [Unreleased]: https://github.com/cardene777/cdl/compare/v0.30.3...HEAD
1579
+ [0.30.3]: https://github.com/cardene777/cdl/compare/v0.30.2...v0.30.3
1580
+ [0.30.2]: https://github.com/cardene777/cdl/compare/v0.30.1...v0.30.2
1523
1581
  [0.30.1]: https://github.com/cardene777/cdl/compare/v0.30.0...v0.30.1
1524
1582
  [0.30.0]: https://github.com/cardene777/cdl/compare/v0.29.0...v0.30.0
1525
1583
  [0.29.0]: https://github.com/cardene777/cdl/compare/v0.28.0...v0.29.0
package/dist/index.cjs CHANGED
@@ -13357,6 +13357,234 @@ function resolveEdgeLabelOverlapsWithChainAndPropagate(edges, obstacles, minClea
13357
13357
  edgePathShifts: edgePathShiftMap
13358
13358
  };
13359
13359
  }
13360
+ function resolveLabelClearanceAfterPathPasses(edges, obstacles, maxIter = 8) {
13361
+ if (edges.length === 0) return edges;
13362
+ const labeledIds = new Set(
13363
+ edges.filter((e) => e.label?.trim() || e.sub?.trim()).map((e) => e.id)
13364
+ );
13365
+ if (labeledIds.size === 0) return edges;
13366
+ const \u7BB1\u305F\u3061 = obstacles.filter((o) => o.kind === "node" || o.kind === "lane-label");
13367
+ const edgeById = /* @__PURE__ */ new Map();
13368
+ for (const e of edges) edgeById.set(e.id, e);
13369
+ const \u533A\u9593cache = /* @__PURE__ */ new Map();
13370
+ const \u533A\u9593 = (id, d) => {
13371
+ const hit = \u533A\u9593cache.get(id);
13372
+ if (hit) return hit;
13373
+ const segs = flattenPathSegments(extractPathSegments(d));
13374
+ \u533A\u9593cache.set(id, segs);
13375
+ return segs;
13376
+ };
13377
+ const \u5916\u63A5cache = /* @__PURE__ */ new Map();
13378
+ const \u5916\u63A5 = (id, d) => {
13379
+ const hit = \u5916\u63A5cache.get(id);
13380
+ if (hit !== void 0) return hit;
13381
+ const segs = \u533A\u9593(id, d);
13382
+ if (segs.length === 0) {
13383
+ \u5916\u63A5cache.set(id, null);
13384
+ return null;
13385
+ }
13386
+ let x1 = Infinity, y1 = Infinity, x2 = -Infinity, y2 = -Infinity;
13387
+ for (const g of segs) {
13388
+ x1 = Math.min(x1, g.x1, g.x2);
13389
+ y1 = Math.min(y1, g.y1, g.y2);
13390
+ x2 = Math.max(x2, g.x1, g.x2);
13391
+ y2 = Math.max(y2, g.y1, g.y2);
13392
+ }
13393
+ const box = { x: x1, y: y1, w: x2 - x1, h: y2 - y1 };
13394
+ \u5916\u63A5cache.set(id, box);
13395
+ return box;
13396
+ };
13397
+ const \u9060\u3044 = (box, edgeId, d, \u4F59\u88D5) => {
13398
+ const b = \u5916\u63A5(edgeId, d);
13399
+ if (!b) return true;
13400
+ const dx = Math.max(0, Math.max(box.x - (b.x + b.w), b.x - (box.x + box.w)));
13401
+ const dy = Math.max(0, Math.max(box.y - (b.y + b.h), b.y - (box.y + box.h)));
13402
+ return Math.hypot(dx, dy) >= \u4F59\u88D5;
13403
+ };
13404
+ const \u6700\u5927\u79FB\u52D5 = CHAIN_SHIFT_MAX_ACCUMULATED * 2;
13405
+ const shiftMap = /* @__PURE__ */ new Map();
13406
+ const getShift = (id) => shiftMap.get(id) ?? { dx: 0, dy: 0 };
13407
+ const labelBBoxes = () => {
13408
+ const out = [];
13409
+ for (const e of edges) {
13410
+ if (!labeledIds.has(e.id)) continue;
13411
+ const base = computeLabelBBoxWorld(e);
13412
+ const s = getShift(e.id);
13413
+ out.push({ kind: "edge-label", id: e.id, x: base.x + s.dx, y: base.y + s.dy, w: base.w, h: base.h });
13414
+ }
13415
+ return out;
13416
+ };
13417
+ const \u96E2\u308C\u91CF = (a, b) => ({
13418
+ x: Math.max(a.x - (b.x + b.w), b.x - (a.x + a.w)),
13419
+ y: Math.max(a.y - (b.y + b.h), b.y - (a.y + a.h))
13420
+ });
13421
+ const \u52D5\u304B\u3057\u305F\u7BB1 = (box, axis, sign, amount) => ({
13422
+ ...box,
13423
+ x: box.x + (axis === "x" ? sign * amount : 0),
13424
+ y: box.y + (axis === "y" ? sign * amount : 0)
13425
+ });
13426
+ for (let iter = 0; iter < maxIter; iter += 1) {
13427
+ const labels = labelBBoxes();
13428
+ const \u672D\u306E\u8981\u6C42 = requiredNearClearance("edge-label", "edge-label");
13429
+ let \u52D5\u3044\u305F = false;
13430
+ const \u4ECA\u56DE\u52D5\u304B\u3057\u305F = /* @__PURE__ */ new Set();
13431
+ const \u5B89\u5168\u304B = (label, \u884C\u304D\u5148) => {
13432
+ for (const obs of \u7BB1\u305F\u3061) {
13433
+ const \u524D = computeGap(label, obs);
13434
+ const \u5F8C = computeGap(\u884C\u304D\u5148, obs);
13435
+ if (\u5F8C < Math.min(\u524D, requiredNearClearance("edge-label", obs.kind))) return false;
13436
+ }
13437
+ for (const \u4ED6 of labels) {
13438
+ if (\u4ED6.id === label.id) continue;
13439
+ const \u524D = computeGap(label, \u4ED6);
13440
+ const \u5F8C = computeGap(\u884C\u304D\u5148, \u4ED6);
13441
+ if (\u5F8C < Math.min(\u524D, \u672D\u306E\u8981\u6C42)) return false;
13442
+ }
13443
+ const \u81EA\u5206 = edgeById.get(label.id);
13444
+ const \u81EA\u5206\u306E\u533A\u9593 = \u81EA\u5206 ? \u533A\u9593(\u81EA\u5206.id, \u81EA\u5206.d) : [];
13445
+ if (\u81EA\u5206\u306E\u533A\u9593.length > 0) {
13446
+ const \u524D = measurePathLabelGapSegs(\u81EA\u5206\u306E\u533A\u9593, label);
13447
+ const \u5F8C = measurePathLabelGapSegs(\u81EA\u5206\u306E\u533A\u9593, \u884C\u304D\u5148);
13448
+ if (\u5F8C > Math.max(\u524D, DIST_LABEL_PATH_MAX)) return false;
13449
+ }
13450
+ for (const \u76F8\u624B of edges) {
13451
+ if (\u76F8\u624B.id === label.id) continue;
13452
+ if (\u9060\u3044(label, \u76F8\u624B.id, \u76F8\u624B.d, LABEL_TO_PATH_CLEARANCE) && \u9060\u3044(\u884C\u304D\u5148, \u76F8\u624B.id, \u76F8\u624B.d, LABEL_TO_PATH_CLEARANCE)) continue;
13453
+ const segs = segmentsExcludingOwnPath(\u533A\u9593(\u76F8\u624B.id, \u76F8\u624B.d), \u81EA\u5206\u306E\u533A\u9593);
13454
+ if (segs.length === 0) continue;
13455
+ const \u524D = measurePathLabelGapSegs(segs, label);
13456
+ const \u5F8C = measurePathLabelGapSegs(segs, \u884C\u304D\u5148);
13457
+ if (\u5F8C < Math.min(\u524D, LABEL_TO_PATH_CLEARANCE)) return false;
13458
+ }
13459
+ return true;
13460
+ };
13461
+ const \u9003\u304C\u3059 = (label, \u5019\u88DC) => {
13462
+ for (const c of \u5019\u88DC.slice().sort((a, b) => a.amount - b.amount)) {
13463
+ if (c.amount <= 0) continue;
13464
+ const \u884C\u304D\u5148 = \u52D5\u304B\u3057\u305F\u7BB1(label, c.axis, c.sign, c.amount);
13465
+ if (!\u5B89\u5168\u304B(label, \u884C\u304D\u5148)) continue;
13466
+ const cur = getShift(label.id);
13467
+ const dx = c.axis === "x" ? cur.dx + c.sign * c.amount : cur.dx;
13468
+ const dy = c.axis === "y" ? cur.dy + c.sign * c.amount : cur.dy;
13469
+ if (Math.hypot(dx, dy) > \u6700\u5927\u79FB\u52D5) continue;
13470
+ shiftMap.set(label.id, { dx, dy });
13471
+ \u4ECA\u56DE\u52D5\u304B\u3057\u305F.add(label.id);
13472
+ return true;
13473
+ }
13474
+ return false;
13475
+ };
13476
+ const \u77E9\u5F62\u306E\u5019\u88DC = (label, static\u7BB1, required) => {
13477
+ const sep = \u96E2\u308C\u91CF(label, static\u7BB1);
13478
+ const \u5411\u304D = (axis) => {
13479
+ const a = axis === "x" ? label.x + label.w / 2 : label.y + label.h / 2;
13480
+ const b = axis === "x" ? static\u7BB1.x + static\u7BB1.w / 2 : static\u7BB1.y + static\u7BB1.h / 2;
13481
+ return a >= b ? 1 : -1;
13482
+ };
13483
+ return [
13484
+ { axis: "x", sign: \u5411\u304D("x"), amount: required - sep.x + NEAR_MISS_MARGIN },
13485
+ { axis: "y", sign: \u5411\u304D("y"), amount: required - sep.y + NEAR_MISS_MARGIN }
13486
+ ];
13487
+ };
13488
+ for (const label of labels) {
13489
+ if (\u4ECA\u56DE\u52D5\u304B\u3057\u305F.has(label.id)) continue;
13490
+ for (const obs of \u7BB1\u305F\u3061) {
13491
+ const required = requiredNearClearance(label.kind, obs.kind);
13492
+ if (required <= 0) continue;
13493
+ if (computeGap(label, obs) >= required) continue;
13494
+ if (\u9003\u304C\u3059(label, \u77E9\u5F62\u306E\u5019\u88DC(label, obs, required))) {
13495
+ \u52D5\u3044\u305F = true;
13496
+ break;
13497
+ }
13498
+ }
13499
+ }
13500
+ const \u672D\u9806 = /* @__PURE__ */ new Map();
13501
+ edges.forEach((e, i) => \u672D\u9806.set(e.id, i));
13502
+ for (let i = 0; i < labels.length; i += 1) {
13503
+ for (let j = i + 1; j < labels.length; j += 1) {
13504
+ const a = labels[i];
13505
+ const b = labels[j];
13506
+ if (computeGap(a, b) >= \u672D\u306E\u8981\u6C42) continue;
13507
+ const aIsLater = \u672D\u9806.get(a.id) > \u672D\u9806.get(b.id);
13508
+ const moveBox = aIsLater ? a : b;
13509
+ const staticBox = aIsLater ? b : a;
13510
+ if (\u4ECA\u56DE\u52D5\u304B\u3057\u305F.has(moveBox.id)) continue;
13511
+ if (\u9003\u304C\u3059(moveBox, \u77E9\u5F62\u306E\u5019\u88DC(moveBox, staticBox, \u672D\u306E\u8981\u6C42))) \u52D5\u3044\u305F = true;
13512
+ }
13513
+ }
13514
+ for (const label of labels) {
13515
+ if (\u4ECA\u56DE\u52D5\u304B\u3057\u305F.has(label.id)) continue;
13516
+ const \u81EA\u5206 = edgeById.get(label.id);
13517
+ if (!\u81EA\u5206) continue;
13518
+ const \u81EA\u5206\u306E\u533A\u9593 = \u533A\u9593(\u81EA\u5206.id, \u81EA\u5206.d);
13519
+ for (const \u4ED6 of edges) {
13520
+ if (\u4ED6.id === label.id) continue;
13521
+ if (\u81EA\u5206.from === \u4ED6.from && \u81EA\u5206.to === \u4ED6.to) continue;
13522
+ if (\u9060\u3044(label, \u4ED6.id, \u4ED6.d, LABEL_TO_PATH_CLEARANCE)) continue;
13523
+ const segs = segmentsExcludingOwnPath(\u533A\u9593(\u4ED6.id, \u4ED6.d), \u81EA\u5206\u306E\u533A\u9593);
13524
+ if (segs.length === 0) continue;
13525
+ const gap = measurePathLabelGapSegs(segs, label);
13526
+ if (gap >= LABEL_TO_PATH_CLEARANCE) continue;
13527
+ const \u82AF = { x: label.x + label.w / 2, y: label.y + label.h / 2 };
13528
+ let \u8FD1\u3044\u70B9 = null;
13529
+ let \u6700\u77ED = Infinity;
13530
+ for (const seg of segs) {
13531
+ for (const \u70B9 of [
13532
+ { x: seg.x1, y: seg.y1 },
13533
+ { x: seg.x2, y: seg.y2 },
13534
+ { x: (seg.x1 + seg.x2) / 2, y: (seg.y1 + seg.y2) / 2 }
13535
+ ]) {
13536
+ const d = Math.hypot(\u70B9.x - \u82AF.x, \u70B9.y - \u82AF.y);
13537
+ if (d < \u6700\u77ED) {
13538
+ \u6700\u77ED = d;
13539
+ \u8FD1\u3044\u70B9 = \u70B9;
13540
+ }
13541
+ }
13542
+ }
13543
+ if (!\u8FD1\u3044\u70B9) continue;
13544
+ const \u5FC5\u8981\u91CF = (axis, sign) => {
13545
+ const \u6E80\u305F\u3059 = (\u91CF) => measurePathLabelGapSegs(segs, \u52D5\u304B\u3057\u305F\u7BB1(label, axis, sign, \u91CF)) >= LABEL_TO_PATH_CLEARANCE;
13546
+ let lo = 0;
13547
+ let hi = Math.max(4, LABEL_TO_PATH_CLEARANCE - gap);
13548
+ while (!\u6E80\u305F\u3059(hi)) {
13549
+ if (hi >= \u6700\u5927\u79FB\u52D5) return null;
13550
+ lo = hi;
13551
+ hi = Math.min(hi * 2, \u6700\u5927\u79FB\u52D5);
13552
+ }
13553
+ for (let k = 0; k < 12; k += 1) {
13554
+ const mid = (lo + hi) / 2;
13555
+ if (\u6E80\u305F\u3059(mid)) hi = mid;
13556
+ else lo = mid;
13557
+ }
13558
+ return hi + NEAR_MISS_MARGIN;
13559
+ };
13560
+ const dx = \u82AF.x - \u8FD1\u3044\u70B9.x;
13561
+ const dy = \u82AF.y - \u8FD1\u3044\u70B9.y;
13562
+ const \u5019\u88DC = [];
13563
+ for (const c of [
13564
+ { axis: "x", sign: dx >= 0 ? 1 : -1 },
13565
+ { axis: "y", sign: dy >= 0 ? 1 : -1 },
13566
+ // 近い点から離れる向きが塞がれていることがある。 反対向きも候補に入れる。
13567
+ { axis: "x", sign: dx >= 0 ? -1 : 1 },
13568
+ { axis: "y", sign: dy >= 0 ? -1 : 1 }
13569
+ ]) {
13570
+ const \u91CF = \u5FC5\u8981\u91CF(c.axis, c.sign);
13571
+ if (\u91CF !== null) \u5019\u88DC.push({ ...c, amount: \u91CF });
13572
+ }
13573
+ if (\u9003\u304C\u3059(label, \u5019\u88DC)) {
13574
+ \u52D5\u3044\u305F = true;
13575
+ break;
13576
+ }
13577
+ }
13578
+ }
13579
+ if (!\u52D5\u3044\u305F) break;
13580
+ }
13581
+ if (shiftMap.size === 0) return edges;
13582
+ return edges.map((e) => {
13583
+ const s = shiftMap.get(e.id);
13584
+ if (!s || s.dx === 0 && s.dy === 0) return e;
13585
+ return { ...e, labelX: e.labelX + s.dx, labelY: e.labelY + s.dy };
13586
+ });
13587
+ }
13360
13588
  function applyEdgeLabelAndPathShifts(edges, labelShiftMap, edgePathShiftMap) {
13361
13589
  if (labelShiftMap.size === 0 && edgePathShiftMap.size === 0) return edges;
13362
13590
  return edges.map((e) => {
@@ -19882,11 +20110,66 @@ function \u672B\u5C3E\u3092\u8FBA\u306B\u6CBF\u3063\u3066\u305A\u3089\u3059(e, \
19882
20110
  parts.push(`L ${s.x2} ${s.y2}`);
19883
20111
  }
19884
20112
  const \u6298\u308C1\u306F\u76F4\u524D\u306E\u6298\u308C = Math.abs(\u6298\u308C1.x - last.x1) <= 0.5 && Math.abs(\u6298\u308C1.y - last.y1) <= 0.5;
20113
+ if (\u6298\u308C1\u306F\u76F4\u524D\u306E\u6298\u308C) {
20114
+ const \u6298\u308A\u8FD4\u3055\u306A\u3044 = \u76F4\u524D\u306E\u6298\u308C\u3054\u3068\u305A\u3089\u3059(segs, \u6A2A, d, nodes, e);
20115
+ if (\u6298\u308A\u8FD4\u3055\u306A\u3044) return \u6298\u308A\u8FD4\u3055\u306A\u3044;
20116
+ }
19885
20117
  if (!\u6298\u308C1\u306F\u76F4\u524D\u306E\u6298\u308C) parts.push(`L ${round1(\u6298\u308C1.x)} ${round1(\u6298\u308C1.y)}`);
19886
20118
  parts.push(`L ${round1(\u6298\u308C2.x)} ${round1(\u6298\u308C2.y)}`);
19887
20119
  parts.push(`L ${round1(\u7D42\u70B9.x)} ${round1(\u7D42\u70B9.y)}`);
19888
20120
  return parts.join(" ");
19889
20121
  }
20122
+ function \u76F4\u524D\u306E\u6298\u308C\u3054\u3068\u305A\u3089\u3059(segs, \u6A2A, d, nodes, e) {
20123
+ const \u5E73\u884C\u304B = (s) => \u6A2A ? Math.abs(s.x2 - s.x1) < 0.5 && Math.abs(s.y2 - s.y1) > 0.5 : Math.abs(s.y2 - s.y1) < 0.5 && Math.abs(s.x2 - s.x1) > 0.5;
20124
+ let \u5438\u53CE = -1;
20125
+ for (let i = segs.length - 2; i >= 0; i -= 1) {
20126
+ if (\u5E73\u884C\u304B(segs[i])) {
20127
+ \u5438\u53CE = i;
20128
+ break;
20129
+ }
20130
+ }
20131
+ if (\u5438\u53CE < 0) return null;
20132
+ const \u5143\u306E\u9577\u3055 = \u6A2A ? Math.abs(segs[\u5438\u53CE].y2 - segs[\u5438\u53CE].y1) : Math.abs(segs[\u5438\u53CE].x2 - segs[\u5438\u53CE].x1);
20133
+ const \u5411\u304D = \u6A2A ? Math.sign(segs[\u5438\u53CE].y2 - segs[\u5438\u53CE].y1) : Math.sign(segs[\u5438\u53CE].x2 - segs[\u5438\u53CE].x1);
20134
+ const \u5F8C\u306E\u9577\u3055 = \u5143\u306E\u9577\u3055 + \u5411\u304D * d;
20135
+ if (\u5F8C\u306E\u9577\u3055 < ENDPOINT_FAN_RUNUP) return null;
20136
+ const \u305A\u3089\u3059 = (p) => \u6A2A ? { ...p, y: p.y + d } : { ...p, x: p.x + d };
20137
+ const \u65B0segs = segs.map((s, i) => {
20138
+ if (i <= \u5438\u53CE) return s;
20139
+ return {
20140
+ ...s,
20141
+ x1: \u305A\u3089\u3059({ x: s.x1, y: s.y1 }).x,
20142
+ y1: \u305A\u3089\u3059({ x: s.x1, y: s.y1 }).y,
20143
+ x2: \u305A\u3089\u3059({ x: s.x2, y: s.y2 }).x,
20144
+ y2: \u305A\u3089\u3059({ x: s.x2, y: s.y2 }).y,
20145
+ ...s.ctrl ? { ctrl: s.ctrl.map((c) => \u305A\u3089\u3059(c)) } : {}
20146
+ };
20147
+ });
20148
+ \u65B0segs[\u5438\u53CE] = {
20149
+ ...segs[\u5438\u53CE],
20150
+ x2: \u6A2A ? segs[\u5438\u53CE].x2 : segs[\u5438\u53CE].x2 + d,
20151
+ y2: \u6A2A ? segs[\u5438\u53CE].y2 + d : segs[\u5438\u53CE].y2
20152
+ };
20153
+ if (nodes.some(
20154
+ (n) => n.id !== e.from && n.id !== e.to && segmentsCrossRect(
20155
+ \u65B0segs.slice(\u5438\u53CE).map((s) => ({ x1: s.x1, y1: s.y1, x2: s.x2, y2: s.y2 })),
20156
+ nodeRect(n)
20157
+ )
20158
+ )) {
20159
+ return null;
20160
+ }
20161
+ const head = \u65B0segs[0];
20162
+ const parts = [`M ${round1(head.x1)} ${round1(head.y1)}`];
20163
+ for (const s of \u65B0segs) {
20164
+ if (s.cmd === "Q") {
20165
+ const c = s.ctrl?.[0];
20166
+ parts.push(c ? `Q ${round1(c.x)} ${round1(c.y)}, ${round1(s.x2)} ${round1(s.y2)}` : `L ${round1(s.x2)} ${round1(s.y2)}`);
20167
+ continue;
20168
+ }
20169
+ parts.push(`L ${round1(s.x2)} ${round1(s.y2)}`);
20170
+ }
20171
+ return parts.join(" ");
20172
+ }
19890
20173
 
19891
20174
  // src/layout/lanes.ts
19892
20175
  var LANE_POSITION_EPSILON = 1e-9;
@@ -20371,6 +20654,14 @@ function layout(diag) {
20371
20654
  hopEdgeCrossings(edges);
20372
20655
  repositionParallelLabels(edges, nodesFinal);
20373
20656
  slideLabelsAlongOwnPath(edges, nodesFinal);
20657
+ if (!isChainEdgeLabelDisabled()) {
20658
+ edges = resolveLabelClearanceAfterPathPasses(
20659
+ edges,
20660
+ collectBBoxes(containedLanesFinal, nodesFinal, []).filter(
20661
+ (b) => b.kind === "node" || b.kind === "lane-label"
20662
+ )
20663
+ );
20664
+ }
20374
20665
  const bboxes = collectBBoxes(containedLanesFinal, nodesFinal, edges);
20375
20666
  const collisions = detectCollisions(bboxes, edges);
20376
20667
  const nearCollisions = detectNearCollisions(bboxes, edges);
@@ -31680,6 +31971,7 @@ exports.requiredRowsWidth = requiredRowsWidth;
31680
31971
  exports.requiredSpecClearance = requiredSpecClearance;
31681
31972
  exports.resolveEdgeLabelOverlapsWithChain = resolveEdgeLabelOverlapsWithChain;
31682
31973
  exports.resolveEdgeLabelOverlapsWithChainAndPropagate = resolveEdgeLabelOverlapsWithChainAndPropagate;
31974
+ exports.resolveLabelClearanceAfterPathPasses = resolveLabelClearanceAfterPathPasses;
31683
31975
  exports.resolveOverlapsWithChain = resolveOverlapsWithChain;
31684
31976
  exports.resolveTarget = resolveTarget;
31685
31977
  exports.resolveTargetAll = resolveTargetAll;