@lucastho/d3-sankey-circular-ng 0.1.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/LICENSE +27 -0
- package/README.md +199 -0
- package/dist/d3-sankey-circular-ng.js +784 -0
- package/dist/d3-sankey-circular-ng.min.js +2 -0
- package/package.json +59 -0
- package/src/align.js +23 -0
- package/src/constant.js +5 -0
- package/src/index.js +3 -0
- package/src/sankey.js +644 -0
- package/src/sankeyLinkCircular.js +97 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// src/sankeyLinkCircular.js
|
|
2
|
+
// sankeyLinkCircular.js
|
|
3
|
+
//
|
|
4
|
+
// Generates an SVG path that is the CENTERLINE of a link. The link's visible
|
|
5
|
+
// thickness comes entirely from stroke-width === link.width at render time
|
|
6
|
+
// (fill: none, stroke-linejoin: round, stroke-linecap: round/butt).
|
|
7
|
+
//
|
|
8
|
+
// Normal links: a cubic Bézier centerline from (source.x1, y0) to (target.x0, y1),
|
|
9
|
+
// identical in shape to d3.sankeyLinkHorizontal — but we STROKE it instead of
|
|
10
|
+
// filling a ribbon. Same visual result, far simpler.
|
|
11
|
+
//
|
|
12
|
+
// Circular links: a rounded-corner polyline read straight out of
|
|
13
|
+
// link.circularPathData.points (computed by the layout). No inner/outer radius
|
|
14
|
+
// math — we round each interior vertex of a single centerline with one radius.
|
|
15
|
+
//
|
|
16
|
+
// debug: set linkPath.debug(true) to flag debug mode; linkPath.points(d) exposes
|
|
17
|
+
// the underlying vertex list for overlay rendering (corners + endpoints).
|
|
18
|
+
|
|
19
|
+
export default function sankeyLinkCircular() {
|
|
20
|
+
let _debug = false;
|
|
21
|
+
|
|
22
|
+
function link(d) {
|
|
23
|
+
return d.circular ? circularPath(d) : normalPath(d);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Centerline of a normal link: a single horizontal cubic Bézier.
|
|
27
|
+
function normalPath(d) {
|
|
28
|
+
const x0 = d.source.x1;
|
|
29
|
+
const x1 = d.target.x0;
|
|
30
|
+
const xi = (x0 + x1) / 2;
|
|
31
|
+
return `M${x0},${d.y0}C${xi},${d.y0} ${xi},${d.y1} ${x1},${d.y1}`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Round a polyline of {x,y} points with a single radius `r`. Endpoints are
|
|
35
|
+
// hit exactly; each interior vertex is replaced by a circular fillet whose
|
|
36
|
+
// radius is clamped so it never exceeds half of either adjacent segment.
|
|
37
|
+
function roundedPolyline(points, r) {
|
|
38
|
+
if (points.length < 2) return "";
|
|
39
|
+
if (points.length === 2) {
|
|
40
|
+
return `M${points[0].x},${points[0].y}L${points[1].x},${points[1].y}`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
let d = `M${points[0].x},${points[0].y}`;
|
|
44
|
+
for (let i = 1; i < points.length - 1; ++i) {
|
|
45
|
+
const p0 = points[i - 1];
|
|
46
|
+
const p1 = points[i]; // the corner
|
|
47
|
+
const p2 = points[i + 1];
|
|
48
|
+
|
|
49
|
+
const v1x = p0.x - p1.x, v1y = p0.y - p1.y;
|
|
50
|
+
const v2x = p2.x - p1.x, v2y = p2.y - p1.y;
|
|
51
|
+
const len1 = Math.hypot(v1x, v1y);
|
|
52
|
+
const len2 = Math.hypot(v2x, v2y);
|
|
53
|
+
|
|
54
|
+
if (len1 < 1e-6 || len2 < 1e-6) {
|
|
55
|
+
d += `L${p1.x},${p1.y}`;
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Clamp the fillet radius to fit both adjacent segments. This clamp — not
|
|
60
|
+
// the curve type — is what guarantees the fillet never self-intersects.
|
|
61
|
+
const rr = Math.min(r, len1 / 2, len2 / 2);
|
|
62
|
+
|
|
63
|
+
const a1x = p1.x + (v1x / len1) * rr;
|
|
64
|
+
const a1y = p1.y + (v1y / len1) * rr;
|
|
65
|
+
const a2x = p1.x + (v2x / len2) * rr;
|
|
66
|
+
const a2y = p1.y + (v2y / len2) * rr;
|
|
67
|
+
|
|
68
|
+
d += `L${a1x},${a1y}`;
|
|
69
|
+
// Quadratic fillet using the corner as the control point. A quadratic
|
|
70
|
+
// Bézier through a1 -> (control p1) -> a2 gives a smooth, robust round —
|
|
71
|
+
// and unlike SVG arcTo it can't blow up on degenerate radii.
|
|
72
|
+
d += `Q${p1.x},${p1.y} ${a2x},${a2y}`;
|
|
73
|
+
}
|
|
74
|
+
const last = points[points.length - 1];
|
|
75
|
+
d += `L${last.x},${last.y}`;
|
|
76
|
+
return d;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function circularPath(d) {
|
|
80
|
+
const p = d.circularPathData;
|
|
81
|
+
return roundedPolyline(p.points, p.radius);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// --- debug accessors ---------------------------------------------------
|
|
85
|
+
link.points = function (d) {
|
|
86
|
+
return d.circular ? d.circularPathData.points : [
|
|
87
|
+
{ x: d.source.x1, y: d.y0 },
|
|
88
|
+
{ x: d.target.x0, y: d.y1 }
|
|
89
|
+
];
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
link.debug = function (_) {
|
|
93
|
+
return arguments.length ? ((_debug = !!_), link) : _debug;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
return link;
|
|
97
|
+
}
|