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