@cardenelabs/cdl 0.121.0 → 0.122.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 +36 -1
- package/README.md +73 -0
- package/SPEC.md +1 -1
- package/dist/index.cjs +464 -66
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -10
- package/dist/index.d.ts +16 -10
- package/dist/index.js +464 -66
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +453 -57
- 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 +453 -57
- package/dist/react.js.map +1 -1
- package/dist/{render-A5AKjNuy.d.cts → render-U_pC-Ksh.d.cts} +21 -6
- package/dist/{render-A5AKjNuy.d.ts → render-U_pC-Ksh.d.ts} +21 -6
- package/package.json +1 -1
- package/src/builder.ts +3 -2
- package/src/index.ts +1 -0
- package/src/kinds/dyn-shape.tsx +22 -9
- package/src/kinds/station.tsx +29 -0
- package/src/layout/collisions.ts +29 -20
- package/src/layout/curve-path.ts +125 -0
- package/src/layout/edges.ts +207 -39
- package/src/layout/head-label.ts +8 -4
- package/src/layout/metro-path.ts +184 -0
- package/src/layout/spec.ts +16 -1
- package/src/layout/tokens.ts +2 -0
- package/src/layout-with-validation.ts +10 -5
- package/src/layout.ts +22 -12
- package/src/render/edges.tsx +1 -0
- package/src/render/nodes.tsx +3 -0
- package/src/types.ts +22 -4
- package/src/visual-validate.ts +24 -4
package/dist/react.cjs
CHANGED
|
@@ -988,6 +988,7 @@ var KINDS_DRAWING_TITLE_OUTSIDE = /* @__PURE__ */ new Set([
|
|
|
988
988
|
"dyn-arc",
|
|
989
989
|
"dyn-wave",
|
|
990
990
|
"dyn-polygon",
|
|
991
|
+
"station",
|
|
991
992
|
...KINDS_DRAWING_OWN_PAYLOAD
|
|
992
993
|
]);
|
|
993
994
|
function drawsTitleOutsideBox(kind) {
|
|
@@ -995,7 +996,7 @@ function drawsTitleOutsideBox(kind) {
|
|
|
995
996
|
}
|
|
996
997
|
var TITLE_FONT_SIZE_DEFAULT = 18;
|
|
997
998
|
var TITLE_FONT_SIZE = new Map([
|
|
998
|
-
...["dyn-rect", "dyn-circle", "dyn-arc", "dyn-wave", "dyn-polygon"].map(
|
|
999
|
+
...["dyn-rect", "dyn-circle", "dyn-arc", "dyn-wave", "dyn-polygon", "station"].map(
|
|
999
1000
|
(k) => [k, 13]
|
|
1000
1001
|
),
|
|
1001
1002
|
...[
|
|
@@ -1046,6 +1047,10 @@ var TITLE_FONT_SIZE = new Map([
|
|
|
1046
1047
|
function titleFontSize(kind) {
|
|
1047
1048
|
return TITLE_FONT_SIZE.get(kind ?? "") ?? TITLE_FONT_SIZE_DEFAULT;
|
|
1048
1049
|
}
|
|
1050
|
+
var KINDS_WITH_TITLE_WIDTH_OUTSIDE_NODE = /* @__PURE__ */ new Set(["station"]);
|
|
1051
|
+
function skipsNodeTitleWidthCheck(kind) {
|
|
1052
|
+
return KINDS_WITH_TITLE_WIDTH_OUTSIDE_NODE.has(kind ?? "");
|
|
1053
|
+
}
|
|
1049
1054
|
var TITLE_BOX_PADDING = 52;
|
|
1050
1055
|
var KINDS_SCALING_TITLE_WHEN_LOW = /* @__PURE__ */ new Set(["actor", "event", "function", "storage"]);
|
|
1051
1056
|
var KINDS_SCALING_TITLE_ALWAYS = /* @__PURE__ */ new Set([
|
|
@@ -1128,7 +1133,8 @@ var KINDS_DRAWING_ONLY_SHAPE = [
|
|
|
1128
1133
|
"dyn-circle",
|
|
1129
1134
|
"dyn-arc",
|
|
1130
1135
|
"dyn-wave",
|
|
1131
|
-
"dyn-polygon"
|
|
1136
|
+
"dyn-polygon",
|
|
1137
|
+
"station"
|
|
1132
1138
|
];
|
|
1133
1139
|
var KINDS_WITHOUT_EYEBROW_TEXT = /* @__PURE__ */ new Set([
|
|
1134
1140
|
...KINDS_DRAWING_ONLY_SHAPE,
|
|
@@ -1831,13 +1837,13 @@ function detectCollisions(bboxes, edges) {
|
|
|
1831
1837
|
}
|
|
1832
1838
|
function extractPathSegments(d) {
|
|
1833
1839
|
const segs = [];
|
|
1834
|
-
const commandRegex = /([
|
|
1840
|
+
const commandRegex = /([MLCQAZmlcqaz])([^MLCQAZmlcqaz]*)/g;
|
|
1835
1841
|
let cur = { x: 0, y: 0 };
|
|
1836
1842
|
let m;
|
|
1837
1843
|
while ((m = commandRegex.exec(d)) !== null) {
|
|
1838
1844
|
const cmd = m[1];
|
|
1839
1845
|
const args = (m[2] ?? "").trim().split(/[\s,]+/).filter(Boolean).map(parseFloat);
|
|
1840
|
-
const need = cmd === "M" || cmd === "L" ? 2 : cmd === "Q" ? 4 : cmd === "C" ? 6 : 0;
|
|
1846
|
+
const need = cmd === "M" || cmd === "L" ? 2 : cmd === "Q" ? 4 : cmd === "C" ? 6 : cmd === "A" ? 7 : 0;
|
|
1841
1847
|
if (need > 0 && (args.length < need || !args.slice(0, need).every(Number.isFinite))) continue;
|
|
1842
1848
|
if (cmd === "M") {
|
|
1843
1849
|
cur = { x: args[0], y: args[1] };
|
|
@@ -1867,6 +1873,10 @@ function extractPathSegments(d) {
|
|
|
1867
1873
|
ctrl: [{ x: args[0], y: args[1] }, { x: args[2], y: args[3] }]
|
|
1868
1874
|
});
|
|
1869
1875
|
cur = next;
|
|
1876
|
+
} else if (cmd === "A") {
|
|
1877
|
+
const next = { x: args[5], y: args[6] };
|
|
1878
|
+
segs.push({ x1: cur.x, y1: cur.y, x2: next.x, y2: next.y, cmd: "A" });
|
|
1879
|
+
cur = next;
|
|
1870
1880
|
}
|
|
1871
1881
|
}
|
|
1872
1882
|
return segs;
|
|
@@ -2973,6 +2983,232 @@ function applyEdgeLabelAndPathShifts(edges, labelShiftMap, edgePathShiftMap) {
|
|
|
2973
2983
|
});
|
|
2974
2984
|
}
|
|
2975
2985
|
|
|
2986
|
+
// src/layout/curve-path.ts
|
|
2987
|
+
var SAME_HEIGHT_THRESHOLD = 1;
|
|
2988
|
+
function curvePath(from, to, edge, labelMarginExtra = 0) {
|
|
2989
|
+
const fromLeft = from.cx - from.w / 2;
|
|
2990
|
+
const fromRight = from.cx + from.w / 2;
|
|
2991
|
+
const toLeft = to.cx - to.w / 2;
|
|
2992
|
+
const toRight = to.cx + to.w / 2;
|
|
2993
|
+
let fromSide;
|
|
2994
|
+
let toSide;
|
|
2995
|
+
let x1;
|
|
2996
|
+
let x2;
|
|
2997
|
+
if (toLeft >= fromRight) {
|
|
2998
|
+
fromSide = "right";
|
|
2999
|
+
toSide = "left";
|
|
3000
|
+
x1 = fromRight;
|
|
3001
|
+
x2 = toLeft;
|
|
3002
|
+
} else if (toRight <= fromLeft) {
|
|
3003
|
+
fromSide = "left";
|
|
3004
|
+
toSide = "right";
|
|
3005
|
+
x1 = fromLeft;
|
|
3006
|
+
x2 = toRight;
|
|
3007
|
+
} else {
|
|
3008
|
+
fromSide = "right";
|
|
3009
|
+
toSide = "right";
|
|
3010
|
+
x1 = fromRight;
|
|
3011
|
+
x2 = toRight;
|
|
3012
|
+
}
|
|
3013
|
+
const y1 = from.cy;
|
|
3014
|
+
const y2 = to.cy;
|
|
3015
|
+
const sameHeight = Math.abs(y2 - y1) < SAME_HEIGHT_THRESHOLD;
|
|
3016
|
+
let d;
|
|
3017
|
+
let midX;
|
|
3018
|
+
let midY;
|
|
3019
|
+
let tangentX;
|
|
3020
|
+
let tangentY;
|
|
3021
|
+
if (sameHeight) {
|
|
3022
|
+
d = `M ${x1} ${y1} L ${x2} ${y2}`;
|
|
3023
|
+
midX = (x1 + x2) / 2;
|
|
3024
|
+
midY = (y1 + y2) / 2;
|
|
3025
|
+
tangentX = x2 - x1;
|
|
3026
|
+
tangentY = y2 - y1;
|
|
3027
|
+
} else {
|
|
3028
|
+
let c1x;
|
|
3029
|
+
let c2x;
|
|
3030
|
+
if (fromSide !== toSide) {
|
|
3031
|
+
const direction = x2 > x1 ? 1 : -1;
|
|
3032
|
+
const bulge = Math.max(Math.abs(x2 - x1) / 2, EDGE_STUB_OUT);
|
|
3033
|
+
c1x = x1 + direction * bulge;
|
|
3034
|
+
c2x = x2 - direction * bulge;
|
|
3035
|
+
} else {
|
|
3036
|
+
const bulgeX = Math.max(x1, x2) + EDGE_STUB_OUT;
|
|
3037
|
+
c1x = bulgeX;
|
|
3038
|
+
c2x = bulgeX;
|
|
3039
|
+
}
|
|
3040
|
+
d = `M ${x1} ${y1} C ${c1x} ${y1}, ${c2x} ${y2}, ${x2} ${y2}`;
|
|
3041
|
+
midX = (x1 + 3 * c1x + 3 * c2x + x2) / 8;
|
|
3042
|
+
midY = (y1 + 3 * y1 + 3 * y2 + y2) / 8;
|
|
3043
|
+
tangentX = x2 + c2x - c1x - x1;
|
|
3044
|
+
tangentY = y2 + y2 - y1 - y1;
|
|
3045
|
+
}
|
|
3046
|
+
if (edge?.overlay) {
|
|
3047
|
+
return { d, fromSide, toSide, labelX: midX, labelY: midY, labelAnchor: "middle" };
|
|
3048
|
+
}
|
|
3049
|
+
const tangentLength = Math.hypot(tangentX, tangentY);
|
|
3050
|
+
let normalX = tangentY / tangentLength;
|
|
3051
|
+
let normalY = -tangentX / tangentLength;
|
|
3052
|
+
if (normalY > Number.EPSILON || Math.abs(normalY) <= Number.EPSILON && normalX < 0) {
|
|
3053
|
+
normalX *= -1;
|
|
3054
|
+
normalY *= -1;
|
|
3055
|
+
}
|
|
3056
|
+
const labelW = computeLabelBoxW(edge?.label, edge?.sub);
|
|
3057
|
+
const labelH = labelPillH(edge?.sub);
|
|
3058
|
+
const distance = labelPathClearanceHorizontal(edge) + (Math.abs(normalX) * labelW + Math.abs(normalY) * labelH) / 2 + labelMarginExtra;
|
|
3059
|
+
return {
|
|
3060
|
+
d,
|
|
3061
|
+
fromSide,
|
|
3062
|
+
toSide,
|
|
3063
|
+
labelX: midX + normalX * distance,
|
|
3064
|
+
labelY: midY + normalY * distance,
|
|
3065
|
+
labelAnchor: "middle"
|
|
3066
|
+
};
|
|
3067
|
+
}
|
|
3068
|
+
|
|
3069
|
+
// src/layout/metro-path.ts
|
|
3070
|
+
var METRO_CORNER_RADIUS = 24;
|
|
3071
|
+
var T = Math.SQRT2 - 1;
|
|
3072
|
+
function round2(v) {
|
|
3073
|
+
return Math.round(v * 100) / 100;
|
|
3074
|
+
}
|
|
3075
|
+
function point(p) {
|
|
3076
|
+
return `${round2(p.x)} ${round2(p.y)}`;
|
|
3077
|
+
}
|
|
3078
|
+
function add(p, v, scale) {
|
|
3079
|
+
return { x: p.x + v.x * scale, y: p.y + v.y * scale };
|
|
3080
|
+
}
|
|
3081
|
+
function polyline(points) {
|
|
3082
|
+
const distinct = points.filter((p, i) => {
|
|
3083
|
+
const previous = points[i - 1];
|
|
3084
|
+
return previous === void 0 || p.x !== previous.x || p.y !== previous.y;
|
|
3085
|
+
});
|
|
3086
|
+
return distinct.map((p, i) => `${i === 0 ? "M" : "L"} ${point(p)}`).join(" ");
|
|
3087
|
+
}
|
|
3088
|
+
function metroPath(from, to, edge, labelMarginExtra = 0) {
|
|
3089
|
+
const fromLeft = from.cx - from.w / 2;
|
|
3090
|
+
const fromRight = from.cx + from.w / 2;
|
|
3091
|
+
const toLeft = to.cx - to.w / 2;
|
|
3092
|
+
const toRight = to.cx + to.w / 2;
|
|
3093
|
+
let fromSide;
|
|
3094
|
+
let toSide;
|
|
3095
|
+
let x1;
|
|
3096
|
+
let x2;
|
|
3097
|
+
let dir;
|
|
3098
|
+
if (toLeft >= fromRight) {
|
|
3099
|
+
fromSide = "right";
|
|
3100
|
+
toSide = "left";
|
|
3101
|
+
x1 = fromRight;
|
|
3102
|
+
x2 = toLeft;
|
|
3103
|
+
dir = 1;
|
|
3104
|
+
} else if (toRight <= fromLeft) {
|
|
3105
|
+
fromSide = "left";
|
|
3106
|
+
toSide = "right";
|
|
3107
|
+
x1 = fromLeft;
|
|
3108
|
+
x2 = toRight;
|
|
3109
|
+
dir = -1;
|
|
3110
|
+
} else {
|
|
3111
|
+
fromSide = "right";
|
|
3112
|
+
toSide = "right";
|
|
3113
|
+
x1 = fromRight;
|
|
3114
|
+
x2 = toRight;
|
|
3115
|
+
dir = 1;
|
|
3116
|
+
}
|
|
3117
|
+
const y1 = from.cy;
|
|
3118
|
+
const y2 = to.cy;
|
|
3119
|
+
const dy = y2 - y1;
|
|
3120
|
+
const sgn = Math.sign(dy);
|
|
3121
|
+
let d;
|
|
3122
|
+
let mid;
|
|
3123
|
+
let tangent;
|
|
3124
|
+
if (Math.abs(dy) < SAME_HEIGHT_THRESHOLD) {
|
|
3125
|
+
d = `M ${point({ x: x1, y: y1 })} L ${point({ x: x2, y: y2 })}`;
|
|
3126
|
+
mid = { x: (x1 + x2) / 2, y: (y1 + y2) / 2 };
|
|
3127
|
+
tangent = { x: x2 - x1, y: y2 - y1 };
|
|
3128
|
+
} else if (fromSide !== toSide && Math.abs(x2 - x1) >= Math.abs(dy)) {
|
|
3129
|
+
const horizontal = Math.abs(x2 - x1);
|
|
3130
|
+
const s = (horizontal - Math.abs(dy)) / 2;
|
|
3131
|
+
const p0 = { x: x1, y: y1 };
|
|
3132
|
+
const p1 = { x: x1 + dir * s, y: y1 };
|
|
3133
|
+
const p2 = { x: x2 - dir * s, y: y2 };
|
|
3134
|
+
const p3 = { x: x2, y: y2 };
|
|
3135
|
+
const u = { x: dir, y: 0 };
|
|
3136
|
+
const w = { x: dir / Math.SQRT2, y: sgn / Math.SQRT2 };
|
|
3137
|
+
const r = Math.min(
|
|
3138
|
+
METRO_CORNER_RADIUS,
|
|
3139
|
+
s / T,
|
|
3140
|
+
Math.SQRT2 * Math.abs(dy) / (2 * T)
|
|
3141
|
+
);
|
|
3142
|
+
const t = r * T;
|
|
3143
|
+
if (r >= 0.5) {
|
|
3144
|
+
const f1 = dir * sgn > 0 ? 1 : 0;
|
|
3145
|
+
d = [
|
|
3146
|
+
`M ${point(p0)}`,
|
|
3147
|
+
`L ${point(add(p1, u, -t))}`,
|
|
3148
|
+
`A ${round2(r)} ${round2(r)} 0 0 ${f1} ${point(add(p1, w, t))}`,
|
|
3149
|
+
`L ${point(add(p2, w, -t))}`,
|
|
3150
|
+
`A ${round2(r)} ${round2(r)} 0 0 ${1 - f1} ${point(add(p2, u, t))}`,
|
|
3151
|
+
`L ${point(p3)}`
|
|
3152
|
+
].join(" ");
|
|
3153
|
+
} else {
|
|
3154
|
+
d = polyline([p0, p1, p2, p3]);
|
|
3155
|
+
}
|
|
3156
|
+
mid = { x: (x1 + x2) / 2, y: (y1 + y2) / 2 };
|
|
3157
|
+
tangent = w;
|
|
3158
|
+
} else {
|
|
3159
|
+
fromSide = "right";
|
|
3160
|
+
toSide = "right";
|
|
3161
|
+
x1 = fromRight;
|
|
3162
|
+
x2 = toRight;
|
|
3163
|
+
const xa = Math.max(x1, x2) + EDGE_STUB_OUT;
|
|
3164
|
+
const h = Math.abs(dy) / 2;
|
|
3165
|
+
const p0 = { x: x1, y: y1 };
|
|
3166
|
+
const p1 = { x: xa, y: y1 };
|
|
3167
|
+
const p2 = { x: xa + h, y: y1 + sgn * h };
|
|
3168
|
+
const p3 = { x: xa, y: y2 };
|
|
3169
|
+
const p4 = { x: x2, y: y2 };
|
|
3170
|
+
const u = { x: 1, y: 0 };
|
|
3171
|
+
const w1 = { x: 1 / Math.SQRT2, y: sgn / Math.SQRT2 };
|
|
3172
|
+
const w2 = { x: -1 / Math.SQRT2, y: sgn / Math.SQRT2 };
|
|
3173
|
+
const r = Math.min(METRO_CORNER_RADIUS, h);
|
|
3174
|
+
const t = r * T;
|
|
3175
|
+
const sweep = sgn > 0 ? 1 : 0;
|
|
3176
|
+
d = [
|
|
3177
|
+
`M ${point(p0)}`,
|
|
3178
|
+
`L ${point(add(p1, u, -t))}`,
|
|
3179
|
+
`A ${round2(r)} ${round2(r)} 0 0 ${sweep} ${point(add(p1, w1, t))}`,
|
|
3180
|
+
`L ${point(add(p2, w1, -r))}`,
|
|
3181
|
+
`A ${round2(r)} ${round2(r)} 0 0 ${sweep} ${point(add(p2, w2, r))}`,
|
|
3182
|
+
`L ${point(add(p3, w2, -t))}`,
|
|
3183
|
+
`A ${round2(r)} ${round2(r)} 0 0 ${sweep} ${point(add(p3, u, -t))}`,
|
|
3184
|
+
`L ${point(p4)}`
|
|
3185
|
+
].join(" ");
|
|
3186
|
+
mid = { x: p2.x - T * r, y: p2.y };
|
|
3187
|
+
tangent = { x: 0, y: sgn };
|
|
3188
|
+
}
|
|
3189
|
+
if (edge?.overlay) {
|
|
3190
|
+
return { d, fromSide, toSide, labelX: mid.x, labelY: mid.y, labelAnchor: "middle" };
|
|
3191
|
+
}
|
|
3192
|
+
const tangentLength = Math.hypot(tangent.x, tangent.y);
|
|
3193
|
+
let normalX = tangent.y / tangentLength;
|
|
3194
|
+
let normalY = -tangent.x / tangentLength;
|
|
3195
|
+
if (normalY > Number.EPSILON || Math.abs(normalY) <= Number.EPSILON && normalX < 0) {
|
|
3196
|
+
normalX *= -1;
|
|
3197
|
+
normalY *= -1;
|
|
3198
|
+
}
|
|
3199
|
+
const labelW = computeLabelBoxW(edge?.label, edge?.sub);
|
|
3200
|
+
const labelH = labelPillH(edge?.sub);
|
|
3201
|
+
const distance = labelPathClearanceHorizontal(edge) + (Math.abs(normalX) * labelW + Math.abs(normalY) * labelH) / 2 + labelMarginExtra;
|
|
3202
|
+
return {
|
|
3203
|
+
d,
|
|
3204
|
+
fromSide,
|
|
3205
|
+
toSide,
|
|
3206
|
+
labelX: mid.x + normalX * distance,
|
|
3207
|
+
labelY: mid.y + normalY * distance,
|
|
3208
|
+
labelAnchor: "middle"
|
|
3209
|
+
};
|
|
3210
|
+
}
|
|
3211
|
+
|
|
2976
3212
|
// src/types.ts
|
|
2977
3213
|
var TONES = ["accent", "teal", "success", "error", "warning", "info"];
|
|
2978
3214
|
var NODE_KINDS = [
|
|
@@ -3100,6 +3336,8 @@ var NODE_KINDS = [
|
|
|
3100
3336
|
// 状態遷移図が使う。 印そのものが意味を持つので文字は描かない
|
|
3101
3337
|
"mark-start",
|
|
3102
3338
|
"mark-end",
|
|
3339
|
+
// 路線図の駅 (#996) ... 線路上の点を小さな丸で示し、名前と説明は丸の外に置く
|
|
3340
|
+
"station",
|
|
3103
3341
|
// 時系列のやり取り (#578) ... 1 つの箱が図を丸ごと描く。 縦が時間で、参加者は箱を持たない
|
|
3104
3342
|
"sequence-board"
|
|
3105
3343
|
];
|
|
@@ -6259,6 +6497,7 @@ function runAxes(laid, diag, violations, counts, push, profile, skippedAxes, \u5
|
|
|
6259
6497
|
const LIFELINE_W_THRESHOLD = 8;
|
|
6260
6498
|
for (const n of laid.nodes) {
|
|
6261
6499
|
if (n.w < LIFELINE_W_THRESHOLD) continue;
|
|
6500
|
+
if (n.kind === "station") continue;
|
|
6262
6501
|
if (n.w < MIN_NODE_W || n.h < MIN_NODE_H) {
|
|
6263
6502
|
push(
|
|
6264
6503
|
"node-visibility",
|
|
@@ -6280,6 +6519,7 @@ function runAxes(laid, diag, violations, counts, push, profile, skippedAxes, \u5
|
|
|
6280
6519
|
if (n.id.endsWith("-spacer")) continue;
|
|
6281
6520
|
if (!rendersTitleText(n.kind)) continue;
|
|
6282
6521
|
if (fitsOwnTitleWidth(n.kind)) continue;
|
|
6522
|
+
if (skipsNodeTitleWidthCheck(n.kind)) continue;
|
|
6283
6523
|
if (!isRenderedNode(n)) continue;
|
|
6284
6524
|
const titleTrimmed = n.title?.trim() ?? "";
|
|
6285
6525
|
if (!titleTrimmed) continue;
|
|
@@ -6884,6 +7124,9 @@ function runAxes(laid, diag, violations, counts, push, profile, skippedAxes, \u5
|
|
|
6884
7124
|
for (let i = 0; i + 1 < sorted.length; i++) {
|
|
6885
7125
|
const upper = sorted[i];
|
|
6886
7126
|
const lower = sorted[i + 1];
|
|
7127
|
+
if (upper.cx + upper.w / 2 <= lower.cx - lower.w / 2 || lower.cx + lower.w / 2 <= upper.cx - upper.w / 2) {
|
|
7128
|
+
continue;
|
|
7129
|
+
}
|
|
6887
7130
|
const upperBottom = upper.cy + upper.h / 2;
|
|
6888
7131
|
const lowerTop = lower.cy - lower.h / 2;
|
|
6889
7132
|
const gap = lowerTop - upperBottom;
|
|
@@ -7515,11 +7758,18 @@ function runAxes(laid, diag, violations, counts, push, profile, skippedAxes, \u5
|
|
|
7515
7758
|
{
|
|
7516
7759
|
const crestSpans = (segs, peakY) => {
|
|
7517
7760
|
const out = [];
|
|
7761
|
+
let \u5186\u5F27\u306E\u59CB\u70B9 = null;
|
|
7518
7762
|
for (const s of segs) {
|
|
7519
|
-
if (
|
|
7520
|
-
|
|
7763
|
+
if (s.cmd === "A") {
|
|
7764
|
+
\u5186\u5F27\u306E\u59CB\u70B9 ??= { x: s.x1, y: s.y1 };
|
|
7765
|
+
continue;
|
|
7766
|
+
}
|
|
7767
|
+
const \u59CB\u70B9 = \u5186\u5F27\u306E\u59CB\u70B9 ?? { x: s.x1, y: s.y1 };
|
|
7768
|
+
\u5186\u5F27\u306E\u59CB\u70B9 = null;
|
|
7769
|
+
if (Math.abs(\u59CB\u70B9.y - s.y2) > 1) continue;
|
|
7770
|
+
if (Math.abs(\u59CB\u70B9.y - peakY) > 1) continue;
|
|
7521
7771
|
if (s.cmd !== "L" && !(s.ctrl ?? []).every((c) => Math.abs(c.y - peakY) <= 1)) continue;
|
|
7522
|
-
out.push({ minX: Math.min(
|
|
7772
|
+
out.push({ minX: Math.min(\u59CB\u70B9.x, s.x2), maxX: Math.max(\u59CB\u70B9.x, s.x2) });
|
|
7523
7773
|
}
|
|
7524
7774
|
return out;
|
|
7525
7775
|
};
|
|
@@ -7956,6 +8206,8 @@ var TOKENS = {
|
|
|
7956
8206
|
// 箱としての最小 (80x40) より大きく、名前を持つ箱 (card 320x150) よりは明らかに小さい
|
|
7957
8207
|
"mark-start": { w: 96, h: 96 },
|
|
7958
8208
|
"mark-end": { w: 96, h: 96 },
|
|
8209
|
+
// 路線図の駅 (#996) ... 正方形にして小さな丸が縦横どちらにも歪まないようにする。
|
|
8210
|
+
station: { w: 24, h: 24 },
|
|
7959
8211
|
// 時系列のやり取り (#578)。 実寸は中身から決まる (`sequenceBoardMetrics`) ので、
|
|
7960
8212
|
// ここは書かなかった時の受け皿
|
|
7961
8213
|
"sequence-board": { w: 960, h: 640 }
|
|
@@ -7980,6 +8232,7 @@ var LANE_PAD_MIN = 25;
|
|
|
7980
8232
|
// src/layout/edges.ts
|
|
7981
8233
|
var VERTICAL_DETOUR_GAP = 50;
|
|
7982
8234
|
var VERTICAL_DETOUR_CORNER = 14;
|
|
8235
|
+
var OVERLAY_LABEL_ON_PATH_TOLERANCE = 1;
|
|
7983
8236
|
function verticalCornerDir(from, to) {
|
|
7984
8237
|
return to < from ? -1 : 1;
|
|
7985
8238
|
}
|
|
@@ -8063,6 +8316,36 @@ function layoutEdges(diag, nodes, lanes = []) {
|
|
|
8063
8316
|
});
|
|
8064
8317
|
} else if (isSelfLoop(e)) {
|
|
8065
8318
|
selfLoopEdges.push(e);
|
|
8319
|
+
} else if (e.routing === "curve") {
|
|
8320
|
+
const from = nmap.get(e.from);
|
|
8321
|
+
const to = nmap.get(e.to);
|
|
8322
|
+
if (!from) throw new Error(`cdl layout: edge "${e.id}" \u306E from "${e.from}" \u304C node \u306B\u7121\u3044`);
|
|
8323
|
+
if (!to) throw new Error(`cdl layout: edge "${e.id}" \u306E to "${e.to}" \u304C node \u306B\u7121\u3044`);
|
|
8324
|
+
const c = curvePath(from, to, e, labelMarginExtra);
|
|
8325
|
+
positionedEdges.push({
|
|
8326
|
+
...e,
|
|
8327
|
+
d: c.d,
|
|
8328
|
+
fromSide: c.fromSide,
|
|
8329
|
+
toSide: c.toSide,
|
|
8330
|
+
labelX: c.labelX + (e.labelOffsetX ?? 0),
|
|
8331
|
+
labelY: c.labelY + (e.labelOffsetY ?? 0),
|
|
8332
|
+
labelAnchor: c.labelAnchor
|
|
8333
|
+
});
|
|
8334
|
+
} else if (e.routing === "metro") {
|
|
8335
|
+
const from = nmap.get(e.from);
|
|
8336
|
+
const to = nmap.get(e.to);
|
|
8337
|
+
if (!from) throw new Error(`cdl layout: edge "${e.id}" \u306E from "${e.from}" \u304C node \u306B\u7121\u3044`);
|
|
8338
|
+
if (!to) throw new Error(`cdl layout: edge "${e.id}" \u306E to "${e.to}" \u304C node \u306B\u7121\u3044`);
|
|
8339
|
+
const m = metroPath(from, to, e, labelMarginExtra);
|
|
8340
|
+
positionedEdges.push({
|
|
8341
|
+
...e,
|
|
8342
|
+
d: m.d,
|
|
8343
|
+
fromSide: m.fromSide,
|
|
8344
|
+
toSide: m.toSide,
|
|
8345
|
+
labelX: m.labelX + (e.labelOffsetX ?? 0),
|
|
8346
|
+
labelY: m.labelY + (e.labelOffsetY ?? 0),
|
|
8347
|
+
labelAnchor: m.labelAnchor
|
|
8348
|
+
});
|
|
8066
8349
|
} else {
|
|
8067
8350
|
autoEdges.push(e);
|
|
8068
8351
|
}
|
|
@@ -9567,9 +9850,11 @@ function hopEdgeCrossings(edges) {
|
|
|
9567
9850
|
ends.set(e.id, pathEnds(segs));
|
|
9568
9851
|
}
|
|
9569
9852
|
for (const e of edges) {
|
|
9853
|
+
if (e.routing === "metro") continue;
|
|
9570
9854
|
if ((e.d.match(/M/g) ?? []).length !== 1) continue;
|
|
9571
9855
|
const own = extractPathSegments(e.d);
|
|
9572
9856
|
if (own.length === 0) continue;
|
|
9857
|
+
if (own.some((s) => s.cmd === "A")) continue;
|
|
9573
9858
|
const myEnds = ends.get(e.id) ?? [];
|
|
9574
9859
|
const hops = /* @__PURE__ */ new Map();
|
|
9575
9860
|
for (let i = 0; i < own.length; i++) {
|
|
@@ -9657,7 +9942,7 @@ function pathVertices(d) {
|
|
|
9657
9942
|
const out = [{ x: first.x1, y: first.y1 }];
|
|
9658
9943
|
for (let i = 0; i < segs.length; i++) {
|
|
9659
9944
|
const s = segs[i];
|
|
9660
|
-
if (s.cmd === "C") return null;
|
|
9945
|
+
if (s.cmd === "C" || s.cmd === "A") return null;
|
|
9661
9946
|
if (s.cmd === "Q") {
|
|
9662
9947
|
const c = s.ctrl?.[0];
|
|
9663
9948
|
if (!c) return null;
|
|
@@ -9720,7 +10005,7 @@ function separateSharedStraightRuns(edges, nodes) {
|
|
|
9720
10005
|
const v = pathVertices(e.d);
|
|
9721
10006
|
if (v && v.length >= 2) verts.set(e.id, v);
|
|
9722
10007
|
}
|
|
9723
|
-
if (verts.size
|
|
10008
|
+
if (verts.size === 0) return;
|
|
9724
10009
|
const moved = /* @__PURE__ */ new Set();
|
|
9725
10010
|
const fits = (e, pts, k, ax, delta) => {
|
|
9726
10011
|
const \u8A66\u3057 = pts.map(
|
|
@@ -9738,33 +10023,40 @@ function separateSharedStraightRuns(edges, nodes) {
|
|
|
9738
10023
|
for (let j = i + 1; j < edges.length; j++) {
|
|
9739
10024
|
const \u524D = edges[i];
|
|
9740
10025
|
const \u5F8C = edges[j];
|
|
9741
|
-
|
|
9742
|
-
const
|
|
9743
|
-
|
|
9744
|
-
|
|
10026
|
+
if (\u524D.routing === "metro" && \u5F8C.routing === "metro") continue;
|
|
10027
|
+
const \u56FA\u5B9A = \u5F8C.routing === "metro" ? \u5F8C : \u524D;
|
|
10028
|
+
const \u52D5\u304B\u3059 = \u5F8C.routing === "metro" ? \u524D : \u5F8C;
|
|
10029
|
+
const B = verts.get(\u52D5\u304B\u3059.id);
|
|
10030
|
+
if (!B) continue;
|
|
10031
|
+
const \u56FA\u5B9A\u306E\u89D2 = verts.get(\u56FA\u5B9A.id);
|
|
10032
|
+
const \u56FA\u5B9A\u306E\u7D4C\u8DEF = \u56FA\u5B9A\u306E\u89D2 ? [\u56FA\u5B9A\u306E\u89D2] : \u56FA\u5B9A.routing === "metro" ? extractPathSegments(\u56FA\u5B9A.d).filter((s) => s.cmd === "L").map((s) => [{ x: s.x1, y: s.y1 }, { x: s.x2, y: s.y2 }]) : [];
|
|
10033
|
+
if (\u56FA\u5B9A\u306E\u7D4C\u8DEF.length === 0) continue;
|
|
10034
|
+
if (\u56FA\u5B9A.from === \u52D5\u304B\u3059.from) continue;
|
|
9745
10035
|
for (const ax of [0, 1]) {
|
|
9746
|
-
for (
|
|
9747
|
-
|
|
9748
|
-
|
|
9749
|
-
|
|
9750
|
-
|
|
9751
|
-
|
|
9752
|
-
|
|
9753
|
-
|
|
9754
|
-
|
|
9755
|
-
|
|
9756
|
-
|
|
9757
|
-
|
|
9758
|
-
|
|
9759
|
-
|
|
9760
|
-
|
|
9761
|
-
|
|
9762
|
-
|
|
9763
|
-
|
|
9764
|
-
|
|
10036
|
+
for (const A of \u56FA\u5B9A\u306E\u7D4C\u8DEF) {
|
|
10037
|
+
for (let ka = 0; ka + 1 < A.length; ka++) {
|
|
10038
|
+
if (!runIsAligned(A, ka, ax)) continue;
|
|
10039
|
+
for (let kb = 0; kb + 1 < B.length; kb++) {
|
|
10040
|
+
if (!runIsAligned(B, kb, ax)) continue;
|
|
10041
|
+
if (kb === 0 || kb + 1 === B.length - 1) continue;
|
|
10042
|
+
const \u5EA7\u6A19A = ax === 0 ? A[ka].x : A[ka].y;
|
|
10043
|
+
const \u5EA7\u6A19B = ax === 0 ? B[kb].x : B[kb].y;
|
|
10044
|
+
if (Math.abs(\u5EA7\u6A19A - \u5EA7\u6A19B) > SHARED_RUN_EPS) continue;
|
|
10045
|
+
const sa = runSpan(A, ka, ax);
|
|
10046
|
+
const sb = runSpan(B, kb, ax);
|
|
10047
|
+
if (Math.min(sa.hi, sb.hi) - Math.max(sa.lo, sb.lo) < SHARED_RUN_MIN) continue;
|
|
10048
|
+
for (const delta of [SHARED_RUN_SEPARATION, -SHARED_RUN_SEPARATION]) {
|
|
10049
|
+
if (!fits(\u52D5\u304B\u3059, B, kb, ax, delta)) continue;
|
|
10050
|
+
if (ax === 0) {
|
|
10051
|
+
B[kb].x += delta;
|
|
10052
|
+
B[kb + 1].x += delta;
|
|
10053
|
+
} else {
|
|
10054
|
+
B[kb].y += delta;
|
|
10055
|
+
B[kb + 1].y += delta;
|
|
10056
|
+
}
|
|
10057
|
+
moved.add(\u52D5\u304B\u3059.id);
|
|
10058
|
+
break;
|
|
9765
10059
|
}
|
|
9766
|
-
moved.add(\u5F8C.id);
|
|
9767
|
-
break;
|
|
9768
10060
|
}
|
|
9769
10061
|
}
|
|
9770
10062
|
}
|
|
@@ -9791,12 +10083,19 @@ function pullOutgoingFirstCorner(edges, nodes) {
|
|
|
9791
10083
|
let \u672D\u3092\u65E7\u7D4C\u8DEF\u3078\u8FFD\u5F93\u6E08\u307F = false;
|
|
9792
10084
|
for (const list of \u675F.values()) {
|
|
9793
10085
|
if (list.length < 2) continue;
|
|
9794
|
-
const \u4E26\u3073 = list.
|
|
10086
|
+
const \u4E26\u3073 = list.map((e) => {
|
|
9795
10087
|
const pts = pathVertices(e.d);
|
|
9796
|
-
if (
|
|
9797
|
-
|
|
9798
|
-
|
|
10088
|
+
if (pts && pts.length >= 2) {
|
|
10089
|
+
return { e, pts, \u6700\u521D\u306E\u9577\u3055: Math.hypot(pts[1].x - pts[0].x, pts[1].y - pts[0].y) };
|
|
10090
|
+
}
|
|
10091
|
+
if (e.routing !== "metro") return null;
|
|
10092
|
+
const first = extractPathSegments(e.d)[0];
|
|
10093
|
+
if (!first) return null;
|
|
10094
|
+
return { e, pts: null, \u6700\u521D\u306E\u9577\u3055: Math.hypot(first.x2 - first.x1, first.y2 - first.y1) };
|
|
10095
|
+
}).filter((x) => x !== null).sort((a, b) => b.\u6700\u521D\u306E\u9577\u3055 - a.\u6700\u521D\u306E\u9577\u3055);
|
|
9799
10096
|
for (const { e, pts, \u6700\u521D\u306E\u9577\u3055 } of \u4E26\u3073.slice(1)) {
|
|
10097
|
+
if (e.routing === "metro") continue;
|
|
10098
|
+
if (!pts) continue;
|
|
9800
10099
|
if ((\u540C\u3058\u7D44\u306E\u6570.get(`${e.from}->${e.to}`) ?? 0) > 1) continue;
|
|
9801
10100
|
if (pts.length < 3 || \u6700\u521D\u306E\u9577\u3055 <= EDGE_STUB_OUT + VERTICAL_DETOUR_CORNER) continue;
|
|
9802
10101
|
const \u6A2A = e.fromSide === "left" || e.fromSide === "right";
|
|
@@ -9850,7 +10149,8 @@ var ENDPOINT_FAN_MIN = 20;
|
|
|
9850
10149
|
var ENDPOINT_FAN_RUNUP = 40;
|
|
9851
10150
|
var MIXED_ROOT_OVERLAP_MIN = 24;
|
|
9852
10151
|
function fanIncomingEndpoints(edges, nodes) {
|
|
9853
|
-
|
|
10152
|
+
const \u7D42\u70B9\u306E\u79FB\u52D5 = /* @__PURE__ */ new Map();
|
|
10153
|
+
if (edges.length < 2) return \u7D42\u70B9\u306E\u79FB\u52D5;
|
|
9854
10154
|
const nmap = new Map(nodes.map((n) => [n.id, n]));
|
|
9855
10155
|
const \u5E73\u5766\u306A\u533A\u9593 = new Map(edges.map((e) => [e.id, flattenPathSegments(extractPathSegments(e.d))]));
|
|
9856
10156
|
const \u8EF8\u306B\u5E73\u884C\u306A\u91CD\u306A\u308A\u9577\u3055 = (\u4E00\u65B9, \u4ED6\u65B9) => {
|
|
@@ -9920,6 +10220,7 @@ function fanIncomingEndpoints(edges, nodes) {
|
|
|
9920
10220
|
const d = \u4E2D\u592E\u304C\u585E\u304C\u3063\u3066\u3044\u308B ? \u4E26\u3073[k].\u6DF1\u3055 < \u8FBA\u306E\u4E2D\u5FC3 ? -(++\u8CA0\u5074\u306E\u5E2D * \u9593\u9694) : ++\u6B63\u5074\u306E\u5E2D * \u9593\u9694 : k * \u9593\u9694 - \u4E2D\u5FC3;
|
|
9921
10221
|
if (Math.abs(d) < 1e-3) continue;
|
|
9922
10222
|
const e = \u4E26\u3073[k].e;
|
|
10223
|
+
if (e.routing === "metro") continue;
|
|
9923
10224
|
const \u524D = e.d;
|
|
9924
10225
|
const \u524D\u306E\u4EA4\u5DEE = countEdgeCrossings(edges).count;
|
|
9925
10226
|
const \u9577\u3044\u52A9\u8D70 = \u4E26\u3073[k].\u9577\u3044\u52A9\u8D70;
|
|
@@ -9931,6 +10232,68 @@ function fanIncomingEndpoints(edges, nodes) {
|
|
|
9931
10232
|
if (countEdgeCrossings(edges).count <= \u524D\u306E\u4EA4\u5DEE) break;
|
|
9932
10233
|
e.d = \u524D;
|
|
9933
10234
|
}
|
|
10235
|
+
if (e.d !== \u524D) \u7D42\u70B9\u306E\u79FB\u52D5.set(e.id, \u6A2A ? { dx: 0, dy: d } : { dx: d, dy: 0 });
|
|
10236
|
+
}
|
|
10237
|
+
}
|
|
10238
|
+
return \u7D42\u70B9\u306E\u79FB\u52D5;
|
|
10239
|
+
}
|
|
10240
|
+
function followLabelsToFannedTails(edges, obstacles, shifts) {
|
|
10241
|
+
if (shifts.size === 0) return;
|
|
10242
|
+
const \u6E2C\u308A\u65B9 = \u7DDA\u306E\u6E2C\u308A\u65B9\u3092\u4F5C\u308B();
|
|
10243
|
+
const labels = edges.filter(hasRenderedLabel).map((e) => ({
|
|
10244
|
+
kind: "edge-label",
|
|
10245
|
+
id: e.id,
|
|
10246
|
+
...computeLabelBBoxWorld(e)
|
|
10247
|
+
}));
|
|
10248
|
+
const labelIndexByEdgeId = new Map(labels.map((label, index) => [label.id, index]));
|
|
10249
|
+
const edgeById = new Map(edges.map((e) => [e.id, e]));
|
|
10250
|
+
const \u5468\u308A = {
|
|
10251
|
+
\u7BB1\u305F\u3061: obstacles.filter((o) => o.kind === "node" || o.kind === "lane-label"),
|
|
10252
|
+
labels,
|
|
10253
|
+
edges,
|
|
10254
|
+
edgeById,
|
|
10255
|
+
\u6E2C\u308A\u65B9
|
|
10256
|
+
};
|
|
10257
|
+
for (const e of edges) {
|
|
10258
|
+
const \u79FB\u52D5 = shifts.get(e.id);
|
|
10259
|
+
if (!\u79FB\u52D5 || !hasRenderedLabel(e)) continue;
|
|
10260
|
+
const labelIndex = labelIndexByEdgeId.get(e.id);
|
|
10261
|
+
if (labelIndex === void 0) continue;
|
|
10262
|
+
const \u5143\u306E\u672D = labels[labelIndex];
|
|
10263
|
+
const \u81EA\u5206\u306E\u533A\u9593 = \u6E2C\u308A\u65B9.\u533A\u9593(e.id, e.d);
|
|
10264
|
+
if (e.overlay && measurePathLabelGapSegs(\u81EA\u5206\u306E\u533A\u9593, {
|
|
10265
|
+
x: \u5143\u306E\u672D.x + \u5143\u306E\u672D.w / 2,
|
|
10266
|
+
y: \u5143\u306E\u672D.y + \u5143\u306E\u672D.h / 2,
|
|
10267
|
+
w: 0,
|
|
10268
|
+
h: 0
|
|
10269
|
+
}) <= OVERLAY_LABEL_ON_PATH_TOLERANCE) continue;
|
|
10270
|
+
const \u6700\u5F8C = \u81EA\u5206\u306E\u533A\u9593.at(-1);
|
|
10271
|
+
if (!\u6700\u5F8C) continue;
|
|
10272
|
+
if (!segmentsCrossRect([\u6700\u5F8C], \u5143\u306E\u672D)) continue;
|
|
10273
|
+
const \u5019\u88DCa = {
|
|
10274
|
+
labelX: e.labelX + \u79FB\u52D5.dx,
|
|
10275
|
+
labelY: e.labelY + \u79FB\u52D5.dy,
|
|
10276
|
+
labelAnchor: e.labelAnchor
|
|
10277
|
+
};
|
|
10278
|
+
const \u5019\u88DCa\u306E\u7BB1 = computeLabelBBoxWorld({ ...e, ...\u5019\u88DCa });
|
|
10279
|
+
const \u6700\u5F8C\u306F\u7E26 = Math.abs(\u6700\u5F8C.x2 - \u6700\u5F8C.x1) <= Math.abs(\u6700\u5F8C.y2 - \u6700\u5F8C.y1);
|
|
10280
|
+
const \u5019\u88DC = [
|
|
10281
|
+
\u5019\u88DCa,
|
|
10282
|
+
{
|
|
10283
|
+
labelX: \u6700\u5F8C\u306F\u7E26 ? \u6700\u5F8C.x1 * 2 - (\u5019\u88DCa\u306E\u7BB1.x + \u5019\u88DCa\u306E\u7BB1.w / 2) : \u5019\u88DCa\u306E\u7BB1.x + \u5019\u88DCa\u306E\u7BB1.w / 2,
|
|
10284
|
+
labelY: \u6700\u5F8C\u306F\u7E26 ? \u5019\u88DCa\u306E\u7BB1.y + \u5019\u88DCa\u306E\u7BB1.h / 2 : \u6700\u5F8C.y1 * 2 - (\u5019\u88DCa\u306E\u7BB1.y + \u5019\u88DCa\u306E\u7BB1.h / 2),
|
|
10285
|
+
labelAnchor: "middle"
|
|
10286
|
+
}
|
|
10287
|
+
];
|
|
10288
|
+
for (const c of \u5019\u88DC) {
|
|
10289
|
+
const \u672D = { kind: "edge-label", id: e.id, ...computeLabelBBoxWorld({ ...e, ...c }) };
|
|
10290
|
+
if (segmentsCrossRect([\u6700\u5F8C], \u672D)) continue;
|
|
10291
|
+
if (\u672D\u306E\u79FB\u52D5\u3092\u585E\u3050\u76F8\u624B\u3092\u63A2\u3059(\u5468\u308A, \u5143\u306E\u672D, \u672D) !== null) continue;
|
|
10292
|
+
e.labelX = c.labelX;
|
|
10293
|
+
e.labelY = c.labelY;
|
|
10294
|
+
e.labelAnchor = c.labelAnchor;
|
|
10295
|
+
labels[labelIndex] = \u672D;
|
|
10296
|
+
break;
|
|
9934
10297
|
}
|
|
9935
10298
|
}
|
|
9936
10299
|
}
|
|
@@ -9947,7 +10310,7 @@ function \u672B\u5C3E\u3092\u8FBA\u306B\u6CBF\u3063\u3066\u305A\u3089\u3059(e, \
|
|
|
9947
10310
|
if ((e.d.match(/M/g) ?? []).length !== 1) return null;
|
|
9948
10311
|
const segs = extractPathSegments(e.d);
|
|
9949
10312
|
if (segs.length === 0) return null;
|
|
9950
|
-
if (segs.some((s) => s.cmd === "C")) return null;
|
|
10313
|
+
if (segs.some((s) => s.cmd === "C" || s.cmd === "A")) return null;
|
|
9951
10314
|
const last = segs[segs.length - 1];
|
|
9952
10315
|
if (last.cmd !== "L") return null;
|
|
9953
10316
|
const \u5782\u76F4\u306B\u5165\u308B = \u6A2A ? Math.abs(last.y2 - last.y1) < 0.5 && Math.abs(last.x2 - last.x1) > 0.5 : Math.abs(last.x2 - last.x1) < 0.5 && Math.abs(last.y2 - last.y1) > 0.5;
|
|
@@ -10238,13 +10601,16 @@ function \u5411\u304D\u3092\u53D6\u308B(end, \u5019\u88DC) {
|
|
|
10238
10601
|
}
|
|
10239
10602
|
return null;
|
|
10240
10603
|
}
|
|
10241
|
-
function \u7AEF\
|
|
10604
|
+
function \u7DDA\u306E\u7AEF\u306E\u5411\u304D(d, \u7AEF) {
|
|
10242
10605
|
const segs = extractPathSegments(d);
|
|
10243
10606
|
if (segs.length === 0) return null;
|
|
10244
10607
|
if (\u7AEF === "head") {
|
|
10245
10608
|
const last = segs[segs.length - 1];
|
|
10246
10609
|
const end2 = { x: last.x2, y: last.y2 };
|
|
10247
|
-
const \u5019\u88DC2 = segs.slice().reverse().flatMap((s) =>
|
|
10610
|
+
const \u5019\u88DC2 = segs.slice().reverse().flatMap((s) => {
|
|
10611
|
+
const \u5236\u5FA1\u70B9 = s.cmd === "C" ? [s.ctrl?.[1], s.ctrl?.[0]] : s.cmd === "Q" ? [s.ctrl?.[0]] : [];
|
|
10612
|
+
return [...\u5236\u5FA1\u70B9, { x: s.x1, y: s.y1 }];
|
|
10613
|
+
});
|
|
10248
10614
|
return \u5411\u304D\u3092\u53D6\u308B(end2, \u5019\u88DC2);
|
|
10249
10615
|
}
|
|
10250
10616
|
const first = segs[0];
|
|
@@ -10255,7 +10621,7 @@ function \u7AEF\u3067\u306E\u5411\u304D(d, \u7AEF) {
|
|
|
10255
10621
|
function \u7AEF\u306E\u5B57\u3092\u7F6E\u304F(edge, \u7AEF = "head") {
|
|
10256
10622
|
const text = (\u7AEF === "head" ? edge.headLabel : edge.tailLabel)?.trim();
|
|
10257
10623
|
if (!text) return null;
|
|
10258
|
-
const \u5411\u304D = \u7AEF\
|
|
10624
|
+
const \u5411\u304D = \u7DDA\u306E\u7AEF\u306E\u5411\u304D(edge.d, \u7AEF);
|
|
10259
10625
|
if (!\u5411\u304D) return null;
|
|
10260
10626
|
const { end, ux, uy } = \u5411\u304D;
|
|
10261
10627
|
const bx = end.x - ux * \u7AEF\u306E\u5B57\u3092\u623B\u3059\u8DDD\u96E2;
|
|
@@ -10583,22 +10949,23 @@ function layout(diag) {
|
|
|
10583
10949
|
}
|
|
10584
10950
|
}
|
|
10585
10951
|
const positionedEdgeIds = new Set(
|
|
10586
|
-
diag.edges.filter(
|
|
10952
|
+
diag.edges.filter(
|
|
10953
|
+
(e) => e.routing === "curve" || e.routing === "metro" || e.posX1 !== void 0 && e.posY1 !== void 0 && e.posX2 !== void 0 && e.posY2 !== void 0
|
|
10954
|
+
).map((e) => e.id)
|
|
10587
10955
|
);
|
|
10588
10956
|
bowSharedStraightPaths(edges, nodesFinal, positionedEdgeIds);
|
|
10589
10957
|
pullOutgoingFirstCorner(edges, nodesFinal);
|
|
10590
|
-
fanIncomingEndpoints(edges, nodesFinal);
|
|
10958
|
+
const fannedTailShifts = fanIncomingEndpoints(edges, nodesFinal);
|
|
10591
10959
|
separateSharedStraightRuns(edges, nodesFinal);
|
|
10592
10960
|
hopEdgeCrossings(edges);
|
|
10961
|
+
const labelClearanceObstacles = collectBBoxes(containedLanesFinal, nodesFinal, []).filter(
|
|
10962
|
+
(b) => b.kind === "node" || b.kind === "lane-label"
|
|
10963
|
+
);
|
|
10964
|
+
followLabelsToFannedTails(edges, labelClearanceObstacles, fannedTailShifts);
|
|
10593
10965
|
repositionParallelLabels(edges, nodesFinal);
|
|
10594
10966
|
slideLabelsAlongOwnPath(edges, nodesFinal);
|
|
10595
10967
|
if (!isChainEdgeLabelDisabled()) {
|
|
10596
|
-
edges = resolveLabelClearanceAfterPathPasses(
|
|
10597
|
-
edges,
|
|
10598
|
-
collectBBoxes(containedLanesFinal, nodesFinal, []).filter(
|
|
10599
|
-
(b) => b.kind === "node" || b.kind === "lane-label"
|
|
10600
|
-
)
|
|
10601
|
-
);
|
|
10968
|
+
edges = resolveLabelClearanceAfterPathPasses(edges, labelClearanceObstacles);
|
|
10602
10969
|
}
|
|
10603
10970
|
const bboxes = collectBBoxes(containedLanesFinal, nodesFinal, edges);
|
|
10604
10971
|
const collisions = detectCollisions(bboxes, edges);
|
|
@@ -11536,6 +11903,7 @@ function CdlEdgeView({
|
|
|
11536
11903
|
"g",
|
|
11537
11904
|
{
|
|
11538
11905
|
"data-cdl-edge": edge.id,
|
|
11906
|
+
"data-cdl-routing": edge.routing === "metro" ? "metro" : void 0,
|
|
11539
11907
|
"data-cdl-from": edge.from,
|
|
11540
11908
|
"data-cdl-to": edge.to,
|
|
11541
11909
|
"data-cdl-active": active ? "true" : "false",
|
|
@@ -12964,17 +13332,22 @@ function DynShapeNode({
|
|
|
12964
13332
|
return renderPolygon(shape, cx, cy, stateValues, fillColor, strokeColor, activeOpacity);
|
|
12965
13333
|
}
|
|
12966
13334
|
})();
|
|
13335
|
+
const \u63C3\u3048 = \u7BB1\u3044\u3063\u3071\u3044\u306B\u63CF\u304F2(shape.kind) ? "start" : "middle";
|
|
13336
|
+
return renderShapeWithOutsideLabels(\u5F62, node, \u63C3\u3048);
|
|
13337
|
+
}
|
|
13338
|
+
function renderShapeWithOutsideLabels(\u5F62, node, \u63C3\u3048) {
|
|
13339
|
+
const cx = node.cx;
|
|
13340
|
+
const y = node.cy - node.h / 2;
|
|
12967
13341
|
const \u540D\u524D = (node.title ?? "").trim();
|
|
12968
13342
|
const \u5024 = (node.subtitle ?? "").trim();
|
|
12969
13343
|
if (\u540D\u524D === "" && \u5024 === "") return \u5F62;
|
|
12970
|
-
const \u63C3\u3048 = \u7BB1\u3044\u3063\u3071\u3044\u306B\u63CF\u304F2(shape.kind) ? "start" : "middle";
|
|
12971
13344
|
return /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
|
|
12972
13345
|
\u5F62,
|
|
12973
13346
|
\u540D\u524D !== "" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
12974
13347
|
"text",
|
|
12975
13348
|
{
|
|
12976
13349
|
"data-cdl-role": "shape-name",
|
|
12977
|
-
x: \u63C3\u3048 === "start" ?
|
|
13350
|
+
x: \u63C3\u3048 === "start" ? node.cx - node.w / 2 : cx,
|
|
12978
13351
|
y: y - NAME_GAP,
|
|
12979
13352
|
textAnchor: \u63C3\u3048,
|
|
12980
13353
|
fontSize: 13,
|
|
@@ -12988,7 +13361,7 @@ function DynShapeNode({
|
|
|
12988
13361
|
{
|
|
12989
13362
|
"data-cdl-role": "shape-value",
|
|
12990
13363
|
x: cx,
|
|
12991
|
-
y: y + h + VALUE_GAP,
|
|
13364
|
+
y: y + node.h + VALUE_GAP,
|
|
12992
13365
|
textAnchor: "middle",
|
|
12993
13366
|
fontSize: 12,
|
|
12994
13367
|
fill: "var(--cdl-text-dim, #5a6270)",
|
|
@@ -13238,6 +13611,27 @@ function interpolateColor(template, stateValues, fallback) {
|
|
|
13238
13611
|
if (!template) return fallback;
|
|
13239
13612
|
return interpolate(template, stateValues);
|
|
13240
13613
|
}
|
|
13614
|
+
var STATION_STROKE_ACTIVE = 4;
|
|
13615
|
+
var STATION_STROKE_IDLE = 3;
|
|
13616
|
+
var STATION_DEFAULT = "var(--cdl-tone-accent, #2d6a8f)";
|
|
13617
|
+
function StationNode({ node, active }) {
|
|
13618
|
+
const strokeWidth = active ? STATION_STROKE_ACTIVE : STATION_STROKE_IDLE;
|
|
13619
|
+
const radius = Math.max(0, Math.min(node.w, node.h) / 2 - strokeWidth / 2);
|
|
13620
|
+
const \u5F62 = /* @__PURE__ */ jsxRuntime.jsx(
|
|
13621
|
+
"circle",
|
|
13622
|
+
{
|
|
13623
|
+
"data-cdl-role": "node-body",
|
|
13624
|
+
"data-cdl-mark": "station",
|
|
13625
|
+
cx: node.cx,
|
|
13626
|
+
cy: node.cy,
|
|
13627
|
+
r: radius,
|
|
13628
|
+
fill: "var(--cdl-node-fill, #ffffff)",
|
|
13629
|
+
stroke: nodeAccent(node, STATION_DEFAULT),
|
|
13630
|
+
strokeWidth
|
|
13631
|
+
}
|
|
13632
|
+
);
|
|
13633
|
+
return renderShapeWithOutsideLabels(\u5F62, node, "middle");
|
|
13634
|
+
}
|
|
13241
13635
|
|
|
13242
13636
|
// src/kinds/label-stack.ts
|
|
13243
13637
|
function \u540D\u672D\u306E\u6BB5\u3092\u307B\u3069\u304F(\u771F\u306Ey, \u9593\u9694, \u4E0A\u9650, \u4E0B\u9650) {
|
|
@@ -20466,6 +20860,8 @@ function CdlNodeView({
|
|
|
20466
20860
|
return /* @__PURE__ */ jsxRuntime.jsx(MarkStartNode, { node: resolvedNode, active });
|
|
20467
20861
|
case "mark-end":
|
|
20468
20862
|
return /* @__PURE__ */ jsxRuntime.jsx(MarkEndNode, { node: resolvedNode, active });
|
|
20863
|
+
case "station":
|
|
20864
|
+
return /* @__PURE__ */ jsxRuntime.jsx(StationNode, { node: resolvedNode, active });
|
|
20469
20865
|
default:
|
|
20470
20866
|
return /* @__PURE__ */ jsxRuntime.jsx(GenericNode, { node: resolvedNode, active, value: node.value, stateValues, \u5024\u306E\u540D\u524D });
|
|
20471
20867
|
}
|
|
@@ -20698,15 +21094,15 @@ function CdlStage({
|
|
|
20698
21094
|
}, [laid]);
|
|
20699
21095
|
const edgeHeadMarkers = react.useMemo(() => {
|
|
20700
21096
|
const referenced = /* @__PURE__ */ new Set();
|
|
20701
|
-
const
|
|
21097
|
+
const add2 = (tone, head, fill) => {
|
|
20702
21098
|
for (const small of [false, true]) {
|
|
20703
21099
|
const id = edgeHeadMarkerName(tone, head, small, fill);
|
|
20704
21100
|
if (id !== void 0) referenced.add(id);
|
|
20705
21101
|
}
|
|
20706
21102
|
};
|
|
20707
21103
|
for (const edge of laid.edges) {
|
|
20708
|
-
|
|
20709
|
-
if (edge.tailHead !== void 0)
|
|
21104
|
+
add2(edge.tone, edge.head, edge.headFill);
|
|
21105
|
+
if (edge.tailHead !== void 0) add2(edge.tone, edge.tailHead, edge.tailHeadFill);
|
|
20710
21106
|
}
|
|
20711
21107
|
return Object.entries(TONE).flatMap(
|
|
20712
21108
|
([tone, color]) => [
|