@cardenelabs/cdl 0.64.0 → 0.64.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,36 @@ CDL (Chainome Diagram Language) の主要変更履歴。
5
5
 
6
6
  ## [Unreleased]
7
7
 
8
+ ## [0.64.1] - 2026-09-15
9
+
10
+ ### Fixed
11
+
12
+ - **別の段に置いた lane の枠線を、説明文が跨いだと指摘しないようにした** (#863)
13
+
14
+ 検証器の軸 64 (`lane-border-clearance`) は説明文の箱と lane の左右の枠線を横の範囲だけで比べ、全ての lane を
15
+ 「図の上から下まで続く列」 として扱っていた。 縦の範囲を持つ lane の真下や真上に別の段があると、その段の
16
+ 説明文が枠線の延長上に来ただけで指摘していた。 枠線はそこに描かれていないので、見た目は何も跨いでいない。
17
+
18
+ 縦の範囲を持つ lane は、説明文と縦に重なる時だけ比べる。
19
+
20
+ | lane | 縦の範囲 | 比べ方 |
21
+ |---|---|---|
22
+ | `contain: true` | 中の node を囲む枠 (`y` / `height`) | 枠と縦に重なる説明文だけ |
23
+ | `posX` / `posY` / `posH` を揃えた lane | 置いた `posY` から `posH` まで | 同上 |
24
+ | 上の 2 つ以外 | 持たない (`height` は名札の帯の高さ) | 変更前と同じく上下いっぱいの列 |
25
+
26
+ 範囲を持つ lane でも、中の node が範囲の外にあれば その node の上下まで範囲を広げる (高さを指定した lane に
27
+ 座標を持たない node を入れると、node は段の位置に並んで範囲の外に出るため)。
28
+
29
+ 判定は `rectCrossesLaneBorder` (`layout/spec.ts`) に 1 本化し、engine が説明文の置き場所を選ぶ 2 か所
30
+ (左への迂回 `leftRouteIsBlocked` と説明文の反転 `flipLabelToFreeSide`) も同じ関数を呼ぶ。 これまでは
31
+ 「検証器と同一規約」 と書きながら別々に実装しており、検証器だけを直すと engine が別の段の枠線を避け続けて
32
+ 空いている側へ説明文を移さなくなる。
33
+
34
+ dragon のカタログ 502 図で配置 (edge の経路と説明文の位置) は 1 図も変わらない。 効くのは部品のように
35
+ `posX` / `posY` / `posH` で段に分けて置いた図で、dragon は部品 80 種を 2 段に並べた時の指摘が消える
36
+ (dragon#1993)。
37
+
8
38
  ## [0.64.0] - 2026-09-14
9
39
 
10
40
  ### Fixed
package/dist/index.cjs CHANGED
@@ -12557,6 +12557,25 @@ var ARROW_ENDPOINT_CENTER_TOL = 8;
12557
12557
  var ROW_GAP_VARIANCE_TOL = 40;
12558
12558
  var COLUMN_GAP_VARIANCE_TOL = 40;
12559
12559
  var LANE_BORDER_CLEARANCE_TOL = 2;
12560
+ function laneVerticalRange(lane, nodes) {
12561
+ const pinnedWithHeight = lane.posX !== void 0 && lane.posY !== void 0 && lane.posH !== void 0;
12562
+ if (!lane.contain && !pinnedWithHeight) return void 0;
12563
+ let top = lane.y;
12564
+ let bottom = lane.y + lane.height;
12565
+ for (const n of nodes) {
12566
+ if (n.lane !== lane.id) continue;
12567
+ top = Math.min(top, n.cy - n.h / 2);
12568
+ bottom = Math.max(bottom, n.cy + n.h / 2);
12569
+ }
12570
+ return { top, bottom };
12571
+ }
12572
+ function rectCrossesLaneBorder(rect, lane, range) {
12573
+ if (range !== void 0 && (rect.y + rect.h <= range.top || rect.y >= range.bottom)) return void 0;
12574
+ const crosses = (lineX) => rect.x < lineX - LANE_BORDER_CLEARANCE_TOL && rect.x + rect.w > lineX + LANE_BORDER_CLEARANCE_TOL;
12575
+ if (crosses(lane.x)) return "left";
12576
+ if (crosses(lane.x + lane.width)) return "right";
12577
+ return void 0;
12578
+ }
12560
12579
  var NEAR_COLLISION_POLICY = {
12561
12580
  "node|node": 70,
12562
12581
  "node|edge-label": 32,
@@ -18148,6 +18167,7 @@ function runAxes(laid, diag, violations, counts, push, profile, skippedAxes, \u5
18148
18167
  }
18149
18168
  }
18150
18169
  {
18170
+ const rangeByLane = new Map(laid.lanes.map((lane) => [lane.id, laneVerticalRange(lane, laid.nodes)]));
18151
18171
  for (const e of laid.edges) {
18152
18172
  if (!hasRenderedLabel(e)) continue;
18153
18173
  const fromNode = nodeById.get(e.from);
@@ -18156,21 +18176,14 @@ function runAxes(laid, diag, violations, counts, push, profile, skippedAxes, \u5
18156
18176
  const allowedLanes = /* @__PURE__ */ new Set();
18157
18177
  if (fromNode) allowedLanes.add(fromNode.lane);
18158
18178
  if (toNode) allowedLanes.add(toNode.lane);
18159
- const boxW = computeLabelBoxW(e.label, e.sub);
18160
- const anchor = e.labelAnchor;
18161
- const boxLeftX = anchor === "start" ? 0 : anchor === "end" ? -boxW : -boxW / 2;
18162
- const bx = e.labelX + boxLeftX;
18163
- const bxRight = bx + boxW;
18179
+ const box = computeLabelBBoxWorld(e);
18164
18180
  for (const lane of laid.lanes) {
18165
18181
  if (allowedLanes.has(lane.id)) continue;
18166
- const laneLeft = lane.x;
18167
- const laneRight = lane.x + lane.width;
18168
- const crossesLeft = bx < laneLeft - LANE_BORDER_CLEARANCE_TOL && bxRight > laneLeft + LANE_BORDER_CLEARANCE_TOL;
18169
- const crossesRight = bx < laneRight - LANE_BORDER_CLEARANCE_TOL && bxRight > laneRight + LANE_BORDER_CLEARANCE_TOL;
18170
- if (crossesLeft || crossesRight) {
18182
+ const side = rectCrossesLaneBorder(box, lane, rangeByLane.get(lane.id));
18183
+ if (side !== void 0) {
18171
18184
  push(
18172
18185
  "lane-border-clearance",
18173
- `edge "${e.id}" label bbox (${bx.toFixed(0)}..${bxRight.toFixed(0)}) \u304C\u975E\u767A\u7740 lane "${lane.id}" (${laneLeft.toFixed(0)}..${laneRight.toFixed(0)}) \u306E ${crossesLeft ? "\u5DE6" : "\u53F3"} border \u3092\u8CAB\u901A`
18186
+ `edge "${e.id}" label bbox (${box.x.toFixed(0)}..${(box.x + box.w).toFixed(0)}) \u304C\u975E\u767A\u7740 lane "${lane.id}" (${lane.x.toFixed(0)}..${(lane.x + lane.width).toFixed(0)}) \u306E ${side === "left" ? "\u5DE6" : "\u53F3"} border \u3092\u8CAB\u901A`
18174
18187
  );
18175
18188
  break;
18176
18189
  }
@@ -18793,6 +18806,7 @@ function layoutEdges(diag, nodes, lanes = []) {
18793
18806
  const labelMarginExtra = (diag.viewport?.labelMargin ?? diag.viewport?.gap ?? DEFAULT_LABEL_MARGIN) - DEFAULT_LABEL_MARGIN;
18794
18807
  const nmap = new Map(nodes.map((n) => [n.id, n]));
18795
18808
  const lmap = new Map(lanes.map((l) => [l.id, l]));
18809
+ const laneRangeById = new Map(lanes.map((l) => [l.id, laneVerticalRange(l, nodes)]));
18796
18810
  const positionedEdges = [];
18797
18811
  const autoEdges = [];
18798
18812
  const selfLoopEdges = [];
@@ -19017,7 +19031,7 @@ function layoutEdges(diag, nodes, lanes = []) {
19017
19031
  nodes.filter((n) => n.id === c.from || n.id === c.to).map((n) => n.lane)
19018
19032
  );
19019
19033
  return lanes.some(
19020
- (l) => !endpointLanes.has(l.id) && (crossesVerticalLine(pill, l.x) || crossesVerticalLine(pill, l.x + l.width))
19034
+ (l) => !endpointLanes.has(l.id) && rectCrossesLaneBorder(pill, l, laneRangeById.get(l.id)) !== void 0
19021
19035
  );
19022
19036
  };
19023
19037
  const assignDetourSides = () => {
@@ -19237,7 +19251,7 @@ function layoutEdges(diag, nodes, lanes = []) {
19237
19251
  );
19238
19252
  const crossesLaneBorder = lanes.some((l) => {
19239
19253
  if (endpointLanes.has(l.id)) return false;
19240
- return crossesVerticalLine(alt, l.x) || crossesVerticalLine(alt, l.x + l.width);
19254
+ return rectCrossesLaneBorder(alt, l, laneRangeById.get(l.id)) !== void 0;
19241
19255
  });
19242
19256
  if (crossesLaneBorder) continue;
19243
19257
  r.baseLabelX = r.labelAltX;
@@ -19528,9 +19542,6 @@ function segmentsBBox2(segs) {
19528
19542
  }
19529
19543
  return { x: minX, y: minY, w: maxX - minX, h: maxY - minY };
19530
19544
  }
19531
- function crossesVerticalLine(rect, lineX) {
19532
- return rect.x < lineX - LANE_BORDER_CLEARANCE_TOL && rect.x + rect.w > lineX + LANE_BORDER_CLEARANCE_TOL;
19533
- }
19534
19545
  function distributeBySides(list) {
19535
19546
  const from = list[0].from;
19536
19547
  const tol = 20;