Home > Enterprise >  D3.js various link distance
D3.js various link distance

Time:10-26

I am in need to implement a various link distance. I added a function call during the initialization of the force / simulation. Still, it looks like the settings are ignored. I want to add another node very close, that barely a hair would fit between. Is this even possible?

I noticed a similar question on stackoverflow but this requires another plugin, furthermore this question was created 4 years ago. Maybe vanilla d3.js already added the feature.

            var graph = {
                "nodes": [
                    {
                        "id": 0
                    },
                    {
                        "id": 1
                    },
                    {
                        "id": 2
                    },
                    {
                        "id": 3
                    }
                ],
                "links": [
                    {
                        "source": 0,
                        "target": 1,
                        "distance": 0.1
                    },
                    {
                        "source": 0,
                        "target": 2,
                        "distance": 150
                    },
                    {
                        "source": 0,
                        "target": 3,
                        "distance": 20
                    }
                ]
            }

            var svg = d3.select("svg")
                .attr("class", "canvas")
                .attr("width", window.innerWidth)
                .attr("height", window.innerHeight)
                .call(d3.zoom().on("zoom", function (event) {
                    svg.attr("transform", event.transform)
                }))
                .append("g")

            var linksContainer = svg.append("g").attr("class", linksContainer)
            var nodesContainer = svg.append("g").attr("class", nodesContainer)
            var sourceNode;

            var force = d3.forceSimulation()
                //.force("link", d3.forceLink().id(function (d) { return d.id }).distance(80))
                .force("link", d3.forceLink().distance(linkDistance).strength(0.1))
                .force("charge", d3.forceManyBody().strength(-100))
                .force("center", d3.forceCenter(window.innerWidth / 2, window.innerHeight / 2))
                .force("collision", d3.forceCollide().radius(90))

            function linkDistance(d) {
                console.log(d.distance)
                return d.distance;
            }


            initialize()

            function initialize() {

                link = linksContainer.selectAll(".link")
                    .data(graph.links)
                    .join("line")
                    .attr("class", "link")
                    .style("stroke", "black")
                    .style("stroke-width", 1)

                node = nodesContainer.selectAll(".node")
                    .data(graph.nodes, d => d.id)
                    .join("g")
                    .attr("class", "node")
                    .call(d3.drag()
                        .on("start", dragStarted)
                        .on("drag", dragged)
                        .on("end", dragEnded)
                    )
                    .on("click", addNode)

                node.selectAll("circle")
                    .data(d => [d])
                    .join("circle")
                    .attr("r", 30)
                    .style("fill", "whitesmoke")

                node.append("text")
                    .attr("dominant-baseline", "central")
                    .attr("text-anchor", "middle")
                    .attr("font-size", 15)
                    .attr("pointer-events", "none")
                    .text(function (d) {
                        return d.id
                    })

                force
                    .nodes(graph.nodes)
                    .on("tick", ticked);

                force
                    .force("link")
                    .links(graph.links)
            }

            function ticked() {
                // update link positions
                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;
                    });

                // update node positions
                node
                    .attr("transform", function (d) {
                        return "translate("   d.x   ", "   d.y   ")";
                    });
            }

            function dragStarted(event, d) {
                if (!event.active) force.alphaTarget(0.3).restart();
                d.fx = d.x;
                d.fy = d.y;

                PosX = d.x
                PosY = d.y
            }

            function dragged(event, d) {
                d.fx = event.x;
                d.fy = event.y;
            }

            function dragEnded(event, d) {
                if (!event.active) force.alphaTarget(0);
                d.fx = undefined;
                d.fy = undefined;
            }

            function addNode(event, d) {
                const nodeId = graph.nodes.length

                graph.nodes.push({id: nodeId})
                graph.links.push({source: d, target: nodeId, distance: 1})

                initialize()
            }
        body {
            height: 100%;
            background: #e6e7ee;
            overflow: hidden;
            margin: 0px;
        }

        .faded {][1]
            opacity: 0.1;
            transition: 0.3s opacity;
        }

        .highlight {
            opacity: 1;
        }
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <!-- d3.js framework -->
        <script src="https://d3js.org/d3.v6.js"></script>
        <!-- fontawesome stylesheet https://fontawesome.com/ -->
        <script src="https://kit.fontawesome.com/39094309d6.js" crossorigin="anonymous"></script>
    </head>

    <style>
    </style>

    <body>
        <svg id="svg"></svg>
    </body>
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

CodePudding user response:

The distance function is correctly set and specified.

Problem comes from the forceCollide, which is set to a radius of 90, and pushes the nodes apart regardless of specified distance.

Decreasing the radius of forceCollide fixes the problem, as illustrated below :)

var graph = {
                "nodes": [
                    {
                        "id": 0
                    },
                    {
                        "id": 1
                    },
                    {
                        "id": 2
                    },
                    {
                        "id": 3
                    }
                ],
                "links": [
                    {
                        "source": 0,
                        "target": 1,
                        "distance": 0.1
                    },
                    {
                        "source": 0,
                        "target": 2,
                        "distance": 150
                    },
                    {
                        "source": 0,
                        "target": 3,
                        "distance": 20
                    }
                ]
            }

            var svg = d3.select("svg")
                .attr("class", "canvas")
                .attr("width", window.innerWidth)
                .attr("height", window.innerHeight)
                .call(d3.zoom().on("zoom", function (event) {
                    svg.attr("transform", event.transform)
                }))
                .append("g")

            var linksContainer = svg.append("g").attr("class", linksContainer)
            var nodesContainer = svg.append("g").attr("class", nodesContainer)
            var sourceNode;

            var force = d3.forceSimulation()
                //.force("link", d3.forceLink().id(function (d) { return d.id }).distance(80))
                .force("link", d3.forceLink().distance(linkDistance).strength(.1))
                .force("charge", d3.forceManyBody().strength(-100))
                .force("center", d3.forceCenter(window.innerWidth / 2, window.innerHeight / 2))
                .force("collision", d3.forceCollide().radius(9))

            function linkDistance(d) {
                console.log('distance: ', d.distance)
                return d.distance;
            }


            initialize()

            function initialize() {

                link = linksContainer.selectAll(".link")
                    .data(graph.links)
                    .join("line")
                    .attr("class", "link")
                    .style("stroke", "black")
                    .style("stroke-width", 1)

                node = nodesContainer.selectAll(".node")
                    .data(graph.nodes, d => d.id)
                    .join("g")
                    .attr("class", "node")
                    .call(d3.drag()
                        .on("start", dragStarted)
                        .on("drag", dragged)
                        .on("end", dragEnded)
                    )
                    .on("click", addNode)

                node.selectAll("circle")
                    .data(d => [d])
                    .join("circle")
                    .attr("r", 30)
                    .style("fill", "whitesmoke")

                node.append("text")
                    .attr("dominant-baseline", "central")
                    .attr("text-anchor", "middle")
                    .attr("font-size", 15)
                    .attr("pointer-events", "none")
                    .text(function (d) {
                        return d.id
                    })

                force
                    .nodes(graph.nodes)
                    .on("tick", ticked);

                force
                    .force("link")
                    .links(graph.links)
            }

            function ticked() {
                // update link positions
                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;
                    });

                // update node positions
                node
                    .attr("transform", function (d) {
                        return "translate("   d.x   ", "   d.y   ")";
                    });
            }

            function dragStarted(event, d) {
                if (!event.active) force.alphaTarget(0.3).restart();
                d.fx = d.x;
                d.fy = d.y;

                PosX = d.x
                PosY = d.y
            }

            function dragged(event, d) {
                d.fx = event.x;
                d.fy = event.y;
            }

            function dragEnded(event, d) {
                if (!event.active) force.alphaTarget(0);
                d.fx = undefined;
                d.fy = undefined;
            }

            function addNode(event, d) {
                const nodeId = graph.nodes.length

                graph.nodes.push({id: nodeId})
                graph.links.push({source: d, target: nodeId, distance: 1})

                initialize()
            }
body {
    height: 100%;
    background: #e6e7ee;
    overflow: hidden;
    margin: 0px;
}

.faded {][1]
    opacity: 0.1;
    transition: 0.3s opacity;
}

.highlight {
    opacity: 1;
}
<!-- d3.js framework -->
<script src="https://d3js.org/d3.v6.js"></script>
<!-- fontawesome stylesheet https://fontawesome.com/ -->
<script src="https://kit.fontawesome.com/39094309d6.js" crossorigin="anonymous"></script>
<svg id="svg"></svg>
<iframe name="sif2" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

  • Related