'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