'D3 nested links out of position
The D3v3 graph below contains multiple forces, to be able to place nodes inside other nodes. Now I struggle to position the links for the orange nodes proper. The links exist but fall out of position completely. In case you cant see it, full screen should do the job.
I assume I need to add an unknown value a certain value to the innerAlayout.on("tick", function() {}) for the innerAlinks. if a D3 expert could help me out I would be gratefull.
var width = window.innerWidth,
height = window.innerHeight;
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.call(d3.behavior.zoom().on("zoom", function() {
svg.attr("transform", "translate(" + d3.event.translate + ")" + "scale(" + d3.event.scale + ")")
}))
.append("g")
////////////////////////
// outer force layout
var outerData = {
"nodes": [
{ "id": "A" },
{ "id": "B" },
{ "id": "C" }
],
"links": [
{ "source": 0, "target": 1},
{ "source": 1, "target": 2},
{ "source": 2, "target": 0}
]
};
var outerLayout = d3.layout.force()
.size([width, height])
.charge(-12000)
.gravity(0.85)
.distance(500)
.links(outerData.links)
.nodes(outerData.nodes)
.start();
var outerLinks = svg.selectAll("g.outerLink")
.data(outerData.links)
.enter().append("line")
.attr("class", "g.outerLink")
.attr("stroke", "black")
.attr("stroke-widht", 3)
var outerNodes = svg.selectAll("g.outer")
.data(outerData.nodes, function (d) { return d.id; })
.enter()
.append("g")
.attr("class", "outer")
.attr("id", function (d) { return d.id; })
.call(outerLayout.drag());
outerNodes
.append("circle")
.style("fill", "lightgrey")
.style("stroke", "blue")
.attr("r", 80);
outerLayout
.on("tick", function() {
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;
});
outerNodes
.attr("transform", function (d) { return "translate(" + d.x + "," + d.y + ")"; });
})
////////////////////////
// inner force layouts
var innerAdata = {
"nodes": [
{ "id": "A1" },
{ "id": "A2" },
{ "id": "A3" },
],
"links": [
{ "source": 0, "target": 1},
{ "source": 1, "target": 2},
{ "source": 2, "target": 0},
]
};
var innerAlayout = d3.layout.force()
.size([40, 40])
.charge(-2500)
.gravity(0.75)
.links(innerAdata.links)
.nodes(innerAdata.nodes)
.start();
var innerAlinks = svg.selectAll("g.innerLink")
.data(innerAdata.links)
.enter().append("line")
.attr("class", "g.innerLink")
.attr("stroke", "black")
.attr("stroke-width", 5)
var aNode = svg.select("g.outer#A");
var innerAnodes = aNode.selectAll("g.inner")
.data(innerAdata.nodes, function (d) { return d.id; })
.enter()
.append("g")
.attr("class", "inner")
.attr("id", function (d) { return d.id; })
.call(innerAlayout.drag()
.on("dragstart", function () {
d3.event.sourceEvent.stopPropagation();
})
);
innerAnodes
.append("circle")
.style("fill", "orange")
.style("stroke", "blue")
.attr("r", 25);
innerAlayout
.on("tick", function() {
// needs to be re-positioned
innerAlinks.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;
});
innerAnodes
.attr("transform", function (d) { return "translate(" + (d.x - 20) + "," + (d.y - 20) + ")"; });
})
///////
var innerBdata = [{ "id": "B1" }, { "id": "B2" }];
var innerBlayout = d3.layout.force()
.size([40, 40])
.charge(-2500)
.gravity(0.75)
.links([])
.nodes(innerBdata)
.on("tick", innerBtick)
.start();
var bNode = svg.select("g.outer#B");
var innerBnodes = bNode.selectAll("g.inner")
.data(innerBdata, function (d) { return d.id; })
.enter()
.append("g")
.attr("class", "inner")
.attr("id", function (d) { return d.id; })
.call(innerBlayout.drag()
.on("dragstart", function () {
d3.event.sourceEvent.stopPropagation();
})
);
innerBnodes
.append("circle")
.style("fill", "orange")
.style("stroke", "blue")
.attr("r", 25);
////////////////////////
// inner force child layouts
var innerChildAdata = {
"nodes": [
{ "id": "AC1" },
{ "id": "AC2" }
],
"links": [
{"source": "AC1", "target": "AC2"}
]};
var innerChildAlayout = d3.layout.force()
.size([40, 40])
.charge(-100)
.gravity(0.45)
//.distance(10)
//.links(innerChildAdata.links)
.nodes(innerChildAdata.nodes)
.on("tick", innerChildAtick)
.start();
var link = svg.selectAll(".link")
.data(innerChildAdata.links)
.enter().append("line")
.attr("stroke", "black")
var aChildNode = svg.select("g.inner#A1");
var innerChildAnodes = aChildNode.selectAll("g.inner")
.data(innerChildAdata.nodes, function (d) { return d.id; })
.enter()
.append("g")
.attr("class", "inner")
.attr("id", function (d) { return d.id; })
.call(innerChildAlayout.drag()
.on("dragstart", function () {
d3.event.sourceEvent.stopPropagation();
})
);
innerChildAnodes
.append("circle")
.style("fill", "white")
.style("stroke", "blue")
.attr("r", 7);
///////
////////////////////////
// functions
function innerBtick(e) { innerBnodes.attr("transform", function (d) { return "translate(" + (d.x - 20) + "," + (d.y - 20) + ")"; }); }
function innerChildAtick(e) {
/*
link.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;
});
*/
innerChildAnodes.attr("transform", function (d) {
return "translate(" + (d.x - 20) + "," + (d.y - 20) + ")"; });
}
body {
background: whitesmoke,´;
overflow: hidden;
margin: 0px;
}
.link {
stroke: black;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>D3v3 Multiple forces</title>
<!-- d3.js framework -->
<script src="https://d3js.org/d3.v3.js"></script>
</head>
</html>
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
