@cardenelabs/cdl 0.123.0 → 0.124.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 +13 -1
- package/README.md +6 -1
- package/dist/index.cjs +60 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +60 -1
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +60 -1
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/react.js +60 -1
- package/dist/react.js.map +1 -1
- package/dist/{render-CVczwfRb.d.cts → render-U6omSNH2.d.cts} +3 -2
- package/dist/{render-CVczwfRb.d.ts → render-U6omSNH2.d.ts} +3 -2
- package/package.json +1 -1
- package/src/layout/edges.ts +103 -4
- package/src/types.ts +3 -2
|
@@ -145,7 +145,7 @@ type EdgeStyle = "solid" | "dotted-flow" | "dashed";
|
|
|
145
145
|
* 線の道筋。
|
|
146
146
|
*
|
|
147
147
|
* - `"default"` ... 自動。 箱を避け、角を丸めた直角の線を引く。 書かない時も同じ
|
|
148
|
-
* - `"back-detour"` ...
|
|
148
|
+
* - `"back-detour"` ... 箱の上を回って前の箱へ戻り、途中の箱と交わる時は外側を回る
|
|
149
149
|
* - `"curve"` ... 始点から水平に出て終点へ水平に入る 3 次曲線。 途中の箱は避けず、
|
|
150
150
|
* 2 点が同じ高さなら直線になる。 終点の矢印は水平を向く
|
|
151
151
|
* - `"metro"` ... 横線と傾き ±1 の斜線だけで結び、角を半径 r の円弧で丸める。
|
|
@@ -766,7 +766,8 @@ type CdlEdge = {
|
|
|
766
766
|
/** label の y 方向 fine offset (default 0、 + で下、 - で上) */
|
|
767
767
|
labelOffsetY?: number;
|
|
768
768
|
/**
|
|
769
|
-
* 線の道筋。 `default` は箱を避ける自動経路、 `back-detour`
|
|
769
|
+
* 線の道筋。 `default` は箱を避ける自動経路、 `back-detour` は箱の上を回り、途中の箱と
|
|
770
|
+
* 交わる時は外側を回る戻り線、
|
|
770
771
|
* `curve` は途中の箱を避けず、両端で水平になる 3 次曲線。 `metro` は途中の箱を避けず、
|
|
771
772
|
* 横線と傾き ±1 の斜線を半径 r の円弧で結ぶ。2 点が同じ高さなら横 1 本。書かなければ `default`。
|
|
772
773
|
*/
|
|
@@ -145,7 +145,7 @@ type EdgeStyle = "solid" | "dotted-flow" | "dashed";
|
|
|
145
145
|
* 線の道筋。
|
|
146
146
|
*
|
|
147
147
|
* - `"default"` ... 自動。 箱を避け、角を丸めた直角の線を引く。 書かない時も同じ
|
|
148
|
-
* - `"back-detour"` ...
|
|
148
|
+
* - `"back-detour"` ... 箱の上を回って前の箱へ戻り、途中の箱と交わる時は外側を回る
|
|
149
149
|
* - `"curve"` ... 始点から水平に出て終点へ水平に入る 3 次曲線。 途中の箱は避けず、
|
|
150
150
|
* 2 点が同じ高さなら直線になる。 終点の矢印は水平を向く
|
|
151
151
|
* - `"metro"` ... 横線と傾き ±1 の斜線だけで結び、角を半径 r の円弧で丸める。
|
|
@@ -766,7 +766,8 @@ type CdlEdge = {
|
|
|
766
766
|
/** label の y 方向 fine offset (default 0、 + で下、 - で上) */
|
|
767
767
|
labelOffsetY?: number;
|
|
768
768
|
/**
|
|
769
|
-
* 線の道筋。 `default` は箱を避ける自動経路、 `back-detour`
|
|
769
|
+
* 線の道筋。 `default` は箱を避ける自動経路、 `back-detour` は箱の上を回り、途中の箱と
|
|
770
|
+
* 交わる時は外側を回る戻り線、
|
|
770
771
|
* `curve` は途中の箱を避けず、両端で水平になる 3 次曲線。 `metro` は途中の箱を避けず、
|
|
771
772
|
* 横線と傾き ±1 の斜線を半径 r の円弧で結ぶ。2 点が同じ高さなら横 1 本。書かなければ `default`。
|
|
772
773
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cardenelabs/cdl",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.124.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/edges.ts
CHANGED
|
@@ -1522,10 +1522,98 @@ interface Rect {
|
|
|
1522
1522
|
h: number;
|
|
1523
1523
|
}
|
|
1524
1524
|
|
|
1525
|
+
/** 経路の角の点。 */
|
|
1526
|
+
type Vertex = { x: number; y: number };
|
|
1527
|
+
|
|
1525
1528
|
function nodeRect(n: LaidNode): Rect {
|
|
1526
1529
|
return { x: n.cx - n.w / 2, y: n.cy - n.h / 2, w: n.w, h: n.h };
|
|
1527
1530
|
}
|
|
1528
1531
|
|
|
1532
|
+
/** 絵の `edge-node-cross` と同じ判定で、経路が貫く箱を 1 箱 1 回で数える。 */
|
|
1533
|
+
function countPathObstacleCrossings(d: string, obstacles: readonly LaidNode[]): number {
|
|
1534
|
+
const segments = flattenPathSegments(extractPathSegments(d));
|
|
1535
|
+
return obstacles.filter((obstacle) => segmentsCrossRect(segments, nodeRect(obstacle))).length;
|
|
1536
|
+
}
|
|
1537
|
+
|
|
1538
|
+
type BackDetourCandidate = {
|
|
1539
|
+
d: string;
|
|
1540
|
+
labelX: number;
|
|
1541
|
+
crossings: number;
|
|
1542
|
+
length: number;
|
|
1543
|
+
};
|
|
1544
|
+
|
|
1545
|
+
/** top-top の戻り線を、帯に掛かる箱を避けた最も近い外側の列へ回す候補を作る。 */
|
|
1546
|
+
function makeBackDetourCandidate(
|
|
1547
|
+
A: Vertex,
|
|
1548
|
+
B: Vertex,
|
|
1549
|
+
from: LaidNode,
|
|
1550
|
+
to: LaidNode,
|
|
1551
|
+
obstacles: readonly LaidNode[],
|
|
1552
|
+
detourY: number,
|
|
1553
|
+
side: DetourSide,
|
|
1554
|
+
): BackDetourCandidate {
|
|
1555
|
+
const lowIsA = A.y >= B.y;
|
|
1556
|
+
const low = lowIsA ? A : B;
|
|
1557
|
+
const lowBendY = low.y - VERTICAL_DETOUR_EXIT;
|
|
1558
|
+
const bandTop = Math.min(detourY, lowBendY);
|
|
1559
|
+
const bandBottom = Math.max(detourY, lowBendY);
|
|
1560
|
+
const bandObstacles = obstacles
|
|
1561
|
+
.map(nodeRect)
|
|
1562
|
+
.filter((rect) => rect.y + rect.h >= bandTop && rect.y <= bandBottom);
|
|
1563
|
+
let columnX = detourColumnFor([from, to], side);
|
|
1564
|
+
while (true) {
|
|
1565
|
+
const blockers = bandObstacles.filter(
|
|
1566
|
+
(rect) => rect.x - VERTICAL_DETOUR_GAP < columnX
|
|
1567
|
+
&& columnX < rect.x + rect.w + VERTICAL_DETOUR_GAP,
|
|
1568
|
+
);
|
|
1569
|
+
if (blockers.length === 0) break;
|
|
1570
|
+
columnX = side === "left"
|
|
1571
|
+
? Math.min(...blockers.map((rect) => rect.x - VERTICAL_DETOUR_GAP))
|
|
1572
|
+
: Math.max(...blockers.map((rect) => rect.x + rect.w + VERTICAL_DETOUR_GAP));
|
|
1573
|
+
}
|
|
1574
|
+
const bendAY = lowIsA ? lowBendY : detourY;
|
|
1575
|
+
const bendBY = lowIsA ? detourY : lowBendY;
|
|
1576
|
+
const vertices: Vertex[] = [
|
|
1577
|
+
A,
|
|
1578
|
+
{ x: A.x, y: bendAY },
|
|
1579
|
+
{ x: columnX, y: bendAY },
|
|
1580
|
+
{ x: columnX, y: bendBY },
|
|
1581
|
+
{ x: B.x, y: bendBY },
|
|
1582
|
+
B,
|
|
1583
|
+
];
|
|
1584
|
+
const d = verticesToPath(vertices);
|
|
1585
|
+
const high = lowIsA ? B : A;
|
|
1586
|
+
return {
|
|
1587
|
+
d,
|
|
1588
|
+
labelX: (high.x + columnX) / 2,
|
|
1589
|
+
crossings: countPathObstacleCrossings(d, obstacles),
|
|
1590
|
+
length: vertices.slice(1).reduce((sum, vertex, index) => {
|
|
1591
|
+
const previous = vertices[index]!;
|
|
1592
|
+
return sum + Math.hypot(vertex.x - previous.x, vertex.y - previous.y);
|
|
1593
|
+
}, 0),
|
|
1594
|
+
};
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1597
|
+
/** 交差数、長さ、終点へ入る線の並びを保つ側の順で、左右候補を 1 つに決める。 */
|
|
1598
|
+
function chooseBackDetourCandidate(
|
|
1599
|
+
A: Vertex,
|
|
1600
|
+
B: Vertex,
|
|
1601
|
+
from: LaidNode,
|
|
1602
|
+
to: LaidNode,
|
|
1603
|
+
obstacles: readonly LaidNode[],
|
|
1604
|
+
detourY: number,
|
|
1605
|
+
): BackDetourCandidate {
|
|
1606
|
+
const left = makeBackDetourCandidate(A, B, from, to, obstacles, detourY, "left");
|
|
1607
|
+
const right = makeBackDetourCandidate(A, B, from, to, obstacles, detourY, "right");
|
|
1608
|
+
if (left.crossings !== right.crossings) return left.crossings < right.crossings ? left : right;
|
|
1609
|
+
if (Math.abs(left.length - right.length) >= 0.5) return left.length < right.length ? left : right;
|
|
1610
|
+
|
|
1611
|
+
// 同じ長さなら始点側へ回すと、同じ上辺へ入る線の左右の並びを従来から入れ替えずに済む。
|
|
1612
|
+
// 同じ列は左右の基準を持たないため、縦 detour と同じ右を既定にする。
|
|
1613
|
+
const preferred: DetourSide = Math.abs(A.x - B.x) < 0.5 ? "right" : A.x < B.x ? "left" : "right";
|
|
1614
|
+
return preferred === "left" ? left : right;
|
|
1615
|
+
}
|
|
1616
|
+
|
|
1529
1617
|
/** 2 rect の最短距離 (world unit)。 重なっていれば 0。 */
|
|
1530
1618
|
function rectGap(a: Rect, b: Rect): number {
|
|
1531
1619
|
const dx = Math.max(0, a.x - (b.x + b.w), b.x - (a.x + a.w));
|
|
@@ -2374,7 +2462,7 @@ function routePath(
|
|
|
2374
2462
|
const detourY = Math.min(A.y, B.y) - 100;
|
|
2375
2463
|
const corner = 14;
|
|
2376
2464
|
const xDir = B.x > A.x ? 1 : -1;
|
|
2377
|
-
|
|
2465
|
+
const existing = {
|
|
2378
2466
|
d: `M ${A.x} ${A.y}
|
|
2379
2467
|
L ${A.x} ${detourY + corner}
|
|
2380
2468
|
Q ${A.x} ${detourY}, ${A.x + xDir * corner} ${detourY}
|
|
@@ -2386,6 +2474,20 @@ function routePath(
|
|
|
2386
2474
|
// CAR-518 SSOT ... dotted-flow は 62 world (marker glow radius 分拡張) を labelInit で参照。
|
|
2387
2475
|
// top-top detour の水平 segment 上方 labelInit world = spec 準拠距離。
|
|
2388
2476
|
labelY: detourY - labelInit,
|
|
2477
|
+
labelAnchor: "middle" as const,
|
|
2478
|
+
};
|
|
2479
|
+
const existingCrossings = countPathObstacleCrossings(existing.d, obstacles);
|
|
2480
|
+
if (existingCrossings === 0) return existing;
|
|
2481
|
+
|
|
2482
|
+
// 帯に掛かる箱を避けた最も近い外側の列なら、経路に関わらない遠い箱を足しても列を動かさない。
|
|
2483
|
+
// 遠回りは交差を実際に減らす時だけ採り、減らない時は短く読める従来経路を保って、
|
|
2484
|
+
// 残る交差を `edge-node-cross` に報告させる。
|
|
2485
|
+
const candidate = chooseBackDetourCandidate(A, B, from, to, obstacles, detourY);
|
|
2486
|
+
if (candidate.crossings >= existingCrossings) return existing;
|
|
2487
|
+
return {
|
|
2488
|
+
d: candidate.d,
|
|
2489
|
+
labelX: candidate.labelX,
|
|
2490
|
+
labelY: detourY - labelInit,
|
|
2389
2491
|
labelAnchor: "middle",
|
|
2390
2492
|
};
|
|
2391
2493
|
}
|
|
@@ -3062,9 +3164,6 @@ const SHARED_RUN_CORNER = VERTICAL_DETOUR_CORNER;
|
|
|
3062
3164
|
/** 同じ直線上とみなす座標の差 (world unit)。 */
|
|
3063
3165
|
const SHARED_RUN_EPS = 0.5;
|
|
3064
3166
|
|
|
3065
|
-
/** 経路の角の点。 */
|
|
3066
|
-
type Vertex = { x: number; y: number };
|
|
3067
|
-
|
|
3068
3167
|
/**
|
|
3069
3168
|
* 経路を「角の点の並び」 に開く。 開けない形なら `null` を返す。
|
|
3070
3169
|
*
|
package/src/types.ts
CHANGED
|
@@ -222,7 +222,7 @@ export type EdgeStyle = "solid" | "dotted-flow" | "dashed";
|
|
|
222
222
|
* 線の道筋。
|
|
223
223
|
*
|
|
224
224
|
* - `"default"` ... 自動。 箱を避け、角を丸めた直角の線を引く。 書かない時も同じ
|
|
225
|
-
* - `"back-detour"` ...
|
|
225
|
+
* - `"back-detour"` ... 箱の上を回って前の箱へ戻り、途中の箱と交わる時は外側を回る
|
|
226
226
|
* - `"curve"` ... 始点から水平に出て終点へ水平に入る 3 次曲線。 途中の箱は避けず、
|
|
227
227
|
* 2 点が同じ高さなら直線になる。 終点の矢印は水平を向く
|
|
228
228
|
* - `"metro"` ... 横線と傾き ±1 の斜線だけで結び、角を半径 r の円弧で丸める。
|
|
@@ -891,7 +891,8 @@ export type CdlEdge = {
|
|
|
891
891
|
/** label の y 方向 fine offset (default 0、 + で下、 - で上) */
|
|
892
892
|
labelOffsetY?: number;
|
|
893
893
|
/**
|
|
894
|
-
* 線の道筋。 `default` は箱を避ける自動経路、 `back-detour`
|
|
894
|
+
* 線の道筋。 `default` は箱を避ける自動経路、 `back-detour` は箱の上を回り、途中の箱と
|
|
895
|
+
* 交わる時は外側を回る戻り線、
|
|
895
896
|
* `curve` は途中の箱を避けず、両端で水平になる 3 次曲線。 `metro` は途中の箱を避けず、
|
|
896
897
|
* 横線と傾き ±1 の斜線を半径 r の円弧で結ぶ。2 点が同じ高さなら横 1 本。書かなければ `default`。
|
|
897
898
|
*/
|