@cardenelabs/cdl 0.119.0 → 0.120.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 +53 -1
- package/dist/index.cjs +36 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +36 -8
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +36 -8
- package/dist/react.cjs.map +1 -1
- package/dist/react.js +36 -8
- package/dist/react.js.map +1 -1
- package/package.json +1 -1
- package/src/layout/head-label.ts +17 -8
- package/src/render/edges.tsx +26 -7
- package/src/render/utils.ts +47 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cardenelabs/cdl",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.120.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/head-label.ts
CHANGED
|
@@ -22,8 +22,23 @@ import { extractPathSegments } from "./collisions";
|
|
|
22
22
|
import { hasRenderedLabel } from "./spec";
|
|
23
23
|
import { measureTextWidth } from "./text-width";
|
|
24
24
|
|
|
25
|
-
/**
|
|
26
|
-
export const
|
|
25
|
+
/** 端の字の大きさ。 札の下の行と同じ等幅の字 = 多重度を札に入れていた頃と同じ見え方 */
|
|
26
|
+
export const 端の字の大きさ = EDGE_LABEL_TEXT.sub.fontSize;
|
|
27
|
+
|
|
28
|
+
/** 字の箱の高さ。 字の大きさに上下の余白を足す */
|
|
29
|
+
const 箱の高さ = Math.round(端の字の大きさ * 1.2);
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* 端の印と端の字の間に空ける余白 (world)。 線に沿う向きと直角の向きで同じ値を使う。
|
|
33
|
+
*
|
|
34
|
+
* **字の箱の高さの半分で測る** (#989)。 使う側は図を縮めて出すので、余白を world の小さい
|
|
35
|
+
* 数で持つと画面では字より狭くなる。 見本帳はクラス図を 0.42 倍で出しており、余白 8 は
|
|
36
|
+
* 3 画素、字の高さ 19 は 8 画素だった = 線と数が 1 つの帯として読めた。
|
|
37
|
+
*
|
|
38
|
+
* 字の箱の半分にすると、縮めた後も **字の半行分** の空きが残る。
|
|
39
|
+
* 字を大きくした図では余白も一緒に広がる。
|
|
40
|
+
*/
|
|
41
|
+
export const 端の字と印の余白 = Math.round(箱の高さ / 2);
|
|
27
42
|
|
|
28
43
|
/** 端の印が線の芯から左右へ張り出す長さ (world)。 印の縦の半分 */
|
|
29
44
|
export const 端の印の張り出し = EDGE_HEAD_MARKER_SIZE.既定 / 2;
|
|
@@ -52,12 +67,6 @@ export const 端の字を戻す距離 = EDGE_HEAD_MARKER_SIZE.既定 + 端の字
|
|
|
52
67
|
*/
|
|
53
68
|
export const 端の字と線の隙間 = 端の印の張り出し + 端の字と印の余白;
|
|
54
69
|
|
|
55
|
-
/** 端の字の大きさ。 札の下の行と同じ等幅の字 = 多重度を札に入れていた頃と同じ見え方 */
|
|
56
|
-
export const 端の字の大きさ = EDGE_LABEL_TEXT.sub.fontSize;
|
|
57
|
-
|
|
58
|
-
/** 字の箱の高さ。 字の大きさに上下の余白を足す */
|
|
59
|
-
const 箱の高さ = Math.round(端の字の大きさ * 1.2);
|
|
60
|
-
|
|
61
70
|
/** 字の大きさに対する baseline の深さ。 等幅の字の ascent が字の大きさの 8 割ほど */
|
|
62
71
|
const BASELINE_RATIO = 0.8;
|
|
63
72
|
|
package/src/render/edges.tsx
CHANGED
|
@@ -2,7 +2,7 @@ import { 等幅の字 } from "./mono-text";
|
|
|
2
2
|
import type { JSX } from "react";
|
|
3
3
|
import { computeLabelBBoxWorld, hasRenderedLabel, LABEL_PILL_PAD_X } from "../layout/spec";
|
|
4
4
|
import { 端の字の大きさ, 端の字を置く, type 線の端 } from "../layout/head-label";
|
|
5
|
-
import type
|
|
5
|
+
import { EDGE_HEAD_MARKER_SIZE, type EdgeRole, type LaidEdge, type Tone } from "../types";
|
|
6
6
|
import { edgeHeadMarkerName } from "./edge-head";
|
|
7
7
|
import { toneColor } from "./tone";
|
|
8
8
|
|
|
@@ -27,7 +27,13 @@ export function edgeColor(edge: { tone: Tone; role?: EdgeRole }): string {
|
|
|
27
27
|
return edge.role === "main" ? "var(--cdl-now, #c0421f)" : toneColor(edge.tone);
|
|
28
28
|
}
|
|
29
29
|
import { EDGE_LABEL_TEXT, fontFamilyAttr, type EdgeLabelLine } from "../label-text";
|
|
30
|
-
import {
|
|
30
|
+
import {
|
|
31
|
+
interpolate,
|
|
32
|
+
pathSubpath,
|
|
33
|
+
resolveNumericAttr,
|
|
34
|
+
shrinkPathEnd,
|
|
35
|
+
shrinkPathStart,
|
|
36
|
+
} from "./utils";
|
|
31
37
|
|
|
32
38
|
/**
|
|
33
39
|
* 不透明度が 1 の行では属性自体を出さない。 SSOT (`EDGE_LABEL_TEXT`) を 1 未満に変えれば
|
|
@@ -102,7 +108,11 @@ export function CdlEdgeView({
|
|
|
102
108
|
// 旧実装は passthrough edge で dThrough (node center 直線) を particle path にして visible path
|
|
103
109
|
// (orthogonal routing) と乖離させていたが、 視覚的にも整合性的にも問題なので統一。
|
|
104
110
|
// 貫通効果が必要な場合は別途 phase activation で表現する。
|
|
105
|
-
|
|
111
|
+
// 丸の通り道は線の両端から印の長さだけ縮める (#988)。 縮めないと丸が端の印の中まで
|
|
112
|
+
// 入り、輪郭だけの三角に濃い丸が重なって印に穴が開いたように見える。
|
|
113
|
+
// 点線の粒を `shrinkPathEnd` で隠しているのと同じ理由で、向きが両端にあるぶん対で縮める
|
|
114
|
+
const 通り道を縮める = EDGE_HEAD_MARKER_SIZE.既定 + 走る丸の半径.芯;
|
|
115
|
+
const glowPathD = shrinkPathStart(shrinkPathEnd(edge.d, 通り道を縮める), 通り道を縮める);
|
|
106
116
|
void isPassThrough;
|
|
107
117
|
|
|
108
118
|
return (
|
|
@@ -168,7 +178,7 @@ export function CdlEdgeView({
|
|
|
168
178
|
*/}
|
|
169
179
|
{flowing && !isDotted && (
|
|
170
180
|
<g data-cdl-role="edge-flow">
|
|
171
|
-
<path id={glowPathId} d={
|
|
181
|
+
<path id={glowPathId} d={glowPathD} fill="none" stroke="none" />
|
|
172
182
|
<EdgeGlowCircles color={color} edgeId={edge.id} />
|
|
173
183
|
</g>
|
|
174
184
|
)}
|
|
@@ -250,11 +260,20 @@ function SolidEdgePath({
|
|
|
250
260
|
/**
|
|
251
261
|
* EdgeGlowCircles ... 進行点 glow 3 重円 (28/18/11px) を mpath で path 沿い移動。
|
|
252
262
|
*/
|
|
263
|
+
/**
|
|
264
|
+
* 走る丸の 3 枚の半径 (world)。 濃さは外から順に 0.18 / 0.45 / 1 で、芯だけが不透明。
|
|
265
|
+
*
|
|
266
|
+
* **通り道を縮める長さが芯の半径を読む** (#988)。 芯は端の印の上に乗ると穴が開いたように
|
|
267
|
+
* 見えるので、印の長さに芯の半径を足した分だけ通り道を縮める。 ここに数を直に書くと、
|
|
268
|
+
* 丸を大きくした時に芯だけが印へはみ出す。
|
|
269
|
+
*/
|
|
270
|
+
export const 走る丸の半径 = { 外: 28, 中: 18, 芯: 11 } as const;
|
|
271
|
+
|
|
253
272
|
function EdgeGlowCircles({ color, edgeId }: { color: string; edgeId: string }): JSX.Element {
|
|
254
273
|
const ref = `#cdl-edge-${edgeId}`;
|
|
255
274
|
return (
|
|
256
275
|
<g data-cdl-particle={edgeId} data-cdl-particle-path-ref={ref}>
|
|
257
|
-
<circle r={
|
|
276
|
+
<circle r={走る丸の半径.外} fill={color}>
|
|
258
277
|
<animateMotion dur="2s" begin="-1s" repeatCount="indefinite" rotate="auto">
|
|
259
278
|
<mpath href={ref} />
|
|
260
279
|
</animateMotion>
|
|
@@ -267,7 +286,7 @@ function EdgeGlowCircles({ color, edgeId }: { color: string; edgeId: string }):
|
|
|
267
286
|
repeatCount="indefinite"
|
|
268
287
|
/>
|
|
269
288
|
</circle>
|
|
270
|
-
<circle r={
|
|
289
|
+
<circle r={走る丸の半径.中} fill={color}>
|
|
271
290
|
<animateMotion dur="2s" begin="-1s" repeatCount="indefinite" rotate="auto">
|
|
272
291
|
<mpath href={ref} />
|
|
273
292
|
</animateMotion>
|
|
@@ -280,7 +299,7 @@ function EdgeGlowCircles({ color, edgeId }: { color: string; edgeId: string }):
|
|
|
280
299
|
repeatCount="indefinite"
|
|
281
300
|
/>
|
|
282
301
|
</circle>
|
|
283
|
-
<circle r={
|
|
302
|
+
<circle r={走る丸の半径.芯} fill={color}>
|
|
284
303
|
<animateMotion dur="2s" begin="-1s" repeatCount="indefinite" rotate="auto">
|
|
285
304
|
<mpath href={ref} />
|
|
286
305
|
</animateMotion>
|
package/src/render/utils.ts
CHANGED
|
@@ -254,6 +254,53 @@ export function shrinkPathEnd(d: string, shrinkPx: number): string {
|
|
|
254
254
|
return tokens.join(" ");
|
|
255
255
|
}
|
|
256
256
|
|
|
257
|
+
/**
|
|
258
|
+
* SVG path d 文字列の先頭を shrinkPx だけ先へ送る (#988)。 `shrinkPathEnd` と対になる。
|
|
259
|
+
*
|
|
260
|
+
* 走る丸の通り道を両端で縮めるために使う。 丸は通り道の端まで行き着くので、縮めないと
|
|
261
|
+
* 端に乗っている印 (三角 / 菱) の中に重なり、印に穴が開いたように見える。
|
|
262
|
+
*
|
|
263
|
+
* 向きは **最初の区間の行き先** から取る。 曲線は最初の制御点を使う = 弦を使うと、
|
|
264
|
+
* 大きく膨らむ曲線で線が端から出ていく向きと別の向きになる。
|
|
265
|
+
*
|
|
266
|
+
* 縮めると残りが点に潰れる短い線は、**縮めずにそのまま返す**。 潰れた通り道では丸が
|
|
267
|
+
* 1 点に止まって見え、走っていないのと区別が付かない。
|
|
268
|
+
*/
|
|
269
|
+
export function shrinkPathStart(d: string, shrinkPx: number): string {
|
|
270
|
+
const 区間 = [...d.matchAll(/([MLCQZmlcqz])([^MLCQZmlcqz]*)/g)].map((m) => ({
|
|
271
|
+
cmd: m[1]!,
|
|
272
|
+
args: (m[2] ?? "").trim().split(/[\s,]+/).filter(Boolean).map(parseFloat),
|
|
273
|
+
}));
|
|
274
|
+
if (区間.length < 2) return d;
|
|
275
|
+
const 先頭 = 区間[0]!;
|
|
276
|
+
if (先頭.cmd !== "M" || 先頭.args.length < 2) return d;
|
|
277
|
+
const 次 = 区間[1]!;
|
|
278
|
+
// 曲線は最初の制御点が接線を決める。 直線は行き先そのもの
|
|
279
|
+
const 向き先 =
|
|
280
|
+
次.cmd === "Q" || 次.cmd === "C"
|
|
281
|
+
? { x: 次.args[0]!, y: 次.args[1]! }
|
|
282
|
+
: 次.cmd === "L" || 次.cmd === "M"
|
|
283
|
+
? { x: 次.args[0]!, y: 次.args[1]! }
|
|
284
|
+
: null;
|
|
285
|
+
if (!向き先 || !Number.isFinite(向き先.x) || !Number.isFinite(向き先.y)) return d;
|
|
286
|
+
|
|
287
|
+
const x0 = 先頭.args[0]!;
|
|
288
|
+
const y0 = 先頭.args[1]!;
|
|
289
|
+
const dx = 向き先.x - x0;
|
|
290
|
+
const dy = 向き先.y - y0;
|
|
291
|
+
const len = Math.hypot(dx, dy);
|
|
292
|
+
if (len < shrinkPx + 1) return d;
|
|
293
|
+
const 比 = shrinkPx / len;
|
|
294
|
+
const 新x = x0 + dx * 比;
|
|
295
|
+
const 新y = y0 + dy * 比;
|
|
296
|
+
|
|
297
|
+
const tokens = d.trim().split(/\s+/);
|
|
298
|
+
if (tokens.length < 3) return d;
|
|
299
|
+
tokens[1] = 新x.toFixed(1);
|
|
300
|
+
tokens[2] = 新y.toFixed(1);
|
|
301
|
+
return tokens.join(" ");
|
|
302
|
+
}
|
|
303
|
+
|
|
257
304
|
/**
|
|
258
305
|
* SVG path d (M / L のみ対応) を progress (0-1) で切り詰めた新 d を返す。
|
|
259
306
|
* cdl の routePath は直線 + L-bend (M x y L x y [L x y]) のみを返すため、
|