@cardenelabs/cdl 0.48.0 → 0.49.0
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 +26 -1
- package/dist/index.cjs +171 -86
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +171 -86
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +160 -82
- package/dist/react.cjs.map +1 -1
- package/dist/react.js +160 -82
- package/dist/react.js.map +1 -1
- package/package.json +1 -1
- package/src/layout/collisions.ts +293 -139
- package/src/layout-with-validation.ts +4 -2
- package/src/visual-validate.ts +62 -6
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,30 @@ CDL (Chainome Diagram Language) の主要変更履歴。
|
|
|
5
5
|
|
|
6
6
|
## [Unreleased]
|
|
7
7
|
|
|
8
|
+
## [0.49.0] - 2026-09-11
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **`clearance` の違反に、 engine がなぜ直せなかったかを添える** (#810)
|
|
13
|
+
|
|
14
|
+
違反の報告は「どの 2 つが何 px で、要求が何 px か」 しか持たず、なぜ engine が直せなかったかを
|
|
15
|
+
知るには逃がし pass まで層を 3 つ読む必要があった。 札が絡む違反に `reason` を付け、
|
|
16
|
+
各向きを誰が塞いだかを 1 行で返す。
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
detail: edge-label:code-exchange ↔ edge-path:api-client gap=10.0px (need 14px)
|
|
20
|
+
reason: 逃げ場なし: 上=edge-label:consent-client (36→0) / 下=edge-label:token-issue (36→28) / 左=上限 200 を越える / 右=上限 200 を越える
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
`(36→0)` は塞いだ相手との隙間が、動かす前と動かした後でいくつになるか。
|
|
24
|
+
|
|
25
|
+
理由は **最終の位置から測り直す**。 逃がし pass と同じ候補と同じ判定 (`札の移動を塞ぐ相手を探す`)
|
|
26
|
+
を使うので、報告と実物がずれない。 どれか 1 つの向きでも安全なら理由を付けない
|
|
27
|
+
(動けるのに塞がれているとは言わない)。
|
|
28
|
+
|
|
29
|
+
`detail` は変えない = 消費側が `detail` から id を読んでいる。 `layoutWithValidation` の
|
|
30
|
+
console 出力は `(diagram=...)` の後ろに理由を足す。
|
|
31
|
+
|
|
8
32
|
## [0.48.0] - 2026-09-11
|
|
9
33
|
|
|
10
34
|
### Fixed
|
|
@@ -2381,7 +2405,8 @@ Initial OSS release.
|
|
|
2381
2405
|
使わない = annotated tag を push しても GitHub Release は作られず、 Release を作っていない版で
|
|
2382
2406
|
行き止まりになる。
|
|
2383
2407
|
|
|
2384
|
-
[Unreleased]: https://github.com/cardene777/cdl/compare/v0.
|
|
2408
|
+
[Unreleased]: https://github.com/cardene777/cdl/compare/v0.49.0...HEAD
|
|
2409
|
+
[0.49.0]: https://github.com/cardene777/cdl/compare/v0.48.0...v0.49.0
|
|
2385
2410
|
[0.48.0]: https://github.com/cardene777/cdl/compare/v0.47.0...v0.48.0
|
|
2386
2411
|
[0.47.0]: https://github.com/cardene777/cdl/compare/v0.46.0...v0.47.0
|
|
2387
2412
|
[0.46.0]: https://github.com/cardene777/cdl/compare/v0.45.0...v0.46.0
|
package/dist/index.cjs
CHANGED
|
@@ -13167,7 +13167,7 @@ function resolveEdgeLabelOverlapsWithChainAndPropagate(edges, obstacles, minClea
|
|
|
13167
13167
|
{ axis: "y", sign: dy >= 0 ? 1 : -1 },
|
|
13168
13168
|
{ axis: "x", sign: dx >= 0 ? 1 : -1 }
|
|
13169
13169
|
];
|
|
13170
|
-
const \u52D5\u304B\u3057\u305F\
|
|
13170
|
+
const \u52D5\u304B\u3057\u305F\u7BB12 = (axis, sign) => ({
|
|
13171
13171
|
...label,
|
|
13172
13172
|
x: label.x + (axis === "x" ? sign * \u91CF : 0),
|
|
13173
13173
|
y: label.y + (axis === "y" ? sign * \u91CF : 0)
|
|
@@ -13192,7 +13192,7 @@ function resolveEdgeLabelOverlapsWithChainAndPropagate(edges, obstacles, minClea
|
|
|
13192
13192
|
return false;
|
|
13193
13193
|
};
|
|
13194
13194
|
for (const \u5019 of \u5019\u88DC) {
|
|
13195
|
-
if (\u60AA\u304F\u3059\u308B(\u52D5\u304B\u3057\u305F\
|
|
13195
|
+
if (\u60AA\u304F\u3059\u308B(\u52D5\u304B\u3057\u305F\u7BB12(\u5019.axis, \u5019.sign))) continue;
|
|
13196
13196
|
out.push({
|
|
13197
13197
|
target: { kind: "label", id: label.id },
|
|
13198
13198
|
axis: \u5019.axis,
|
|
@@ -13358,15 +13358,7 @@ function resolveEdgeLabelOverlapsWithChainAndPropagate(edges, obstacles, minClea
|
|
|
13358
13358
|
edgePathShifts: edgePathShiftMap
|
|
13359
13359
|
};
|
|
13360
13360
|
}
|
|
13361
|
-
function
|
|
13362
|
-
if (edges.length === 0) return edges;
|
|
13363
|
-
const labeledIds = new Set(
|
|
13364
|
-
edges.filter((e) => e.label?.trim() || e.sub?.trim()).map((e) => e.id)
|
|
13365
|
-
);
|
|
13366
|
-
if (labeledIds.size === 0) return edges;
|
|
13367
|
-
const \u7BB1\u305F\u3061 = obstacles.filter((o) => o.kind === "node" || o.kind === "lane-label");
|
|
13368
|
-
const edgeById = /* @__PURE__ */ new Map();
|
|
13369
|
-
for (const e of edges) edgeById.set(e.id, e);
|
|
13361
|
+
function \u7DDA\u306E\u6E2C\u308A\u65B9\u3092\u4F5C\u308B() {
|
|
13370
13362
|
const \u533A\u9593cache = /* @__PURE__ */ new Map();
|
|
13371
13363
|
const \u533A\u9593 = (id, d) => {
|
|
13372
13364
|
const hit = \u533A\u9593cache.get(id);
|
|
@@ -13402,7 +13394,143 @@ function resolveLabelClearanceAfterPathPasses(edges, obstacles, maxIter = 8) {
|
|
|
13402
13394
|
const dy = Math.max(0, Math.max(box.y - (b.y + b.h), b.y - (box.y + box.h)));
|
|
13403
13395
|
return Math.hypot(dx, dy) >= \u4F59\u88D5;
|
|
13404
13396
|
};
|
|
13405
|
-
|
|
13397
|
+
return { \u533A\u9593, \u9060\u3044 };
|
|
13398
|
+
}
|
|
13399
|
+
var \u672D\u306E\u6700\u5927\u79FB\u52D5 = CHAIN_SHIFT_MAX_ACCUMULATED * 2;
|
|
13400
|
+
var \u96E2\u308C\u91CF = (a, b) => ({
|
|
13401
|
+
x: Math.max(a.x - (b.x + b.w), b.x - (a.x + a.w)),
|
|
13402
|
+
y: Math.max(a.y - (b.y + b.h), b.y - (a.y + a.h))
|
|
13403
|
+
});
|
|
13404
|
+
var \u52D5\u304B\u3057\u305F\u7BB1 = (box, axis, sign, amount) => ({
|
|
13405
|
+
...box,
|
|
13406
|
+
x: box.x + (axis === "x" ? sign * amount : 0),
|
|
13407
|
+
y: box.y + (axis === "y" ? sign * amount : 0)
|
|
13408
|
+
});
|
|
13409
|
+
function \u672D\u306E\u79FB\u52D5\u3092\u585E\u3050\u76F8\u624B\u3092\u63A2\u3059(\u5468\u308A, label, \u884C\u304D\u5148) {
|
|
13410
|
+
const { \u7BB1\u305F\u3061, labels, edges, edgeById, \u6E2C\u308A\u65B9 } = \u5468\u308A;
|
|
13411
|
+
for (const obs of \u7BB1\u305F\u3061) {
|
|
13412
|
+
const \u524D = computeGap(label, obs);
|
|
13413
|
+
const \u5F8C = computeGap(\u884C\u304D\u5148, obs);
|
|
13414
|
+
if (\u5F8C < Math.min(\u524D, requiredNearClearance("edge-label", obs.kind))) return { \u76F8\u624B: `${obs.kind}:${obs.id}`, \u524D, \u5F8C };
|
|
13415
|
+
}
|
|
13416
|
+
const \u672D\u306E\u8981\u6C42 = requiredNearClearance("edge-label", "edge-label");
|
|
13417
|
+
for (const \u4ED6 of labels) {
|
|
13418
|
+
if (\u4ED6.id === label.id) continue;
|
|
13419
|
+
const \u524D = computeGap(label, \u4ED6);
|
|
13420
|
+
const \u5F8C = computeGap(\u884C\u304D\u5148, \u4ED6);
|
|
13421
|
+
if (\u5F8C < Math.min(\u524D, \u672D\u306E\u8981\u6C42)) return { \u76F8\u624B: `edge-label:${\u4ED6.id}`, \u524D, \u5F8C };
|
|
13422
|
+
}
|
|
13423
|
+
const \u81EA\u5206 = edgeById.get(label.id);
|
|
13424
|
+
const \u81EA\u5206\u306E\u533A\u9593 = \u81EA\u5206 ? \u6E2C\u308A\u65B9.\u533A\u9593(\u81EA\u5206.id, \u81EA\u5206.d) : [];
|
|
13425
|
+
if (\u81EA\u5206\u306E\u533A\u9593.length > 0) {
|
|
13426
|
+
const \u524D = measurePathLabelGapSegs(\u81EA\u5206\u306E\u533A\u9593, label);
|
|
13427
|
+
const \u5F8C = measurePathLabelGapSegs(\u81EA\u5206\u306E\u533A\u9593, \u884C\u304D\u5148);
|
|
13428
|
+
if (\u5F8C > Math.max(\u524D, DIST_LABEL_PATH_MAX)) return { \u76F8\u624B: "\u81EA\u5206\u306E\u7DDA", \u524D, \u5F8C };
|
|
13429
|
+
}
|
|
13430
|
+
for (const \u76F8\u624B of edges) {
|
|
13431
|
+
if (\u76F8\u624B.id === label.id) continue;
|
|
13432
|
+
if (\u6E2C\u308A\u65B9.\u9060\u3044(label, \u76F8\u624B.id, \u76F8\u624B.d, LABEL_TO_PATH_CLEARANCE) && \u6E2C\u308A\u65B9.\u9060\u3044(\u884C\u304D\u5148, \u76F8\u624B.id, \u76F8\u624B.d, LABEL_TO_PATH_CLEARANCE)) continue;
|
|
13433
|
+
const segs = segmentsExcludingOwnPath(\u6E2C\u308A\u65B9.\u533A\u9593(\u76F8\u624B.id, \u76F8\u624B.d), \u81EA\u5206\u306E\u533A\u9593);
|
|
13434
|
+
if (segs.length === 0) continue;
|
|
13435
|
+
const \u524D = measurePathLabelGapSegs(segs, label);
|
|
13436
|
+
const \u5F8C = measurePathLabelGapSegs(segs, \u884C\u304D\u5148);
|
|
13437
|
+
if (\u5F8C < Math.min(\u524D, LABEL_TO_PATH_CLEARANCE)) return { \u76F8\u624B: `edge-path:${\u76F8\u624B.id}`, \u524D, \u5F8C };
|
|
13438
|
+
}
|
|
13439
|
+
return null;
|
|
13440
|
+
}
|
|
13441
|
+
function \u77E9\u5F62\u306E\u5019\u88DC(label, static\u7BB1, required) {
|
|
13442
|
+
const sep = \u96E2\u308C\u91CF(label, static\u7BB1);
|
|
13443
|
+
const \u5411\u304D = (axis) => {
|
|
13444
|
+
const a = axis === "x" ? label.x + label.w / 2 : label.y + label.h / 2;
|
|
13445
|
+
const b = axis === "x" ? static\u7BB1.x + static\u7BB1.w / 2 : static\u7BB1.y + static\u7BB1.h / 2;
|
|
13446
|
+
return a >= b ? 1 : -1;
|
|
13447
|
+
};
|
|
13448
|
+
return [
|
|
13449
|
+
{ axis: "x", sign: \u5411\u304D("x"), amount: required - sep.x + NEAR_MISS_MARGIN },
|
|
13450
|
+
{ axis: "y", sign: \u5411\u304D("y"), amount: required - sep.y + NEAR_MISS_MARGIN }
|
|
13451
|
+
];
|
|
13452
|
+
}
|
|
13453
|
+
function \u7DDA\u304B\u3089\u96E2\u308C\u308B\u91CF(segs, label, gap, axis, sign) {
|
|
13454
|
+
const \u6700\u5927\u79FB\u52D5 = \u672D\u306E\u6700\u5927\u79FB\u52D5;
|
|
13455
|
+
const \u6E80\u305F\u3059 = (\u91CF) => measurePathLabelGapSegs(segs, \u52D5\u304B\u3057\u305F\u7BB1(label, axis, sign, \u91CF)) >= LABEL_TO_PATH_CLEARANCE;
|
|
13456
|
+
let lo = 0;
|
|
13457
|
+
let hi = Math.max(4, LABEL_TO_PATH_CLEARANCE - gap);
|
|
13458
|
+
while (!\u6E80\u305F\u3059(hi)) {
|
|
13459
|
+
if (hi >= \u6700\u5927\u79FB\u52D5) return null;
|
|
13460
|
+
lo = hi;
|
|
13461
|
+
hi = Math.min(hi * 2, \u6700\u5927\u79FB\u52D5);
|
|
13462
|
+
}
|
|
13463
|
+
for (let k = 0; k < 12; k += 1) {
|
|
13464
|
+
const mid = (lo + hi) / 2;
|
|
13465
|
+
if (\u6E80\u305F\u3059(mid)) hi = mid;
|
|
13466
|
+
else lo = mid;
|
|
13467
|
+
}
|
|
13468
|
+
return hi + NEAR_MISS_MARGIN;
|
|
13469
|
+
}
|
|
13470
|
+
var \u5411\u304D\u306E\u9806 = [
|
|
13471
|
+
{ axis: "y", sign: -1, \u540D\u524D: "\u4E0A" },
|
|
13472
|
+
{ axis: "y", sign: 1, \u540D\u524D: "\u4E0B" },
|
|
13473
|
+
{ axis: "x", sign: -1, \u540D\u524D: "\u5DE6" },
|
|
13474
|
+
{ axis: "x", sign: 1, \u540D\u524D: "\u53F3" }
|
|
13475
|
+
];
|
|
13476
|
+
function \u672D\u304C\u52D5\u3051\u306A\u3044\u7406\u7531(labelId, \u76F8\u624B, edges, obstacles) {
|
|
13477
|
+
const edgeById = new Map(edges.map((e) => [e.id, e]));
|
|
13478
|
+
const \u81EA\u5206 = edgeById.get(labelId);
|
|
13479
|
+
if (!\u81EA\u5206) return null;
|
|
13480
|
+
const labels = edges.filter((e) => e.label?.trim() || e.sub?.trim()).map((e) => ({ kind: "edge-label", id: e.id, ...computeLabelBBoxWorld(e) }));
|
|
13481
|
+
const label = labels.find((l) => l.id === labelId);
|
|
13482
|
+
if (!label) return null;
|
|
13483
|
+
const \u6E2C\u308A\u65B9 = \u7DDA\u306E\u6E2C\u308A\u65B9\u3092\u4F5C\u308B();
|
|
13484
|
+
const \u5468\u308A = {
|
|
13485
|
+
\u7BB1\u305F\u3061: obstacles.filter((o) => o.kind === "node" || o.kind === "lane-label"),
|
|
13486
|
+
labels,
|
|
13487
|
+
edges,
|
|
13488
|
+
edgeById,
|
|
13489
|
+
\u6E2C\u308A\u65B9
|
|
13490
|
+
};
|
|
13491
|
+
const \u5019\u88DC = [];
|
|
13492
|
+
if (\u76F8\u624B.kind === "edge-path") {
|
|
13493
|
+
const \u7DDA = edgeById.get(\u76F8\u624B.id);
|
|
13494
|
+
if (!\u7DDA) return null;
|
|
13495
|
+
const segs = segmentsExcludingOwnPath(\u6E2C\u308A\u65B9.\u533A\u9593(\u7DDA.id, \u7DDA.d), \u6E2C\u308A\u65B9.\u533A\u9593(\u81EA\u5206.id, \u81EA\u5206.d));
|
|
13496
|
+
if (segs.length === 0) return null;
|
|
13497
|
+
const gap = measurePathLabelGapSegs(segs, label);
|
|
13498
|
+
for (const \u5411\u304D of \u5411\u304D\u306E\u9806) {
|
|
13499
|
+
\u5019\u88DC.push({ axis: \u5411\u304D.axis, sign: \u5411\u304D.sign, amount: \u7DDA\u304B\u3089\u96E2\u308C\u308B\u91CF(segs, label, gap, \u5411\u304D.axis, \u5411\u304D.sign) });
|
|
13500
|
+
}
|
|
13501
|
+
} else {
|
|
13502
|
+
const required = requiredNearClearance("edge-label", \u76F8\u624B.kind);
|
|
13503
|
+
for (const c of \u77E9\u5F62\u306E\u5019\u88DC(label, \u76F8\u624B, required)) {
|
|
13504
|
+
if (c.amount > 0) \u5019\u88DC.push(c);
|
|
13505
|
+
}
|
|
13506
|
+
}
|
|
13507
|
+
if (\u5019\u88DC.length === 0) return null;
|
|
13508
|
+
const \u7406\u7531 = [];
|
|
13509
|
+
for (const c of \u5019\u88DC) {
|
|
13510
|
+
const \u9806 = \u5411\u304D\u306E\u9806.findIndex((v) => v.axis === c.axis && v.sign === c.sign);
|
|
13511
|
+
const \u540D\u524D = \u5411\u304D\u306E\u9806[\u9806].\u540D\u524D;
|
|
13512
|
+
if (c.amount === null || c.amount > \u672D\u306E\u6700\u5927\u79FB\u52D5) {
|
|
13513
|
+
\u7406\u7531.push({ \u9806, \u6587: `${\u540D\u524D}=\u4E0A\u9650 ${\u672D\u306E\u6700\u5927\u79FB\u52D5} \u3092\u8D8A\u3048\u308B` });
|
|
13514
|
+
continue;
|
|
13515
|
+
}
|
|
13516
|
+
const \u585E\u3050 = \u672D\u306E\u79FB\u52D5\u3092\u585E\u3050\u76F8\u624B\u3092\u63A2\u3059(\u5468\u308A, label, \u52D5\u304B\u3057\u305F\u7BB1(label, c.axis, c.sign, c.amount));
|
|
13517
|
+
if (!\u585E\u3050) return null;
|
|
13518
|
+
\u7406\u7531.push({ \u9806, \u6587: `${\u540D\u524D}=${\u585E\u3050.\u76F8\u624B} (${\u585E\u3050.\u524D.toFixed(0)}\u2192${\u585E\u3050.\u5F8C.toFixed(0)})` });
|
|
13519
|
+
}
|
|
13520
|
+
return `\u9003\u3052\u5834\u306A\u3057: ${\u7406\u7531.sort((a, b) => a.\u9806 - b.\u9806).map((r) => r.\u6587).join(" / ")}`;
|
|
13521
|
+
}
|
|
13522
|
+
function resolveLabelClearanceAfterPathPasses(edges, obstacles, maxIter = 8) {
|
|
13523
|
+
if (edges.length === 0) return edges;
|
|
13524
|
+
const labeledIds = new Set(
|
|
13525
|
+
edges.filter((e) => e.label?.trim() || e.sub?.trim()).map((e) => e.id)
|
|
13526
|
+
);
|
|
13527
|
+
if (labeledIds.size === 0) return edges;
|
|
13528
|
+
const \u7BB1\u305F\u3061 = obstacles.filter((o) => o.kind === "node" || o.kind === "lane-label");
|
|
13529
|
+
const edgeById = /* @__PURE__ */ new Map();
|
|
13530
|
+
for (const e of edges) edgeById.set(e.id, e);
|
|
13531
|
+
const \u6E2C\u308A\u65B9 = \u7DDA\u306E\u6E2C\u308A\u65B9\u3092\u4F5C\u308B();
|
|
13532
|
+
const { \u533A\u9593, \u9060\u3044 } = \u6E2C\u308A\u65B9;
|
|
13533
|
+
const \u6700\u5927\u79FB\u52D5 = \u672D\u306E\u6700\u5927\u79FB\u52D5;
|
|
13406
13534
|
const shiftMap = /* @__PURE__ */ new Map();
|
|
13407
13535
|
const getShift = (id) => shiftMap.get(id) ?? { dx: 0, dy: 0 };
|
|
13408
13536
|
const labelBBoxes = () => {
|
|
@@ -13415,50 +13543,12 @@ function resolveLabelClearanceAfterPathPasses(edges, obstacles, maxIter = 8) {
|
|
|
13415
13543
|
}
|
|
13416
13544
|
return out;
|
|
13417
13545
|
};
|
|
13418
|
-
const \u96E2\u308C\u91CF = (a, b) => ({
|
|
13419
|
-
x: Math.max(a.x - (b.x + b.w), b.x - (a.x + a.w)),
|
|
13420
|
-
y: Math.max(a.y - (b.y + b.h), b.y - (a.y + a.h))
|
|
13421
|
-
});
|
|
13422
|
-
const \u52D5\u304B\u3057\u305F\u7BB1 = (box, axis, sign, amount) => ({
|
|
13423
|
-
...box,
|
|
13424
|
-
x: box.x + (axis === "x" ? sign * amount : 0),
|
|
13425
|
-
y: box.y + (axis === "y" ? sign * amount : 0)
|
|
13426
|
-
});
|
|
13427
13546
|
for (let iter = 0; iter < maxIter; iter += 1) {
|
|
13428
13547
|
const labels = labelBBoxes();
|
|
13429
13548
|
const \u672D\u306E\u8981\u6C42 = requiredNearClearance("edge-label", "edge-label");
|
|
13430
13549
|
let \u52D5\u3044\u305F = false;
|
|
13431
13550
|
const \u4ECA\u56DE\u52D5\u304B\u3057\u305F = /* @__PURE__ */ new Set();
|
|
13432
|
-
const \u5B89\u5168\u304B = (label, \u884C\u304D\u5148) => {
|
|
13433
|
-
for (const obs of \u7BB1\u305F\u3061) {
|
|
13434
|
-
const \u524D = computeGap(label, obs);
|
|
13435
|
-
const \u5F8C = computeGap(\u884C\u304D\u5148, obs);
|
|
13436
|
-
if (\u5F8C < Math.min(\u524D, requiredNearClearance("edge-label", obs.kind))) return false;
|
|
13437
|
-
}
|
|
13438
|
-
for (const \u4ED6 of labels) {
|
|
13439
|
-
if (\u4ED6.id === label.id) continue;
|
|
13440
|
-
const \u524D = computeGap(label, \u4ED6);
|
|
13441
|
-
const \u5F8C = computeGap(\u884C\u304D\u5148, \u4ED6);
|
|
13442
|
-
if (\u5F8C < Math.min(\u524D, \u672D\u306E\u8981\u6C42)) return false;
|
|
13443
|
-
}
|
|
13444
|
-
const \u81EA\u5206 = edgeById.get(label.id);
|
|
13445
|
-
const \u81EA\u5206\u306E\u533A\u9593 = \u81EA\u5206 ? \u533A\u9593(\u81EA\u5206.id, \u81EA\u5206.d) : [];
|
|
13446
|
-
if (\u81EA\u5206\u306E\u533A\u9593.length > 0) {
|
|
13447
|
-
const \u524D = measurePathLabelGapSegs(\u81EA\u5206\u306E\u533A\u9593, label);
|
|
13448
|
-
const \u5F8C = measurePathLabelGapSegs(\u81EA\u5206\u306E\u533A\u9593, \u884C\u304D\u5148);
|
|
13449
|
-
if (\u5F8C > Math.max(\u524D, DIST_LABEL_PATH_MAX)) return false;
|
|
13450
|
-
}
|
|
13451
|
-
for (const \u76F8\u624B of edges) {
|
|
13452
|
-
if (\u76F8\u624B.id === label.id) continue;
|
|
13453
|
-
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;
|
|
13454
|
-
const segs = segmentsExcludingOwnPath(\u533A\u9593(\u76F8\u624B.id, \u76F8\u624B.d), \u81EA\u5206\u306E\u533A\u9593);
|
|
13455
|
-
if (segs.length === 0) continue;
|
|
13456
|
-
const \u524D = measurePathLabelGapSegs(segs, label);
|
|
13457
|
-
const \u5F8C = measurePathLabelGapSegs(segs, \u884C\u304D\u5148);
|
|
13458
|
-
if (\u5F8C < Math.min(\u524D, LABEL_TO_PATH_CLEARANCE)) return false;
|
|
13459
|
-
}
|
|
13460
|
-
return true;
|
|
13461
|
-
};
|
|
13551
|
+
const \u5B89\u5168\u304B = (label, \u884C\u304D\u5148) => \u672D\u306E\u79FB\u52D5\u3092\u585E\u3050\u76F8\u624B\u3092\u63A2\u3059({ \u7BB1\u305F\u3061, labels, edges, edgeById, \u6E2C\u308A\u65B9 }, label, \u884C\u304D\u5148) === null;
|
|
13462
13552
|
const \u9003\u304C\u3059 = (label, \u5019\u88DC) => {
|
|
13463
13553
|
for (const c of \u5019\u88DC.slice().sort((a, b) => a.amount - b.amount)) {
|
|
13464
13554
|
if (c.amount <= 0) continue;
|
|
@@ -13474,18 +13564,6 @@ function resolveLabelClearanceAfterPathPasses(edges, obstacles, maxIter = 8) {
|
|
|
13474
13564
|
}
|
|
13475
13565
|
return false;
|
|
13476
13566
|
};
|
|
13477
|
-
const \u77E9\u5F62\u306E\u5019\u88DC = (label, static\u7BB1, required) => {
|
|
13478
|
-
const sep = \u96E2\u308C\u91CF(label, static\u7BB1);
|
|
13479
|
-
const \u5411\u304D = (axis) => {
|
|
13480
|
-
const a = axis === "x" ? label.x + label.w / 2 : label.y + label.h / 2;
|
|
13481
|
-
const b = axis === "x" ? static\u7BB1.x + static\u7BB1.w / 2 : static\u7BB1.y + static\u7BB1.h / 2;
|
|
13482
|
-
return a >= b ? 1 : -1;
|
|
13483
|
-
};
|
|
13484
|
-
return [
|
|
13485
|
-
{ axis: "x", sign: \u5411\u304D("x"), amount: required - sep.x + NEAR_MISS_MARGIN },
|
|
13486
|
-
{ axis: "y", sign: \u5411\u304D("y"), amount: required - sep.y + NEAR_MISS_MARGIN }
|
|
13487
|
-
];
|
|
13488
|
-
};
|
|
13489
13567
|
for (const label of labels) {
|
|
13490
13568
|
if (\u4ECA\u56DE\u52D5\u304B\u3057\u305F.has(label.id)) continue;
|
|
13491
13569
|
for (const obs of \u7BB1\u305F\u3061) {
|
|
@@ -13542,22 +13620,7 @@ function resolveLabelClearanceAfterPathPasses(edges, obstacles, maxIter = 8) {
|
|
|
13542
13620
|
}
|
|
13543
13621
|
}
|
|
13544
13622
|
if (!\u8FD1\u3044\u70B9) continue;
|
|
13545
|
-
const \u5FC5\u8981\u91CF = (axis, sign) =>
|
|
13546
|
-
const \u6E80\u305F\u3059 = (\u91CF) => measurePathLabelGapSegs(segs, \u52D5\u304B\u3057\u305F\u7BB1(label, axis, sign, \u91CF)) >= LABEL_TO_PATH_CLEARANCE;
|
|
13547
|
-
let lo = 0;
|
|
13548
|
-
let hi = Math.max(4, LABEL_TO_PATH_CLEARANCE - gap);
|
|
13549
|
-
while (!\u6E80\u305F\u3059(hi)) {
|
|
13550
|
-
if (hi >= \u6700\u5927\u79FB\u52D5) return null;
|
|
13551
|
-
lo = hi;
|
|
13552
|
-
hi = Math.min(hi * 2, \u6700\u5927\u79FB\u52D5);
|
|
13553
|
-
}
|
|
13554
|
-
for (let k = 0; k < 12; k += 1) {
|
|
13555
|
-
const mid = (lo + hi) / 2;
|
|
13556
|
-
if (\u6E80\u305F\u3059(mid)) hi = mid;
|
|
13557
|
-
else lo = mid;
|
|
13558
|
-
}
|
|
13559
|
-
return hi + NEAR_MISS_MARGIN;
|
|
13560
|
-
};
|
|
13623
|
+
const \u5FC5\u8981\u91CF = (axis, sign) => \u7DDA\u304B\u3089\u96E2\u308C\u308B\u91CF(segs, label, gap, axis, sign);
|
|
13561
13624
|
const dx = \u82AF.x - \u8FD1\u3044\u70B9.x;
|
|
13562
13625
|
const dy = \u82AF.y - \u8FD1\u3044\u70B9.y;
|
|
13563
13626
|
const \u5019\u88DC = [];
|
|
@@ -15989,9 +16052,15 @@ function visualValidateLaid(laid, diag, opts) {
|
|
|
15989
16052
|
const counts = emptyCounts();
|
|
15990
16053
|
const profile = opts?.profile ?? "production";
|
|
15991
16054
|
const skipped = SKIPPED_AXES[profile];
|
|
15992
|
-
const push = (axis, detail, severity = "error") => {
|
|
16055
|
+
const push = (axis, detail, severity = "error", reason) => {
|
|
15993
16056
|
if (skipped.has(axis)) return;
|
|
15994
|
-
violations.push({
|
|
16057
|
+
violations.push({
|
|
16058
|
+
axis,
|
|
16059
|
+
diagramId: safeDiagramId(diag),
|
|
16060
|
+
detail,
|
|
16061
|
+
severity,
|
|
16062
|
+
...reason !== void 0 ? { reason } : {}
|
|
16063
|
+
});
|
|
15995
16064
|
counts[axis]++;
|
|
15996
16065
|
};
|
|
15997
16066
|
return visualValidateInner(
|
|
@@ -16010,9 +16079,15 @@ function visualValidate(diag, opts) {
|
|
|
16010
16079
|
const counts = emptyCounts();
|
|
16011
16080
|
const profile = opts?.profile ?? "production";
|
|
16012
16081
|
const skipped = SKIPPED_AXES[profile];
|
|
16013
|
-
const push = (axis, detail, severity = "error") => {
|
|
16082
|
+
const push = (axis, detail, severity = "error", reason) => {
|
|
16014
16083
|
if (skipped.has(axis)) return;
|
|
16015
|
-
violations.push({
|
|
16084
|
+
violations.push({
|
|
16085
|
+
axis,
|
|
16086
|
+
diagramId: safeDiagramId(diag),
|
|
16087
|
+
detail,
|
|
16088
|
+
severity,
|
|
16089
|
+
...reason !== void 0 ? { reason } : {}
|
|
16090
|
+
});
|
|
16016
16091
|
counts[axis]++;
|
|
16017
16092
|
};
|
|
16018
16093
|
const diagProblems = findMalformedInput(void 0, diag).filter(
|
|
@@ -16349,10 +16424,19 @@ function runAxes(laid, diag, violations, counts, push, profile, skippedAxes, \u5
|
|
|
16349
16424
|
}
|
|
16350
16425
|
}
|
|
16351
16426
|
const nears = detectNearCollisions(bboxes, laid.edges);
|
|
16427
|
+
const \u672D\u306E\u969C\u5BB3\u7269 = bboxes.filter((b) => b.kind === "node" || b.kind === "lane-label");
|
|
16428
|
+
const \u8FBA\u306E\u9806 = new Map(laid.edges.map((e, i) => [e.id, i]));
|
|
16352
16429
|
for (const n of nears) {
|
|
16430
|
+
const a\u306F\u672D = n.a.kind === "edge-label";
|
|
16431
|
+
const b\u306F\u672D = n.b.kind === "edge-label";
|
|
16432
|
+
const \u52D5\u304B\u3059 = a\u306F\u672D && b\u306F\u672D ? (\u8FBA\u306E\u9806.get(n.a.id) ?? 0) > (\u8FBA\u306E\u9806.get(n.b.id) ?? 0) ? n.a : n.b : a\u306F\u672D ? n.a : b\u306F\u672D ? n.b : null;
|
|
16433
|
+
const \u76F8\u624B = \u52D5\u304B\u3059 === n.a ? n.b : n.a;
|
|
16434
|
+
const reason = \u52D5\u304B\u3059 !== null ? \u672D\u304C\u52D5\u3051\u306A\u3044\u7406\u7531(\u52D5\u304B\u3059.id, \u76F8\u624B, laid.edges, \u672D\u306E\u969C\u5BB3\u7269) ?? void 0 : void 0;
|
|
16353
16435
|
push(
|
|
16354
16436
|
"clearance",
|
|
16355
|
-
`${n.a.kind}:${n.a.id} \u2194 ${n.b.kind}:${n.b.id} gap=${n.gap.toFixed(1)}px (need ${n.required}px)
|
|
16437
|
+
`${n.a.kind}:${n.a.id} \u2194 ${n.b.kind}:${n.b.id} gap=${n.gap.toFixed(1)}px (need ${n.required}px)`,
|
|
16438
|
+
"error",
|
|
16439
|
+
reason
|
|
16356
16440
|
);
|
|
16357
16441
|
}
|
|
16358
16442
|
const PROXIMITY_WARN_THRESHOLD = DIST_LABEL_PATH_MAX;
|
|
@@ -21272,10 +21356,11 @@ function layoutWithValidation(diag, opts = {}) {
|
|
|
21272
21356
|
}
|
|
21273
21357
|
if (opts.emitConsole) {
|
|
21274
21358
|
for (const v of report.violations) {
|
|
21359
|
+
const \u7406\u7531 = v.reason !== void 0 ? ` ${v.reason}` : "";
|
|
21275
21360
|
if (v.severity === "error") {
|
|
21276
|
-
console.error(`[cdl geometry ${v.axis}] ${v.detail} (diagram=${v.diagramId})`);
|
|
21361
|
+
console.error(`[cdl geometry ${v.axis}] ${v.detail} (diagram=${v.diagramId})${\u7406\u7531}`);
|
|
21277
21362
|
} else {
|
|
21278
|
-
console.warn(`[cdl geometry ${v.axis}] ${v.detail} (diagram=${v.diagramId})`);
|
|
21363
|
+
console.warn(`[cdl geometry ${v.axis}] ${v.detail} (diagram=${v.diagramId})${\u7406\u7531}`);
|
|
21279
21364
|
}
|
|
21280
21365
|
}
|
|
21281
21366
|
}
|