Public
Edited
Apr 21, 2023
Insert cell
Insert cell
{
const nodeRadius = 20;
const height = 400;
const svgNode = svg`<svg width=${width} height=${height} viewbox="${-nodeRadius} ${-nodeRadius} ${
width + 2 * nodeRadius
} ${height + 2 * nodeRadius}"></svg>`;
const svgSelection = d3.select(svgNode);
const unConnected = ["coucou", "tu", "veux", "voir", "ma"];
const newSample = [
["Cours 1", "Cours 2"],
["Cours 1", "Cours 4"],
["Cours 2", "Cours 3"]
];
const layout = d3.zherebko().size([height, width]);
const dagCreator = d3.dagConnect();
const dag = dagCreator(newSample);
console.log(dag.descendants());
const arrow = d3
.symbol()
.type(d3.symbolTriangle)
.size((nodeRadius * nodeRadius) / 5);

const colors = ["blue", "green", "red", "purple"];

// Use computed layout
layout(dag);

// How to draw edges
const line = d3
.line()
.curve(d3.curveMonotoneX)
.x((d) => d.y)
.y((d) => d.x);

// Plot edges
svgSelection
.append("g")
.selectAll("path")
.data(dag.links())
.enter()
.append("path")
.attr("d", ({ data }) => line(data.points))
.attr("fill", "none")
.attr("stroke-width", 3)
.attr("stroke", (d, i) => colors[i]);

// Select nodes
const nodes = svgSelection
.append("g")
.selectAll("g")
.data(dag.descendants())
.enter()
.append("g")
.attr("transform", ({ x, y }) => `translate(${y}, ${x})`);
const xs = dag.descendants().map((d) => d.x);
const ys = dag.descendants().map((d) => d.y);
console.log("xs");
console.log(xs);
const maxX = Math.max(...xs);
const minY = Math.min(...ys);
console.log("minY");
console.log(minY);
const unconnectedNodes = svgSelection
.append("g")
.selectAll("g")
.data(unConnected)
.enter()
.append("g")
.attr(
"transform",
(dp, index) => `translate(${minY}, ${maxX + 50 * (index + 1)})`
);
// Plot node circles
nodes.append("circle").attr("r", 20);
unconnectedNodes.append("circle").attr("r", 20).attr("fill", "red");
unconnectedNodes
.append("text")
.text((d) => d)
.attr("font-weight", "bold")
.attr("font-size", "10px")
.attr("font-family", "sans-serif")
.attr("text-anchor", "middle")
.attr("alignment-baseline", "middle")
.attr("dominant-baseline", "middle")
.attr("fill", "white");

// Add text to nodes
nodes
.append("text")
.text((d) => d.id)
.attr("font-weight", "bold")
.attr("font-size", "10px")
.attr("font-family", "sans-serif")
.attr("text-anchor", "middle")
.attr("alignment-baseline", "middle")
.attr("dominant-baseline", "middle")
.attr("fill", "white");

svgSelection
.append("g")
.selectAll("path")
.data(dag.links())
.enter()
.append("path")
.attr("d", arrow)
.attr("transform", ({ source, target, data }) => {
const middleIndex = Math.floor(data.points.length / 2);
const maxDeflection = +data.points[middleIndex].x;
const dx = source.x - target.x;
const dy = source.y - target.y;
return `translate(${
(target.y - source.y) / 2 + source.y
}, ${maxDeflection}) rotate(${90})`;
})
.attr("fill", (d, i) => colors[i])
.attr("stroke", (d, i) => colors[i])
.attr("stroke-width", 1.5);

return svgNode;
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
tasks = [1, 2, 3, 4, 5, 6, 7, 8]
Insert cell
dependencies = [[1, 3], [2, 3], [2, 4], [3, 4], [5, 2], [8, 2], [6, 7], [6, 3]]
Insert cell
Insert cell
Insert cell
Insert cell
topologicalSort(tasks, dependencies)
Insert cell
Insert cell
Insert cell
Insert cell
topologicalSort([1, 2, 3, 4], [[1, 3], [2, 3], [2, 4], [3, 4]])
Insert cell
function topologicalSort(tasks, deps) {
const taskToDeps = deps.reduce((acc, curr) => {
const [dep, task] = curr;
if (!acc.has(task)) {
acc.set(task, new Set([dep]));
} else {
acc.get(task).add(dep);
}

return acc;
}, new Map());

const depToTasks = deps.reduce((acc, curr) => {
const [dep, task] = curr;
if (!acc.has(dep)) {
acc.set(dep, new Set([task]));
} else {
acc.get(dep).add(task);
}

return acc;
}, new Map());

const tasksSet = new Set(tasks);
const tasksWithoutDependencies = Array.from(tasksSet).flatMap(task =>
taskToDeps.has(task) ? [] : task
);
let order = [];

while (order.length !== tasksSet.size) {
if (!tasksWithoutDependencies.length) {
return [];
}

const task = tasksWithoutDependencies.pop();
order.push(task);

if (depToTasks.has(task)) {
for (let taskToUpdate of depToTasks.get(task)) {
depToTasks.get(task).delete(taskToUpdate);
taskToDeps.get(taskToUpdate).delete(task);
if (!taskToDeps.get(taskToUpdate).size) {
tasksWithoutDependencies.push(taskToUpdate);
taskToDeps.delete(taskToUpdate);
}
}

depToTasks.delete(task);
}
}

return order;
}
Insert cell
Insert cell
d3 = {
const d3_base = await require('d3');
const d3_dag = await require('d3-dag@0.3.0');
return Object.assign({}, d3_base, d3_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