@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/index.cjs
CHANGED
|
@@ -12390,6 +12390,7 @@ var KINDS_DRAWING_TITLE_OUTSIDE = /* @__PURE__ */ new Set([
|
|
|
12390
12390
|
"dyn-arc",
|
|
12391
12391
|
"dyn-wave",
|
|
12392
12392
|
"dyn-polygon",
|
|
12393
|
+
"station",
|
|
12393
12394
|
...KINDS_DRAWING_OWN_PAYLOAD
|
|
12394
12395
|
]);
|
|
12395
12396
|
function drawsTitleOutsideBox(kind) {
|
|
@@ -12397,7 +12398,7 @@ function drawsTitleOutsideBox(kind) {
|
|
|
12397
12398
|
}
|
|
12398
12399
|
var TITLE_FONT_SIZE_DEFAULT = 18;
|
|
12399
12400
|
var TITLE_FONT_SIZE = new Map([
|
|
12400
|
-
...["dyn-rect", "dyn-circle", "dyn-arc", "dyn-wave", "dyn-polygon"].map(
|
|
12401
|
+
...["dyn-rect", "dyn-circle", "dyn-arc", "dyn-wave", "dyn-polygon", "station"].map(
|
|
12401
12402
|
(k) => [k, 13]
|
|
12402
12403
|
),
|
|
12403
12404
|
...[
|
|
@@ -12448,6 +12449,10 @@ var TITLE_FONT_SIZE = new Map([
|
|
|
12448
12449
|
function titleFontSize(kind) {
|
|
12449
12450
|
return TITLE_FONT_SIZE.get(kind ?? "") ?? TITLE_FONT_SIZE_DEFAULT;
|
|
12450
12451
|
}
|
|
12452
|
+
var KINDS_WITH_TITLE_WIDTH_OUTSIDE_NODE = /* @__PURE__ */ new Set(["station"]);
|
|
12453
|
+
function skipsNodeTitleWidthCheck(kind) {
|
|
12454
|
+
return KINDS_WITH_TITLE_WIDTH_OUTSIDE_NODE.has(kind ?? "");
|
|
12455
|
+
}
|
|
12451
12456
|
var TITLE_BOX_PADDING = 52;
|
|
12452
12457
|
var KINDS_SCALING_TITLE_WHEN_LOW = /* @__PURE__ */ new Set(["actor", "event", "function", "storage"]);
|
|
12453
12458
|
var KINDS_SCALING_TITLE_ALWAYS = /* @__PURE__ */ new Set([
|
|
@@ -12530,7 +12535,8 @@ var KINDS_DRAWING_ONLY_SHAPE = [
|
|
|
12530
12535
|
"dyn-circle",
|
|
12531
12536
|
"dyn-arc",
|
|
12532
12537
|
"dyn-wave",
|
|
12533
|
-
"dyn-polygon"
|
|
12538
|
+
"dyn-polygon",
|
|
12539
|
+
"station"
|
|
12534
12540
|
];
|
|
12535
12541
|
var KINDS_WITHOUT_EYEBROW_TEXT = /* @__PURE__ */ new Set([
|
|
12536
12542
|
...KINDS_DRAWING_ONLY_SHAPE,
|
|
@@ -13310,13 +13316,13 @@ function detectCollisions(bboxes, edges) {
|
|
|
13310
13316
|
}
|
|
13311
13317
|
function extractPathSegments(d) {
|
|
13312
13318
|
const segs = [];
|
|
13313
|
-
const commandRegex = /([
|
|
13319
|
+
const commandRegex = /([MLCQAZmlcqaz])([^MLCQAZmlcqaz]*)/g;
|
|
13314
13320
|
let cur = { x: 0, y: 0 };
|
|
13315
13321
|
let m;
|
|
13316
13322
|
while ((m = commandRegex.exec(d)) !== null) {
|
|
13317
13323
|
const cmd = m[1];
|
|
13318
13324
|
const args = (m[2] ?? "").trim().split(/[\s,]+/).filter(Boolean).map(parseFloat);
|
|
13319
|
-
const need = cmd === "M" || cmd === "L" ? 2 : cmd === "Q" ? 4 : cmd === "C" ? 6 : 0;
|
|
13325
|
+
const need = cmd === "M" || cmd === "L" ? 2 : cmd === "Q" ? 4 : cmd === "C" ? 6 : cmd === "A" ? 7 : 0;
|
|
13320
13326
|
if (need > 0 && (args.length < need || !args.slice(0, need).every(Number.isFinite))) continue;
|
|
13321
13327
|
if (cmd === "M") {
|
|
13322
13328
|
cur = { x: args[0], y: args[1] };
|
|
@@ -13346,6 +13352,10 @@ function extractPathSegments(d) {
|
|
|
13346
13352
|
ctrl: [{ x: args[0], y: args[1] }, { x: args[2], y: args[3] }]
|
|
13347
13353
|
});
|
|
13348
13354
|
cur = next;
|
|
13355
|
+
} else if (cmd === "A") {
|
|
13356
|
+
const next = { x: args[5], y: args[6] };
|
|
13357
|
+
segs.push({ x1: cur.x, y1: cur.y, x2: next.x, y2: next.y, cmd: "A" });
|
|
13358
|
+
cur = next;
|
|
13349
13359
|
}
|
|
13350
13360
|
}
|
|
13351
13361
|
return segs;
|
|
@@ -14548,6 +14558,232 @@ function applyEdgeLabelAndPathShifts(edges, labelShiftMap, edgePathShiftMap) {
|
|
|
14548
14558
|
});
|
|
14549
14559
|
}
|
|
14550
14560
|
|
|
14561
|
+
// src/layout/curve-path.ts
|
|
14562
|
+
var SAME_HEIGHT_THRESHOLD = 1;
|
|
14563
|
+
function curvePath(from, to, edge, labelMarginExtra = 0) {
|
|
14564
|
+
const fromLeft = from.cx - from.w / 2;
|
|
14565
|
+
const fromRight = from.cx + from.w / 2;
|
|
14566
|
+
const toLeft = to.cx - to.w / 2;
|
|
14567
|
+
const toRight = to.cx + to.w / 2;
|
|
14568
|
+
let fromSide;
|
|
14569
|
+
let toSide;
|
|
14570
|
+
let x1;
|
|
14571
|
+
let x2;
|
|
14572
|
+
if (toLeft >= fromRight) {
|
|
14573
|
+
fromSide = "right";
|
|
14574
|
+
toSide = "left";
|
|
14575
|
+
x1 = fromRight;
|
|
14576
|
+
x2 = toLeft;
|
|
14577
|
+
} else if (toRight <= fromLeft) {
|
|
14578
|
+
fromSide = "left";
|
|
14579
|
+
toSide = "right";
|
|
14580
|
+
x1 = fromLeft;
|
|
14581
|
+
x2 = toRight;
|
|
14582
|
+
} else {
|
|
14583
|
+
fromSide = "right";
|
|
14584
|
+
toSide = "right";
|
|
14585
|
+
x1 = fromRight;
|
|
14586
|
+
x2 = toRight;
|
|
14587
|
+
}
|
|
14588
|
+
const y1 = from.cy;
|
|
14589
|
+
const y2 = to.cy;
|
|
14590
|
+
const sameHeight = Math.abs(y2 - y1) < SAME_HEIGHT_THRESHOLD;
|
|
14591
|
+
let d;
|
|
14592
|
+
let midX;
|
|
14593
|
+
let midY;
|
|
14594
|
+
let tangentX;
|
|
14595
|
+
let tangentY;
|
|
14596
|
+
if (sameHeight) {
|
|
14597
|
+
d = `M ${x1} ${y1} L ${x2} ${y2}`;
|
|
14598
|
+
midX = (x1 + x2) / 2;
|
|
14599
|
+
midY = (y1 + y2) / 2;
|
|
14600
|
+
tangentX = x2 - x1;
|
|
14601
|
+
tangentY = y2 - y1;
|
|
14602
|
+
} else {
|
|
14603
|
+
let c1x;
|
|
14604
|
+
let c2x;
|
|
14605
|
+
if (fromSide !== toSide) {
|
|
14606
|
+
const direction = x2 > x1 ? 1 : -1;
|
|
14607
|
+
const bulge = Math.max(Math.abs(x2 - x1) / 2, EDGE_STUB_OUT);
|
|
14608
|
+
c1x = x1 + direction * bulge;
|
|
14609
|
+
c2x = x2 - direction * bulge;
|
|
14610
|
+
} else {
|
|
14611
|
+
const bulgeX = Math.max(x1, x2) + EDGE_STUB_OUT;
|
|
14612
|
+
c1x = bulgeX;
|
|
14613
|
+
c2x = bulgeX;
|
|
14614
|
+
}
|
|
14615
|
+
d = `M ${x1} ${y1} C ${c1x} ${y1}, ${c2x} ${y2}, ${x2} ${y2}`;
|
|
14616
|
+
midX = (x1 + 3 * c1x + 3 * c2x + x2) / 8;
|
|
14617
|
+
midY = (y1 + 3 * y1 + 3 * y2 + y2) / 8;
|
|
14618
|
+
tangentX = x2 + c2x - c1x - x1;
|
|
14619
|
+
tangentY = y2 + y2 - y1 - y1;
|
|
14620
|
+
}
|
|
14621
|
+
if (edge?.overlay) {
|
|
14622
|
+
return { d, fromSide, toSide, labelX: midX, labelY: midY, labelAnchor: "middle" };
|
|
14623
|
+
}
|
|
14624
|
+
const tangentLength = Math.hypot(tangentX, tangentY);
|
|
14625
|
+
let normalX = tangentY / tangentLength;
|
|
14626
|
+
let normalY = -tangentX / tangentLength;
|
|
14627
|
+
if (normalY > Number.EPSILON || Math.abs(normalY) <= Number.EPSILON && normalX < 0) {
|
|
14628
|
+
normalX *= -1;
|
|
14629
|
+
normalY *= -1;
|
|
14630
|
+
}
|
|
14631
|
+
const labelW = computeLabelBoxW(edge?.label, edge?.sub);
|
|
14632
|
+
const labelH = labelPillH(edge?.sub);
|
|
14633
|
+
const distance = labelPathClearanceHorizontal(edge) + (Math.abs(normalX) * labelW + Math.abs(normalY) * labelH) / 2 + labelMarginExtra;
|
|
14634
|
+
return {
|
|
14635
|
+
d,
|
|
14636
|
+
fromSide,
|
|
14637
|
+
toSide,
|
|
14638
|
+
labelX: midX + normalX * distance,
|
|
14639
|
+
labelY: midY + normalY * distance,
|
|
14640
|
+
labelAnchor: "middle"
|
|
14641
|
+
};
|
|
14642
|
+
}
|
|
14643
|
+
|
|
14644
|
+
// src/layout/metro-path.ts
|
|
14645
|
+
var METRO_CORNER_RADIUS = 24;
|
|
14646
|
+
var T = Math.SQRT2 - 1;
|
|
14647
|
+
function round2(v) {
|
|
14648
|
+
return Math.round(v * 100) / 100;
|
|
14649
|
+
}
|
|
14650
|
+
function point(p) {
|
|
14651
|
+
return `${round2(p.x)} ${round2(p.y)}`;
|
|
14652
|
+
}
|
|
14653
|
+
function add(p, v, scale) {
|
|
14654
|
+
return { x: p.x + v.x * scale, y: p.y + v.y * scale };
|
|
14655
|
+
}
|
|
14656
|
+
function polyline(points) {
|
|
14657
|
+
const distinct = points.filter((p, i) => {
|
|
14658
|
+
const previous = points[i - 1];
|
|
14659
|
+
return previous === void 0 || p.x !== previous.x || p.y !== previous.y;
|
|
14660
|
+
});
|
|
14661
|
+
return distinct.map((p, i) => `${i === 0 ? "M" : "L"} ${point(p)}`).join(" ");
|
|
14662
|
+
}
|
|
14663
|
+
function metroPath(from, to, edge, labelMarginExtra = 0) {
|
|
14664
|
+
const fromLeft = from.cx - from.w / 2;
|
|
14665
|
+
const fromRight = from.cx + from.w / 2;
|
|
14666
|
+
const toLeft = to.cx - to.w / 2;
|
|
14667
|
+
const toRight = to.cx + to.w / 2;
|
|
14668
|
+
let fromSide;
|
|
14669
|
+
let toSide;
|
|
14670
|
+
let x1;
|
|
14671
|
+
let x2;
|
|
14672
|
+
let dir;
|
|
14673
|
+
if (toLeft >= fromRight) {
|
|
14674
|
+
fromSide = "right";
|
|
14675
|
+
toSide = "left";
|
|
14676
|
+
x1 = fromRight;
|
|
14677
|
+
x2 = toLeft;
|
|
14678
|
+
dir = 1;
|
|
14679
|
+
} else if (toRight <= fromLeft) {
|
|
14680
|
+
fromSide = "left";
|
|
14681
|
+
toSide = "right";
|
|
14682
|
+
x1 = fromLeft;
|
|
14683
|
+
x2 = toRight;
|
|
14684
|
+
dir = -1;
|
|
14685
|
+
} else {
|
|
14686
|
+
fromSide = "right";
|
|
14687
|
+
toSide = "right";
|
|
14688
|
+
x1 = fromRight;
|
|
14689
|
+
x2 = toRight;
|
|
14690
|
+
dir = 1;
|
|
14691
|
+
}
|
|
14692
|
+
const y1 = from.cy;
|
|
14693
|
+
const y2 = to.cy;
|
|
14694
|
+
const dy = y2 - y1;
|
|
14695
|
+
const sgn = Math.sign(dy);
|
|
14696
|
+
let d;
|
|
14697
|
+
let mid;
|
|
14698
|
+
let tangent;
|
|
14699
|
+
if (Math.abs(dy) < SAME_HEIGHT_THRESHOLD) {
|
|
14700
|
+
d = `M ${point({ x: x1, y: y1 })} L ${point({ x: x2, y: y2 })}`;
|
|
14701
|
+
mid = { x: (x1 + x2) / 2, y: (y1 + y2) / 2 };
|
|
14702
|
+
tangent = { x: x2 - x1, y: y2 - y1 };
|
|
14703
|
+
} else if (fromSide !== toSide && Math.abs(x2 - x1) >= Math.abs(dy)) {
|
|
14704
|
+
const horizontal = Math.abs(x2 - x1);
|
|
14705
|
+
const s = (horizontal - Math.abs(dy)) / 2;
|
|
14706
|
+
const p0 = { x: x1, y: y1 };
|
|
14707
|
+
const p1 = { x: x1 + dir * s, y: y1 };
|
|
14708
|
+
const p2 = { x: x2 - dir * s, y: y2 };
|
|
14709
|
+
const p3 = { x: x2, y: y2 };
|
|
14710
|
+
const u = { x: dir, y: 0 };
|
|
14711
|
+
const w = { x: dir / Math.SQRT2, y: sgn / Math.SQRT2 };
|
|
14712
|
+
const r = Math.min(
|
|
14713
|
+
METRO_CORNER_RADIUS,
|
|
14714
|
+
s / T,
|
|
14715
|
+
Math.SQRT2 * Math.abs(dy) / (2 * T)
|
|
14716
|
+
);
|
|
14717
|
+
const t = r * T;
|
|
14718
|
+
if (r >= 0.5) {
|
|
14719
|
+
const f1 = dir * sgn > 0 ? 1 : 0;
|
|
14720
|
+
d = [
|
|
14721
|
+
`M ${point(p0)}`,
|
|
14722
|
+
`L ${point(add(p1, u, -t))}`,
|
|
14723
|
+
`A ${round2(r)} ${round2(r)} 0 0 ${f1} ${point(add(p1, w, t))}`,
|
|
14724
|
+
`L ${point(add(p2, w, -t))}`,
|
|
14725
|
+
`A ${round2(r)} ${round2(r)} 0 0 ${1 - f1} ${point(add(p2, u, t))}`,
|
|
14726
|
+
`L ${point(p3)}`
|
|
14727
|
+
].join(" ");
|
|
14728
|
+
} else {
|
|
14729
|
+
d = polyline([p0, p1, p2, p3]);
|
|
14730
|
+
}
|
|
14731
|
+
mid = { x: (x1 + x2) / 2, y: (y1 + y2) / 2 };
|
|
14732
|
+
tangent = w;
|
|
14733
|
+
} else {
|
|
14734
|
+
fromSide = "right";
|
|
14735
|
+
toSide = "right";
|
|
14736
|
+
x1 = fromRight;
|
|
14737
|
+
x2 = toRight;
|
|
14738
|
+
const xa = Math.max(x1, x2) + EDGE_STUB_OUT;
|
|
14739
|
+
const h = Math.abs(dy) / 2;
|
|
14740
|
+
const p0 = { x: x1, y: y1 };
|
|
14741
|
+
const p1 = { x: xa, y: y1 };
|
|
14742
|
+
const p2 = { x: xa + h, y: y1 + sgn * h };
|
|
14743
|
+
const p3 = { x: xa, y: y2 };
|
|
14744
|
+
const p4 = { x: x2, y: y2 };
|
|
14745
|
+
const u = { x: 1, y: 0 };
|
|
14746
|
+
const w1 = { x: 1 / Math.SQRT2, y: sgn / Math.SQRT2 };
|
|
14747
|
+
const w2 = { x: -1 / Math.SQRT2, y: sgn / Math.SQRT2 };
|
|
14748
|
+
const r = Math.min(METRO_CORNER_RADIUS, h);
|
|
14749
|
+
const t = r * T;
|
|
14750
|
+
const sweep = sgn > 0 ? 1 : 0;
|
|
14751
|
+
d = [
|
|
14752
|
+
`M ${point(p0)}`,
|
|
14753
|
+
`L ${point(add(p1, u, -t))}`,
|
|
14754
|
+
`A ${round2(r)} ${round2(r)} 0 0 ${sweep} ${point(add(p1, w1, t))}`,
|
|
14755
|
+
`L ${point(add(p2, w1, -r))}`,
|
|
14756
|
+
`A ${round2(r)} ${round2(r)} 0 0 ${sweep} ${point(add(p2, w2, r))}`,
|
|
14757
|
+
`L ${point(add(p3, w2, -t))}`,
|
|
14758
|
+
`A ${round2(r)} ${round2(r)} 0 0 ${sweep} ${point(add(p3, u, -t))}`,
|
|
14759
|
+
`L ${point(p4)}`
|
|
14760
|
+
].join(" ");
|
|
14761
|
+
mid = { x: p2.x - T * r, y: p2.y };
|
|
14762
|
+
tangent = { x: 0, y: sgn };
|
|
14763
|
+
}
|
|
14764
|
+
if (edge?.overlay) {
|
|
14765
|
+
return { d, fromSide, toSide, labelX: mid.x, labelY: mid.y, labelAnchor: "middle" };
|
|
14766
|
+
}
|
|
14767
|
+
const tangentLength = Math.hypot(tangent.x, tangent.y);
|
|
14768
|
+
let normalX = tangent.y / tangentLength;
|
|
14769
|
+
let normalY = -tangent.x / tangentLength;
|
|
14770
|
+
if (normalY > Number.EPSILON || Math.abs(normalY) <= Number.EPSILON && normalX < 0) {
|
|
14771
|
+
normalX *= -1;
|
|
14772
|
+
normalY *= -1;
|
|
14773
|
+
}
|
|
14774
|
+
const labelW = computeLabelBoxW(edge?.label, edge?.sub);
|
|
14775
|
+
const labelH = labelPillH(edge?.sub);
|
|
14776
|
+
const distance = labelPathClearanceHorizontal(edge) + (Math.abs(normalX) * labelW + Math.abs(normalY) * labelH) / 2 + labelMarginExtra;
|
|
14777
|
+
return {
|
|
14778
|
+
d,
|
|
14779
|
+
fromSide,
|
|
14780
|
+
toSide,
|
|
14781
|
+
labelX: mid.x + normalX * distance,
|
|
14782
|
+
labelY: mid.y + normalY * distance,
|
|
14783
|
+
labelAnchor: "middle"
|
|
14784
|
+
};
|
|
14785
|
+
}
|
|
14786
|
+
|
|
14551
14787
|
// src/types.ts
|
|
14552
14788
|
var TONES = ["accent", "teal", "success", "error", "warning", "info"];
|
|
14553
14789
|
var NODE_KINDS = [
|
|
@@ -14675,6 +14911,8 @@ var NODE_KINDS = [
|
|
|
14675
14911
|
// 状態遷移図が使う。 印そのものが意味を持つので文字は描かない
|
|
14676
14912
|
"mark-start",
|
|
14677
14913
|
"mark-end",
|
|
14914
|
+
// 路線図の駅 (#996) ... 線路上の点を小さな丸で示し、名前と説明は丸の外に置く
|
|
14915
|
+
"station",
|
|
14678
14916
|
// 時系列のやり取り (#578) ... 1 つの箱が図を丸ごと描く。 縦が時間で、参加者は箱を持たない
|
|
14679
14917
|
"sequence-board"
|
|
14680
14918
|
];
|
|
@@ -17386,6 +17624,7 @@ function runAxes(laid, diag, violations, counts, push, profile, skippedAxes, \u5
|
|
|
17386
17624
|
const LIFELINE_W_THRESHOLD = 8;
|
|
17387
17625
|
for (const n of laid.nodes) {
|
|
17388
17626
|
if (n.w < LIFELINE_W_THRESHOLD) continue;
|
|
17627
|
+
if (n.kind === "station") continue;
|
|
17389
17628
|
if (n.w < MIN_NODE_W || n.h < MIN_NODE_H) {
|
|
17390
17629
|
push(
|
|
17391
17630
|
"node-visibility",
|
|
@@ -17407,6 +17646,7 @@ function runAxes(laid, diag, violations, counts, push, profile, skippedAxes, \u5
|
|
|
17407
17646
|
if (n.id.endsWith("-spacer")) continue;
|
|
17408
17647
|
if (!rendersTitleText(n.kind)) continue;
|
|
17409
17648
|
if (fitsOwnTitleWidth(n.kind)) continue;
|
|
17649
|
+
if (skipsNodeTitleWidthCheck(n.kind)) continue;
|
|
17410
17650
|
if (!isRenderedNode(n)) continue;
|
|
17411
17651
|
const titleTrimmed = n.title?.trim() ?? "";
|
|
17412
17652
|
if (!titleTrimmed) continue;
|
|
@@ -18011,6 +18251,9 @@ function runAxes(laid, diag, violations, counts, push, profile, skippedAxes, \u5
|
|
|
18011
18251
|
for (let i = 0; i + 1 < sorted.length; i++) {
|
|
18012
18252
|
const upper = sorted[i];
|
|
18013
18253
|
const lower = sorted[i + 1];
|
|
18254
|
+
if (upper.cx + upper.w / 2 <= lower.cx - lower.w / 2 || lower.cx + lower.w / 2 <= upper.cx - upper.w / 2) {
|
|
18255
|
+
continue;
|
|
18256
|
+
}
|
|
18014
18257
|
const upperBottom = upper.cy + upper.h / 2;
|
|
18015
18258
|
const lowerTop = lower.cy - lower.h / 2;
|
|
18016
18259
|
const gap = lowerTop - upperBottom;
|
|
@@ -18642,11 +18885,18 @@ function runAxes(laid, diag, violations, counts, push, profile, skippedAxes, \u5
|
|
|
18642
18885
|
{
|
|
18643
18886
|
const crestSpans = (segs, peakY) => {
|
|
18644
18887
|
const out = [];
|
|
18888
|
+
let \u5186\u5F27\u306E\u59CB\u70B9 = null;
|
|
18645
18889
|
for (const s of segs) {
|
|
18646
|
-
if (
|
|
18647
|
-
|
|
18890
|
+
if (s.cmd === "A") {
|
|
18891
|
+
\u5186\u5F27\u306E\u59CB\u70B9 ??= { x: s.x1, y: s.y1 };
|
|
18892
|
+
continue;
|
|
18893
|
+
}
|
|
18894
|
+
const \u59CB\u70B9 = \u5186\u5F27\u306E\u59CB\u70B9 ?? { x: s.x1, y: s.y1 };
|
|
18895
|
+
\u5186\u5F27\u306E\u59CB\u70B9 = null;
|
|
18896
|
+
if (Math.abs(\u59CB\u70B9.y - s.y2) > 1) continue;
|
|
18897
|
+
if (Math.abs(\u59CB\u70B9.y - peakY) > 1) continue;
|
|
18648
18898
|
if (s.cmd !== "L" && !(s.ctrl ?? []).every((c) => Math.abs(c.y - peakY) <= 1)) continue;
|
|
18649
|
-
out.push({ minX: Math.min(
|
|
18899
|
+
out.push({ minX: Math.min(\u59CB\u70B9.x, s.x2), maxX: Math.max(\u59CB\u70B9.x, s.x2) });
|
|
18650
18900
|
}
|
|
18651
18901
|
return out;
|
|
18652
18902
|
};
|
|
@@ -19337,6 +19587,8 @@ var TOKENS = {
|
|
|
19337
19587
|
// 箱としての最小 (80x40) より大きく、名前を持つ箱 (card 320x150) よりは明らかに小さい
|
|
19338
19588
|
"mark-start": { w: 96, h: 96 },
|
|
19339
19589
|
"mark-end": { w: 96, h: 96 },
|
|
19590
|
+
// 路線図の駅 (#996) ... 正方形にして小さな丸が縦横どちらにも歪まないようにする。
|
|
19591
|
+
station: { w: 24, h: 24 },
|
|
19340
19592
|
// 時系列のやり取り (#578)。 実寸は中身から決まる (`sequenceBoardMetrics`) ので、
|
|
19341
19593
|
// ここは書かなかった時の受け皿
|
|
19342
19594
|
"sequence-board": { w: 960, h: 640 }
|
|
@@ -19361,6 +19613,7 @@ var LANE_PAD_MIN = 25;
|
|
|
19361
19613
|
// src/layout/edges.ts
|
|
19362
19614
|
var VERTICAL_DETOUR_GAP = 50;
|
|
19363
19615
|
var VERTICAL_DETOUR_CORNER = 14;
|
|
19616
|
+
var OVERLAY_LABEL_ON_PATH_TOLERANCE = 1;
|
|
19364
19617
|
function verticalCornerDir(from, to) {
|
|
19365
19618
|
return to < from ? -1 : 1;
|
|
19366
19619
|
}
|
|
@@ -19444,6 +19697,36 @@ function layoutEdges(diag, nodes, lanes = []) {
|
|
|
19444
19697
|
});
|
|
19445
19698
|
} else if (isSelfLoop(e)) {
|
|
19446
19699
|
selfLoopEdges.push(e);
|
|
19700
|
+
} else if (e.routing === "curve") {
|
|
19701
|
+
const from = nmap.get(e.from);
|
|
19702
|
+
const to = nmap.get(e.to);
|
|
19703
|
+
if (!from) throw new Error(`cdl layout: edge "${e.id}" \u306E from "${e.from}" \u304C node \u306B\u7121\u3044`);
|
|
19704
|
+
if (!to) throw new Error(`cdl layout: edge "${e.id}" \u306E to "${e.to}" \u304C node \u306B\u7121\u3044`);
|
|
19705
|
+
const c = curvePath(from, to, e, labelMarginExtra);
|
|
19706
|
+
positionedEdges.push({
|
|
19707
|
+
...e,
|
|
19708
|
+
d: c.d,
|
|
19709
|
+
fromSide: c.fromSide,
|
|
19710
|
+
toSide: c.toSide,
|
|
19711
|
+
labelX: c.labelX + (e.labelOffsetX ?? 0),
|
|
19712
|
+
labelY: c.labelY + (e.labelOffsetY ?? 0),
|
|
19713
|
+
labelAnchor: c.labelAnchor
|
|
19714
|
+
});
|
|
19715
|
+
} else if (e.routing === "metro") {
|
|
19716
|
+
const from = nmap.get(e.from);
|
|
19717
|
+
const to = nmap.get(e.to);
|
|
19718
|
+
if (!from) throw new Error(`cdl layout: edge "${e.id}" \u306E from "${e.from}" \u304C node \u306B\u7121\u3044`);
|
|
19719
|
+
if (!to) throw new Error(`cdl layout: edge "${e.id}" \u306E to "${e.to}" \u304C node \u306B\u7121\u3044`);
|
|
19720
|
+
const m = metroPath(from, to, e, labelMarginExtra);
|
|
19721
|
+
positionedEdges.push({
|
|
19722
|
+
...e,
|
|
19723
|
+
d: m.d,
|
|
19724
|
+
fromSide: m.fromSide,
|
|
19725
|
+
toSide: m.toSide,
|
|
19726
|
+
labelX: m.labelX + (e.labelOffsetX ?? 0),
|
|
19727
|
+
labelY: m.labelY + (e.labelOffsetY ?? 0),
|
|
19728
|
+
labelAnchor: m.labelAnchor
|
|
19729
|
+
});
|
|
19447
19730
|
} else {
|
|
19448
19731
|
autoEdges.push(e);
|
|
19449
19732
|
}
|
|
@@ -20948,9 +21231,11 @@ function hopEdgeCrossings(edges) {
|
|
|
20948
21231
|
ends.set(e.id, pathEnds(segs));
|
|
20949
21232
|
}
|
|
20950
21233
|
for (const e of edges) {
|
|
21234
|
+
if (e.routing === "metro") continue;
|
|
20951
21235
|
if ((e.d.match(/M/g) ?? []).length !== 1) continue;
|
|
20952
21236
|
const own = extractPathSegments(e.d);
|
|
20953
21237
|
if (own.length === 0) continue;
|
|
21238
|
+
if (own.some((s) => s.cmd === "A")) continue;
|
|
20954
21239
|
const myEnds = ends.get(e.id) ?? [];
|
|
20955
21240
|
const hops = /* @__PURE__ */ new Map();
|
|
20956
21241
|
for (let i = 0; i < own.length; i++) {
|
|
@@ -21038,7 +21323,7 @@ function pathVertices(d) {
|
|
|
21038
21323
|
const out = [{ x: first.x1, y: first.y1 }];
|
|
21039
21324
|
for (let i = 0; i < segs.length; i++) {
|
|
21040
21325
|
const s = segs[i];
|
|
21041
|
-
if (s.cmd === "C") return null;
|
|
21326
|
+
if (s.cmd === "C" || s.cmd === "A") return null;
|
|
21042
21327
|
if (s.cmd === "Q") {
|
|
21043
21328
|
const c = s.ctrl?.[0];
|
|
21044
21329
|
if (!c) return null;
|
|
@@ -21101,7 +21386,7 @@ function separateSharedStraightRuns(edges, nodes) {
|
|
|
21101
21386
|
const v = pathVertices(e.d);
|
|
21102
21387
|
if (v && v.length >= 2) verts.set(e.id, v);
|
|
21103
21388
|
}
|
|
21104
|
-
if (verts.size
|
|
21389
|
+
if (verts.size === 0) return;
|
|
21105
21390
|
const moved = /* @__PURE__ */ new Set();
|
|
21106
21391
|
const fits = (e, pts, k, ax, delta) => {
|
|
21107
21392
|
const \u8A66\u3057 = pts.map(
|
|
@@ -21119,33 +21404,40 @@ function separateSharedStraightRuns(edges, nodes) {
|
|
|
21119
21404
|
for (let j = i + 1; j < edges.length; j++) {
|
|
21120
21405
|
const \u524D = edges[i];
|
|
21121
21406
|
const \u5F8C = edges[j];
|
|
21122
|
-
|
|
21123
|
-
const
|
|
21124
|
-
|
|
21125
|
-
|
|
21407
|
+
if (\u524D.routing === "metro" && \u5F8C.routing === "metro") continue;
|
|
21408
|
+
const \u56FA\u5B9A = \u5F8C.routing === "metro" ? \u5F8C : \u524D;
|
|
21409
|
+
const \u52D5\u304B\u3059 = \u5F8C.routing === "metro" ? \u524D : \u5F8C;
|
|
21410
|
+
const B = verts.get(\u52D5\u304B\u3059.id);
|
|
21411
|
+
if (!B) continue;
|
|
21412
|
+
const \u56FA\u5B9A\u306E\u89D2 = verts.get(\u56FA\u5B9A.id);
|
|
21413
|
+
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 }]) : [];
|
|
21414
|
+
if (\u56FA\u5B9A\u306E\u7D4C\u8DEF.length === 0) continue;
|
|
21415
|
+
if (\u56FA\u5B9A.from === \u52D5\u304B\u3059.from) continue;
|
|
21126
21416
|
for (const ax of [0, 1]) {
|
|
21127
|
-
for (
|
|
21128
|
-
|
|
21129
|
-
|
|
21130
|
-
|
|
21131
|
-
|
|
21132
|
-
|
|
21133
|
-
|
|
21134
|
-
|
|
21135
|
-
|
|
21136
|
-
|
|
21137
|
-
|
|
21138
|
-
|
|
21139
|
-
|
|
21140
|
-
|
|
21141
|
-
|
|
21142
|
-
|
|
21143
|
-
|
|
21144
|
-
|
|
21145
|
-
|
|
21417
|
+
for (const A of \u56FA\u5B9A\u306E\u7D4C\u8DEF) {
|
|
21418
|
+
for (let ka = 0; ka + 1 < A.length; ka++) {
|
|
21419
|
+
if (!runIsAligned(A, ka, ax)) continue;
|
|
21420
|
+
for (let kb = 0; kb + 1 < B.length; kb++) {
|
|
21421
|
+
if (!runIsAligned(B, kb, ax)) continue;
|
|
21422
|
+
if (kb === 0 || kb + 1 === B.length - 1) continue;
|
|
21423
|
+
const \u5EA7\u6A19A = ax === 0 ? A[ka].x : A[ka].y;
|
|
21424
|
+
const \u5EA7\u6A19B = ax === 0 ? B[kb].x : B[kb].y;
|
|
21425
|
+
if (Math.abs(\u5EA7\u6A19A - \u5EA7\u6A19B) > SHARED_RUN_EPS) continue;
|
|
21426
|
+
const sa = runSpan(A, ka, ax);
|
|
21427
|
+
const sb = runSpan(B, kb, ax);
|
|
21428
|
+
if (Math.min(sa.hi, sb.hi) - Math.max(sa.lo, sb.lo) < SHARED_RUN_MIN) continue;
|
|
21429
|
+
for (const delta of [SHARED_RUN_SEPARATION, -SHARED_RUN_SEPARATION]) {
|
|
21430
|
+
if (!fits(\u52D5\u304B\u3059, B, kb, ax, delta)) continue;
|
|
21431
|
+
if (ax === 0) {
|
|
21432
|
+
B[kb].x += delta;
|
|
21433
|
+
B[kb + 1].x += delta;
|
|
21434
|
+
} else {
|
|
21435
|
+
B[kb].y += delta;
|
|
21436
|
+
B[kb + 1].y += delta;
|
|
21437
|
+
}
|
|
21438
|
+
moved.add(\u52D5\u304B\u3059.id);
|
|
21439
|
+
break;
|
|
21146
21440
|
}
|
|
21147
|
-
moved.add(\u5F8C.id);
|
|
21148
|
-
break;
|
|
21149
21441
|
}
|
|
21150
21442
|
}
|
|
21151
21443
|
}
|
|
@@ -21172,12 +21464,19 @@ function pullOutgoingFirstCorner(edges, nodes) {
|
|
|
21172
21464
|
let \u672D\u3092\u65E7\u7D4C\u8DEF\u3078\u8FFD\u5F93\u6E08\u307F = false;
|
|
21173
21465
|
for (const list of \u675F.values()) {
|
|
21174
21466
|
if (list.length < 2) continue;
|
|
21175
|
-
const \u4E26\u3073 = list.
|
|
21467
|
+
const \u4E26\u3073 = list.map((e) => {
|
|
21176
21468
|
const pts = pathVertices(e.d);
|
|
21177
|
-
if (
|
|
21178
|
-
|
|
21179
|
-
|
|
21469
|
+
if (pts && pts.length >= 2) {
|
|
21470
|
+
return { e, pts, \u6700\u521D\u306E\u9577\u3055: Math.hypot(pts[1].x - pts[0].x, pts[1].y - pts[0].y) };
|
|
21471
|
+
}
|
|
21472
|
+
if (e.routing !== "metro") return null;
|
|
21473
|
+
const first = extractPathSegments(e.d)[0];
|
|
21474
|
+
if (!first) return null;
|
|
21475
|
+
return { e, pts: null, \u6700\u521D\u306E\u9577\u3055: Math.hypot(first.x2 - first.x1, first.y2 - first.y1) };
|
|
21476
|
+
}).filter((x) => x !== null).sort((a, b) => b.\u6700\u521D\u306E\u9577\u3055 - a.\u6700\u521D\u306E\u9577\u3055);
|
|
21180
21477
|
for (const { e, pts, \u6700\u521D\u306E\u9577\u3055 } of \u4E26\u3073.slice(1)) {
|
|
21478
|
+
if (e.routing === "metro") continue;
|
|
21479
|
+
if (!pts) continue;
|
|
21181
21480
|
if ((\u540C\u3058\u7D44\u306E\u6570.get(`${e.from}->${e.to}`) ?? 0) > 1) continue;
|
|
21182
21481
|
if (pts.length < 3 || \u6700\u521D\u306E\u9577\u3055 <= EDGE_STUB_OUT + VERTICAL_DETOUR_CORNER) continue;
|
|
21183
21482
|
const \u6A2A = e.fromSide === "left" || e.fromSide === "right";
|
|
@@ -21231,7 +21530,8 @@ var ENDPOINT_FAN_MIN = 20;
|
|
|
21231
21530
|
var ENDPOINT_FAN_RUNUP = 40;
|
|
21232
21531
|
var MIXED_ROOT_OVERLAP_MIN = 24;
|
|
21233
21532
|
function fanIncomingEndpoints(edges, nodes) {
|
|
21234
|
-
|
|
21533
|
+
const \u7D42\u70B9\u306E\u79FB\u52D5 = /* @__PURE__ */ new Map();
|
|
21534
|
+
if (edges.length < 2) return \u7D42\u70B9\u306E\u79FB\u52D5;
|
|
21235
21535
|
const nmap = new Map(nodes.map((n) => [n.id, n]));
|
|
21236
21536
|
const \u5E73\u5766\u306A\u533A\u9593 = new Map(edges.map((e) => [e.id, flattenPathSegments(extractPathSegments(e.d))]));
|
|
21237
21537
|
const \u8EF8\u306B\u5E73\u884C\u306A\u91CD\u306A\u308A\u9577\u3055 = (\u4E00\u65B9, \u4ED6\u65B9) => {
|
|
@@ -21301,6 +21601,7 @@ function fanIncomingEndpoints(edges, nodes) {
|
|
|
21301
21601
|
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;
|
|
21302
21602
|
if (Math.abs(d) < 1e-3) continue;
|
|
21303
21603
|
const e = \u4E26\u3073[k].e;
|
|
21604
|
+
if (e.routing === "metro") continue;
|
|
21304
21605
|
const \u524D = e.d;
|
|
21305
21606
|
const \u524D\u306E\u4EA4\u5DEE = countEdgeCrossings(edges).count;
|
|
21306
21607
|
const \u9577\u3044\u52A9\u8D70 = \u4E26\u3073[k].\u9577\u3044\u52A9\u8D70;
|
|
@@ -21312,6 +21613,68 @@ function fanIncomingEndpoints(edges, nodes) {
|
|
|
21312
21613
|
if (countEdgeCrossings(edges).count <= \u524D\u306E\u4EA4\u5DEE) break;
|
|
21313
21614
|
e.d = \u524D;
|
|
21314
21615
|
}
|
|
21616
|
+
if (e.d !== \u524D) \u7D42\u70B9\u306E\u79FB\u52D5.set(e.id, \u6A2A ? { dx: 0, dy: d } : { dx: d, dy: 0 });
|
|
21617
|
+
}
|
|
21618
|
+
}
|
|
21619
|
+
return \u7D42\u70B9\u306E\u79FB\u52D5;
|
|
21620
|
+
}
|
|
21621
|
+
function followLabelsToFannedTails(edges, obstacles, shifts) {
|
|
21622
|
+
if (shifts.size === 0) return;
|
|
21623
|
+
const \u6E2C\u308A\u65B9 = \u7DDA\u306E\u6E2C\u308A\u65B9\u3092\u4F5C\u308B();
|
|
21624
|
+
const labels = edges.filter(hasRenderedLabel).map((e) => ({
|
|
21625
|
+
kind: "edge-label",
|
|
21626
|
+
id: e.id,
|
|
21627
|
+
...computeLabelBBoxWorld(e)
|
|
21628
|
+
}));
|
|
21629
|
+
const labelIndexByEdgeId = new Map(labels.map((label, index) => [label.id, index]));
|
|
21630
|
+
const edgeById = new Map(edges.map((e) => [e.id, e]));
|
|
21631
|
+
const \u5468\u308A = {
|
|
21632
|
+
\u7BB1\u305F\u3061: obstacles.filter((o) => o.kind === "node" || o.kind === "lane-label"),
|
|
21633
|
+
labels,
|
|
21634
|
+
edges,
|
|
21635
|
+
edgeById,
|
|
21636
|
+
\u6E2C\u308A\u65B9
|
|
21637
|
+
};
|
|
21638
|
+
for (const e of edges) {
|
|
21639
|
+
const \u79FB\u52D5 = shifts.get(e.id);
|
|
21640
|
+
if (!\u79FB\u52D5 || !hasRenderedLabel(e)) continue;
|
|
21641
|
+
const labelIndex = labelIndexByEdgeId.get(e.id);
|
|
21642
|
+
if (labelIndex === void 0) continue;
|
|
21643
|
+
const \u5143\u306E\u672D = labels[labelIndex];
|
|
21644
|
+
const \u81EA\u5206\u306E\u533A\u9593 = \u6E2C\u308A\u65B9.\u533A\u9593(e.id, e.d);
|
|
21645
|
+
if (e.overlay && measurePathLabelGapSegs(\u81EA\u5206\u306E\u533A\u9593, {
|
|
21646
|
+
x: \u5143\u306E\u672D.x + \u5143\u306E\u672D.w / 2,
|
|
21647
|
+
y: \u5143\u306E\u672D.y + \u5143\u306E\u672D.h / 2,
|
|
21648
|
+
w: 0,
|
|
21649
|
+
h: 0
|
|
21650
|
+
}) <= OVERLAY_LABEL_ON_PATH_TOLERANCE) continue;
|
|
21651
|
+
const \u6700\u5F8C = \u81EA\u5206\u306E\u533A\u9593.at(-1);
|
|
21652
|
+
if (!\u6700\u5F8C) continue;
|
|
21653
|
+
if (!segmentsCrossRect([\u6700\u5F8C], \u5143\u306E\u672D)) continue;
|
|
21654
|
+
const \u5019\u88DCa = {
|
|
21655
|
+
labelX: e.labelX + \u79FB\u52D5.dx,
|
|
21656
|
+
labelY: e.labelY + \u79FB\u52D5.dy,
|
|
21657
|
+
labelAnchor: e.labelAnchor
|
|
21658
|
+
};
|
|
21659
|
+
const \u5019\u88DCa\u306E\u7BB1 = computeLabelBBoxWorld({ ...e, ...\u5019\u88DCa });
|
|
21660
|
+
const \u6700\u5F8C\u306F\u7E26 = Math.abs(\u6700\u5F8C.x2 - \u6700\u5F8C.x1) <= Math.abs(\u6700\u5F8C.y2 - \u6700\u5F8C.y1);
|
|
21661
|
+
const \u5019\u88DC = [
|
|
21662
|
+
\u5019\u88DCa,
|
|
21663
|
+
{
|
|
21664
|
+
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,
|
|
21665
|
+
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),
|
|
21666
|
+
labelAnchor: "middle"
|
|
21667
|
+
}
|
|
21668
|
+
];
|
|
21669
|
+
for (const c of \u5019\u88DC) {
|
|
21670
|
+
const \u672D = { kind: "edge-label", id: e.id, ...computeLabelBBoxWorld({ ...e, ...c }) };
|
|
21671
|
+
if (segmentsCrossRect([\u6700\u5F8C], \u672D)) continue;
|
|
21672
|
+
if (\u672D\u306E\u79FB\u52D5\u3092\u585E\u3050\u76F8\u624B\u3092\u63A2\u3059(\u5468\u308A, \u5143\u306E\u672D, \u672D) !== null) continue;
|
|
21673
|
+
e.labelX = c.labelX;
|
|
21674
|
+
e.labelY = c.labelY;
|
|
21675
|
+
e.labelAnchor = c.labelAnchor;
|
|
21676
|
+
labels[labelIndex] = \u672D;
|
|
21677
|
+
break;
|
|
21315
21678
|
}
|
|
21316
21679
|
}
|
|
21317
21680
|
}
|
|
@@ -21328,7 +21691,7 @@ function \u672B\u5C3E\u3092\u8FBA\u306B\u6CBF\u3063\u3066\u305A\u3089\u3059(e, \
|
|
|
21328
21691
|
if ((e.d.match(/M/g) ?? []).length !== 1) return null;
|
|
21329
21692
|
const segs = extractPathSegments(e.d);
|
|
21330
21693
|
if (segs.length === 0) return null;
|
|
21331
|
-
if (segs.some((s) => s.cmd === "C")) return null;
|
|
21694
|
+
if (segs.some((s) => s.cmd === "C" || s.cmd === "A")) return null;
|
|
21332
21695
|
const last = segs[segs.length - 1];
|
|
21333
21696
|
if (last.cmd !== "L") return null;
|
|
21334
21697
|
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;
|
|
@@ -21619,13 +21982,16 @@ function \u5411\u304D\u3092\u53D6\u308B(end, \u5019\u88DC) {
|
|
|
21619
21982
|
}
|
|
21620
21983
|
return null;
|
|
21621
21984
|
}
|
|
21622
|
-
function \u7AEF\
|
|
21985
|
+
function \u7DDA\u306E\u7AEF\u306E\u5411\u304D(d, \u7AEF) {
|
|
21623
21986
|
const segs = extractPathSegments(d);
|
|
21624
21987
|
if (segs.length === 0) return null;
|
|
21625
21988
|
if (\u7AEF === "head") {
|
|
21626
21989
|
const last = segs[segs.length - 1];
|
|
21627
21990
|
const end2 = { x: last.x2, y: last.y2 };
|
|
21628
|
-
const \u5019\u88DC2 = segs.slice().reverse().flatMap((s) =>
|
|
21991
|
+
const \u5019\u88DC2 = segs.slice().reverse().flatMap((s) => {
|
|
21992
|
+
const \u5236\u5FA1\u70B9 = s.cmd === "C" ? [s.ctrl?.[1], s.ctrl?.[0]] : s.cmd === "Q" ? [s.ctrl?.[0]] : [];
|
|
21993
|
+
return [...\u5236\u5FA1\u70B9, { x: s.x1, y: s.y1 }];
|
|
21994
|
+
});
|
|
21629
21995
|
return \u5411\u304D\u3092\u53D6\u308B(end2, \u5019\u88DC2);
|
|
21630
21996
|
}
|
|
21631
21997
|
const first = segs[0];
|
|
@@ -21636,7 +22002,7 @@ function \u7AEF\u3067\u306E\u5411\u304D(d, \u7AEF) {
|
|
|
21636
22002
|
function \u7AEF\u306E\u5B57\u3092\u7F6E\u304F(edge, \u7AEF = "head") {
|
|
21637
22003
|
const text = (\u7AEF === "head" ? edge.headLabel : edge.tailLabel)?.trim();
|
|
21638
22004
|
if (!text) return null;
|
|
21639
|
-
const \u5411\u304D = \u7AEF\
|
|
22005
|
+
const \u5411\u304D = \u7DDA\u306E\u7AEF\u306E\u5411\u304D(edge.d, \u7AEF);
|
|
21640
22006
|
if (!\u5411\u304D) return null;
|
|
21641
22007
|
const { end, ux, uy } = \u5411\u304D;
|
|
21642
22008
|
const bx = end.x - ux * \u7AEF\u306E\u5B57\u3092\u623B\u3059\u8DDD\u96E2;
|
|
@@ -21964,22 +22330,23 @@ function layout(diag) {
|
|
|
21964
22330
|
}
|
|
21965
22331
|
}
|
|
21966
22332
|
const positionedEdgeIds = new Set(
|
|
21967
|
-
diag.edges.filter(
|
|
22333
|
+
diag.edges.filter(
|
|
22334
|
+
(e) => e.routing === "curve" || e.routing === "metro" || e.posX1 !== void 0 && e.posY1 !== void 0 && e.posX2 !== void 0 && e.posY2 !== void 0
|
|
22335
|
+
).map((e) => e.id)
|
|
21968
22336
|
);
|
|
21969
22337
|
bowSharedStraightPaths(edges, nodesFinal, positionedEdgeIds);
|
|
21970
22338
|
pullOutgoingFirstCorner(edges, nodesFinal);
|
|
21971
|
-
fanIncomingEndpoints(edges, nodesFinal);
|
|
22339
|
+
const fannedTailShifts = fanIncomingEndpoints(edges, nodesFinal);
|
|
21972
22340
|
separateSharedStraightRuns(edges, nodesFinal);
|
|
21973
22341
|
hopEdgeCrossings(edges);
|
|
22342
|
+
const labelClearanceObstacles = collectBBoxes(containedLanesFinal, nodesFinal, []).filter(
|
|
22343
|
+
(b) => b.kind === "node" || b.kind === "lane-label"
|
|
22344
|
+
);
|
|
22345
|
+
followLabelsToFannedTails(edges, labelClearanceObstacles, fannedTailShifts);
|
|
21974
22346
|
repositionParallelLabels(edges, nodesFinal);
|
|
21975
22347
|
slideLabelsAlongOwnPath(edges, nodesFinal);
|
|
21976
22348
|
if (!isChainEdgeLabelDisabled()) {
|
|
21977
|
-
edges = resolveLabelClearanceAfterPathPasses(
|
|
21978
|
-
edges,
|
|
21979
|
-
collectBBoxes(containedLanesFinal, nodesFinal, []).filter(
|
|
21980
|
-
(b) => b.kind === "node" || b.kind === "lane-label"
|
|
21981
|
-
)
|
|
21982
|
-
);
|
|
22349
|
+
edges = resolveLabelClearanceAfterPathPasses(edges, labelClearanceObstacles);
|
|
21983
22350
|
}
|
|
21984
22351
|
const bboxes = collectBBoxes(containedLanesFinal, nodesFinal, edges);
|
|
21985
22352
|
const collisions = detectCollisions(bboxes, edges);
|
|
@@ -22588,11 +22955,13 @@ function applyHeuristics(diag, laid, violations, applied) {
|
|
|
22588
22955
|
violations.filter((v) => v.axis === "edge-node-cross").map((v) => extractEdgeId(v.detail)).filter((id) => id !== null)
|
|
22589
22956
|
);
|
|
22590
22957
|
if (h3Edges.size > 0) {
|
|
22591
|
-
const
|
|
22592
|
-
|
|
22593
|
-
(e) => h3Edges.has(e.id) && e.routing !== "back-detour" ? { ...e, routing: "back-detour" } : e
|
|
22958
|
+
const eligibleEdges = new Set(
|
|
22959
|
+
edges.filter((e) => h3Edges.has(e.id) && (e.routing === void 0 || e.routing === "default")).map((e) => e.id)
|
|
22594
22960
|
);
|
|
22595
|
-
if (
|
|
22961
|
+
if (eligibleEdges.size > 0) {
|
|
22962
|
+
edges = edges.map(
|
|
22963
|
+
(e) => eligibleEdges.has(e.id) ? { ...e, routing: "back-detour" } : e
|
|
22964
|
+
);
|
|
22596
22965
|
applied.push("h3-edge-back-detour");
|
|
22597
22966
|
mutated = true;
|
|
22598
22967
|
}
|
|
@@ -22912,15 +23281,15 @@ function extractPathEndpoints(d) {
|
|
|
22912
23281
|
if (!start || !end) return null;
|
|
22913
23282
|
return { start, end };
|
|
22914
23283
|
}
|
|
22915
|
-
function isPointOnNodeBoundary(
|
|
23284
|
+
function isPointOnNodeBoundary(point2, node, tol) {
|
|
22916
23285
|
const left = node.cx - node.w / 2;
|
|
22917
23286
|
const right = node.cx + node.w / 2;
|
|
22918
23287
|
const top = node.cy - node.h / 2;
|
|
22919
23288
|
const bot = node.cy + node.h / 2;
|
|
22920
|
-
if (Math.abs(
|
|
22921
|
-
if (Math.abs(
|
|
22922
|
-
if (Math.abs(
|
|
22923
|
-
if (Math.abs(
|
|
23289
|
+
if (Math.abs(point2.x - left) <= tol && point2.y >= top - tol && point2.y <= bot + tol) return true;
|
|
23290
|
+
if (Math.abs(point2.x - right) <= tol && point2.y >= top - tol && point2.y <= bot + tol) return true;
|
|
23291
|
+
if (Math.abs(point2.y - top) <= tol && point2.x >= left - tol && point2.x <= right + tol) return true;
|
|
23292
|
+
if (Math.abs(point2.y - bot) <= tol && point2.x >= left - tol && point2.x <= right + tol) return true;
|
|
22924
23293
|
return false;
|
|
22925
23294
|
}
|
|
22926
23295
|
async function verifyBoundingBoxes(page, laid, options) {
|
|
@@ -24132,6 +24501,7 @@ function CdlEdgeView({
|
|
|
24132
24501
|
"g",
|
|
24133
24502
|
{
|
|
24134
24503
|
"data-cdl-edge": edge.id,
|
|
24504
|
+
"data-cdl-routing": edge.routing === "metro" ? "metro" : void 0,
|
|
24135
24505
|
"data-cdl-from": edge.from,
|
|
24136
24506
|
"data-cdl-to": edge.to,
|
|
24137
24507
|
"data-cdl-active": active ? "true" : "false",
|
|
@@ -25563,17 +25933,22 @@ function DynShapeNode({
|
|
|
25563
25933
|
return renderPolygon(shape, cx, cy, stateValues, fillColor, strokeColor, activeOpacity);
|
|
25564
25934
|
}
|
|
25565
25935
|
})();
|
|
25936
|
+
const \u63C3\u3048 = \u7BB1\u3044\u3063\u3071\u3044\u306B\u63CF\u304F2(shape.kind) ? "start" : "middle";
|
|
25937
|
+
return renderShapeWithOutsideLabels(\u5F62, node, \u63C3\u3048);
|
|
25938
|
+
}
|
|
25939
|
+
function renderShapeWithOutsideLabels(\u5F62, node, \u63C3\u3048) {
|
|
25940
|
+
const cx = node.cx;
|
|
25941
|
+
const y = node.cy - node.h / 2;
|
|
25566
25942
|
const \u540D\u524D = (node.title ?? "").trim();
|
|
25567
25943
|
const \u5024 = (node.subtitle ?? "").trim();
|
|
25568
25944
|
if (\u540D\u524D === "" && \u5024 === "") return \u5F62;
|
|
25569
|
-
const \u63C3\u3048 = \u7BB1\u3044\u3063\u3071\u3044\u306B\u63CF\u304F2(shape.kind) ? "start" : "middle";
|
|
25570
25945
|
return /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
|
|
25571
25946
|
\u5F62,
|
|
25572
25947
|
\u540D\u524D !== "" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
25573
25948
|
"text",
|
|
25574
25949
|
{
|
|
25575
25950
|
"data-cdl-role": "shape-name",
|
|
25576
|
-
x: \u63C3\u3048 === "start" ?
|
|
25951
|
+
x: \u63C3\u3048 === "start" ? node.cx - node.w / 2 : cx,
|
|
25577
25952
|
y: y - NAME_GAP,
|
|
25578
25953
|
textAnchor: \u63C3\u3048,
|
|
25579
25954
|
fontSize: 13,
|
|
@@ -25587,7 +25962,7 @@ function DynShapeNode({
|
|
|
25587
25962
|
{
|
|
25588
25963
|
"data-cdl-role": "shape-value",
|
|
25589
25964
|
x: cx,
|
|
25590
|
-
y: y + h + VALUE_GAP,
|
|
25965
|
+
y: y + node.h + VALUE_GAP,
|
|
25591
25966
|
textAnchor: "middle",
|
|
25592
25967
|
fontSize: 12,
|
|
25593
25968
|
fill: "var(--cdl-text-dim, #5a6270)",
|
|
@@ -25837,6 +26212,27 @@ function interpolateColor(template, stateValues, fallback) {
|
|
|
25837
26212
|
if (!template) return fallback;
|
|
25838
26213
|
return interpolate(template, stateValues);
|
|
25839
26214
|
}
|
|
26215
|
+
var STATION_STROKE_ACTIVE = 4;
|
|
26216
|
+
var STATION_STROKE_IDLE = 3;
|
|
26217
|
+
var STATION_DEFAULT = "var(--cdl-tone-accent, #2d6a8f)";
|
|
26218
|
+
function StationNode({ node, active }) {
|
|
26219
|
+
const strokeWidth = active ? STATION_STROKE_ACTIVE : STATION_STROKE_IDLE;
|
|
26220
|
+
const radius = Math.max(0, Math.min(node.w, node.h) / 2 - strokeWidth / 2);
|
|
26221
|
+
const \u5F62 = /* @__PURE__ */ jsxRuntime.jsx(
|
|
26222
|
+
"circle",
|
|
26223
|
+
{
|
|
26224
|
+
"data-cdl-role": "node-body",
|
|
26225
|
+
"data-cdl-mark": "station",
|
|
26226
|
+
cx: node.cx,
|
|
26227
|
+
cy: node.cy,
|
|
26228
|
+
r: radius,
|
|
26229
|
+
fill: "var(--cdl-node-fill, #ffffff)",
|
|
26230
|
+
stroke: nodeAccent(node, STATION_DEFAULT),
|
|
26231
|
+
strokeWidth
|
|
26232
|
+
}
|
|
26233
|
+
);
|
|
26234
|
+
return renderShapeWithOutsideLabels(\u5F62, node, "middle");
|
|
26235
|
+
}
|
|
25840
26236
|
|
|
25841
26237
|
// src/kinds/label-stack.ts
|
|
25842
26238
|
function \u540D\u672D\u306E\u6BB5\u3092\u307B\u3069\u304F(\u771F\u306Ey, \u9593\u9694, \u4E0A\u9650, \u4E0B\u9650) {
|
|
@@ -33065,6 +33461,8 @@ function CdlNodeView({
|
|
|
33065
33461
|
return /* @__PURE__ */ jsxRuntime.jsx(MarkStartNode, { node: resolvedNode, active });
|
|
33066
33462
|
case "mark-end":
|
|
33067
33463
|
return /* @__PURE__ */ jsxRuntime.jsx(MarkEndNode, { node: resolvedNode, active });
|
|
33464
|
+
case "station":
|
|
33465
|
+
return /* @__PURE__ */ jsxRuntime.jsx(StationNode, { node: resolvedNode, active });
|
|
33068
33466
|
default:
|
|
33069
33467
|
return /* @__PURE__ */ jsxRuntime.jsx(GenericNode, { node: resolvedNode, active, value: node.value, stateValues, \u5024\u306E\u540D\u524D });
|
|
33070
33468
|
}
|
|
@@ -33297,15 +33695,15 @@ function CdlStage({
|
|
|
33297
33695
|
}, [laid]);
|
|
33298
33696
|
const edgeHeadMarkers = react.useMemo(() => {
|
|
33299
33697
|
const referenced = /* @__PURE__ */ new Set();
|
|
33300
|
-
const
|
|
33698
|
+
const add2 = (tone, head, fill) => {
|
|
33301
33699
|
for (const small of [false, true]) {
|
|
33302
33700
|
const id = edgeHeadMarkerName(tone, head, small, fill);
|
|
33303
33701
|
if (id !== void 0) referenced.add(id);
|
|
33304
33702
|
}
|
|
33305
33703
|
};
|
|
33306
33704
|
for (const edge of laid.edges) {
|
|
33307
|
-
|
|
33308
|
-
if (edge.tailHead !== void 0)
|
|
33705
|
+
add2(edge.tone, edge.head, edge.headFill);
|
|
33706
|
+
if (edge.tailHead !== void 0) add2(edge.tone, edge.tailHead, edge.tailHeadFill);
|
|
33309
33707
|
}
|
|
33310
33708
|
return Object.entries(TONE).flatMap(
|
|
33311
33709
|
([tone, color]) => [
|