@cardenelabs/cdl 0.20.0 → 0.21.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 +38 -1
- package/dist/index.cjs +68 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +68 -3
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +68 -3
- package/dist/react.cjs.map +1 -1
- package/dist/react.js +68 -3
- package/dist/react.js.map +1 -1
- package/package.json +1 -1
- package/src/layout/collisions.ts +100 -2
- package/src/layout/nodes.ts +8 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cardenelabs/cdl",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.0",
|
|
4
4
|
"description": "CDL (Chainome Diagram Language). Mermaid-like declarative DSL that compiles to animated SVG diagrams. Built for blockchain / Solidity flows, generic enough for sequence / flow / state / ER / topology diagrams.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "cardene777",
|
package/src/layout/collisions.ts
CHANGED
|
@@ -9,6 +9,15 @@ import { measureTextWidth } from "./text-width";
|
|
|
9
9
|
*/
|
|
10
10
|
const LANE_LABEL_CLEARANCE_MARGIN = 2;
|
|
11
11
|
|
|
12
|
+
/**
|
|
13
|
+
* 近接を解消する時に、要求ちょうどではなくこの分だけ余分に押し出す (world unit)。
|
|
14
|
+
*
|
|
15
|
+
* 判定は `gap < required` の strict 比較だが、要求ちょうどに置くと px へ投影する丸めで
|
|
16
|
+
* 割れる (実測 = 押し出した先が `gap=32.0px (need 32px)` で error)。
|
|
17
|
+
* `LANE_LABEL_CLEARANCE_MARGIN` と同じ理由で、同じ幅を取る。
|
|
18
|
+
*/
|
|
19
|
+
const NEAR_MISS_MARGIN = LANE_LABEL_CLEARANCE_MARGIN;
|
|
20
|
+
|
|
12
21
|
/**
|
|
13
22
|
* 全要素の AABB を集約。 engine が「キャンバスに何があるか」 を全部把握する SSOT。
|
|
14
23
|
*/
|
|
@@ -1570,8 +1579,8 @@ export function resolveEdgeLabelOverlapsWithChainAndPropagate(
|
|
|
1570
1579
|
const shift = computeOverlapShift(label, inflated);
|
|
1571
1580
|
if (!shift) continue;
|
|
1572
1581
|
// computeOverlapShift は minClearance を上乗せするが、 膨張分で確保済なので差し引く。
|
|
1573
|
-
const amount = shift.amount - minClearance;
|
|
1574
|
-
if (amount <=
|
|
1582
|
+
const amount = shift.amount - minClearance + NEAR_MISS_MARGIN;
|
|
1583
|
+
if (amount <= NEAR_MISS_MARGIN) continue;
|
|
1575
1584
|
out.push({
|
|
1576
1585
|
target: { kind: "label", id: label.id },
|
|
1577
1586
|
axis: shift.axis,
|
|
@@ -1634,6 +1643,51 @@ export function resolveEdgeLabelOverlapsWithChainAndPropagate(
|
|
|
1634
1643
|
}
|
|
1635
1644
|
}
|
|
1636
1645
|
|
|
1646
|
+
/*
|
|
1647
|
+
* (2b) label ↔ label の **近接** (重なってはいないが要求 clearance 未満)。
|
|
1648
|
+
*
|
|
1649
|
+
* (2) は重なった時しか動かさない (`computeOverlapShift` が重なり量 0 で null を返す)。
|
|
1650
|
+
* 一方 `clearance` 軸は 36 を要求するので、33 しか空いていない組が残っていた
|
|
1651
|
+
* (実測で見本 7 図が同じ形)。 node と lane-label には (1a) / (1b) で近接の面倒を見る
|
|
1652
|
+
* rule があり、label どうしだけが抜けていた。
|
|
1653
|
+
*
|
|
1654
|
+
* 静止側を要求ぶん膨らませて (1a) と同じ形で押し出す。 押す側は (2) と同じ順序規則
|
|
1655
|
+
* (後に declare した方) にする = 同じ組で (2) と (2b) が逆向きに押し合うのを避ける。
|
|
1656
|
+
*
|
|
1657
|
+
* chain: false = 微調整なので node / path の連鎖は起こさない。
|
|
1658
|
+
*/
|
|
1659
|
+
for (let i = 0; i < labels.length; i++) {
|
|
1660
|
+
for (let j = i + 1; j < labels.length; j++) {
|
|
1661
|
+
const a = labels[i]!;
|
|
1662
|
+
const b = labels[j]!;
|
|
1663
|
+
if (computeOverlapShift(a, b)) continue;
|
|
1664
|
+
const orderA = edgeOrder.get(a.id)!;
|
|
1665
|
+
const orderB = edgeOrder.get(b.id)!;
|
|
1666
|
+
const moveBox = orderA > orderB ? a : b;
|
|
1667
|
+
const staticBox = orderA > orderB ? b : a;
|
|
1668
|
+
const required = requiredNearClearance(moveBox.kind, staticBox.kind);
|
|
1669
|
+
const inflated: BBox = {
|
|
1670
|
+
...staticBox,
|
|
1671
|
+
x: staticBox.x - required,
|
|
1672
|
+
y: staticBox.y - required,
|
|
1673
|
+
w: staticBox.w + required * 2,
|
|
1674
|
+
h: staticBox.h + required * 2,
|
|
1675
|
+
};
|
|
1676
|
+
const shift = computeOverlapShift(moveBox, inflated);
|
|
1677
|
+
if (!shift) continue;
|
|
1678
|
+
// 膨張分で確保済なので、`computeOverlapShift` が上乗せした分を差し引く
|
|
1679
|
+
const amount = shift.amount - minClearance + NEAR_MISS_MARGIN;
|
|
1680
|
+
if (amount <= NEAR_MISS_MARGIN) continue;
|
|
1681
|
+
out.push({
|
|
1682
|
+
target: { kind: "label", id: moveBox.id },
|
|
1683
|
+
axis: shift.axis,
|
|
1684
|
+
sign: shift.sign,
|
|
1685
|
+
amount,
|
|
1686
|
+
chain: false,
|
|
1687
|
+
});
|
|
1688
|
+
}
|
|
1689
|
+
}
|
|
1690
|
+
|
|
1637
1691
|
// (3) CAR-482 chain 伝搬 = label が shift 方向にある obstacle と衝突するなら、
|
|
1638
1692
|
// obstacle も同方向同量 shift 連鎖。 label ↔ obstacle が既に (1) で処理されているため、
|
|
1639
1693
|
// ここでは「shift 済 label の shift 累積方向にある obstacle」 を検出、 obstacle 側 shift 発火。
|
|
@@ -1811,12 +1865,56 @@ export function resolveEdgeLabelOverlapsWithChainAndPropagate(
|
|
|
1811
1865
|
const ordered = edgeProbes
|
|
1812
1866
|
.map((p, idx) => ({ p, idx }))
|
|
1813
1867
|
.sort((a, b) => probeDist[a.idx]! - probeDist[b.idx]!);
|
|
1868
|
+
let 線を動かせた = false;
|
|
1814
1869
|
for (const { p } of ordered) {
|
|
1815
1870
|
const axis = p.axis as "x" | "y";
|
|
1816
1871
|
if (!pathShiftIsSafe(e.id, axis, p.sign, amount)) continue;
|
|
1817
1872
|
out.push({ target: { kind: "edge-path", id: e.id }, axis, sign: p.sign, amount });
|
|
1873
|
+
線を動かせた = true;
|
|
1818
1874
|
break;
|
|
1819
1875
|
}
|
|
1876
|
+
/*
|
|
1877
|
+
* 線をどの向きにも動かせない時は **説明の側を離す**。
|
|
1878
|
+
*
|
|
1879
|
+
* 線を動かさないのは「説明の近接より経路の破損の方が重い」 ためだが、動かせないまま
|
|
1880
|
+
* 何もしないと近接がそのまま残る (実測で `gap=9.0px (need 14px)`)。 説明は動かしても
|
|
1881
|
+
* 経路を壊さないので、こちらを逃がす。
|
|
1882
|
+
*
|
|
1883
|
+
* 向きは、線から見て説明が居る側 (最も近い segment の中点から説明の中心へ向かう向き) の
|
|
1884
|
+
* 縦横どちらか、離れている方の軸を選ぶ。
|
|
1885
|
+
*/
|
|
1886
|
+
if (!線を動かせた) {
|
|
1887
|
+
const 芯 = { x: label.x + label.w / 2, y: label.y + label.h / 2 };
|
|
1888
|
+
let 近い点: { x: number; y: number } | null = null;
|
|
1889
|
+
let 最短 = Infinity;
|
|
1890
|
+
for (const seg of segs) {
|
|
1891
|
+
for (const 点 of [
|
|
1892
|
+
{ x: seg.x1, y: seg.y1 },
|
|
1893
|
+
{ x: seg.x2, y: seg.y2 },
|
|
1894
|
+
{ x: (seg.x1 + seg.x2) / 2, y: (seg.y1 + seg.y2) / 2 },
|
|
1895
|
+
]) {
|
|
1896
|
+
const d = Math.hypot(点.x - 芯.x, 点.y - 芯.y);
|
|
1897
|
+
if (d < 最短) {
|
|
1898
|
+
最短 = d;
|
|
1899
|
+
近い点 = 点;
|
|
1900
|
+
}
|
|
1901
|
+
}
|
|
1902
|
+
}
|
|
1903
|
+
if (近い点) {
|
|
1904
|
+
const dx = 芯.x - 近い点.x;
|
|
1905
|
+
const dy = 芯.y - 近い点.y;
|
|
1906
|
+
const axis: "x" | "y" = Math.abs(dx) >= Math.abs(dy) ? "x" : "y";
|
|
1907
|
+
const 向き = axis === "x" ? dx : dy;
|
|
1908
|
+
const sign: 1 | -1 = 向き >= 0 ? 1 : -1;
|
|
1909
|
+
out.push({
|
|
1910
|
+
target: { kind: "label", id: label.id },
|
|
1911
|
+
axis,
|
|
1912
|
+
sign,
|
|
1913
|
+
amount: pathLabelClearance - gap + NEAR_MISS_MARGIN,
|
|
1914
|
+
chain: false,
|
|
1915
|
+
});
|
|
1916
|
+
}
|
|
1917
|
+
}
|
|
1820
1918
|
}
|
|
1821
1919
|
}
|
|
1822
1920
|
|
package/src/layout/nodes.ts
CHANGED
|
@@ -8,7 +8,6 @@ import {
|
|
|
8
8
|
SUBTITLE_ROW_SHIFT,
|
|
9
9
|
中身から出す高さ,
|
|
10
10
|
目印なしの繰り上げ,
|
|
11
|
-
段を積む箱か,
|
|
12
11
|
} from "./spec";
|
|
13
12
|
import { DEFAULT_NODE_GAP, HEADER_RESERVE, LANE_TOP_PAD, NODE_SIZE, STACK_GAP } from "./tokens";
|
|
14
13
|
|
|
@@ -245,10 +244,14 @@ export function layoutNodes(diag: CdlDiagram, lanes: LaidLane[]): LaidNode[] {
|
|
|
245
244
|
* 並びの重心が 1 本に通る。 見た目検査の `row-alignment` もこの前提で書かれている。
|
|
246
245
|
*/
|
|
247
246
|
//
|
|
248
|
-
//
|
|
249
|
-
//
|
|
250
|
-
|
|
251
|
-
|
|
247
|
+
// **段を積む箱 (#589) はここに入れない**。 書いた段の数から高さを出すが、その値は段の
|
|
248
|
+
// 高さ (行内の最大) を通してから使う。
|
|
249
|
+
//
|
|
250
|
+
// 自前の高さで並べると、同じ縦列の中で箱ごとに高さが変わり、隣り合う箱の端どうしの
|
|
251
|
+
// 間隔が揃わなくなる (実測 = `pattern-rollback` で最小 100 に対し最大 187、
|
|
252
|
+
// `column-gap-uniform` が error)。 縦列が 1 本の図 (流れ図) では行に箱が 1 つしか無いので
|
|
253
|
+
// 段の高さ = その箱の高さになり、縮む効果はそのまま残る。
|
|
254
|
+
const 自前の高さ = n.rowMarks !== undefined;
|
|
252
255
|
const h = n.h ?? (自前の高さ ? 箱の高さ(n, size.h) : 段の高さ);
|
|
253
256
|
const cy = 段の芯;
|
|
254
257
|
// n.w 明示時は尊重、 未指定で rows[] を持つ node は内容長に合わせて auto 拡張。
|