@cardenelabs/cdl 0.47.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/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\u7BB1 = (axis, sign) => ({
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\u7BB1(\u5019.axis, \u5019.sign))) continue;
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 resolveLabelClearanceAfterPathPasses(edges, obstacles, maxIter = 8) {
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
- const \u6700\u5927\u79FB\u52D5 = CHAIN_SHIFT_MAX_ACCUMULATED * 2;
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({ axis, diagramId: safeDiagramId(diag), detail, severity });
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({ axis, diagramId: safeDiagramId(diag), detail, severity });
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;
@@ -19576,33 +19660,73 @@ function placeAvoidingSiblingPaths(args) {
19576
19660
  }
19577
19661
  };
19578
19662
  consider([...wants]);
19663
+ const \u62BC\u3059\u91CF = (idx, side, pos) => {
19664
+ let \u8981\u308B = 0;
19665
+ for (const i of idx) {
19666
+ const box = boxes[i];
19667
+ const rect = { x: box.x, y: pos[i] - box.h / 2, w: box.w, h: box.h };
19668
+ for (const probe of probeSegs[i]) {
19669
+ if (measurePathLabelGapSegs(probe.segs, rect) >= CLEARANCE_PATH_LABEL) continue;
19670
+ let lo = Infinity;
19671
+ let hi = -Infinity;
19672
+ for (const seg of probe.segs) {
19673
+ const c = clipY(seg, box.x, box.x + box.w);
19674
+ if (!c) continue;
19675
+ lo = Math.min(lo, c.lo);
19676
+ hi = Math.max(hi, c.hi);
19677
+ }
19678
+ if (lo > hi) continue;
19679
+ const \u884C\u304D\u5148 = side === "above" ? lo - box.h / 2 - CLEARANCE_PATH_LABEL : hi + box.h / 2 + CLEARANCE_PATH_LABEL;
19680
+ \u8981\u308B = Math.max(\u8981\u308B, side === "above" ? pos[i] - \u884C\u304D\u5148 : \u884C\u304D\u5148 - pos[i]);
19681
+ }
19682
+ }
19683
+ return Math.max(0, \u8981\u308B);
19684
+ };
19579
19685
  if (bands.some((b) => b !== null)) {
19580
19686
  for (let split = 0; split <= order.length; split++) {
19581
19687
  const above = order.slice(0, split);
19582
19688
  const below = order.slice(split);
19583
- const out = new Array(members.length);
19584
- for (const [idx, side] of [
19585
- [above, "above"],
19586
- [below, "below"]
19587
- ]) {
19588
- if (idx.length === 0) continue;
19589
- const ys = spread(idx);
19590
- let shift = 0;
19591
- for (let k = 0; k < idx.length; k++) {
19592
- const band = bands[idx[k]];
19593
- if (!band) continue;
19594
- shift = side === "above" ? Math.min(shift, band.lo - ys[k]) : Math.max(shift, band.hi - ys[k]);
19595
- }
19596
- for (let k = 0; k < idx.length; k++) out[idx[k]] = ys[k] + shift;
19597
- }
19598
- if (above.length > 0 && below.length > 0) {
19689
+ const \u9593\u3092\u7A7A\u3051\u308B = (out) => {
19690
+ if (above.length === 0 || below.length === 0) return;
19599
19691
  const lastIdx = above[above.length - 1];
19600
19692
  const firstIdx = below[0];
19601
19693
  const need = (sizes[lastIdx] + sizes[firstIdx]) / 2 + CLEARANCE_LABEL_LABEL;
19602
19694
  const deficit = need - (out[firstIdx] - out[lastIdx]);
19603
- if (deficit > 0) for (const i of below) out[i] += deficit;
19604
- }
19605
- consider(out);
19695
+ if (deficit > 0) for (const i of below) out[i] = out[i] + deficit;
19696
+ };
19697
+ const \u5E2F\u3092\u8D8A\u3048\u308B = () => {
19698
+ const out = new Array(members.length);
19699
+ for (const [idx, side] of [
19700
+ [above, "above"],
19701
+ [below, "below"]
19702
+ ]) {
19703
+ if (idx.length === 0) continue;
19704
+ const ys = spread(idx);
19705
+ let shift = 0;
19706
+ for (let k = 0; k < idx.length; k++) {
19707
+ const band = bands[idx[k]];
19708
+ if (!band) continue;
19709
+ shift = side === "above" ? Math.min(shift, band.lo - ys[k]) : Math.max(shift, band.hi - ys[k]);
19710
+ }
19711
+ for (let k = 0; k < idx.length; k++) out[idx[k]] = ys[k] + shift;
19712
+ }
19713
+ \u9593\u3092\u7A7A\u3051\u308B(out);
19714
+ return out;
19715
+ };
19716
+ const \u62BC\u3057\u3066\u8D8A\u3048\u308B = () => {
19717
+ const out = \u5E2F\u3092\u8D8A\u3048\u308B();
19718
+ for (let round = 0; round < 4; round += 1) {
19719
+ const a = \u62BC\u3059\u91CF(above, "above", out);
19720
+ const b = \u62BC\u3059\u91CF(below, "below", out);
19721
+ if (a === 0 && b === 0) return out;
19722
+ for (const i of above) out[i] = out[i] - a;
19723
+ for (const i of below) out[i] = out[i] + b;
19724
+ }
19725
+ return \u62BC\u3059\u91CF(above, "above", out) === 0 && \u62BC\u3059\u91CF(below, "below", out) === 0 ? out : null;
19726
+ };
19727
+ consider(\u5E2F\u3092\u8D8A\u3048\u308B());
19728
+ const \u62BC\u3057\u305F = \u62BC\u3057\u3066\u8D8A\u3048\u308B();
19729
+ if (\u62BC\u3057\u305F) consider(\u62BC\u3057\u305F);
19606
19730
  }
19607
19731
  }
19608
19732
  const plain = spread(all);
@@ -21230,10 +21354,11 @@ function layoutWithValidation(diag, opts = {}) {
21230
21354
  }
21231
21355
  if (opts.emitConsole) {
21232
21356
  for (const v of report.violations) {
21357
+ const \u7406\u7531 = v.reason !== void 0 ? ` ${v.reason}` : "";
21233
21358
  if (v.severity === "error") {
21234
- 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}`);
21235
21360
  } else {
21236
- 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}`);
21237
21362
  }
21238
21363
  }
21239
21364
  }