Published
Edited
Sep 9, 2021
1 star
Also listed in…
Work Utils
Insert cell
Insert cell
Insert cell
Insert cell
viewof arrowTransformY = Inputs.range([-20, 20], {
value: 5,
step: 1,
label: "Arrow Y"
})
Insert cell
Insert cell
[...Array(linksCount).keys()]
Insert cell
svgE = svg`<svg width=700 height=200 xmlns="http://www.w3.org/2000/svg">
<defs>
<marker id="arrow" markerWidth="500" markerHeight="500" orient="auto" >
<path transform="translate(${
(0, arrowTransformY)
})" fill="red" d="M0,-5L10,0L0,5" />
</marker>
</defs>

<path d="M30,30 200,120 240,50" fill="none" stroke="black"
marker-mid="url(#arrow)"
/>

</svg>`
Insert cell
Insert cell
mutable debug = 1
Insert cell
function linkGroupArc({ source, target, count, lineIndex }) {
if (source == target) {
var x = source.x;
var y = source.y;
var r = source.r - intervalPadding + 10;

var alpha = Math.PI / 3;

if (lineIndex) {
alpha -= 0.1 * lineIndex;
}
var dx = Math.cos(alpha) * r;
var dy = Math.sin(alpha) * r;

var dx1 = x - dx;
var dx2 = x + dx;
var dy1 = y - dy;
var dy2 = y - dy;

var dr = (20 * 10) / r;

if (lineIndex) {
dr += 5 * lineIndex;
}
return `M ${dx1}, ${dy1} A ${dr}, ${dr} 0 1 1 ${dx2}, ${dy2}`;
}

var x1 = source.x;
var y1 = source.y;
var r1 = source.r - intervalPadding + 10;

var x2 = target.x;
var y2 = target.y;
var r2 = target.r - intervalPadding + 10;

var dx = x2 - x1;
var dy = y2 - y1;
var dr = Math.sqrt(dx * dx + dy * dy);
/** Control point */
var mx = x1 + dx / 2;
var my = y1 + dy / 2;

var d = (50 * dr) / 500;
var inc = (50 * dr) / 500;

if (lineIndex) {
d += inc * Math.floor((lineIndex + 1) / 2);
} else {
d += inc * Math.round((lineIndex + 1) / 2);

if (count % 2) {
d = 1;
} else {
d = -inc * Math.round((lineIndex + 1) / 2);
}
}

if ((!dx && lineIndex % 2) || (!dy && (lineIndex + 1) % 2)) {
d = -d;
}
mutable debug = { dx, dy, d };

var cx = 0;
var cy = 0;
var b = 0;
var m = dy / dx;

if (dx == 0) {
cx = mx + d;
cy = my;
} else if (dy == 0) {
cx = mx;
cy = my - d;
} else {
if (lineIndex % 2) {
cx = mx + Math.sqrt(Math.pow(d, 2) / (1 + 1 / Math.pow(m, 2)));
} else {
cx = mx - Math.sqrt(Math.pow(d, 2) / (1 + 1 / Math.pow(m, 2)));
}
/*
if (x1 > x2) {
cx = mx + Math.sqrt(Math.pow(d, 2) / (1 + 1 / Math.pow(m, 2)));
} else {
cx = mx - Math.sqrt(Math.pow(d, 2) / (1 + 1 / Math.pow(m, 2)));
}
*/

b = my - (-1 / m) * mx;
cy = (-1 / m) * cx + b;
}

var m1 = (cy - y1) / (cx - x1);
var d1 = y1 - m1 * x1;
var sita1 =
Math.pow(r1, 2) * (1 + Math.pow(m1, 2)) - Math.pow(y1 - m1 * x1 - d1, 2);

var x11 = (x1 + y1 * m1 - d1 * m1 + Math.sqrt(sita1)) / (1 + Math.pow(m1, 2));
var y11 =
(d1 + x1 * m1 + y1 * Math.pow(m1, 2) + m1 * Math.sqrt(sita1)) /
(1 + Math.pow(m1, 2));

if (!((x11 > cx && x11 < x1) || (x11 > x1 && x11 < cx))) {
x11 = (x1 + y1 * m1 - d1 * m1 - Math.sqrt(sita1)) / (1 + Math.pow(m1, 2));
y11 =
(d1 + x1 * m1 + y1 * Math.pow(m1, 2) - m1 * Math.sqrt(sita1)) /
(1 + Math.pow(m1, 2));
}

var m2 = (y2 - cy) / (x2 - cx);
var d2 = y2 - m2 * x2;
var sita2 =
Math.pow(r2, 2) * (1 + Math.pow(m2, 2)) - Math.pow(y2 - m2 * x2 - d2, 2);

var x22 = (x2 + y2 * m2 - d2 * m2 - Math.sqrt(sita2)) / (1 + Math.pow(m2, 2));
var y22 =
(d2 + x2 * m2 + y2 * Math.pow(m2, 2) - m2 * Math.sqrt(sita2)) /
(1 + Math.pow(m2, 2));

if (!((x22 > cx && x22 < x2) || (x22 > cx && x22 < x2))) {
x22 = (x2 + y2 * m2 - d2 * m2 + Math.sqrt(sita2)) / (1 + Math.pow(m2, 2));
y22 =
(d2 + x2 * m2 + y2 * Math.pow(m2, 2) + m2 * Math.sqrt(sita2)) /
(1 + Math.pow(m2, 2));
}

if (isNaN(x11)) x11 = 0;
if (isNaN(y11)) y11 = 0;
if (isNaN(cx)) cx = 0;
if (isNaN(cy)) cy = 0;
if (isNaN(x22)) x22 = 0;
if (isNaN(y22)) y22 = 0;

return `M ${x11} , ${y11} Q ${cx}, ${cy} ${x22}, ${y22}`;
}
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more