@cardenelabs/cdl 0.119.0 → 0.121.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 +92 -1
- package/dist/index.cjs +38 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +38 -8
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +38 -8
- package/dist/react.cjs.map +1 -1
- package/dist/react.js +38 -8
- package/dist/react.js.map +1 -1
- package/package.json +1 -1
- package/src/layout/head-label.ts +27 -8
- package/src/render/edges.tsx +41 -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.121.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,33 @@ import { extractPathSegments } from "./collisions";
|
|
|
22
22
|
import { hasRenderedLabel } from "./spec";
|
|
23
23
|
import { measureTextWidth } from "./text-width";
|
|
24
24
|
|
|
25
|
-
/**
|
|
26
|
-
|
|
25
|
+
/**
|
|
26
|
+
* 端の字の大きさ (world)。
|
|
27
|
+
*
|
|
28
|
+
* **札の下の行とは別に持つ** (#994)。 元は `EDGE_LABEL_TEXT.sub.fontSize` を引いていたが、
|
|
29
|
+
* 札は箱の間に置くので縮めても読め、端の字は線の脇で単独に立つので同じ大きさでは負ける。
|
|
30
|
+
* 見本帳はクラス図を 0.42 倍で出しており、19 では画面 8 画素と、箱の中の字 (11 画素) より
|
|
31
|
+
* 一段小さく見えた。 26 にすると画面で 11 画素になり、箱の中の字と並ぶ。
|
|
32
|
+
*/
|
|
33
|
+
export const 端の字の大きさ = 26;
|
|
34
|
+
|
|
35
|
+
/** 字の箱の高さ。 字の大きさに上下の余白を足す */
|
|
36
|
+
const 箱の高さ = Math.round(端の字の大きさ * 1.2);
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* 端の印と端の字の間に空ける余白 (world)。 線に沿う向きと直角の向きで同じ値を使う。
|
|
40
|
+
*
|
|
41
|
+
* **字の大きさに連れて広げない** (#994)。 #989 では字の箱の半分から導いていたが、字を 26 に
|
|
42
|
+
* 上げると余白まで 16 に広がり、印の大きさは変わらないのに字だけが線から遠ざかった。
|
|
43
|
+
* 実機で見た利用者の言葉は「間伸びしてる」 で、図も縦に伸びていた。
|
|
44
|
+
*
|
|
45
|
+
* この余白が避ける相手は **印** で、印は字の大きさに連れて変わらない (`EDGE_HEAD_MARKER_SIZE`
|
|
46
|
+
* は固定)。 避ける相手が動かないなら、避ける幅も動かさない。
|
|
47
|
+
*
|
|
48
|
+
* 値は #989 が字 19 で導いた 12 をそのまま据え置く。 見本帳の 0.42 倍では 5 画素で、
|
|
49
|
+
* 印の縁と字の箱の間に 1 行の半分ほどが残る。
|
|
50
|
+
*/
|
|
51
|
+
export const 端の字と印の余白 = 12;
|
|
27
52
|
|
|
28
53
|
/** 端の印が線の芯から左右へ張り出す長さ (world)。 印の縦の半分 */
|
|
29
54
|
export const 端の印の張り出し = EDGE_HEAD_MARKER_SIZE.既定 / 2;
|
|
@@ -52,12 +77,6 @@ export const 端の字を戻す距離 = EDGE_HEAD_MARKER_SIZE.既定 + 端の字
|
|
|
52
77
|
*/
|
|
53
78
|
export const 端の字と線の隙間 = 端の印の張り出し + 端の字と印の余白;
|
|
54
79
|
|
|
55
|
-
/** 端の字の大きさ。 札の下の行と同じ等幅の字 = 多重度を札に入れていた頃と同じ見え方 */
|
|
56
|
-
export const 端の字の大きさ = EDGE_LABEL_TEXT.sub.fontSize;
|
|
57
|
-
|
|
58
|
-
/** 字の箱の高さ。 字の大きさに上下の余白を足す */
|
|
59
|
-
const 箱の高さ = Math.round(端の字の大きさ * 1.2);
|
|
60
|
-
|
|
61
80
|
/** 字の大きさに対する baseline の深さ。 等幅の字の ascent が字の大きさの 8 割ほど */
|
|
62
81
|
const BASELINE_RATIO = 0.8;
|
|
63
82
|
|
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,12 @@ 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
|
+
// 縮める幅は芯ではなく **玉の一番外の縁** で測る (#994) = 使う側が縁を足すと芯だけでは足りない
|
|
115
|
+
const 通り道を縮める = EDGE_HEAD_MARKER_SIZE.既定 + 走る丸の外側;
|
|
116
|
+
const glowPathD = shrinkPathStart(shrinkPathEnd(edge.d, 通り道を縮める), 通り道を縮める);
|
|
106
117
|
void isPassThrough;
|
|
107
118
|
|
|
108
119
|
return (
|
|
@@ -168,7 +179,7 @@ export function CdlEdgeView({
|
|
|
168
179
|
*/}
|
|
169
180
|
{flowing && !isDotted && (
|
|
170
181
|
<g data-cdl-role="edge-flow">
|
|
171
|
-
<path id={glowPathId} d={
|
|
182
|
+
<path id={glowPathId} d={glowPathD} fill="none" stroke="none" />
|
|
172
183
|
<EdgeGlowCircles color={color} edgeId={edge.id} />
|
|
173
184
|
</g>
|
|
174
185
|
)}
|
|
@@ -250,11 +261,34 @@ function SolidEdgePath({
|
|
|
250
261
|
/**
|
|
251
262
|
* EdgeGlowCircles ... 進行点 glow 3 重円 (28/18/11px) を mpath で path 沿い移動。
|
|
252
263
|
*/
|
|
264
|
+
/**
|
|
265
|
+
* 走る丸の 3 枚の半径 (world)。 濃さは外から順に 0.18 / 0.45 / 1 で、芯だけが不透明。
|
|
266
|
+
*
|
|
267
|
+
* **通り道を縮める長さが芯の半径を読む** (#988)。 芯は端の印の上に乗ると穴が開いたように
|
|
268
|
+
* 見えるので、印の長さに芯の半径を足した分だけ通り道を縮める。 ここに数を直に書くと、
|
|
269
|
+
* 丸を大きくした時に芯だけが印へはみ出す。
|
|
270
|
+
*/
|
|
271
|
+
export const 走る丸の半径 = { 外: 28, 中: 18, 芯: 12 } as const;
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* 使う側が芯に足す縁の太さの見込み (world、半分の値)。
|
|
275
|
+
*
|
|
276
|
+
* 描く側は芯を塗りだけで描くが、使う側は紙の色の縁を回して線から浮かせることがある
|
|
277
|
+
* (見本帳は 3.5 の縁を足しており、外側は 12 + 1.75 = 13.75 になる)。
|
|
278
|
+
* その縁は CSS で足されるので描く側からは見えない = 見込みとして持つしかない。
|
|
279
|
+
*
|
|
280
|
+
* 2 にするのは見本帳の 1.75 を丸めた値。 **これより太い縁を足すと玉が印に入る**。
|
|
281
|
+
*/
|
|
282
|
+
export const 走る丸の縁の見込み = 2;
|
|
283
|
+
|
|
284
|
+
/** 玉の一番外の縁までの長さ (world)。 通り道を端から縮める幅の基準になる */
|
|
285
|
+
export const 走る丸の外側 = 走る丸の半径.芯 + 走る丸の縁の見込み;
|
|
286
|
+
|
|
253
287
|
function EdgeGlowCircles({ color, edgeId }: { color: string; edgeId: string }): JSX.Element {
|
|
254
288
|
const ref = `#cdl-edge-${edgeId}`;
|
|
255
289
|
return (
|
|
256
290
|
<g data-cdl-particle={edgeId} data-cdl-particle-path-ref={ref}>
|
|
257
|
-
<circle r={
|
|
291
|
+
<circle r={走る丸の半径.外} fill={color}>
|
|
258
292
|
<animateMotion dur="2s" begin="-1s" repeatCount="indefinite" rotate="auto">
|
|
259
293
|
<mpath href={ref} />
|
|
260
294
|
</animateMotion>
|
|
@@ -267,7 +301,7 @@ function EdgeGlowCircles({ color, edgeId }: { color: string; edgeId: string }):
|
|
|
267
301
|
repeatCount="indefinite"
|
|
268
302
|
/>
|
|
269
303
|
</circle>
|
|
270
|
-
<circle r={
|
|
304
|
+
<circle r={走る丸の半径.中} fill={color}>
|
|
271
305
|
<animateMotion dur="2s" begin="-1s" repeatCount="indefinite" rotate="auto">
|
|
272
306
|
<mpath href={ref} />
|
|
273
307
|
</animateMotion>
|
|
@@ -280,7 +314,7 @@ function EdgeGlowCircles({ color, edgeId }: { color: string; edgeId: string }):
|
|
|
280
314
|
repeatCount="indefinite"
|
|
281
315
|
/>
|
|
282
316
|
</circle>
|
|
283
|
-
<circle r={
|
|
317
|
+
<circle r={走る丸の半径.芯} fill={color}>
|
|
284
318
|
<animateMotion dur="2s" begin="-1s" repeatCount="indefinite" rotate="auto">
|
|
285
319
|
<mpath href={ref} />
|
|
286
320
|
</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]) のみを返すため、
|