Collision/overlap 在 d3 转换中检测圆圈
Collision/overlap detection of circles in a d3 transition
我正在使用 d3 为地图上的路线(路径)制作动画。当路线到达路线上的某个点时,我想弹出一些信息。
我的大部分代码都基于以下示例。 http://bl.ocks.org/mbostock/1705868。在这个例子中,我真的只是想确定是否有一种方法可以检测过渡圆何时与任何固定圆碰撞或重叠。
最简单的方法就是检查 "close" 过渡圆与其他点的关系。
var pop = d3.select("body").append("div")
.style("position","absolute")
.style("top",0)
.style("left",0)
.style("display", "none")
.style("background", "yellow")
.style("border", "1px solid black");
// Returns an attrTween for translating along the specified path element.
function translateAlong(path) {
var l = path.getTotalLength();
var epsilon = 5;
return function(d, i, a) {
return function(t) {
var p = path.getPointAtLength(t * l);
points.forEach(function(d,i){
if ((Math.abs(d[0] - p.x) < epsilon) &&
(Math.abs(d[1] - p.y) < epsilon)){
pop.style("left",d[0]+"px")
.style("top",d[1]+20+"px")
.style("display","block")
.html(d);
return false;
}
})
return "translate(" + p.x + "," + p.y + ")";
};
};
}
圆圈移动得越快,您的 epsilon 就需要越大。
例子here.
您可以 detect collision 在您的补间函数中。定义要从补间函数内部调用的 collide
函数,如下所示:
function collide(node){
var trans = d3.transform(d3.select(node).attr("transform")).translate,
x1 = trans[0],
x2 = trans[0] + (+d3.select(node).attr("r")),
y1 = trans[1],
y2 = trans[1] + (+d3.select(node).attr("r"));
var colliding = false;
points.each(function(d,i){
var ntrans = d3.transform(d3.select(this).attr("transform")).translate,
nx1 = ntrans[0],
nx2 = ntrans[0] + (+d3.select(this).attr("r")),
ny1 = ntrans[1],
ny2 = ntrans[1] + (+d3.select(this).attr("r"));
if(!(x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1))
colliding=true;
})
return colliding;
}
其中points
为驻点,node
为过渡元素。 collide
所做的是检查 node
是否与任何点重叠 (如 collision detection example here 所示)。
因为我们需要将 node
传递给补间函数,所以我们将 Mike 示例中使用的 attrTween
替换为 tween
:
circle.transition()
.duration(10000)
.tween("attr", translateAlong(path.node()))
.each("end", transition);
最后,补间函数调用我们的 collide
:
function translateAlong(path) {
var l = path.getTotalLength();
return function(d, i, a) {
return function(t) {
var p = path.getPointAtLength(t * l);
d3.select(this).attr("transform","translate(" + p.x + "," + p.y + ")");
if(collide(this))
d3.select(this).style("fill", "red")
else
d3.select(this).style("fill", "steelblue")
};
};
}
我正在使用 d3 为地图上的路线(路径)制作动画。当路线到达路线上的某个点时,我想弹出一些信息。
我的大部分代码都基于以下示例。 http://bl.ocks.org/mbostock/1705868。在这个例子中,我真的只是想确定是否有一种方法可以检测过渡圆何时与任何固定圆碰撞或重叠。
最简单的方法就是检查 "close" 过渡圆与其他点的关系。
var pop = d3.select("body").append("div")
.style("position","absolute")
.style("top",0)
.style("left",0)
.style("display", "none")
.style("background", "yellow")
.style("border", "1px solid black");
// Returns an attrTween for translating along the specified path element.
function translateAlong(path) {
var l = path.getTotalLength();
var epsilon = 5;
return function(d, i, a) {
return function(t) {
var p = path.getPointAtLength(t * l);
points.forEach(function(d,i){
if ((Math.abs(d[0] - p.x) < epsilon) &&
(Math.abs(d[1] - p.y) < epsilon)){
pop.style("left",d[0]+"px")
.style("top",d[1]+20+"px")
.style("display","block")
.html(d);
return false;
}
})
return "translate(" + p.x + "," + p.y + ")";
};
};
}
圆圈移动得越快,您的 epsilon 就需要越大。
例子here.
您可以 detect collision 在您的补间函数中。定义要从补间函数内部调用的 collide
函数,如下所示:
function collide(node){
var trans = d3.transform(d3.select(node).attr("transform")).translate,
x1 = trans[0],
x2 = trans[0] + (+d3.select(node).attr("r")),
y1 = trans[1],
y2 = trans[1] + (+d3.select(node).attr("r"));
var colliding = false;
points.each(function(d,i){
var ntrans = d3.transform(d3.select(this).attr("transform")).translate,
nx1 = ntrans[0],
nx2 = ntrans[0] + (+d3.select(this).attr("r")),
ny1 = ntrans[1],
ny2 = ntrans[1] + (+d3.select(this).attr("r"));
if(!(x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1))
colliding=true;
})
return colliding;
}
其中points
为驻点,node
为过渡元素。 collide
所做的是检查 node
是否与任何点重叠 (如 collision detection example here 所示)。
因为我们需要将 node
传递给补间函数,所以我们将 Mike 示例中使用的 attrTween
替换为 tween
:
circle.transition()
.duration(10000)
.tween("attr", translateAlong(path.node()))
.each("end", transition);
最后,补间函数调用我们的 collide
:
function translateAlong(path) {
var l = path.getTotalLength();
return function(d, i, a) {
return function(t) {
var p = path.getPointAtLength(t * l);
d3.select(this).attr("transform","translate(" + p.x + "," + p.y + ")");
if(collide(this))
d3.select(this).style("fill", "red")
else
d3.select(this).style("fill", "steelblue")
};
};
}