我一直在修补将用于标签云的力布局,每个标签都由<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个标签。输出通常也是这样的:
我希望更大的节点最终位于中心。
答案 0 :(得分:1)
另一种可能性是给节点类似质量的东西并在碰撞函数中考虑它。然后你就可以开启重力,让它们争夺位置 经过一阵激动,This example到达那里。
这是修改过的碰撞函数......
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 = 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),
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 = 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 the velocity of the biggest one
// and accelerate the smaller one
d.x += (x *= l);
d.y += (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;
});
};
}
}
答案 1 :(得分:0)
这是我为了让它发挥作用而添加的内容:
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;
};
}