I want to visualize the "children" insight each node. I guess the D3v6 .join() function can be nested. Unfortunately I can´t find any example. The snippet below contains an outerGraph with 3 nodes and children as attribute. So far those children aren´t used yet.
The innerGraph instead visualize the small nodes which will be obsolete as soon as the children approach is working. Another Idea would be to work with those two graphs and create a gravity / cluster, which will be the parent.
Goal: Either utilize the children attribute or combine both graphs with the help of an cluster /gravity or even nested join(). I am appreciating any hint / tip. The visuals result should be:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>D3v6 nested nodes</title>
<script src="https://d3js.org/d3.v6.min.js"></script>
</head>
<style>
body {
background-color: whitesmoke;
}
</style>
<body>
<script>
var svg = d3.select("body").append("svg")
.attr("width", window.innerWidth)
.attr("height", window.innerHeight);
var width = window.innerWidth
var height = window.innerHeight
var outerLinkContainer = svg.append("g").attr("class", "outerLinkContainer")
var outerNodeContainer = svg.append("g").attr("class", "outerNodeContainer")
var innerLinkContainer = svg.append("g").attr("class", "innerLinkContainer")
var innerNodeContainer = svg.append("g").attr("class", "innerNodeContainer")
//###############################################
//############# outer force Layouts #############
//###############################################
var outerGraph = {
"nodes": [
{
"id": "A",
"children": [
{
"id": "A1"
},
{
"id": "A2"
}
]
},
{
"id": "B",
"children": [
{
"id": "B1"
},
{
"id": "B2"
}
]
},
{
"id": "C",
"children": [
{
"id": "C1"
},
{
"id": "C2"
}
]
}
],
"links": [
{
"source": "A",
"target": "B"
},
{
"source": "B",
"target": "C"
},
{
"source": "C",
"target": "A"
},
]
}
var outerLayout = d3.forceSimulation()
.force("center", d3.forceCenter(width / 2, height / 2))
.force("charge", d3.forceManyBody().strength(-500))
.force("link", d3.forceLink().id(function (d) {
return d.id;
}).distance(200))
var outerLinks = outerLinkContainer.selectAll(".link")
.data(outerGraph.links)
.join("line")
.attr("class", "link")
.style("stroke", "black")
.style("opacity", 0.2)
var outerNodes = outerNodeContainer.selectAll("g.outer")
.data(outerGraph.nodes, function (d) { return d.id; })
.join("circle")
.attr("class", "outer")
.style("fill", "pink")
.style("stroke", "blue")
.attr("r", 40)
.call(d3.drag()
.on("start", dragStarted)
.on("drag", dragged)
.on("end", dragEnded)
)
outerLayout
.nodes(outerGraph.nodes)
.on("tick", ticked)
outerLayout
.force("link")
.links(outerGraph.links)
//###############################################
//############## inner force Layout #############
//###############################################
var innerGraph = {
"nodes": [
{ "id": "A1" },
{ "id": "A2" }
],
"links": [
{
"source": "A1",
"target": "A2"
}
]
}
var innerlayout = d3.forceSimulation()
.force("center", d3.forceCenter(width / 2, height / 2))
.force("charge", d3.forceManyBody().strength(-500))
.force("link", d3.forceLink().id(function (d) {
return d.id;
}).distance(200))
var innerLinks = innerLinkContainer.selectAll(".link")
.data(innerGraph.links)
.join("line")
.attr("class", "link")
.style("stroke", "black")
var innerNodes = innerNodeContainer.selectAll("g.inner")
.data(innerGraph.nodes, function (d) { return d.id; })
.join("circle")
.style("fill", "orange")
.style("stroke", "blue")
.attr("r", 6)
.attr("class", "inner")
.attr("id", function (d) { return d.id; })
.call(d3.drag()
.on("start", dragStarted)
.on("drag", dragged)
.on("end", dragEnded)
)
innerlayout
.nodes(innerGraph.nodes)
.on("tick", ticked)
innerlayout
.force("link")
.links(innerGraph.links)
//###############################################
//################## functons ###################
//###############################################
function ticked() {
outerLinks
.attr("x1", function (d) {
return d.source.x;
})
.attr("y1", function (d) {
return d.source.y;
})
.attr("x2", function (d) {
return d.target.x;
})
.attr("y2", function (d) {
return d.target.y;
});
innerLinks
.attr("x1", function (d) {
return d.source.x;
})
.attr("y1", function (d) {
return d.source.y;
})
.attr("x2", function (d) {
return d.target.x;
})
.attr("y2", function (d) {
return d.target.y;
});
outerNodes.attr("transform", function (d) {
return "translate(" + d.x + "," + d.y + ")";
});
innerNodes.attr("transform", function (d) {
return "translate(" + (d.x) + "," + (d.y) + ")";
});
}
function dragStarted(event, d) {
if (!event.active)
outerLayout.alphaTarget(0.3).restart();
innerlayout.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)
outerLayout.alphaTarget(0)
innerlayout.alphaTarget(0)
d.fx = undefined;
d.fy = undefined;
}
</script>
</body>
</html>
I will update the post as soon as I found a solution.