是否有一个漂浮在碰撞检测周围的例子,通过操纵半径而不是x,y坐标来避免碰撞?我知道Mike Bostock和其他人组合在一起的例子,但我没有使用力图,我的点是地理的,不能操纵它们的坐标。
我最好的猜测是从半径为0的圆开始,迭代它们并增加它们各自的半径,只要它们不与另一个圆相撞。我认为这会产生一种奇妙的可视化效果,但我不确定如何有效地确定一个圆是否与另一个圆相撞。
带有内联D3js的地图的JSBin(JavaScript选项卡只是一个600kb的GeoJSON数据集):http://jsbin.com/tapuhefamu/1/edit?html,output
注意缩放时标记是如何重叠的,它在小提琴中看起来不是什么大问题(只是进一步放大,对吧?)但是我正在使用的地图只有少数几个群集了~2,000个针脚单击时需要显示信息DIV。由于重叠,某些引脚几乎完全被遮挡,无法与之交互。
答案 0 :(得分:2)
我为你编写了一些东西。检测碰撞非常容易,基本上计算两个中心点之间的距离,如果距离小于加在一起的两个半径,那么它们必然会发生碰撞。
我遇到了jsbin的一些问题,所以我把它变成了一个要点,你可以在http://bl.ocks.org/benlyall/6a81499abf7a0e2ad304查看
有趣的是:
添加radiusStep
参数 - 使用此参数来平衡迭代次数与节点之间潜在重叠量之间的权衡。
radiusStep = 0.01,
从缩放处理程序中删除半径缩放:
zoom = d3.behavior.zoom().on("zoom",function() {
g.attr("transform","translate("+ d3.event.translate.join(",")+")scale("+d3.event.scale+")");
//g.selectAll("circle")
//.attr("r", nodeRadius / d3.event.scale);
g.selectAll("path")
.style('stroke-width', countyBorderWidth / d3.event.scale )
.attr("d", path.projection(projection));
}),
创建一个新结构,以跟踪节点是否与另一个节点相撞,半径以及x和y位置(预先根据您的投影计算)
nodes = nodeGeoData.map(function(n) {
var pos = projection(n);
return {
collided: false,
x: pos[0],
y: pos[1],
r: 0
};
});
两个新功能,用于检测碰撞并扩展半径,直到检测到碰撞。
function tick() {
nodes.forEach(collided);
nodes.forEach(function(n) {
if (!n.collided) {
n.r += radiusStep;
if (n.r > nodeRadius) {
n.r = nodeRadius;
n.collided = true;
}
}
});
}
此tick
函数首先在每个节点上调用collide以确定它是否与任何其他节点发生冲突。然后,它会将未发生碰撞的任何节点的半径增加radiusStep
。如果半径大于nodeRadius
参数,则将半径设置为该值,并将其标记为碰撞以阻止其增加。
function collided(node, i) {
if (node.collided) return;
nodes.forEach(function(n, j) {
if (n !== node) {
var dx = node.x - n.x, dy = node.y - n.y,
l = Math.sqrt(dx*dx+dy*dy);
if (l < node.r + n.r) {
node.collided = true;
n.collided = true;
}
}
});
}
碰撞函数检查每个节点是否与任何其他节点发生碰撞(出于显而易见的原因除外)。如果它检测到冲突,则比较中的两个节点都标记为冲突。为了检测实际碰撞,计算x和y位置的差异,然后使用毕达哥拉斯计算它们之间的距离。如果该距离小于加在一起的两个节点的半径,则发生碰撞。
更新drawMap
函数以在绘制节点之前计算半径。
while (nodes.filter(function(n) { return n.collided; }).length < nodes.length) {
tick();
}
这基本上只调用tick
函数,直到所有节点都标记为冲突。
drawNodes
函数已更新为使用新的nodes
数据结构:
function drawNodes(nodes) {
g.selectAll('circle').data(nodes).enter().append("circle")
.attr("cx", function (d) { return d.x; })
.attr("cy", function (d) { return d.y; })
.attr("r", function(d, i) { return d.r; })
.attr("class", "map-marker");
}
此处的更改仅引用先前创建的每个节点对象的x
,y
和r
属性。
虽然这很有效,但似乎非常有效,但由于tick
和collided
函数的组合为O(n^2)
,因此很容易陷入困境,很快就会陷入困境。