0

I have a react application where I am trying to display a graph with nodes that have specified link distances. I have the following code below where the distance is specified with values for each link. The graph is displayed nicely but the link distances are not correct. For example, the node furthest away could have a distance that is smaller than a node closer to the center. Does anyone know why this would be happening? I'm a newbie to d3 so this is all very confusing. enter image description hereLink Distance

let FORCE = (function(nsp){

let that = this,
    width = 700,
    height = 500,
    color = d3.scaleOrdinal(d3.schemeCategory10),
    PointColors = ['#1769aa', 'magenta'],
    div = d3.select("body").append("div")
        .style("opacity", 0)
        .style("position", "absolute")
        .style("text-align", "center")
        .style("width",  "220px")
        .style("height",  "40px")
        .style("padding",  "2px")
        .style("font-family", "Roboto")
        .style("background", "#f5f5f5")
        .style("border", "0px")
        .style("border-radius", "8px")
        .style("pointer-events", "none"),



initForce = (nodes, links) => {
        nsp.force = d3.forceSimulation(nodes)
            .force("charge", d3.forceManyBody().strength(-500))
            .force("link", d3.forceLink(links).distance(function (d) { return (d.distance)*100;}))
            .force("center", d3.forceCenter().x(nsp.width /2).y(nsp.height / 2))
            .force("collide", d3.forceCollide([5]).iterations([5]));
    },


    enterNode = (selection) => {
        let circle = selection.select('circle')
            .attr("r", 10)
            .style("fill", '#1769aa' )
            .style("stroke", "#fff")
            .style("stroke-width", "2px")
            .on("mouseover", function(d){
                console.log(d)
                let matrix = this.getScreenCTM()
                    .translate(+ this.getAttribute("cx"), + this.getAttribute("cy"));
                PointColors = [PointColors[1], PointColors[0]]
                d3.select(this).style("fill", PointColors[0]);
                div.transition()
                   .duration(200)
                    .style("opacity", .9)
                div.html(d.name+'</br>Distance: '+d.distance)
                    .style("left", (window.pageXOffset + matrix.e + 15) + "px")
                    .style("top", (window.pageYOffset + matrix.f - 30) + "px")

                 })
            .on("mouseout", function(d) {
                PointColors = [PointColors[1], PointColors[0]]
                d3.select(this).style("fill", PointColors[0]);
                div.transition()
                    .duration(500)
                    .style("opacity", 0);
                })
            .on("click", function(d) {
                console.log(d);
                window.open(d.url, '_blank');
            });

            selection.select('text')
            .style("fill", "honeydew")
            .style("font-weight", "600")
            .style("text-anchor", "middle")
            .style("alignment-baseline", "middle")
            .style("font-size", "12px")
    },


    updateNode = (selection) => {
        selection
            .attr("transform", (d) => "translate(" + d.x + "," + d.y + ")")
            .attr("cx", function(d) { return d.x = Math.max(30, Math.min(width - 30, d.x)); })
            .attr("cy", function(d) { return d.y = Math.max(30, Math.min(height - 30, d.y)); })
    },


    enterLink = (selection) => {
        selection
            .attr("stroke-width", 2)
            .attr("stroke","#505050")
    },

    updateLink = (selection) => {
        selection
            .attr("x1", (d) => d.source.x)
            .attr("y1", (d) => d.source.y)
            .attr("x2", (d) => d.target.x)
            .attr("y2", (d) => d.target.y);
    },

    updateGraph = (selection) => {
        selection.selectAll('.node')
            .call(updateNode)
        selection.selectAll('.link')
            .call(updateLink);
    },

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

    dragging = (d) => {
        d.fx = d3.event.x;
        d.fy = d3.event.y
    },

    dragEnded = (d) => {
        if (!d3.event.active) nsp.force.alphaTarget(0);
        d.fx = null;
        d.fy = null
    },

    drag = () => d3.selectAll('g.node')
        .call(d3.drag()
            .on("start", dragStarted)
            .on("drag", dragging)
            .on("end", dragEnded)
        ),

    tick = (that) => {
        that.d3Graph = d3.select(ReactDOM.findDOMNode(that));
        nsp.force.on('tick', () => {
            that.d3Graph.call(updateGraph)
        });
    };

nsp.width = width;
nsp.height = height;
nsp.enterNode = enterNode;
nsp.updateNode = updateNode;
nsp.enterLink = enterLink;
nsp.updateLink = updateLink;
nsp.updateGraph = updateGraph;
nsp.initForce = initForce;
nsp.dragStarted = dragStarted;
nsp.dragging = dragging;
nsp.dragEnded = dragEnded;
nsp.drag = drag;
nsp.tick = tick;

return nsp

})(FORCE || {})

JDun
  • 189
  • 3
  • 13
  • You have many forces applied on the nodes, the forces are being balanced in a compromise that can result in link distortion. If you want to ensure that the distance is what is specified you can specify only a link force to start with, or phase out the other forces as the simulation progresses (see [this answer](https://stackoverflow.com/a/47084669/7106086), either approach can ensure proper length links (assuming its geometrically possible). – Andrew Reid Aug 20 '18 at 17:18
  • It would help to see a sample of the input data -- is the "distance" value part of your original dataset? – SteveR Aug 20 '18 at 17:21
  • Yes, @SteveR distance is part of my object that I have coming from database. Looks like this: nodes: [ {"name": "fruit", "id": 0, "url": "http://www.google.com"}, {"name": "apple", "id": 1, "url": "http://www.google.com"}, {"name": "orange", "id": 2, "url": "http://www.google.com"}, ], links: [ {"source": 0, "target": 1, "id": 0, "distance": .04567}, {"source": 0, "target": 2, "id": 1, "distance": .09345}, ] – JDun Aug 20 '18 at 18:08
  • Possible duplicate of [d3 force directed layout - link distance priority](https://stackoverflow.com/questions/38253560/d3-force-directed-layout-link-distance-priority) – Andrew Reid Aug 20 '18 at 18:31

0 Answers0