D3力定向布局与边界框 [英] D3 force directed layout with bounding box

查看:210
本文介绍了D3力定向布局与边界框的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是新的D3,并有麻烦设置我的力导向布局的边界。我已经成功地(从例子)我想要的,但我需要包含图。在tick函数中,transform / translate将正确显示我的图形,但是当我使用cx和cy和Math.max / min(见注释代码)时,节点被固定到
左上角,



这是我在下面...我做错了什么?

  var w = 960,h = 500,r = 8,z = d3.scale.category20(); 

var color = d3.scale.category20();

var force = d3.layout.force()
.linkDistance(function(d){return(d.value * 180)})
.linkStrength ){return(1 /(1 + d.value))})
.charge(-1000)
//.gravity(.08)
.size([w,h] );

var vis = d3.select(#chart)。append(svg:svg)
.attr(width,w)
.attr height,h)
.append(svg:g)
.attr(transform,translate(+ w / 4 +,+ h / 3 +)) ;

vis.append(svg:rect)
.attr(width,w)
.attr(height,h)
.style (stroke,#000);


d3.json(miserables.json,function(json){

var link = vis.selectAll(line.link)
.data(json.links);

link.enter()。append(svg:line)
.attr(class,link)
.attr(x1,function(d){return d.source.x;})
.attr(y1,function(d){return d.source.y;})
.attr(x2,function(d){return d.source.x;})
.attr(y2,function(d){return d.source.y;})
.style(stroke-width,function(d){return(1 /(1 + d.value))* 5});

var node = vis.selectAll(节点)
.data(json.nodes);

var nodeEnter = node.enter()。append(svg:g)
.attr ,node)
.on(mouseover,fade(.1))
.on(mouseout,fade(1))
.call(force.drag)

nodeEnter.append(svg:circle)
.attr(r,r)
.style(fill,function(d){return z d.group); })
.style(stroke,function(d){return
d3.rgb(z(d.group))。darker();});

nodeEnter.append(svg:text)
.attr(text-anchor,middle)
.attr(dy,.35em )
.text(function(d){return d.name;});

force
.nodes(json.nodes)
.links(json.links)
.on(tick,tick)
.start ();

function tick(){

//这样做
node.attr(transform,function(d){returntranslate(+ dx + ,
+ dy +);});

//这包含边界内的行,但节点是
卡在左上角
//node.attr(\"cx,function(d) {return dx = Math.max(r,Math.min(w
-r,dx));})
// .attr(cy,function(d){return dy = Math .max(r,Math.min(h -
r,dy));});

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

var linkedByIndex = {};

json.links.forEach(function(d){
linkedByIndex [d.source.index +,+ d.target.index] = 1;
}) ;

function isConnected(a,b){
return linkedByIndex [a.index +,+ b.index] ||
linkedByIndex [b.index +,+ a.index] || a.index == b.index;
}

function fade(opacity){
return function(d){
node.style(stroke-opacity,function(o){
thisOpacity = isConnected(d,o)?1:opacity;
this.setAttribute('fill-opacity',thisOpacity);
return thisOpacity;
}

link.style(stroke-opacity,opacity).style(stroke-opacity,
function(o){
return o.source === d || o.target === d?1:opacity;
});
};
}

});


解决方案

有一个边框框示例,位于我的强制布局 。位置Verlet集成允许您定义几何约束(例如边界框和碰撞检测) tick事件侦听器;简单地移动节点以符合约束,并且模拟将相应地适应。

也就是说,重力绝对是一种更灵活的方式来处理这个问题,因为它允许用户将图形临时拖动到边界框外,然后图形将恢复。根据图形的大小和显示区域的大小,你应该尝试不同的重力和荷电(排斥力)的相对强度,使你的图形适合。


I am new to D3 and having trouble setting the bounds for my force directed layout. I have managed to piece together (from examples) what I would like, but I need the graph to be contained. In the tick function, a transform/translate will display my graph correctly, but when i use cx and cy with Math.max/min (See commented code), the nodes are pinned to the top left corner while the lines are contained properly.

Here is what I have below... what am I doing wrong??

var w=960, h=500, r=8,  z = d3.scale.category20();

var color = d3.scale.category20();

var force = d3.layout.force()
       .linkDistance( function(d) { return (d.value*180) } )
       .linkStrength( function(d) { return (1/(1+d.value)) } )
       .charge(-1000)
       //.gravity(.08)
       .size([w, h]);

var vis = d3.select("#chart").append("svg:svg")
       .attr("width", w)
       .attr("height", h)
       .append("svg:g")
       .attr("transform", "translate(" + w / 4 + "," + h / 3 + ")");

vis.append("svg:rect")
   .attr("width", w)
   .attr("height", h)
   .style("stroke", "#000");


d3.json("miserables.json", function(json) {

       var link = vis.selectAll("line.link")
               .data(json.links);

       link.enter().append("svg:line")
               .attr("class", "link")
               .attr("x1", function(d) { return d.source.x; })
               .attr("y1", function(d) { return d.source.y; })
               .attr("x2", function(d) { return d.source.x; })
               .attr("y2", function(d) { return d.source.y; })
               .style("stroke-width", function(d) { return (1/(1+d.value))*5 });

       var node = vis.selectAll("g.node")
               .data(json.nodes);

       var nodeEnter = node.enter().append("svg:g")
               .attr("class", "node")
               .on("mouseover", fade(.1))
               .on("mouseout", fade(1))
               .call(force.drag);

       nodeEnter.append("svg:circle")
               .attr("r", r)
               .style("fill", function(d) { return z(d.group); })
               .style("stroke", function(d) { return
d3.rgb(z(d.group)).darker(); });

       nodeEnter.append("svg:text")
               .attr("text-anchor", "middle")
               .attr("dy", ".35em")
               .text(function(d) { return d.name; });

       force
       .nodes(json.nodes)
       .links(json.links)
       .on("tick", tick)
       .start();

       function tick() {

       // This works
               node.attr("transform", function(d) { return "translate(" + d.x + ","
+ d.y + ")"; });

       // This contains the lines within the boundary, but the nodes are
stuck in the top left corner
               //node.attr("cx", function(d) { return d.x = Math.max(r, Math.min(w
- r, d.x)); })
               //      .attr("cy", function(d) { return d.y = Math.max(r, Math.min(h -
r, d.y)); });

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

       var linkedByIndex = {};

   json.links.forEach(function(d) {
       linkedByIndex[d.source.index + "," + d.target.index] = 1;
   });

       function isConnected(a, b) {
       return linkedByIndex[a.index + "," + b.index] ||
linkedByIndex[b.index + "," + a.index] || a.index == b.index;
   }

       function fade(opacity) {
       return function(d) {
           node.style("stroke-opacity", function(o) {
                       thisOpacity = isConnected(d, o) ? 1 : opacity;
                       this.setAttribute('fill-opacity', thisOpacity);
               return thisOpacity;
                       });

                       link.style("stroke-opacity", opacity).style("stroke-opacity",
function(o) {
               return o.source === d || o.target === d ? 1 : opacity;
               });
       };
       }

});

解决方案

There's a bounding box example in my talk on force layouts. The position Verlet integration allows you to define geometric constraints (such as bounding boxes and collision detection) inside the "tick" event listener; simply move the nodes to comply with the constraint and the simulation will adapt accordingly.

That said, gravity is definitely a more flexible way to deal with this problem, since it allows users to drag the graph outside the bounding box temporarily and then the graph will recover. Depend on the size of the graph and the size of the displayed area, you should experiment with different relative strengths of gravity and charge (repulsion) to get your graph to fit.

这篇关于D3力定向布局与边界框的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆