Current Dev State

This commit is contained in:
Tim Lorsbach
2025-06-23 20:13:54 +02:00
parent b4f9bb277d
commit ded50edaa2
22617 changed files with 4345095 additions and 174 deletions

175
static/js/pw.js Normal file
View File

@ -0,0 +1,175 @@
console.log("loaded")
// data = {{ pathway.d3_json | safe }};
// elem = 'vizdiv'
function draw(pathway, elem) {
const nodeRadius = 20;
const linkDistance = 100;
const chargeStrength = -200;
const depthSpacing = 150;
const width = document.getElementById(elem).offsetWidth, height = width * 0.75;
function assignPositions(nodes) {
const levelSpacing = 75; // vertical space between levels
const horizontalSpacing = 75; // horizontal space between nodes
const depthMap = new Map();
nodes.forEach(node => {
if (!depthMap.has(node.depth)) {
depthMap.set(node.depth, 0);
}
const nodesInLevel = nodes.filter(n => n.depth === node.depth).length;
node.fx = width / 2 + depthMap.get(node.depth) * horizontalSpacing - ((nodesInLevel - 1) * horizontalSpacing) / 2;
node.fy = node.depth * levelSpacing + 50;
depthMap.set(node.depth, depthMap.get(node.depth) + 1);
});
}
// Funktion für das Update der Positionen
function ticked() {
link.attr("x1", d => d.source.x)
.attr("y1", d => d.source.y)
.attr("x2", d => d.target.x)
.attr("y2", d => d.target.y);
node.attr("transform", d => `translate(${d.x},${d.y})`);
nodes.forEach(n => {
if (n.pseudo) {
// Alle Kinder dieses Pseudonodes finden
const childLinks = links.filter(l => l.source.id === n.id);
const childNodes = childLinks.map(l => l.target);
if (childNodes.length > 0) {
// Durchschnitt der Kinderpositionen berechnen
const avgX = d3.mean(childNodes, d => d.x);
const avgY = d3.mean(childNodes, d => d.y);
n.fx = avgX;
// keep level as is
n.fy = n.y;
}
}
});
//simulation.alpha(0.3).restart();
}
function dragstarted(event, d) {
if (!event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x; // Setzt die Fixierung auf die aktuelle Position
d.fy = d.y;
}
function dragged(event, d) {
d.fx = event.x; // Position direkt an Maus anpassen
d.fy = event.y;
}
function dragended(event, d) {
if (!event.active) simulation.alphaTarget(0);
// Knoten bleibt an der neuen Position und wird nicht zurückgezogen
}
// t -> ref to "this" from d3
function nodeClick(event, node, t) {
console.log(node);
d3.select(t).select("circle").classed("highlighted", !d3.select(t).select("circle").classed("highlighted"));
}
const tooltip = d3.select("#tooltip");
const zoomable = d3.select("#zoomable");
// Zoom Funktion aktivieren
const zoom = d3.zoom()
.scaleExtent([0.5, 5])
.on("zoom", (event) => {
zoomable.attr("transform", event.transform);
});
d3.select("svg").call(zoom);
nodes = pathway['nodes'];
links = pathway['links'];
// Use "max" depth for a neater viz
nodes.forEach(n => {
const parents = links
.filter(l => l.target === n.id)
.map(l => l.source);
orig_depth = n.depth
// console.log(n.id, parents)
for(idx in parents) {
p = nodes[parents[idx]]
// console.log(p.depth)
if(p.depth >= n.depth) {
// keep the .5 steps for pseudo nodes
n.depth = n.pseudo ? p.depth + 1 : Math.floor(p.depth + 1);
// console.log("Adjusting", orig_depth, Math.floor(p.depth + 1));
}
}
});
assignPositions(nodes);
const simulation = d3.forceSimulation(nodes)
.force("link", d3.forceLink(links).id(d => d.id).distance(linkDistance))
.force("charge", d3.forceManyBody().strength(chargeStrength))
.force("center", d3.forceCenter(width / 2, height / 4))
.on("tick", ticked);
// Kanten zeichnen
const link = zoomable.append("g")
.selectAll("line")
.data(links)
.enter().append("line")
// Check if target is pseudo and draw marker only if not pseudo
.attr("class", d => d.target.pseudo ? "link_no_arrow" : "link")
.on("mouseover", (event, d) => {
tooltip.style("visibility", "visible")
.text(`Link: ${d.source.id}${d.target.id}`)
.style("top", `${event.pageY + 5}px`)
.style("left", `${event.pageX + 5}px`);
})
.on("mouseout", () => tooltip.style("visibility", "hidden"));
// Knoten zeichnen
const node = zoomable.append("g")
.selectAll("g")
.data(nodes)
.enter().append("g")
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended))
.on("click", function(event, d) {
d3.select(this).select("circle").classed("highlighted", !d3.select(this).select("circle").classed("highlighted"));
})
.on("mouseover", (event, d) => {
if (d.pseudo) {
return
}
tooltip.style("visibility", "visible")
.text(`Node: ${d.id} Depth: ${d.depth}`)
.style("top", `${event.pageY + 5}px`)
.style("left", `${event.pageX + 5}px`);
})
.on("mouseout", () => tooltip.style("visibility", "hidden"));
// Kreise für die Knoten hinzufügen
node.append("circle")
// make radius "invisible"
.attr("r", d => d.pseudo ? 0.01 : nodeRadius)
.style("fill", "#e8e8e8");
// Add image only for non pseudo nodes
node.filter(d => !d.pseudo).append("image")
.attr("xlink:href", d => d.image)
.attr("x", -nodeRadius)
.attr("y", -nodeRadius)
.attr("width", nodeRadius * 2)
.attr("height", nodeRadius * 2);
}