I want to create a simple d3 horizontal strait graph.
It would contain nodes, links and lines for each link.
How can I define correctly coordinates of nodes/ edges in the tick to achieve it (without manually setting the coordinates).
Do I need to do some pre calculation before?
I basically want the 1st node to appear on the far left and the last node in the chain (graph) (it's a striat line) to appear on the far right of the chain (graph).
How can I determine the order of the nodes in the graph to align them.
Update
here's the fiddle I reached so far:
here i just hard coded the y axis, which i want dynamically.
and my nodes are also not in order.
const nodes = [
{ id: 'A' },
{ id: 'B' },
{ id: 'C' },
];
const edges = [
{ source: 'A', target: 'B' },
{ source: 'B', target: 'C' },
];
const svg = d3.select("svg");
// Create arrowhead marker
svg.append('defs').append('marker')
.attr('id', 'arrowhead')
.attr('viewBox', '-0 -5 10 10')
.attr('refX', 15)
.attr('refY', 0)
.attr('orient', 'auto')
.attr('markerWidth', 10)
.attr('markerHeight', 10)
.attr('xoverflow', 'visible')
.append('svg:path')
.attr('d', 'M 0,-5 L 10 ,0 L 0,5')
.attr('fill', '#ccc')
.style('stroke','none');
const simulation = d3.forceSimulation(nodes)
.force("link", d3.forceLink(edges).id(d => d.id).distance(150))
.force("charge", d3.forceManyBody().strength(-100))
.force("y", d3.forceY(200))
.force("x", d3.forceX(200));
//.force("center", d3.forceCenter(400, 200));
const link = svg.selectAll(".link")
.data(edges)
.enter().append("line")
.attr("class", "link")
.attr('marker-end', 'url(#arrowhead)');
const node = svg.selectAll(".node")
.data(nodes)
.enter().append("circle")
.attr("class", "node")
.attr("r", 20);
simulation.on("tick", () => {
link.attr("x1", d => d.source.x)
.attr("y1", d => 20)
.attr("x2", d => d.target.x)
.attr("y2", d => 20);
node.attr("cx", d => d.x)
.attr("cy", d => 20);
});
https://jsfiddle.net/g87s23L5/3/
Ideally I'm looking for a way to use the built-in forces of d3
When you create simulation, I've adjusted the force("x", ...) to position the nodes based on their index. This will ensure that the nodes appear in the order from left to right.
This is the fixed code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>D3 Horizontal Straight Graph</title>
<script src="https://d3js.org/d3.v6.min.js"></script>
<style>
.node {
fill: #3498db;
stroke: #2980b9;
stroke-width: 2px;
cursor: pointer;
}
.link {
fill: none;
stroke: #ccc;
stroke-width: 2px;
cursor: pointer;
marker-end: url(#arrowhead); /* Add arrowhead to links */
.line {
stroke: black;
stroke-width: 2px;
}
}
</style>
</head>
<body>
<svg width="800" height="400"></svg>
<script>
const nodes = [
{ id: 'A' },
{ id: 'B' },
{ id: 'C' },
];
const edges = [
{ source: 'A', target: 'B' },
{ source: 'B', target: 'C' },
];
const svg = d3.select("svg");
// Create arrowhead marker
svg.append('defs').append('marker')
.attr('id', 'arrowhead')
.attr('viewBox', '-0 -5 10 10')
.attr('refX', 15)
.attr('refY', 0)
.attr('orient', 'auto')
.attr('markerWidth', 10)
.attr('markerHeight', 10)
.attr('xoverflow', 'visible')
.append('svg:path')
.attr('d', 'M 0,-5 L 10 ,0 L 0,5')
.attr('fill', '#ccc')
.style('stroke','none');
const simulation = d3.forceSimulation(nodes)
.force("link", d3.forceLink(edges).id(d => d.id).distance(150))
.force("charge", d3.forceManyBody().strength(-100))
.force("y", d3.forceY(200))
.force("x", d3.forceX((d, i) => i * 200)); // Position nodes based on index
const link = svg.selectAll(".link")
.data(edges)
.enter().append("line")
.attr("class", "link")
.attr('marker-end', 'url(#arrowhead)');
const node = svg.selectAll(".node")
.data(nodes)
.enter().append("circle")
.attr("class", "node")
.attr("r", 20);
simulation.on("tick", () => {
link.attr("x1", d => d.source.x)
.attr("y1", d => 50)// you can adjust y axis to make it viewable
.attr("x2", d => d.target.x)
.attr("y2", d => 50);// you can adjust y axis to make it viewable
node.attr("cx", d => d.x)
.attr("cy", d => 50);// you can adjust y axis to make it viewable
});
</script>
</body>
</html>
You can check on my fiddle here