Public
Edited
Apr 24, 2023
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
// Use computed layout and get size
const { width, height, dag } = laidout;

// This code only handles rendering
const svgNode = svg`<svg width=${width} height=${height} transform="scale(1)"></svg>`;

const svgSelection = d3.select(svgNode);
const defs = svgSelection.append("defs"); // For gradients

// Initialize color map
const steps = dag.size();
const interp = d3.interpolateRainbow;
const colorMap = {};
for (const [i, node] of [...dag].entries()) {
let nodeType = findNodeType(node.data.id);
colorMap[nodeType] = interp(i / steps);
}

// How to draw edges
const curve =
splines.get(spline) ??
(coord === "Simplex (medium)" ? d3.curveMonotoneY : d3.curveCatmullRom);
const line = d3
.line()
.curve(curve)
.x((d) => d.x)
.y((d) => d.y);

// Plot edges
svgSelection
.append("g")
.selectAll("path")
.data(dag.links())
.enter()
.append("path")
.attr("d", ({ points }) => {
return line(points);
})
.attr("fill", "none")
.attr("stroke-width", 1)
.attr("stroke", ({ source, target }) => {
// encodeURIComponents for spaces, hope id doesn't have a `--` in it
const gradId = encodeURIComponent(
`${source.data.id.replaceAll(" ", "")}--${target.data.id.replaceAll(
" ",
""
)}`
);

const grad = defs
.append("linearGradient")
.attr("id", gradId)
.attr("gradientUnits", "userSpaceOnUse")
.attr("x1", source.x)
.attr("x2", target.x)
.attr("y1", source.y)
.attr("y2", target.y);

grad
.append("stop")
.attr("offset", "0%")
.attr("stop-color", colorMap[findNodeType(source.data.id)]);
grad
.append("stop")
.attr("offset", "100%")
.attr("stop-color", colorMap[findNodeType(target.data.id)]);
return `url(#${gradId})`;
});

// Select nodes
const nodes = svgSelection
.append("g")
.selectAll("g")
.data(dag.descendants())
.enter()
.append("g")
.attr("transform", ({ x, y }) => `translate(${x}, ${y})`);

// Plot node circles
nodes
.append("circle")
.attr("r", nodeRadius)
.attr("fill", (n) => colorMap[findNodeType(n.data.id)]);

// Plot Arrows
if (arrows) {
const arrowSize = (nodeRadius * nodeRadius) / 5.0;
const arrowLen = Math.sqrt((4 * arrowSize) / Math.sqrt(3));
const arrow = d3.symbol().type(d3.symbolTriangle).size(arrowSize);
svgSelection
.append("g")
.selectAll("path")
.data(dag.links())
.enter()
.append("path")
.attr("d", arrow)
.attr("transform", ({ source, target, points }) => {
const [end, start] = points.slice().reverse();
// This sets the arrows the node radius (20) + a little bit (3) away from the node center, on the last line segment of the edge. This means that edges that only span ine level will work perfectly, but if the edge bends, this will be a little off.
const dx = start.x - end.x;
const dy = start.y - end.y;
const scale = (nodeRadius * 1.15) / Math.sqrt(dx * dx + dy * dy);
// This is the angle of the last line segment
const angle = (Math.atan2(-dy, -dx) * 180) / Math.PI + 90;
console.log(angle, dx, dy);
return `translate(${end.x + dx * scale}, ${
end.y + dy * scale
}) rotate(${angle})`;
})
.attr("fill", ({ target }) => colorMap[findNodeType(target.data.id)])
.attr("stroke", "white")
.attr("stroke-width", 1.5)
.attr("stroke-dasharray", `${arrowLen},${arrowLen}`);
}

// Add text to nodes
nodes
.append("text")
.text((d) => d.data.id)
.attr("font-size", "9")
.attr("font-family", "sans-serif")
.attr("text-anchor", "middle")
.attr("alignment-baseline", "middle")
.attr("fill", "black")
.attr("paint-order", "stroke")
.attr("stroke", "white");

yield svgNode;
}
Insert cell
findNodeType = (id) => _.find(dataCrisp.nodes, ["label", id]).type
Insert cell
Insert cell
nodeRadius = 8
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
dag = {
//const [key, reader] = sources.get(source);
//return reader(dag_data);
console.log(dagData);
return d3.dagConnect()(dagData);
}
Insert cell
laidout = {
const layout = d3
.sugiyama()
.layering(layerings.get(layering))
.decross(decrossings.get(decross))
.coord(coords.get(coord))
.nodeSize(nodeSizes.get(nodeSize));
const start = performance.now();
const { width, height } = layout(dag);
const time = performance.now() - start;
return { width, height, time, dag };
}
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