Skip to content

Commit 47a9c96

Browse files
Merge pull request #169 from amosproj/feat/better-graph-representation
Feat/better graph representation
2 parents 7c50cc1 + 93501cd commit 47a9c96

File tree

9 files changed

+1093
-136
lines changed

9 files changed

+1093
-136
lines changed
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
import React, { useEffect, useRef } from 'react';
2+
import * as d3 from 'd3';
3+
4+
const D3Graph = ({ graphData, layout }) => {
5+
const containerRef = useRef(null);
6+
7+
useEffect(() => {
8+
if (!graphData) return;
9+
10+
// Set up SVG dimensions
11+
const width = window.innerWidth;
12+
const height = window.innerHeight - 50;
13+
14+
// Clear previous graph
15+
d3.select(containerRef.current).select('svg').remove();
16+
17+
// Create SVG element
18+
const svg = d3.select(containerRef.current)
19+
.append('svg')
20+
.attr('width', width)
21+
.attr('height', height)
22+
.call(d3.zoom().on('zoom', (event) => {
23+
svg.attr('transform', event.transform);
24+
}))
25+
.append('g');
26+
27+
// Set up the simulation
28+
const simulation = d3.forceSimulation(graphData.nodes)
29+
.force('link', d3.forceLink(graphData.edges).id(d => d.id).distance(100))
30+
.force('charge', d3.forceManyBody().strength(-300))
31+
.force('center', d3.forceCenter(width / 2, height / 2));
32+
33+
// Apply different layout algorithms
34+
if (layout === 'hierarchical') {
35+
simulation.force('y', d3.forceY().strength(0.1));
36+
simulation.force('x', d3.forceX().strength(0.1));
37+
}
38+
39+
// Create links
40+
const link = svg.append('g')
41+
.attr('class', 'links')
42+
.selectAll('line')
43+
.data(graphData.edges)
44+
.enter()
45+
.append('line')
46+
.attr('stroke-width', 2)
47+
.attr('stroke', '#fff');
48+
49+
// Create link labels
50+
const linkLabels = svg.append('g')
51+
.attr('class', 'link-labels')
52+
.selectAll('text')
53+
.data(graphData.edges)
54+
.enter()
55+
.append('text')
56+
.attr('class', 'link-label')
57+
.attr('dx', 15)
58+
.attr('dy', '.35em')
59+
.text(d => d.label) // Correctly reading the label property for edges
60+
.attr('fill', '#fff');
61+
62+
// Create nodes
63+
const node = svg.append('g')
64+
.attr('class', 'nodes')
65+
.selectAll('circle')
66+
.data(graphData.nodes)
67+
.enter()
68+
.append('circle')
69+
.attr('r', 25)
70+
.attr('fill', '#69b3a2')
71+
.attr('stroke', '#508e7f')
72+
.call(d3.drag()
73+
.on('start', dragstarted)
74+
.on('drag', dragged)
75+
.on('end', dragended));
76+
77+
// Node labels
78+
const nodeLabels = svg.append('g')
79+
.attr('class', 'node-labels')
80+
.selectAll('text')
81+
.data(graphData.nodes)
82+
.enter()
83+
.append('text')
84+
.attr('class', 'node-label')
85+
.attr('dx', 15)
86+
.attr('dy', '.35em')
87+
.text(d => d.label) // Correctly reading the label property for nodes
88+
.attr('fill', '#fff');
89+
90+
// Update simulation
91+
simulation
92+
.nodes(graphData.nodes)
93+
.on('tick', ticked);
94+
95+
simulation.force('link')
96+
.links(graphData.edges);
97+
98+
function ticked() {
99+
link
100+
.attr('x1', d => d.source.x)
101+
.attr('y1', d => d.source.y)
102+
.attr('x2', d => d.target.x)
103+
.attr('y2', d => d.target.y);
104+
105+
node
106+
.attr('cx', d => d.x)
107+
.attr('cy', d => d.y);
108+
109+
nodeLabels
110+
.attr('x', d => d.x)
111+
.attr('y', d => d.y);
112+
113+
linkLabels
114+
.attr('x', d => (d.source.x + d.target.x) / 2)
115+
.attr('y', d => (d.source.y + d.target.y) / 2);
116+
}
117+
118+
function dragstarted(event, d) {
119+
if (!event.active) simulation.alphaTarget(0.3).restart();
120+
d.fx = d.x;
121+
d.fy = d.y;
122+
}
123+
124+
function dragged(event, d) {
125+
d.fx = event.x;
126+
d.fy = event.y;
127+
}
128+
129+
function dragended(event, d) {
130+
if (!event.active) simulation.alphaTarget(0);
131+
d.fx = null;
132+
d.fy = null;
133+
}
134+
135+
// Stabilize nodes after a certain time
136+
setTimeout(() => {
137+
simulation.alphaTarget(0).restart();
138+
}, 5000); // 5 seconds stabilization time
139+
140+
}, [graphData, layout]);
141+
142+
return <div ref={containerRef} style={{ width: '100vw', height: 'calc(100vh - 50px)', background: '#1A2130' }} />;
143+
};
144+
145+
export default D3Graph;

0 commit comments

Comments
 (0)