Spaces:
Running
Running
File size: 3,768 Bytes
c5ae2fb afe9b98 c5ae2fb afe9b98 16f8458 0546309 fb8bfbc c5ae2fb e1866e1 c5ae2fb 5baf49c 3142413 c5ae2fb e1866e1 c5ae2fb e1866e1 c5ae2fb e1866e1 c5ae2fb e1866e1 c5ae2fb e1866e1 c5ae2fb | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | // Graph Data - Reconstructed Structure
const graphData = {
nodes: [
{ id: "About", group: 1 },
{ id: "Notes", group: 2 },
{ id: "Projects", group: 2 },
{ id: "Audio", group: 2 },
{ id: "Visual", group: 3 }
],
links: [
{ source: "About", target: "Notes" },
{ source: "About", target: "Projects" },
{ source: "About", target: "Audio" },
{ source: "About", target: "Visual" }
],
};
// Setup Canvas
const container = document.getElementById('graph-container');
let width = container.clientWidth;
let height = container.clientHeight;
// Clear any existing SVG to prevent duplicates on reload
d3.select("#graph-container").selectAll("*").remove();
const svg = d3.select("#graph-container")
.append("svg")
.attr("width", "100%")
.attr("height", "100%")
.attr("viewBox", [0, 0, width, height])
.call(d3.zoom().on("zoom", (event) => {
g.attr("transform", event.transform);
}));
const g = svg.append("g");
// Simulation Setup
const simulation = d3.forceSimulation(graphData.nodes)
.force("link", d3.forceLink(graphData.links).id(d => d.id).distance(150))
.force("charge", d3.forceManyBody().strength(-400))
.force("center", d3.forceCenter(width / 2, height / 2))
.force("collide", d3.forceCollide().radius(30));
// Render Links
const link = g.append("g")
.attr("class", "links")
.selectAll("line")
.data(graphData.links)
.join("line")
.attr("stroke-width", 2)
// CSS variables are handled by style.css targeting 'line'
// but we can add specific classes if needed
.attr("class", "graph-link");
// Render Nodes
const node = g.append("g")
.attr("class", "nodes")
.selectAll("g")
.data(graphData.nodes)
.join("g")
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
// Node Circles
node.append("circle")
.attr("r", 8)
.attr("class", "graph-node");
// Node Labels
node.append("text")
.attr("x", 12)
.attr("y", "0.31em")
.text(d => d.id)
.attr("class", "graph-text")
.clone(true).lower()
.attr("fill", "none")
.attr("stroke", "var(--bg-primary)")
.attr("stroke-width", 3);
// Simulation Tick
simulation.on("tick", () => {
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})`);
});
// Drag Interaction Functions
function dragstarted(event, d) {
if (!event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(event, d) {
d.fx = event.x;
d.fy = event.y;
}
function dragended(event, d) {
if (!event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
// Handle Window Resize
window.addEventListener('resize', () => {
width = container.clientWidth;
height = container.clientHeight;
simulation.force("center", d3.forceCenter(width / 2, height / 2));
simulation.alpha(0.3).restart();
});
// Populate Sidebar (Optional integration)
const nodeList = document.getElementById('node-list');
if (nodeList) {
nodeList.innerHTML = ''; // Clear existing
graphData.nodes.forEach(node => {
const item = document.createElement('div');
item.style.padding = '0.5rem';
item.style.cursor = 'pointer';
item.style.borderBottom = '1px solid var(--border-color)';
item.textContent = node.id;
item.addEventListener('click', () => {
// Optional: Zoom to node
console.log('Focus on:', node.id);
});
nodeList.appendChild(item);
});
} |