[英]D3 Force Layout where larger nodes cluster in center
我一直在修改将用于标签云的力布局,并且每个标签由<circle>
表示,其半径与该标签的问题成比例。 我的问题是确定如何使更受欢迎的标签趋向集群的中心,而使不那么受欢迎的标签聚集在标签云的边缘周围。 到目前为止,我的代码如下所示:
function(err, results) {
var nodes = results.tags;
var width = 1020;
var height = 800;
var extent = d3.extent(nodes, function(tag) { return tag.questions.length; });
var min = extent[0] || 1;
var max = extent[1];
var padding = 2;
var radius = d3.scale.linear()
.clamp(true)
.domain(extent)
.range([15, max * 5 / min]);
// attempted to make gravity proportional?
var gravity = d3.scale.pow().exponent(5)
.domain(extent)
.range([0, 200]);
var minRadius = radius.range()[0];
var maxRadius = radius.range()[1];
var svg = d3.select('#question_force_layout').append('svg')
.attr('width', width)
.attr('height', height);
var node = svg.selectAll('circle')
.data(nodes)
.enter().append('circle')
.attr('class', 'tag')
.attr('r', function(tag) { return (tag.radius = radius(tag.questions.length)); });
var tagForce = d3.layout.force()
.nodes(results.tags)
.size([width, height])
.charge(200) // seemed like an okay effect
.gravity(function(tag) {
return gravity(tag.questions.length);
})
.on('tick', tagTick)
.start();
function tagTick(e) {
node
.each(collide(.5))
.attr('cx', function(d) { return d.x; })
.attr('cy', function(d) { return d.y; });
}
function collide(alpha) {
var quadtree = d3.geom.quadtree(nodes);
return function(d) {
var r = d.radius + maxRadius + padding;
var nx1 = d.x - r;
var nx2 = d.x + r;
var ny1 = d.y - r;
var ny2 = d.y + r;
quadtree.visit(function(quad, x1, y1, x2, y2) {
if (quad.point && (quad.point !== d)) {
var x = d.x - quad.point.x;
var y = d.y - quad.point.y;
var l = Math.sqrt(x * x + y * y);
var r = d.radius + quad.point.radius + padding;
if (l < r) {
l = (l - r) / l * alpha;
d.x -= x *= l;
d.y -= y *= l;
quad.point.x += x;
quad.point.y += y;
}
}
return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1;
});
};
}
}
为了让您大致了解要处理的数据量,目前有52个问题和42个标签。 同样,输出通常以如下形式结束:
我希望较大的节点位于中心。
另一种可能性是给节点一些质量,并在碰撞函数中加以考虑。 然后,您可以打开重力,让它们为位置而战。
这个例子经过一阵忙之后就到达了。
这是修改后的碰撞函数...
function Collide(nodes, padding) {
// Resolve collisions between nodes.
var maxRadius = d3.max(nodes, function(d) {return d.radius});
return function collide(alpha) {
var quadtree = d3.geom.quadtree(nodes);
return function(d) {
var r = d.radius + maxRadius + padding,
nx1 = d.x - r,
nx2 = d.x + r,
ny1 = d.y - r,
ny2 = d.y + r;
quadtree.visit(function(quad, x1, y1, x2, y2) {
var possible = !(x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1);
if (quad.point && (quad.point !== d) && possible) {
var x = d.x - quad.point.x,
y = d.y - quad.point.y,
l = Math.sqrt(x * x + y * y),
r = d.radius + quad.point.radius + padding,
m = Math.pow(quad.point.radius, 3),
mq = Math.pow(d.radius, 3),
mT = m + mq;
if (l < r) {
//move the nodes away from each other along the radial (normal) vector
//taking relative mass into consideration, the sign is already established
//in calculating x and y and the nodes are modelled as spheres for calculating mass
l = (r - l) / l * alpha;
d.x += (x *= l) * m/mT;
d.y += (y *= l) * m/mT;
quad.point.x -= x * mq/mT;
quad.point.y -= y * mq/mT;
}
}
return !possible;
});
};
}
}
p.px
和p.py
)来完成。 使用r 3假设节点是球体来计算质量,并根据相对“质量”计算反弹。 function Collide(nodes, padding) {
// Resolve collisions between nodes.
var maxRadius = d3.max(nodes, function(d) {
return d.q.radius
});
return function collide(alpha) {
var quadtree = d3.geom.quadtree(nodes);
return function(d) {
var r = d.radius + maxRadius + padding,
nx1 = d.x - r,
nx2 = d.x + r,
ny1 = d.y - r,
ny2 = d.y + r;
quadtree.visit(function v(quad, x1, y1, x2, y2) {
var possible = !(x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1);
if(quad.point && (quad.point !== d) && possible) {
var x = d.x - quad.point.x,
y = d.y - quad.point.y,
l = Math.sqrt(x * x + y * y),
r = d.radius + quad.point.radius + padding;
if(l < r) {
for(; Math.abs(l) == 0;) {
x = Math.round(Math.random() * r);
y = Math.round(Math.random() * r);
l = Math.sqrt(x * x + y * y);
}
;
//move the nodes away from each other along the radial (normal) vector
//taking relative size into consideration, the sign is already established
//in calculating x and y
l = (r - l) / l * alpha;
// if the nodes are in the wrong radial order for there size, swap radius ordinate
var rel = d.radius / quad.point.radius, bigger = (rel > 1),
rad = d.r / quad.point.r, farther = rad > 1;
if(bigger && farther || !bigger && !farther) {
var d_r = d.r;
d.r = quad.point.r;
quad.point.r = d_r;
d_r = d.pr;
d.pr = quad.point.pr;
quad.point.pr = d_r;
}
// move nodes apart but preserve their velocity
d.x += (x *= l);
d.y += (y *= l);
d.px += x;
d.py += y;
quad.point.x -= x;
quad.point.y -= y;
quad.point.px -= x;
quad.point.py -= y;
}
}
return !possible;
});
};
}
}
这有点快,但看起来也更自然...
碰撞排序事件
交换节点时,将保留较大节点的速度,同时加快较小节点的速度。 因此,由于较小的节点从碰撞点抛出而提高了分类效率。 使用r 3假设节点是球体来计算质量,并根据相对“质量”计算反弹。
function Collide(nodes, padding) { // Resolve collisions between nodes. var maxRadius = d3.max(nodes, function(d) { return d.radius }); return function collide(alpha) { var quadtree = d3.geom.quadtree(nodes), hit = false; return function c(d) { var r = d.radius + maxRadius + padding, nx1 = dx - r, nx2 = dx + r, ny1 = dy - r, ny2 = dy + r; quadtree.visit(function v(quad, x1, y1, x2, y2) { var possible = !(x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1); if(quad.point && (quad.point !== d) && possible) { var x = dx - quad.point.x, y = dy - quad.point.y, l = (Math.sqrt(x * x + y * y)), r = (d.radius + quad.point.radius + padding), mq = Math.pow(quad.point.radius, 3), m = Math.pow(d.radius, 3); if(hit = (l < r)) { for(; Math.abs(l) == 0;) { x = Math.round(Math.random() * r); y = Math.round(Math.random() * r); l = Math.sqrt(x * x + y * y); } //move the nodes away from each other along the radial (normal) vector //taking relative size into consideration, the sign is already established //in calculating x and y l = (r - l) / l * (1 + alpha); // if the nodes are in the wrong radial order for there size, swap radius ordinate var rel = m / mq, bigger = rel > 1, rad = dr / quad.point.r, farther = rad > 1; if(bigger && farther || !bigger && !farther) { var d_r = dr; dr = quad.point.r; quad.point.r = d_r; d_r = d.pr; d.pr = quad.point.pr; quad.point.pr = d_r; } // move nodes apart but preserve the velocity of the biggest one // and accelerate the smaller one dx += (x *= l); dy += (y *= l); d.px += x * bigger || -alpha; d.py += y * bigger || -alpha; quad.point.x -= x; quad.point.y -= y; quad.point.px -= x * !bigger || -alpha; quad.point.py -= y * !bigger || -alpha; } } return !possible; }); }; } }
这是我为其添加的内容:
var x = width / 2;
var y = height / 2;
var ring = d3.scale.linear()
.clamp(true)
.domain([35, 80]) // range of radius
.range([Math.min(x, y) - 35, 0]);
// smallest radius attracted to edge (35 -> Math.min(x, y) - 35)
// largest radius attracted toward center (80 -> 0)
function tagTick(e) {
node
.each(gravity(.1 * e.alpha)) // added this line
.each(collide(.5))
.attr('cx', function(d) { return d.x; })
.attr('cy', function(d) { return d.y; });
}
function gravity(alpha) {
return function(d) {
var angle = Math.atan2(y - d.y, x - d.x); // angle from center
var rad = ring(d.radius); // radius of ring of attraction
// closest point on ring of attraction
var rx = x - Math.cos(angle) * rad;
var ry = y - Math.sin(angle) * rad;
// move towards point
d.x += (rx - d.x) * alpha;
d.y += (ry - d.y) * alpha;
};
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.