Я пытаюсь создать форсированный граф, используя 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;
}