平静一下力量布局的初始勾号

我刚刚开始涉足d3,发现学习曲线陡峭。 这个过程与我习惯的过程完全不同,math大部分都在我头上。

无论如何,我的项目由代表系统之间的集成图的力布局组成。 这部分工作得非常好,但我确实有一个主要的关注点,这也是在Michael Bostocks网站上的强制定向布局演示中所表示的:当节点启动时,它们似乎被渲染成canvas。 在此之后,一些严重的物理math正在接pipe,模拟引力拉动,使节点在一个相当混乱的path上前后来回,直到它们冷静下来,并在随机坐标上定位。 尽pipe这些动作在演示第一次运行的时候就酷了,但是当你试图从公司的networking接口看pipe理员的angular度,服务器不会停下来的时候,一段时间后会变得很烦人。

我相信我有这个项目的正确布局设置,因为我确实希望服务器自动布局,我想要看到它们之间的链接。 然而,我对引力效应感到矛盾。

我在想; 是否可以手动设置每个节点的初始位置,以便我可以将它们靠近引力中心并缩短“跳动时间”一点?

以上所有的答案误解了ØysteinAmundsen的问题。

稳定开始的力量的唯一方法是设置node.x和node.ya适当的值。 请注意,该节点是d3.js的数据,而不是表示的DOMtypes。

例如,如果你加载

nodes = [{"id": a}, {"id": b}, {"id": c}] 

 d3.layout.force().nodes(nodes) 

你必须设置节点数组中的所有元素的所有.x和.y它将是这样的(在coffeescript中)

 nodes = [{"id": a}, {"id": b}, {"id": c}] for i in [0..2] nodes[i].x = 500 #the initial x position of node nodes[i].y = 300 #the initial y position of node d3.layout.force().nodes(nodes).links(links) 

那么节点将在force.start()的位置开始。 这将避免混乱。

在内部,在“正常”使用情况下,强制布局通过asynchronous方式(通过setIntervalrequestAnimationFrame )重复调用自己的tick()方法,直到布局达成解决scheme。 此时,其alpha()值等于或接近0。

因此,如果您想要通过此解决schemestream程“快速前进”,则可以反复同步调用该tick()方法,直到布局的alpha值达到一个值,根据您的特定要求,该值构成“足够接近”的解决scheme。 像这样:

 var force = d3.layout.force(), safety = 0; while(force.alpha() > 0.05) { // You'll want to try out different, "small" values for this force.tick(); if(safety++ > 500) { break;// Avoids infinite looping in case this solution was a bad idea } } if(safety < 500) { console.log('success??'); } 

运行此代码后,可以根据节点的状态绘制布局。 或者,如果您通过绑定到tick事件(即force.on('tick', drawMyLayout) )来绘制布局,则代码运行您将需要执行绑定,否则您将不必要地渲染在while循环期间同步布局数百次。

JohnS将这种方法简化为一个简洁的函数。 看到他的答案在这个页面上的某个地方。

我刚刚处理了一些这样的事情。 有几件事情要考虑。

1)迭代滴答是模拟一个达到平衡的系统。 所以在系统稳定之前,没有办法避免多次调用tick,而且你有自动布局。 也就是说,你不需要更新你的可视化模拟每一个滴答作品! 事实上,如果你不这样做,迭代速度会快很多。 我的代码的相关部分是:

 var iters = 600; // You can get decent results from 300 if you are pressed for time var thresh = 0.001; if(!hasCachedLayout || optionsChanged || minorOptionsChanged) { force.start(); // Defaults to alpha = 0.1 if(hasCachedLayout) { force.alpha(optionsChanged ? 0.1 : 0.01); } for (var i = iters; i > 0; --i) { force.tick(); if(force.alpha() < thresh) { //console.log("Reached " + force.alpha() + " for " + data.nodes.length + " node chart after " + (iters - i) + " ticks."); break; } } force.stop(); } 

这是同步运行,运行后,我为所有节点和链接创builddom元素。 对于小图,运行速度非常快,但是您会发现大图(100多个节点)有延迟 – 它们在计算上花费更大。

2)你可以caching/种子布局。 如果没有设置位置 ,强制布局将在初始化统一分配节点 所以如果你确定你的节点已经设置了x和y属性,这些将被使用。 特别是当我更新一个现有的图表时,我将重新使用以前的布局中的x和y位置。

你会发现一个好的初始布局,你将需要less得多的迭代来达到稳定的configuration。 (这是CachedLayout在上面的代码中跟踪的内容)。 注意:如果你重新使用相同的节点形成相同的布局,那么你也必须确保将px和py设置为NaN,否则会得到奇怪的结果。

基于其他的答案,我已经做了这个方法:

 function forwardAlpha(layout, alpha, max) { alpha = alpha || 0; max = max || 1000; var i = 0; while(layout.alpha() > alpha && i++ < max) layout.tick(); } 

也许force.friction(0.5) ,或者其他一些低于默认值0.9的数字,会有帮助吗? 至less它在页面加载时不会产生混乱的印象。

  var width = 960, height = 500; var fill = d3.scale.category20(); var force = d3.layout.force() .size([width, height]) .nodes([{}]) // initialize with a single node .linkDistance(30) .charge(-60) .on("tick", tick); var svg = d3.select("body").append("svg") .attr("width", width) .attr("height", height) .on("mousedown", mousedown); svg.append("rect") .attr("width", width) .attr("height", height); var nodes = force.nodes(), links = force.links(), node = svg.selectAll(".node"), link = svg.selectAll(".link"); // var cursor = svg.append("circle") // .attr("r", 30) // .attr("transform", "translate(-100,-100)") // .attr("class", "cursor"); restart(); function mousedown() { var point = d3.mouse(this), node = { x: width / 2, y: height / 2, "number": Math.floor(Math.random() * 100) }, n = nodes.push(node); // add links to any nearby nodes /* nodes.forEach(function(target) { var x = target.x - node.x, y = target.y - node.y; if (Math.sqrt(x * x + y * y) < 30) { links.push({source: node, target: target}); } }); */ restart(); } function tick() { 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(" + dx + "," + dy + ")"; }); } function restart() { link = link.data(links); link.enter().insert("line", ".node") .attr("class", "link"); node = node.data(nodes); // node.enter().insert("circle", ".cursor") // .attr("class", "node") // .attr("r", 5) // .call(force.drag); var nodeEnter = node.enter().insert("svg:g", ".cursor") .attr("class", "node") .call(force.drag); nodeEnter.append("svg:circle") .attr("r", 5) nodeEnter.append("svg:text") .attr("class", "textClass") .attr("x", 14) .attr("y", ".31em") .text(function(d) { return d.number; }); force.start(); } 
  rect { fill: none; pointer-events: all; } .node { fill: #000; } .cursor { fill: none; stroke: brown; pointer-events: none; } .link { stroke: #999; } .textClass { stroke: #323232; font-family: "Lucida Grande", "Droid Sans", Arial, Helvetica, sans-serif; font-weight: normal; stroke-width: .5; font-size: 14px; } 
 <script src="ajax/libs/d3/3.4.11/d3.min.js"></script>