@cardenelabs/cdl 0.20.1 → 0.21.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,47 @@ CDL (Chainome Diagram Language) の主要変更履歴。
5
5
 
6
6
  ## [Unreleased]
7
7
 
8
+ ## [0.21.1] - 2026-08-29
9
+
10
+ ### Fixed
11
+
12
+ - **線の縦区間に張り付いた説明を離すようにした** (#594)
13
+
14
+ 説明が別の線の縦区間の真下に居る形で、隙間が 0 のまま残っていた。
15
+
16
+ 線の押し出しは発火している。 ただし安全判定 (`pathShiftIsSafe`) は「経路を壊さないか」
17
+ しか見ないので、**離れない向きへ動かして終わる**。 その時「線で逃がせた」 と数えるため、
18
+ cardene777/cdl#592 で入れた逃がし (説明の側を動かす) にも回らなかった。 繰り返しを 12 回まで
19
+ 回しても隙間は 0 のままだと実測した。
20
+
21
+ 押し出しは今までどおり出したまま、**離れなかった時は「逃がせた」 と数えない**。
22
+ 出さない形にすると、離れないなりに効いていた押し出しが消えて別の 3 図が壊れる。
23
+
24
+ 逃がす向きは **行き先を線ごとに確かめて** 選ぶ。 全体の最小距離だけで比べると、いま 0 の
25
+ 線が離れるぶん別の線が 0 になっても「悪くなっていない」 と読める。
26
+
27
+ ## [0.21.0] - 2026-08-29
28
+
29
+ ### Fixed
30
+
31
+ - **説明どうしの近接と、線から逃げられない説明を解消した** (#592)
32
+
33
+ 説明を動かす後処理 (`resolveEdgeLabelOverlapsWithChainAndPropagate`) に 2 つの穴があった。
34
+
35
+ 1 つ目は **説明どうしの近接を見ていなかった** こと。 重なった時しか動かさない一方、検査は
36
+ 36 の間隔を要求するので 33 しか空かない組が残っていた (見本 7 図が同じ形)。 節や縦列の
37
+ 見出しには近接を見る rule があり、説明どうしだけが抜けていた。
38
+
39
+ 2 つ目は **線を動かせない時に何もしなかった** こと。 線をどの向きにも動かせない場合は
40
+ 「説明の近接より経路の破損の方が重い」 として諦めていたが、説明は動かしても経路を壊さない。
41
+ 説明の側を逃がす。
42
+
43
+ 押し出す量に 2 の余裕を足した。 要求ちょうどに置くと px へ投影する丸めで割れる
44
+ (実測で押し出した先が `gap=32.0px (need 32px)` で error)。
45
+
46
+ dragon の見本で `clearance` の error が 7 図から 1 図に減った。 残る 1 図は engine が
47
+ world unit で、検査が px で測ることによる差 (viewBox 2511 の図で 14 world が 9px になる)。
48
+
8
49
  ## [0.20.1] - 2026-08-29
9
50
 
10
51
  ### Fixed
@@ -1039,7 +1080,9 @@ Initial OSS release.
1039
1080
  使わない = annotated tag を push しても GitHub Release は作られず、 Release を作っていない版で
1040
1081
  行き止まりになる。
1041
1082
 
1042
- [Unreleased]: https://github.com/cardene777/cdl/compare/v0.20.1...HEAD
1083
+ [Unreleased]: https://github.com/cardene777/cdl/compare/v0.21.1...HEAD
1084
+ [0.21.1]: https://github.com/cardene777/cdl/compare/v0.21.0...v0.21.1
1085
+ [0.21.0]: https://github.com/cardene777/cdl/compare/v0.20.1...v0.21.0
1043
1086
  [0.20.1]: https://github.com/cardene777/cdl/compare/v0.20.0...v0.20.1
1044
1087
  [0.20.0]: https://github.com/cardene777/cdl/compare/v0.19.0...v0.20.0
1045
1088
  [0.19.0]: https://github.com/cardene777/cdl/compare/v0.18.1...v0.19.0
package/dist/index.cjs CHANGED
@@ -12093,6 +12093,7 @@ function footerShapeDrawTop(node) {
12093
12093
 
12094
12094
  // src/layout/collisions.ts
12095
12095
  var LANE_LABEL_CLEARANCE_MARGIN = 2;
12096
+ var NEAR_MISS_MARGIN = LANE_LABEL_CLEARANCE_MARGIN;
12096
12097
  function collectBBoxes(lanes, nodes, edges) {
12097
12098
  const out = [];
12098
12099
  for (const lane of lanes) {
@@ -12920,8 +12921,8 @@ function resolveEdgeLabelOverlapsWithChainAndPropagate(edges, obstacles, minClea
12920
12921
  };
12921
12922
  const shift = computeOverlapShift(label, inflated);
12922
12923
  if (!shift) continue;
12923
- const amount = shift.amount - minClearance;
12924
- if (amount <= 0) continue;
12924
+ const amount = shift.amount - minClearance + NEAR_MISS_MARGIN;
12925
+ if (amount <= NEAR_MISS_MARGIN) continue;
12925
12926
  out.push({
12926
12927
  target: { kind: "label", id: label.id },
12927
12928
  axis: shift.axis,
@@ -12962,6 +12963,36 @@ function resolveEdgeLabelOverlapsWithChainAndPropagate(edges, obstacles, minClea
12962
12963
  out.push({ target: { kind: "label", id: moveBox.id }, ...shift });
12963
12964
  }
12964
12965
  }
12966
+ for (let i = 0; i < labels.length; i++) {
12967
+ for (let j = i + 1; j < labels.length; j++) {
12968
+ const a = labels[i];
12969
+ const b = labels[j];
12970
+ if (computeOverlapShift(a, b)) continue;
12971
+ const orderA = edgeOrder.get(a.id);
12972
+ const orderB = edgeOrder.get(b.id);
12973
+ const moveBox = orderA > orderB ? a : b;
12974
+ const staticBox = orderA > orderB ? b : a;
12975
+ const required = requiredNearClearance(moveBox.kind, staticBox.kind);
12976
+ const inflated = {
12977
+ ...staticBox,
12978
+ x: staticBox.x - required,
12979
+ y: staticBox.y - required,
12980
+ w: staticBox.w + required * 2,
12981
+ h: staticBox.h + required * 2
12982
+ };
12983
+ const shift = computeOverlapShift(moveBox, inflated);
12984
+ if (!shift) continue;
12985
+ const amount = shift.amount - minClearance + NEAR_MISS_MARGIN;
12986
+ if (amount <= NEAR_MISS_MARGIN) continue;
12987
+ out.push({
12988
+ target: { kind: "label", id: moveBox.id },
12989
+ axis: shift.axis,
12990
+ sign: shift.sign,
12991
+ amount,
12992
+ chain: false
12993
+ });
12994
+ }
12995
+ }
12965
12996
  for (const label of labels) {
12966
12997
  const currentShift = getLabelChainShift(label.id);
12967
12998
  if (currentShift.dx === 0 && currentShift.dy === 0) continue;
@@ -13057,12 +13088,83 @@ function resolveEdgeLabelOverlapsWithChainAndPropagate(edges, obstacles, minClea
13057
13088
  if (bestSideIdx < 0) continue;
13058
13089
  const amount = pathLabelClearance - gap + pathLabelClearance;
13059
13090
  const ordered = edgeProbes.map((p, idx) => ({ p, idx })).sort((a, b) => probeDist[a.idx] - probeDist[b.idx]);
13091
+ let \u7DDA\u3092\u52D5\u304B\u305B\u305F = false;
13060
13092
  for (const { p } of ordered) {
13061
13093
  const axis = p.axis;
13062
13094
  if (!pathShiftIsSafe(e.id, axis, p.sign, amount)) continue;
13095
+ const \u52D5\u304B\u3057\u305F\u5F8C = measurePathLabelGapSegs(
13096
+ simulateShiftedSegments(e, axis === "x" ? p.sign * amount : 0, axis === "y" ? p.sign * amount : 0),
13097
+ label
13098
+ );
13063
13099
  out.push({ target: { kind: "edge-path", id: e.id }, axis, sign: p.sign, amount });
13100
+ \u7DDA\u3092\u52D5\u304B\u305B\u305F = \u52D5\u304B\u3057\u305F\u5F8C > gap;
13064
13101
  break;
13065
13102
  }
13103
+ if (!\u7DDA\u3092\u52D5\u304B\u305B\u305F) {
13104
+ const \u82AF = { x: label.x + label.w / 2, y: label.y + label.h / 2 };
13105
+ let \u8FD1\u3044\u70B9 = null;
13106
+ let \u6700\u77ED = Infinity;
13107
+ for (const seg of segs) {
13108
+ for (const \u70B9 of [
13109
+ { x: seg.x1, y: seg.y1 },
13110
+ { x: seg.x2, y: seg.y2 },
13111
+ { x: (seg.x1 + seg.x2) / 2, y: (seg.y1 + seg.y2) / 2 }
13112
+ ]) {
13113
+ const d = Math.hypot(\u70B9.x - \u82AF.x, \u70B9.y - \u82AF.y);
13114
+ if (d < \u6700\u77ED) {
13115
+ \u6700\u77ED = d;
13116
+ \u8FD1\u3044\u70B9 = \u70B9;
13117
+ }
13118
+ }
13119
+ }
13120
+ if (\u8FD1\u3044\u70B9) {
13121
+ const dx = \u82AF.x - \u8FD1\u3044\u70B9.x;
13122
+ const dy = \u82AF.y - \u8FD1\u3044\u70B9.y;
13123
+ const \u91CF = pathLabelClearance - gap + NEAR_MISS_MARGIN;
13124
+ const \u5019\u88DC = Math.abs(dx) >= Math.abs(dy) ? [
13125
+ { axis: "x", sign: dx >= 0 ? 1 : -1 },
13126
+ { axis: "y", sign: dy >= 0 ? 1 : -1 }
13127
+ ] : [
13128
+ { axis: "y", sign: dy >= 0 ? 1 : -1 },
13129
+ { axis: "x", sign: dx >= 0 ? 1 : -1 }
13130
+ ];
13131
+ const \u52D5\u304B\u3057\u305F\u7BB1 = (axis, sign) => ({
13132
+ ...label,
13133
+ x: label.x + (axis === "x" ? sign * \u91CF : 0),
13134
+ y: label.y + (axis === "y" ? sign * \u91CF : 0)
13135
+ });
13136
+ const \u7DDA\u3054\u3068\u306E\u8DDD\u96E2 = (\u7BB1) => {
13137
+ const \u51FA = /* @__PURE__ */ new Map();
13138
+ for (const \u4ED6 of edges) {
13139
+ if (\u4ED6.id === label.id) continue;
13140
+ const segs2 = segmentsExcludingOwnPath(shiftedSegments(\u4ED6.id, \u4ED6.d), ownSegsOf(label.id));
13141
+ if (segs2.length === 0) continue;
13142
+ \u51FA.set(\u4ED6.id, measurePathLabelGapSegs(segs2, \u7BB1));
13143
+ }
13144
+ return \u51FA;
13145
+ };
13146
+ const \u3044\u307E\u306E\u8DDD\u96E2 = \u7DDA\u3054\u3068\u306E\u8DDD\u96E2(label);
13147
+ const \u60AA\u304F\u3059\u308B = (\u7BB1) => {
13148
+ const \u5F8C = \u7DDA\u3054\u3068\u306E\u8DDD\u96E2(\u7BB1);
13149
+ for (const [id, \u524D\u306E\u5024] of \u3044\u307E\u306E\u8DDD\u96E2) {
13150
+ const \u5F8C\u306E\u5024 = \u5F8C.get(id) ?? Infinity;
13151
+ if (\u5F8C\u306E\u5024 < Math.min(\u524D\u306E\u5024, pathLabelClearance)) return true;
13152
+ }
13153
+ return false;
13154
+ };
13155
+ for (const \u5019 of \u5019\u88DC) {
13156
+ if (\u60AA\u304F\u3059\u308B(\u52D5\u304B\u3057\u305F\u7BB1(\u5019.axis, \u5019.sign))) continue;
13157
+ out.push({
13158
+ target: { kind: "label", id: label.id },
13159
+ axis: \u5019.axis,
13160
+ sign: \u5019.sign,
13161
+ amount: \u91CF,
13162
+ chain: false
13163
+ });
13164
+ break;
13165
+ }
13166
+ }
13167
+ }
13066
13168
  }
13067
13169
  }
13068
13170
  for (const label of labels) {