@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/dist/index.d.cts
CHANGED
|
@@ -1580,6 +1580,14 @@ interface Violation {
|
|
|
1580
1580
|
diagramId: string;
|
|
1581
1581
|
detail: string;
|
|
1582
1582
|
severity: "error" | "warn";
|
|
1583
|
+
/**
|
|
1584
|
+
* engine がなぜ直せなかったか (#810)。 分かる時だけ付く。
|
|
1585
|
+
*
|
|
1586
|
+
* `detail` とは別の欄に置く = `detail` は「どの 2 つが何 px か」 を固定の形で持ち、
|
|
1587
|
+
* 消費側がそこから id を読み取っている。 理由を `detail` に足すと、 理由の中の id まで
|
|
1588
|
+
* 違反の対象として拾われる。
|
|
1589
|
+
*/
|
|
1590
|
+
reason?: string;
|
|
1583
1591
|
}
|
|
1584
1592
|
type VisualAxis = "node-visibility" | "edge-label-overlap" | "edge-label-proximity" | "text-readability" | "row-format" | "alignment" | "clearance" | "arrow-endpoint-anchoring" | "label-char-range" | "node-overlap" | "edge-crossing" | "edge-node-cross" | "edge-segment-orthogonality" | "label-inside-viewbox" | "lane-cx-consistency" | "row-vertical-spacing" | "group-boundary-clearance" | "node-vertical-clearance" | "lane-lane-gap" | "arrow-marker-clearance" | "grid-alignment" | "phase-layout-stability" | "responsive-viewport" | "accessibility-basics" | "animation-frame-integrity" | "i18n-cjk-detection" | "contrast-basics" | "print-media-compat" | "color-blind-safety" | "marker-gradient-def-integrity" | "dom-complexity-budget" | "reduced-motion-compat" | "touch-target-size" | "row-content-typing" | "terminal-safe-text" | "gpu-layer-efficiency" | "svg-injection-safety" | "seo-metadata-quality" | "bidi-hyphenation" | "structured-data-extraction" | "diagram-version-semver" | "migration-path-consistency" | "axis-coverage-meta" | "axis-documentation-completeness" | "fixture-drift-detection" | "locale-parity" | "validate-performance-budget" | "node-inside-viewbox" | "shape-label-dropped" | "box-line-dropped" | "node-inside-lane" | "edge-inside-viewbox" | "lane-label-inside-viewbox" | "lane-label-overlap" | "row-alignment" | "column-alignment" | "edge-stubout-min" | "fan-origin-single-point" | "detour-slot-distinct" | "arrow-endpoint-center" | "lane-border-clearance" | "row-gap-uniform" | "column-gap-uniform" | "rows-not-rendered" | "title-row-overlap" | "malformed-input" | "validation-interrupted";
|
|
1585
1593
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -1580,6 +1580,14 @@ interface Violation {
|
|
|
1580
1580
|
diagramId: string;
|
|
1581
1581
|
detail: string;
|
|
1582
1582
|
severity: "error" | "warn";
|
|
1583
|
+
/**
|
|
1584
|
+
* engine がなぜ直せなかったか (#810)。 分かる時だけ付く。
|
|
1585
|
+
*
|
|
1586
|
+
* `detail` とは別の欄に置く = `detail` は「どの 2 つが何 px か」 を固定の形で持ち、
|
|
1587
|
+
* 消費側がそこから id を読み取っている。 理由を `detail` に足すと、 理由の中の id まで
|
|
1588
|
+
* 違反の対象として拾われる。
|
|
1589
|
+
*/
|
|
1590
|
+
reason?: string;
|
|
1583
1591
|
}
|
|
1584
1592
|
type VisualAxis = "node-visibility" | "edge-label-overlap" | "edge-label-proximity" | "text-readability" | "row-format" | "alignment" | "clearance" | "arrow-endpoint-anchoring" | "label-char-range" | "node-overlap" | "edge-crossing" | "edge-node-cross" | "edge-segment-orthogonality" | "label-inside-viewbox" | "lane-cx-consistency" | "row-vertical-spacing" | "group-boundary-clearance" | "node-vertical-clearance" | "lane-lane-gap" | "arrow-marker-clearance" | "grid-alignment" | "phase-layout-stability" | "responsive-viewport" | "accessibility-basics" | "animation-frame-integrity" | "i18n-cjk-detection" | "contrast-basics" | "print-media-compat" | "color-blind-safety" | "marker-gradient-def-integrity" | "dom-complexity-budget" | "reduced-motion-compat" | "touch-target-size" | "row-content-typing" | "terminal-safe-text" | "gpu-layer-efficiency" | "svg-injection-safety" | "seo-metadata-quality" | "bidi-hyphenation" | "structured-data-extraction" | "diagram-version-semver" | "migration-path-consistency" | "axis-coverage-meta" | "axis-documentation-completeness" | "fixture-drift-detection" | "locale-parity" | "validate-performance-budget" | "node-inside-viewbox" | "shape-label-dropped" | "box-line-dropped" | "node-inside-lane" | "edge-inside-viewbox" | "lane-label-inside-viewbox" | "lane-label-overlap" | "row-alignment" | "column-alignment" | "edge-stubout-min" | "fan-origin-single-point" | "detour-slot-distinct" | "arrow-endpoint-center" | "lane-border-clearance" | "row-gap-uniform" | "column-gap-uniform" | "rows-not-rendered" | "title-row-overlap" | "malformed-input" | "validation-interrupted";
|
|
1585
1593
|
/**
|
package/dist/index.js
CHANGED
|
@@ -13165,7 +13165,7 @@ function resolveEdgeLabelOverlapsWithChainAndPropagate(edges, obstacles, minClea
|
|
|
13165
13165
|
{ axis: "y", sign: dy >= 0 ? 1 : -1 },
|
|
13166
13166
|
{ axis: "x", sign: dx >= 0 ? 1 : -1 }
|
|
13167
13167
|
];
|
|
13168
|
-
const \u52D5\u304B\u3057\u305F\
|
|
13168
|
+
const \u52D5\u304B\u3057\u305F\u7BB12 = (axis, sign) => ({
|
|
13169
13169
|
...label,
|
|
13170
13170
|
x: label.x + (axis === "x" ? sign * \u91CF : 0),
|
|
13171
13171
|
y: label.y + (axis === "y" ? sign * \u91CF : 0)
|
|
@@ -13190,7 +13190,7 @@ function resolveEdgeLabelOverlapsWithChainAndPropagate(edges, obstacles, minClea
|
|
|
13190
13190
|
return false;
|
|
13191
13191
|
};
|
|
13192
13192
|
for (const \u5019 of \u5019\u88DC) {
|
|
13193
|
-
if (\u60AA\u304F\u3059\u308B(\u52D5\u304B\u3057\u305F\
|
|
13193
|
+
if (\u60AA\u304F\u3059\u308B(\u52D5\u304B\u3057\u305F\u7BB12(\u5019.axis, \u5019.sign))) continue;
|
|
13194
13194
|
out.push({
|
|
13195
13195
|
target: { kind: "label", id: label.id },
|
|
13196
13196
|
axis: \u5019.axis,
|
|
@@ -13356,15 +13356,7 @@ function resolveEdgeLabelOverlapsWithChainAndPropagate(edges, obstacles, minClea
|
|
|
13356
13356
|
edgePathShifts: edgePathShiftMap
|
|
13357
13357
|
};
|
|
13358
13358
|
}
|
|
13359
|
-
function
|
|
13360
|
-
if (edges.length === 0) return edges;
|
|
13361
|
-
const labeledIds = new Set(
|
|
13362
|
-
edges.filter((e) => e.label?.trim() || e.sub?.trim()).map((e) => e.id)
|
|
13363
|
-
);
|
|
13364
|
-
if (labeledIds.size === 0) return edges;
|
|
13365
|
-
const \u7BB1\u305F\u3061 = obstacles.filter((o) => o.kind === "node" || o.kind === "lane-label");
|
|
13366
|
-
const edgeById = /* @__PURE__ */ new Map();
|
|
13367
|
-
for (const e of edges) edgeById.set(e.id, e);
|
|
13359
|
+
function \u7DDA\u306E\u6E2C\u308A\u65B9\u3092\u4F5C\u308B() {
|
|
13368
13360
|
const \u533A\u9593cache = /* @__PURE__ */ new Map();
|
|
13369
13361
|
const \u533A\u9593 = (id, d) => {
|
|
13370
13362
|
const hit = \u533A\u9593cache.get(id);
|
|
@@ -13400,7 +13392,143 @@ function resolveLabelClearanceAfterPathPasses(edges, obstacles, maxIter = 8) {
|
|
|
13400
13392
|
const dy = Math.max(0, Math.max(box.y - (b.y + b.h), b.y - (box.y + box.h)));
|
|
13401
13393
|
return Math.hypot(dx, dy) >= \u4F59\u88D5;
|
|
13402
13394
|
};
|
|
13403
|
-
|
|
13395
|
+
return { \u533A\u9593, \u9060\u3044 };
|
|
13396
|
+
}
|
|
13397
|
+
var \u672D\u306E\u6700\u5927\u79FB\u52D5 = CHAIN_SHIFT_MAX_ACCUMULATED * 2;
|
|
13398
|
+
var \u96E2\u308C\u91CF = (a, b) => ({
|
|
13399
|
+
x: Math.max(a.x - (b.x + b.w), b.x - (a.x + a.w)),
|
|
13400
|
+
y: Math.max(a.y - (b.y + b.h), b.y - (a.y + a.h))
|
|
13401
|
+
});
|
|
13402
|
+
var \u52D5\u304B\u3057\u305F\u7BB1 = (box, axis, sign, amount) => ({
|
|
13403
|
+
...box,
|
|
13404
|
+
x: box.x + (axis === "x" ? sign * amount : 0),
|
|
13405
|
+
y: box.y + (axis === "y" ? sign * amount : 0)
|
|
13406
|
+
});
|
|
13407
|
+
function \u672D\u306E\u79FB\u52D5\u3092\u585E\u3050\u76F8\u624B\u3092\u63A2\u3059(\u5468\u308A, label, \u884C\u304D\u5148) {
|
|
13408
|
+
const { \u7BB1\u305F\u3061, labels, edges, edgeById, \u6E2C\u308A\u65B9 } = \u5468\u308A;
|
|
13409
|
+
for (const obs of \u7BB1\u305F\u3061) {
|
|
13410
|
+
const \u524D = computeGap(label, obs);
|
|
13411
|
+
const \u5F8C = computeGap(\u884C\u304D\u5148, obs);
|
|
13412
|
+
if (\u5F8C < Math.min(\u524D, requiredNearClearance("edge-label", obs.kind))) return { \u76F8\u624B: `${obs.kind}:${obs.id}`, \u524D, \u5F8C };
|
|
13413
|
+
}
|
|
13414
|
+
const \u672D\u306E\u8981\u6C42 = requiredNearClearance("edge-label", "edge-label");
|
|
13415
|
+
for (const \u4ED6 of labels) {
|
|
13416
|
+
if (\u4ED6.id === label.id) continue;
|
|
13417
|
+
const \u524D = computeGap(label, \u4ED6);
|
|
13418
|
+
const \u5F8C = computeGap(\u884C\u304D\u5148, \u4ED6);
|
|
13419
|
+
if (\u5F8C < Math.min(\u524D, \u672D\u306E\u8981\u6C42)) return { \u76F8\u624B: `edge-label:${\u4ED6.id}`, \u524D, \u5F8C };
|
|
13420
|
+
}
|
|
13421
|
+
const \u81EA\u5206 = edgeById.get(label.id);
|
|
13422
|
+
const \u81EA\u5206\u306E\u533A\u9593 = \u81EA\u5206 ? \u6E2C\u308A\u65B9.\u533A\u9593(\u81EA\u5206.id, \u81EA\u5206.d) : [];
|
|
13423
|
+
if (\u81EA\u5206\u306E\u533A\u9593.length > 0) {
|
|
13424
|
+
const \u524D = measurePathLabelGapSegs(\u81EA\u5206\u306E\u533A\u9593, label);
|
|
13425
|
+
const \u5F8C = measurePathLabelGapSegs(\u81EA\u5206\u306E\u533A\u9593, \u884C\u304D\u5148);
|
|
13426
|
+
if (\u5F8C > Math.max(\u524D, DIST_LABEL_PATH_MAX)) return { \u76F8\u624B: "\u81EA\u5206\u306E\u7DDA", \u524D, \u5F8C };
|
|
13427
|
+
}
|
|
13428
|
+
for (const \u76F8\u624B of edges) {
|
|
13429
|
+
if (\u76F8\u624B.id === label.id) continue;
|
|
13430
|
+
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;
|
|
13431
|
+
const segs = segmentsExcludingOwnPath(\u6E2C\u308A\u65B9.\u533A\u9593(\u76F8\u624B.id, \u76F8\u624B.d), \u81EA\u5206\u306E\u533A\u9593);
|
|
13432
|
+
if (segs.length === 0) continue;
|
|
13433
|
+
const \u524D = measurePathLabelGapSegs(segs, label);
|
|
13434
|
+
const \u5F8C = measurePathLabelGapSegs(segs, \u884C\u304D\u5148);
|
|
13435
|
+
if (\u5F8C < Math.min(\u524D, LABEL_TO_PATH_CLEARANCE)) return { \u76F8\u624B: `edge-path:${\u76F8\u624B.id}`, \u524D, \u5F8C };
|
|
13436
|
+
}
|
|
13437
|
+
return null;
|
|
13438
|
+
}
|
|
13439
|
+
function \u77E9\u5F62\u306E\u5019\u88DC(label, static\u7BB1, required) {
|
|
13440
|
+
const sep = \u96E2\u308C\u91CF(label, static\u7BB1);
|
|
13441
|
+
const \u5411\u304D = (axis) => {
|
|
13442
|
+
const a = axis === "x" ? label.x + label.w / 2 : label.y + label.h / 2;
|
|
13443
|
+
const b = axis === "x" ? static\u7BB1.x + static\u7BB1.w / 2 : static\u7BB1.y + static\u7BB1.h / 2;
|
|
13444
|
+
return a >= b ? 1 : -1;
|
|
13445
|
+
};
|
|
13446
|
+
return [
|
|
13447
|
+
{ axis: "x", sign: \u5411\u304D("x"), amount: required - sep.x + NEAR_MISS_MARGIN },
|
|
13448
|
+
{ axis: "y", sign: \u5411\u304D("y"), amount: required - sep.y + NEAR_MISS_MARGIN }
|
|
13449
|
+
];
|
|
13450
|
+
}
|
|
13451
|
+
function \u7DDA\u304B\u3089\u96E2\u308C\u308B\u91CF(segs, label, gap, axis, sign) {
|
|
13452
|
+
const \u6700\u5927\u79FB\u52D5 = \u672D\u306E\u6700\u5927\u79FB\u52D5;
|
|
13453
|
+
const \u6E80\u305F\u3059 = (\u91CF) => measurePathLabelGapSegs(segs, \u52D5\u304B\u3057\u305F\u7BB1(label, axis, sign, \u91CF)) >= LABEL_TO_PATH_CLEARANCE;
|
|
13454
|
+
let lo = 0;
|
|
13455
|
+
let hi = Math.max(4, LABEL_TO_PATH_CLEARANCE - gap);
|
|
13456
|
+
while (!\u6E80\u305F\u3059(hi)) {
|
|
13457
|
+
if (hi >= \u6700\u5927\u79FB\u52D5) return null;
|
|
13458
|
+
lo = hi;
|
|
13459
|
+
hi = Math.min(hi * 2, \u6700\u5927\u79FB\u52D5);
|
|
13460
|
+
}
|
|
13461
|
+
for (let k = 0; k < 12; k += 1) {
|
|
13462
|
+
const mid = (lo + hi) / 2;
|
|
13463
|
+
if (\u6E80\u305F\u3059(mid)) hi = mid;
|
|
13464
|
+
else lo = mid;
|
|
13465
|
+
}
|
|
13466
|
+
return hi + NEAR_MISS_MARGIN;
|
|
13467
|
+
}
|
|
13468
|
+
var \u5411\u304D\u306E\u9806 = [
|
|
13469
|
+
{ axis: "y", sign: -1, \u540D\u524D: "\u4E0A" },
|
|
13470
|
+
{ axis: "y", sign: 1, \u540D\u524D: "\u4E0B" },
|
|
13471
|
+
{ axis: "x", sign: -1, \u540D\u524D: "\u5DE6" },
|
|
13472
|
+
{ axis: "x", sign: 1, \u540D\u524D: "\u53F3" }
|
|
13473
|
+
];
|
|
13474
|
+
function \u672D\u304C\u52D5\u3051\u306A\u3044\u7406\u7531(labelId, \u76F8\u624B, edges, obstacles) {
|
|
13475
|
+
const edgeById = new Map(edges.map((e) => [e.id, e]));
|
|
13476
|
+
const \u81EA\u5206 = edgeById.get(labelId);
|
|
13477
|
+
if (!\u81EA\u5206) return null;
|
|
13478
|
+
const labels = edges.filter((e) => e.label?.trim() || e.sub?.trim()).map((e) => ({ kind: "edge-label", id: e.id, ...computeLabelBBoxWorld(e) }));
|
|
13479
|
+
const label = labels.find((l) => l.id === labelId);
|
|
13480
|
+
if (!label) return null;
|
|
13481
|
+
const \u6E2C\u308A\u65B9 = \u7DDA\u306E\u6E2C\u308A\u65B9\u3092\u4F5C\u308B();
|
|
13482
|
+
const \u5468\u308A = {
|
|
13483
|
+
\u7BB1\u305F\u3061: obstacles.filter((o) => o.kind === "node" || o.kind === "lane-label"),
|
|
13484
|
+
labels,
|
|
13485
|
+
edges,
|
|
13486
|
+
edgeById,
|
|
13487
|
+
\u6E2C\u308A\u65B9
|
|
13488
|
+
};
|
|
13489
|
+
const \u5019\u88DC = [];
|
|
13490
|
+
if (\u76F8\u624B.kind === "edge-path") {
|
|
13491
|
+
const \u7DDA = edgeById.get(\u76F8\u624B.id);
|
|
13492
|
+
if (!\u7DDA) return null;
|
|
13493
|
+
const segs = segmentsExcludingOwnPath(\u6E2C\u308A\u65B9.\u533A\u9593(\u7DDA.id, \u7DDA.d), \u6E2C\u308A\u65B9.\u533A\u9593(\u81EA\u5206.id, \u81EA\u5206.d));
|
|
13494
|
+
if (segs.length === 0) return null;
|
|
13495
|
+
const gap = measurePathLabelGapSegs(segs, label);
|
|
13496
|
+
for (const \u5411\u304D of \u5411\u304D\u306E\u9806) {
|
|
13497
|
+
\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) });
|
|
13498
|
+
}
|
|
13499
|
+
} else {
|
|
13500
|
+
const required = requiredNearClearance("edge-label", \u76F8\u624B.kind);
|
|
13501
|
+
for (const c of \u77E9\u5F62\u306E\u5019\u88DC(label, \u76F8\u624B, required)) {
|
|
13502
|
+
if (c.amount > 0) \u5019\u88DC.push(c);
|
|
13503
|
+
}
|
|
13504
|
+
}
|
|
13505
|
+
if (\u5019\u88DC.length === 0) return null;
|
|
13506
|
+
const \u7406\u7531 = [];
|
|
13507
|
+
for (const c of \u5019\u88DC) {
|
|
13508
|
+
const \u9806 = \u5411\u304D\u306E\u9806.findIndex((v) => v.axis === c.axis && v.sign === c.sign);
|
|
13509
|
+
const \u540D\u524D = \u5411\u304D\u306E\u9806[\u9806].\u540D\u524D;
|
|
13510
|
+
if (c.amount === null || c.amount > \u672D\u306E\u6700\u5927\u79FB\u52D5) {
|
|
13511
|
+
\u7406\u7531.push({ \u9806, \u6587: `${\u540D\u524D}=\u4E0A\u9650 ${\u672D\u306E\u6700\u5927\u79FB\u52D5} \u3092\u8D8A\u3048\u308B` });
|
|
13512
|
+
continue;
|
|
13513
|
+
}
|
|
13514
|
+
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));
|
|
13515
|
+
if (!\u585E\u3050) return null;
|
|
13516
|
+
\u7406\u7531.push({ \u9806, \u6587: `${\u540D\u524D}=${\u585E\u3050.\u76F8\u624B} (${\u585E\u3050.\u524D.toFixed(0)}\u2192${\u585E\u3050.\u5F8C.toFixed(0)})` });
|
|
13517
|
+
}
|
|
13518
|
+
return `\u9003\u3052\u5834\u306A\u3057: ${\u7406\u7531.sort((a, b) => a.\u9806 - b.\u9806).map((r) => r.\u6587).join(" / ")}`;
|
|
13519
|
+
}
|
|
13520
|
+
function resolveLabelClearanceAfterPathPasses(edges, obstacles, maxIter = 8) {
|
|
13521
|
+
if (edges.length === 0) return edges;
|
|
13522
|
+
const labeledIds = new Set(
|
|
13523
|
+
edges.filter((e) => e.label?.trim() || e.sub?.trim()).map((e) => e.id)
|
|
13524
|
+
);
|
|
13525
|
+
if (labeledIds.size === 0) return edges;
|
|
13526
|
+
const \u7BB1\u305F\u3061 = obstacles.filter((o) => o.kind === "node" || o.kind === "lane-label");
|
|
13527
|
+
const edgeById = /* @__PURE__ */ new Map();
|
|
13528
|
+
for (const e of edges) edgeById.set(e.id, e);
|
|
13529
|
+
const \u6E2C\u308A\u65B9 = \u7DDA\u306E\u6E2C\u308A\u65B9\u3092\u4F5C\u308B();
|
|
13530
|
+
const { \u533A\u9593, \u9060\u3044 } = \u6E2C\u308A\u65B9;
|
|
13531
|
+
const \u6700\u5927\u79FB\u52D5 = \u672D\u306E\u6700\u5927\u79FB\u52D5;
|
|
13404
13532
|
const shiftMap = /* @__PURE__ */ new Map();
|
|
13405
13533
|
const getShift = (id) => shiftMap.get(id) ?? { dx: 0, dy: 0 };
|
|
13406
13534
|
const labelBBoxes = () => {
|
|
@@ -13413,50 +13541,12 @@ function resolveLabelClearanceAfterPathPasses(edges, obstacles, maxIter = 8) {
|
|
|
13413
13541
|
}
|
|
13414
13542
|
return out;
|
|
13415
13543
|
};
|
|
13416
|
-
const \u96E2\u308C\u91CF = (a, b) => ({
|
|
13417
|
-
x: Math.max(a.x - (b.x + b.w), b.x - (a.x + a.w)),
|
|
13418
|
-
y: Math.max(a.y - (b.y + b.h), b.y - (a.y + a.h))
|
|
13419
|
-
});
|
|
13420
|
-
const \u52D5\u304B\u3057\u305F\u7BB1 = (box, axis, sign, amount) => ({
|
|
13421
|
-
...box,
|
|
13422
|
-
x: box.x + (axis === "x" ? sign * amount : 0),
|
|
13423
|
-
y: box.y + (axis === "y" ? sign * amount : 0)
|
|
13424
|
-
});
|
|
13425
13544
|
for (let iter = 0; iter < maxIter; iter += 1) {
|
|
13426
13545
|
const labels = labelBBoxes();
|
|
13427
13546
|
const \u672D\u306E\u8981\u6C42 = requiredNearClearance("edge-label", "edge-label");
|
|
13428
13547
|
let \u52D5\u3044\u305F = false;
|
|
13429
13548
|
const \u4ECA\u56DE\u52D5\u304B\u3057\u305F = /* @__PURE__ */ new Set();
|
|
13430
|
-
const \u5B89\u5168\u304B = (label, \u884C\u304D\u5148) => {
|
|
13431
|
-
for (const obs of \u7BB1\u305F\u3061) {
|
|
13432
|
-
const \u524D = computeGap(label, obs);
|
|
13433
|
-
const \u5F8C = computeGap(\u884C\u304D\u5148, obs);
|
|
13434
|
-
if (\u5F8C < Math.min(\u524D, requiredNearClearance("edge-label", obs.kind))) return false;
|
|
13435
|
-
}
|
|
13436
|
-
for (const \u4ED6 of labels) {
|
|
13437
|
-
if (\u4ED6.id === label.id) continue;
|
|
13438
|
-
const \u524D = computeGap(label, \u4ED6);
|
|
13439
|
-
const \u5F8C = computeGap(\u884C\u304D\u5148, \u4ED6);
|
|
13440
|
-
if (\u5F8C < Math.min(\u524D, \u672D\u306E\u8981\u6C42)) return false;
|
|
13441
|
-
}
|
|
13442
|
-
const \u81EA\u5206 = edgeById.get(label.id);
|
|
13443
|
-
const \u81EA\u5206\u306E\u533A\u9593 = \u81EA\u5206 ? \u533A\u9593(\u81EA\u5206.id, \u81EA\u5206.d) : [];
|
|
13444
|
-
if (\u81EA\u5206\u306E\u533A\u9593.length > 0) {
|
|
13445
|
-
const \u524D = measurePathLabelGapSegs(\u81EA\u5206\u306E\u533A\u9593, label);
|
|
13446
|
-
const \u5F8C = measurePathLabelGapSegs(\u81EA\u5206\u306E\u533A\u9593, \u884C\u304D\u5148);
|
|
13447
|
-
if (\u5F8C > Math.max(\u524D, DIST_LABEL_PATH_MAX)) return false;
|
|
13448
|
-
}
|
|
13449
|
-
for (const \u76F8\u624B of edges) {
|
|
13450
|
-
if (\u76F8\u624B.id === label.id) continue;
|
|
13451
|
-
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;
|
|
13452
|
-
const segs = segmentsExcludingOwnPath(\u533A\u9593(\u76F8\u624B.id, \u76F8\u624B.d), \u81EA\u5206\u306E\u533A\u9593);
|
|
13453
|
-
if (segs.length === 0) continue;
|
|
13454
|
-
const \u524D = measurePathLabelGapSegs(segs, label);
|
|
13455
|
-
const \u5F8C = measurePathLabelGapSegs(segs, \u884C\u304D\u5148);
|
|
13456
|
-
if (\u5F8C < Math.min(\u524D, LABEL_TO_PATH_CLEARANCE)) return false;
|
|
13457
|
-
}
|
|
13458
|
-
return true;
|
|
13459
|
-
};
|
|
13549
|
+
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;
|
|
13460
13550
|
const \u9003\u304C\u3059 = (label, \u5019\u88DC) => {
|
|
13461
13551
|
for (const c of \u5019\u88DC.slice().sort((a, b) => a.amount - b.amount)) {
|
|
13462
13552
|
if (c.amount <= 0) continue;
|
|
@@ -13472,18 +13562,6 @@ function resolveLabelClearanceAfterPathPasses(edges, obstacles, maxIter = 8) {
|
|
|
13472
13562
|
}
|
|
13473
13563
|
return false;
|
|
13474
13564
|
};
|
|
13475
|
-
const \u77E9\u5F62\u306E\u5019\u88DC = (label, static\u7BB1, required) => {
|
|
13476
|
-
const sep = \u96E2\u308C\u91CF(label, static\u7BB1);
|
|
13477
|
-
const \u5411\u304D = (axis) => {
|
|
13478
|
-
const a = axis === "x" ? label.x + label.w / 2 : label.y + label.h / 2;
|
|
13479
|
-
const b = axis === "x" ? static\u7BB1.x + static\u7BB1.w / 2 : static\u7BB1.y + static\u7BB1.h / 2;
|
|
13480
|
-
return a >= b ? 1 : -1;
|
|
13481
|
-
};
|
|
13482
|
-
return [
|
|
13483
|
-
{ axis: "x", sign: \u5411\u304D("x"), amount: required - sep.x + NEAR_MISS_MARGIN },
|
|
13484
|
-
{ axis: "y", sign: \u5411\u304D("y"), amount: required - sep.y + NEAR_MISS_MARGIN }
|
|
13485
|
-
];
|
|
13486
|
-
};
|
|
13487
13565
|
for (const label of labels) {
|
|
13488
13566
|
if (\u4ECA\u56DE\u52D5\u304B\u3057\u305F.has(label.id)) continue;
|
|
13489
13567
|
for (const obs of \u7BB1\u305F\u3061) {
|
|
@@ -13540,22 +13618,7 @@ function resolveLabelClearanceAfterPathPasses(edges, obstacles, maxIter = 8) {
|
|
|
13540
13618
|
}
|
|
13541
13619
|
}
|
|
13542
13620
|
if (!\u8FD1\u3044\u70B9) continue;
|
|
13543
|
-
const \u5FC5\u8981\u91CF = (axis, sign) =>
|
|
13544
|
-
const \u6E80\u305F\u3059 = (\u91CF) => measurePathLabelGapSegs(segs, \u52D5\u304B\u3057\u305F\u7BB1(label, axis, sign, \u91CF)) >= LABEL_TO_PATH_CLEARANCE;
|
|
13545
|
-
let lo = 0;
|
|
13546
|
-
let hi = Math.max(4, LABEL_TO_PATH_CLEARANCE - gap);
|
|
13547
|
-
while (!\u6E80\u305F\u3059(hi)) {
|
|
13548
|
-
if (hi >= \u6700\u5927\u79FB\u52D5) return null;
|
|
13549
|
-
lo = hi;
|
|
13550
|
-
hi = Math.min(hi * 2, \u6700\u5927\u79FB\u52D5);
|
|
13551
|
-
}
|
|
13552
|
-
for (let k = 0; k < 12; k += 1) {
|
|
13553
|
-
const mid = (lo + hi) / 2;
|
|
13554
|
-
if (\u6E80\u305F\u3059(mid)) hi = mid;
|
|
13555
|
-
else lo = mid;
|
|
13556
|
-
}
|
|
13557
|
-
return hi + NEAR_MISS_MARGIN;
|
|
13558
|
-
};
|
|
13621
|
+
const \u5FC5\u8981\u91CF = (axis, sign) => \u7DDA\u304B\u3089\u96E2\u308C\u308B\u91CF(segs, label, gap, axis, sign);
|
|
13559
13622
|
const dx = \u82AF.x - \u8FD1\u3044\u70B9.x;
|
|
13560
13623
|
const dy = \u82AF.y - \u8FD1\u3044\u70B9.y;
|
|
13561
13624
|
const \u5019\u88DC = [];
|
|
@@ -15987,9 +16050,15 @@ function visualValidateLaid(laid, diag, opts) {
|
|
|
15987
16050
|
const counts = emptyCounts();
|
|
15988
16051
|
const profile = opts?.profile ?? "production";
|
|
15989
16052
|
const skipped = SKIPPED_AXES[profile];
|
|
15990
|
-
const push = (axis, detail, severity = "error") => {
|
|
16053
|
+
const push = (axis, detail, severity = "error", reason) => {
|
|
15991
16054
|
if (skipped.has(axis)) return;
|
|
15992
|
-
violations.push({
|
|
16055
|
+
violations.push({
|
|
16056
|
+
axis,
|
|
16057
|
+
diagramId: safeDiagramId(diag),
|
|
16058
|
+
detail,
|
|
16059
|
+
severity,
|
|
16060
|
+
...reason !== void 0 ? { reason } : {}
|
|
16061
|
+
});
|
|
15993
16062
|
counts[axis]++;
|
|
15994
16063
|
};
|
|
15995
16064
|
return visualValidateInner(
|
|
@@ -16008,9 +16077,15 @@ function visualValidate(diag, opts) {
|
|
|
16008
16077
|
const counts = emptyCounts();
|
|
16009
16078
|
const profile = opts?.profile ?? "production";
|
|
16010
16079
|
const skipped = SKIPPED_AXES[profile];
|
|
16011
|
-
const push = (axis, detail, severity = "error") => {
|
|
16080
|
+
const push = (axis, detail, severity = "error", reason) => {
|
|
16012
16081
|
if (skipped.has(axis)) return;
|
|
16013
|
-
violations.push({
|
|
16082
|
+
violations.push({
|
|
16083
|
+
axis,
|
|
16084
|
+
diagramId: safeDiagramId(diag),
|
|
16085
|
+
detail,
|
|
16086
|
+
severity,
|
|
16087
|
+
...reason !== void 0 ? { reason } : {}
|
|
16088
|
+
});
|
|
16014
16089
|
counts[axis]++;
|
|
16015
16090
|
};
|
|
16016
16091
|
const diagProblems = findMalformedInput(void 0, diag).filter(
|
|
@@ -16347,10 +16422,19 @@ function runAxes(laid, diag, violations, counts, push, profile, skippedAxes, \u5
|
|
|
16347
16422
|
}
|
|
16348
16423
|
}
|
|
16349
16424
|
const nears = detectNearCollisions(bboxes, laid.edges);
|
|
16425
|
+
const \u672D\u306E\u969C\u5BB3\u7269 = bboxes.filter((b) => b.kind === "node" || b.kind === "lane-label");
|
|
16426
|
+
const \u8FBA\u306E\u9806 = new Map(laid.edges.map((e, i) => [e.id, i]));
|
|
16350
16427
|
for (const n of nears) {
|
|
16428
|
+
const a\u306F\u672D = n.a.kind === "edge-label";
|
|
16429
|
+
const b\u306F\u672D = n.b.kind === "edge-label";
|
|
16430
|
+
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;
|
|
16431
|
+
const \u76F8\u624B = \u52D5\u304B\u3059 === n.a ? n.b : n.a;
|
|
16432
|
+
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;
|
|
16351
16433
|
push(
|
|
16352
16434
|
"clearance",
|
|
16353
|
-
`${n.a.kind}:${n.a.id} \u2194 ${n.b.kind}:${n.b.id} gap=${n.gap.toFixed(1)}px (need ${n.required}px)
|
|
16435
|
+
`${n.a.kind}:${n.a.id} \u2194 ${n.b.kind}:${n.b.id} gap=${n.gap.toFixed(1)}px (need ${n.required}px)`,
|
|
16436
|
+
"error",
|
|
16437
|
+
reason
|
|
16354
16438
|
);
|
|
16355
16439
|
}
|
|
16356
16440
|
const PROXIMITY_WARN_THRESHOLD = DIST_LABEL_PATH_MAX;
|
|
@@ -21270,10 +21354,11 @@ function layoutWithValidation(diag, opts = {}) {
|
|
|
21270
21354
|
}
|
|
21271
21355
|
if (opts.emitConsole) {
|
|
21272
21356
|
for (const v of report.violations) {
|
|
21357
|
+
const \u7406\u7531 = v.reason !== void 0 ? ` ${v.reason}` : "";
|
|
21273
21358
|
if (v.severity === "error") {
|
|
21274
|
-
console.error(`[cdl geometry ${v.axis}] ${v.detail} (diagram=${v.diagramId})`);
|
|
21359
|
+
console.error(`[cdl geometry ${v.axis}] ${v.detail} (diagram=${v.diagramId})${\u7406\u7531}`);
|
|
21275
21360
|
} else {
|
|
21276
|
-
console.warn(`[cdl geometry ${v.axis}] ${v.detail} (diagram=${v.diagramId})`);
|
|
21361
|
+
console.warn(`[cdl geometry ${v.axis}] ${v.detail} (diagram=${v.diagramId})${\u7406\u7531}`);
|
|
21277
21362
|
}
|
|
21278
21363
|
}
|
|
21279
21364
|
}
|