Public
Edited
Feb 22, 2023
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
const d3SvgNode = d3.select(svg`<svg width=${width} height=400></svg>`);

const targetData = {
x: width / 2 + 100,
y: 50
};

const sourceData = {
x: width / 2 - 100,
y: 200
};

const offsetPointData = {
x: targetData.x,
y: d3.mean([targetData.y, sourceData.y]),
offsetMean: 0
};

const midData = {
x: width / 2 + 200,
y: 300
};

const drag = d3.drag().on("drag", dragged);

const link = d3SvgNode
.append("path")
.attr("d", diagonal(sourceData, targetData, midData, offsetPointData))
.attr("fill", "none")
.attr("stroke", "steelblue");

const source = d3SvgNode
.append("circle")
.datum(sourceData)
.attr("r", 10)
.attr("cx", sourceData.x)
.attr("cy", sourceData.y)
.attr("fill", "steelblue")
.attr("cursor", "move")
.call(drag);

const target = d3SvgNode
.append("circle")
.datum(targetData)
.attr("r", 10)
.attr("cx", targetData.x)
.attr("cy", targetData.y)
.attr("fill", "steelblue")
.attr("cursor", "move")
.call(drag);

const mid = d3SvgNode
.append("circle")
.datum(midData)
.attr("r", 10)
.attr("cx", midData.x)
.attr("cy", midData.y)
.attr("fill", "steelblue")
.attr("cursor", "move")
.call(drag);

const offset = d3SvgNode
.append("circle")
.datum(offsetPointData)
.attr("r", 5)
.attr("cx", targetData.x)
.attr(
"cy",
d3.mean([targetData.y, sourceData.y]) + offsetPointData.offsetMean
)
.attr("fill", "steelblue")
.attr("cursor", "move")
.call(drag);

function dragged(d) {
d.x = d3.event.x;
d.y = d3.event.y;
update();
}

function update() {
source.attr("cx", sourceData.x).attr("cy", sourceData.y);
target.attr("cx", targetData.x).attr("cy", targetData.y);
mid.attr("cx", midData.x).attr("cy", midData.y);

offsetPointData.offsetMean =
d3.mean([targetData.y, sourceData.y]) - offsetPointData.y;
console.log(offsetPointData.offsetMean);

offset
.attr("cx", targetData.x)
.attr(
"cy",
d3.mean([targetData.y, sourceData.y]) - offsetPointData.offsetMean
);

link.attr(
"d",
diagonal(sourceData, targetData, midData, {
offsetMean: offsetPointData.offsetMean
})
);
}

yield d3SvgNode.node();
}
Insert cell
mutable points = ({
s: { x: 0, y: 0 },
t: { x: 0, y: 0 },
m: { x: 0, y: 0 },
offsetMean: 0
})
Insert cell
function diagonal(s, t, m, offsets = { offsetMean: 0 }) {
console.log("offsets", offsets);
mutable points = {
s,
t,
m,
offsetMean: offsets.offsetMean
};
const x = s.x;
const y = s.y;
const ex = t.x;
const ey = t.y;

let mx = m?.x ?? x;
let my = m?.y ?? y;

let xrvs = ex - x < 0 ? -1 : 1;
let yrvs = ey - y < 0 ? -1 : 1;

let rdef = 35;
let r = Math.abs(ex - x) / 2 < rdef ? Math.abs(ex - x) / 2 : rdef;

r = Math.abs(ey - y) / 2 < r ? Math.abs(ey - y) / 2 : r;

let h = Math.abs(ey - y) / 2 - r;
let w = Math.abs(ex - x) - r * 2;
//w=0;
const path = `
M ${mx} ${my}
L ${x} ${my}
L ${x} ${y}
L ${x} ${y + h * yrvs - offsets.offsetMean}
C ${x} ${y + h * yrvs + r * yrvs - offsets.offsetMean} ${x} ${
y + h * yrvs + r * yrvs - offsets.offsetMean
} ${x + r * xrvs} ${y + h * yrvs + r * yrvs - offsets.offsetMean}
L ${x + w * xrvs + r * xrvs} ${
y + h * yrvs + r * yrvs - offsets.offsetMean
}
C ${ex} ${y + h * yrvs + r * yrvs - offsets.offsetMean} ${ex} ${
y + h * yrvs + r * yrvs - offsets.offsetMean
} ${ex} ${ey - h * yrvs - offsets.offsetMean}
L ${ex} ${ey}
`;
return path;
}
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