2022-01-12 14:42:07 +01:00
|
|
|
{% extends "base.html" %}
|
|
|
|
|
2022-01-12 14:48:59 +01:00
|
|
|
{% block header %}
|
|
|
|
<style>
|
|
|
|
svg {
|
|
|
|
width: 50%;
|
|
|
|
height: 50%;
|
|
|
|
}
|
|
|
|
</style>
|
|
|
|
{% endblock %}
|
|
|
|
|
2022-01-12 14:42:07 +01:00
|
|
|
{% block content %}
|
|
|
|
<div class="card mb-0">
|
2022-01-12 14:48:59 +01:00
|
|
|
<svg></svg>
|
2022-01-12 14:42:07 +01:00
|
|
|
</div>
|
|
|
|
{% endblock %}
|
|
|
|
|
|
|
|
{% block footerscript %}
|
2022-01-12 14:48:59 +01:00
|
|
|
<script src="{% static "js/plugins/D3-dag/d3.min.js%}"></script>
|
|
|
|
<script src="{% static "js/plugins/D3-dag/d3-dag.0.8.2.min.js%}"></script>
|
|
|
|
<script>
|
|
|
|
(async () => {
|
|
|
|
// fetch data and render
|
|
|
|
// const resp = await fetch(
|
|
|
|
// "https://raw.githubusercontent.com/erikbrinkman/d3-dag/main/examples/grafo.json"
|
|
|
|
// );
|
|
|
|
const data = [{
|
|
|
|
// Rudy
|
|
|
|
"id": "43/",
|
|
|
|
"parentIds": ["-2", ".41/"]
|
|
|
|
},
|
|
|
|
{
|
|
|
|
// vrille
|
|
|
|
"id": "-2",
|
|
|
|
"parentIds": ["-1"]
|
|
|
|
},
|
|
|
|
{
|
|
|
|
// Barani
|
|
|
|
"id": ".41/",
|
|
|
|
"parentIds": ["-1", ".3-/"]
|
|
|
|
},
|
|
|
|
{
|
|
|
|
// 1/2 vrille
|
|
|
|
"id": "-1",
|
|
|
|
"parentIds": ["|"]
|
|
|
|
},
|
|
|
|
{
|
|
|
|
// Chandelle
|
|
|
|
"id": "|",
|
|
|
|
"parentIds": []
|
|
|
|
},
|
|
|
|
{
|
|
|
|
// 3/4 Avant Tendu
|
|
|
|
"id": ".3-/",
|
|
|
|
"parentIds": [".1"]
|
|
|
|
},
|
|
|
|
{
|
|
|
|
// Ventre
|
|
|
|
"id": ".1",
|
|
|
|
"parentIds": ["4p"]
|
|
|
|
},
|
|
|
|
{
|
|
|
|
// 4 pattes
|
|
|
|
"id": "4p",
|
|
|
|
"parentIds": []
|
|
|
|
}];
|
|
|
|
// const data = await resp.json();
|
|
|
|
|
|
|
|
const dag = d3.dagStratify()(data);
|
|
|
|
const nodeRadius = 20;
|
|
|
|
const layout = d3
|
|
|
|
//
|
|
|
|
// Base layout
|
|
|
|
.sugiyama()
|
|
|
|
//
|
|
|
|
// Layering
|
|
|
|
.layering(d3.layeringSimplex()) // Simplex (shortest edges)
|
|
|
|
// .layering(d3.layeringLongestPath()) // Longest Path (minimum height)
|
|
|
|
// .layering(d3.layeringCoffmanGraham()) // Coffman Graham (constrained width)
|
|
|
|
//
|
|
|
|
// Decrossing
|
|
|
|
.decross(d3.decrossOpt()) // Optimal (can be very slow)
|
|
|
|
// .decross(d3.decrossTwoLayer().order(d3.twolayerAgg())) // Two Layer Agg (fast)
|
|
|
|
// .decross(d3.decrossTwoLayer().order(d3.twolayerOpt())) // Two Layer Opt (can be very slow)
|
|
|
|
//
|
|
|
|
// Coords
|
|
|
|
// .coord(d3.coordCenter()) // Center (fast)
|
|
|
|
.coord(d3.coordGreedy()) // Greedy (fast)
|
|
|
|
// .coord(d3.coordQuad()) // Quadradtic (can be slow)
|
|
|
|
// .coord(d3.coordTopological()) //
|
|
|
|
.nodeSize(
|
|
|
|
(node) => [(node ? 3.6 : 0.25) * nodeRadius, 3 * nodeRadius]
|
|
|
|
); // set node size instead of constraining to fit
|
|
|
|
// .nodeSize((node) => {
|
|
|
|
// const size = node ? base : 5;
|
|
|
|
// return [1.2 * size, size];
|
|
|
|
// });
|
|
|
|
const { width, height } = layout(dag);
|
|
|
|
|
|
|
|
// --------------------------------
|
|
|
|
// This code only handles rendering
|
|
|
|
// --------------------------------
|
|
|
|
const svgSelection = d3.select("svg");
|
|
|
|
svgSelection.attr("viewBox", [0, 0, width, height].join(" "));
|
|
|
|
const defs = svgSelection.append("defs"); // For gradients
|
|
|
|
|
|
|
|
const steps = dag.size();
|
|
|
|
const interp = d3.interpolateRainbow;
|
|
|
|
const colorMap = new Map();
|
|
|
|
for (const [i, node] of dag.idescendants().entries()) {
|
|
|
|
colorMap.set(node.data.id, interp(i / steps));
|
|
|
|
}
|
|
|
|
|
|
|
|
// How to draw edges
|
|
|
|
const line = d3
|
|
|
|
.line()
|
|
|
|
.curve(d3.curveCatmullRom)
|
|
|
|
.x((d) => d.x)
|
|
|
|
.y((d) => d.y);
|
|
|
|
|
|
|
|
// Plot edges
|
|
|
|
svgSelection
|
|
|
|
.append("g")
|
|
|
|
.selectAll("path")
|
|
|
|
.data(dag.links())
|
|
|
|
.enter()
|
|
|
|
.append("path")
|
|
|
|
.attr("d", ({ points }) => line(points))
|
|
|
|
.attr("fill", "none")
|
|
|
|
.attr("stroke-width", 3)
|
|
|
|
.attr("stroke", ({ source, target }) => {
|
|
|
|
// encodeURIComponents for spaces, hope id doesn't have a `--` in it
|
|
|
|
const gradId = encodeURIComponent(`${source.data.id}--${target.data.id}`);
|
|
|
|
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.get(source.data.id));
|
|
|
|
grad
|
|
|
|
.append("stop")
|
|
|
|
.attr("offset", "100%")
|
|
|
|
.attr("stop-color", colorMap.get(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.get(n.data.id));
|
2022-01-12 14:42:07 +01:00
|
|
|
|
2022-01-12 14:48:59 +01:00
|
|
|
// Add text to nodes
|
|
|
|
nodes
|
|
|
|
.append("text")
|
|
|
|
.text((d) => d.data.id)
|
|
|
|
.attr("font-weight", "bold")
|
|
|
|
.attr("font-family", "sans-serif")
|
|
|
|
.attr("text-anchor", "middle")
|
|
|
|
.attr("alignment-baseline", "middle")
|
|
|
|
.attr("fill", "white");
|
|
|
|
})();
|
|
|
|
</script>
|
2022-01-12 14:42:07 +01:00
|
|
|
{% endblock %}
|