как настроить параметры силы в ориентированном графе силы так, чтобы граф был устойчивым - PullRequest
1 голос
/ 13 июня 2019

Я пытаюсь создать форсированный граф, используя d3.js, но по мере увеличения числа узлов график становится крайне нестабильным и начинает вибрировать, а также выходит за пределы холста. Я пытаюсь получить все зависимые узлы под центральным узлом и зависимости над ним, чтобы это было визуально понятно

var colors = d3.scaleOrdinal(d3.schemeCategory10);

var svg = d3.select("svg"),
    width = +svg.attr("width"),
    height = +svg.attr("height"),
    node,
    link;



var simulation = d3.forceSimulation()
    .force("link", d3.forceLink().id(function (d) {return d.id;}).distance(400).strength(0))
    .force("collison",d3.forceCollide(200).strength(0.5).iterations(50))
    .force("center", d3.forceCenter(width/2 , height/2))
    .on("tick", ticked);





var graphDiv = document.getElementById("graph_view");


update(temp2.links,temp2.nodes);

function update(links, nodes) {


    svg.append('defs').append('marker')
    .attrs({'id':'arrowhead',
        'viewBox':'-0 -5 10 15',
        'refX':10,
        'refY':0,
        'orient':'auto',
        'markerWidth':15,
        'markerHeight':15,
        'xoverflow':'visible'})
    .append('polygon')
    .attr('points', '0,-5  10 ,0  0,5')
    .attr('fill', 'black')
    .style('stroke','none');

    var w = graphDiv.clientWidth;
    var h = graphDiv.clientHeight;
    svg
        .attr("width",2000)
        .attr("height",2000);


    node = svg.selectAll()
        .data(nodes)
        .enter()
        .append("g")
        .on("click",click)
        .on("contextmenu",onRightClick)
        .on("mouseover", node_hover)
        .call(d3.drag()                  // draggable function 
                .on("start", dragstarted)
                .on("drag", dragged) 
                //.on("end", dragended)
        );
    console.log("creating graph")
    node.append("rect")
        .attr("x",-150)
        .attr("width", 300)
        .attr("height",75)
        .attr("rx",10)
        .style("fill", "white")
        .style("stroke","black")

    node.append("title")
        .text(function (d) {return d.id;});

    node.append("text")
        .attr("font-size",10)
        .attr("align", "centre")
        .attr("font-family","Arial")
        .text(function (d) {console.log(d); return d.id;})
        .attr("text-anchor","middle")
        .attr("alignment-baseline","central")
        .attr("y", 37.5)
        .attr("x", 0)


    // console.log("------LINKS DATA---------");
    console.log(links);
    link = svg.selectAll()
        .data(links)
        .enter().append("path") 
        .attr("class", "link")
        .attr('marker-end','url(#arrowhead)')

    link.append("title")
        .text(function (d) {return d.type;});



    simulation
        .nodes(nodes)
        .on("tick", ticked);

    simulation.force("link")
        .links(links);
}

function ticked() {

    link.attr("d", function(d) {
            var x1 = d.source.x,
                y1 = d.source.y,
                x2 = d.target.x,
                y2 = d.target.y,
                dx = x2 - x1,
                dy = y2 - y1,
                dr = Math.sqrt(dx * dx + dy * dy),
                drx = dr,
                dry = dr,
                xRotation = 0, // degrees
                largeArc = 0, // 1 or 0
                sweep = 1; // 1 or 0

                // Self edge.
                if ( x1 === x2 && y1 === y2 ) {
                  // Fiddle with this angle to get loop oriented.
                  xRotation = 0;

                  // Needs to be 1.
                  largeArc = 1;

                  drx = 30;
                  dry = 50;
                  x2 = x2 + 10;
                  y2 = y2 + 1;

                  return "M" + x1 + ","+y1 +"A" +drx +","+dry+" "+xRotation +","+ largeArc+ "," + sweep +" "+ x2+","+y2;
                } 
                return "M" + x1 + "," + y1 + "L" +" " + x2 + "," + y2;

          });

    // 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; });

    node
    .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
    .attr("cx", function(d) { return d.x = Math.max(1000, Math.min(width - 1000, d.x)); })
    .attr("cy", function(d) { return d.y = Math.max(100*d.level+1000, Math.min(100*d.level-1000, d.y)); });



}
//Hover function
function node_hover(d)
{
    console.log("on hover");
    console.log(d.id);
    var tip = document.getElementById("node tooltip");
    // console.log(tip);
    tip.innerText = "edit node " + d.id;
    tip.style.visibility ="visible";


}
//new pop up window on Single click
function onRightClick(d){
    console.log("RIght click");
    if(d3.event.defaultPrevented) return;
    console.log(d.id);
    var content = document.getElementById("modal-content-data");
    content.innerText = "This is node " +d.id;
    var modal = document.getElementById("myModal");
    // Get the <span> element that closes the modal
    var span = document.getElementsByClassName("close")[0];
    modal.style.display = "block";

    span.onclick = function() {
        modal.style.display = "none";
    }

    if(force){
        console.log("Stopping force");
        force.stop();
    }
}
//Expand new node on Double Click
function click(d){
    if(d3.event.defaultPrevented) return;
    console.log(d);
    var t = graph_generator(d.id,1);
    console.log("dynamic node ")
    console.log(t);

    d3.selectAll("path").remove();
    d3.selectAll("g").remove();    
    update(t.links,t.nodes);


}

function dragstarted(d) {
    if (!d3.event.active) simulation.alphaTarget(0.3).restart()
    d.fx = d.x;
    d.fy = d.y;
}

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