我正在绘制100个不同大小的圆圈到画布,它们不能重叠。这些圆圈也将从右到左动画(当它们离开屏幕时环回到画布的右边缘),并且还会有一些垂直的“bob”,它们也不能与任何其他圆形重叠。 / p>
以下是我目前正在尝试的内容,这似乎是锁定浏览器。我遍历圆圈集合并执行detectOverlap()
函数,并将圆圈集合传递给它。
然后detectOverlap()
函数循环遍历圆圈,执行以下检查:
detectOverlap: function (bubblesArr) {
while (true) {
var hit = 0;
for (var i=0; i<bubblesArr.length; i++) {
var circle = bubblesArr[i];
var dx = this._x - circle._x;
var dy = this._y - circle._y;
var rr = this._radius + circle._radius;
if (dx * dx + dy * dy < rr * rr) {
hit++;
}
}
if (hit == 0) {
break; // didn't overlap, break out of while loop
}
// if we didn't break then there was an overlap somewhere. calc again.
this._x = Math.round(Math.random() * this.stage.getWidth());
this._y = Math.round(Math.random() * this.stage.getHeight());
}
},
如果hit == 0
,循环中断,我们假设没有重叠。否则,我们随机计算新的X / Y位置并重新启动该过程。
画布类(入口点): 这个类是“舞台”,它构建了气泡对象,然后将它们添加到画布中。
var $container;
var listData;
var bubbles = [];
function init(l, c) {
$container = c;
listData = l;
// this just draws the canvas. full-width + 500px tall.
var stage = new Konva.Stage({
container: $container.selector,
width: window.innerWidth,
height: 500
});
// this creates the drawing layer where the bubbles will live
layer = new Konva.Layer();
// create an instance of the Bubble class for each element in the list.
for (var i=0; i<listData.length; i++) {
bubbles[i] = new celebApp.Bubble.Bubble(listData[i], stage);
}
/** TODO:::: FIGURE OUT COLLISION DETECTION */
for (var i=0; i<bubbles.length; i++) {
bubbles[i].detectOverlap(bubbles);
}
// create the Konva representation for our generated objects
for (var i=0; i<bubbles.length; i++) {
var b = bubbles[i];
layer.add(new Konva.Circle({
x: b._x,
y: b._y,
radius: b._radius,
fill: b._fill,
stroke: b._stroke,
strokeWidth: b._strokeWidth,
}));
}
// add the layer to the stage
stage.add(layer);
}
泡泡课程: 这是表示绘制到屏幕的数据的类。我们需要确保这些对象都不会相互重叠。
var Bubble = function (listData, stage) {
this.stage = stage;
this._x = Math.round(Math.random() * stage.getWidth()),
this._y = Math.round(Math.random() * stage.getHeight()),
this._radius = Math.round(Math.random() * 80);
this._fill = 'red';
this._stroke = 'black';
this._strokeWidth = 4;
this._speed = 3;
};
Bubble.prototype = {
detectOverlap: function (bubblesArr) {
while (true) {
var hit = 0;
for (var i=0; i<bubblesArr.length; i++) {
var circle = bubblesArr[i];
var dx = this._x - circle._x;
var dy = this._y - circle._y;
var rr = this._radius + circle._radius;
if (dx * dx + dy * dy < rr * rr) {
hit++;
}
}
if (hit == 0) {
break; // didn't overlap
}
this._x = Math.round(Math.random() * this.stage.getWidth());
this._y = Math.round(Math.random() * this.stage.getHeight());
}
},
};
编辑:刚刚根据@MarcB的评论尝试了这一点 - 但是,浏览器似乎仍然锁定。是否会导致性能瓶颈,但100个项目都运行自己的while()
循环?
for (var i=0; i<bubblesArr.length; i++) {
var circle = bubblesArr[i];
var combinedRadius = Math.abs(circle._radius + this._radius);
var distance = Math.abs(this._x - circle._x);
if (distance <= combinedRadius) {
hit++;
}
}
答案 0 :(得分:1)
这看起来像一个简单的bug。您初始化一个圆圈列表。然后,对于列表中的每个圆圈,您可以计算列表中有多少个圆圈重叠。如果找到重叠,则移动圆圈并重试。
但是每个圆圈都会在列表中找到它并发现它与自身重叠。你移动它,同样的事情发生。这是一个永无止境的无限循环。
您需要让每个圆圈都能找到与其重叠的以外的
。在算法上,您可以使用像四叉树这样的聪明数据结构来改善此重叠检测。这样您就可以立即找到所有圆圈,这些圆圈的中心位于圆圈的一个小框内,让您找到相应的重叠。
但是,如果性能是一个问题,就没有必要努力工作。而是通过x坐标对圆圈进行排序,绘制相距5的垂直条带,然后将每个圆圈放入与其相交的所有条带中。现在,对于每个圆圈,您只需搜索它相交的所有波段。
效率的下一步将是通过y坐标对每个波段进行排序,以便您可以在该波段中进行二分搜索,以找到与波段相交的所有圆,其距离可能与您的圆相交。但是这些乐队通常应该是空的,所以这不是一场胜利。
答案 1 :(得分:0)
如果仅在创建整个列表后暂存气泡(尽管您也可以通过以随机顺序从现有列表中暂存来模拟随机外观),为什么不通过创建随机间距来完全避免碰撞检测?
可能有更多有趣且类似于有机的程序,但一种简单的方法是按排队顺序细分空间,保持当前框大小的随机截止值以考虑不同的半径。像这样:
Describe the space as a tuple consisting of a middle point and a top left point.
(middle,top_left)
Choose a random point on the top border of the box:
-----x-----------
Choose one random point on the left border of the box and one on the right:
-----x-----------
| | |
| | |
x----- |
| | |
| |----------x
| | |
-----------------
You now have four new middle and top left points, easily calculated.
Add the four new tuples to the queue and remove the tuple
representing the parent box. Customize the function to get appropriately
sized results.
我们最终得到一个元组列表,代表不同大小的非重叠框,给定中间点。剩下的就是随机挑选一些方框(列表可以进行散列以避免碰撞)并在其中放置气泡。 (这假设同一水平空间中的气泡以相同的速度移动。)
这样的事情(可能需要一些调整):
var MIN_WIDTH = MIN_HEIGHT = 20;
function f(top_left,bottom_right){
var w = bottom_right.x - top_left.x - 2 * MIN_WIDTH,
h = bottom_right.y - top_left.y - 2 * MIN_HEIGHT,
random_top = top_left.x + MIN_WIDTH
+ Math.ceil(Math.random() * w),
random_left = top_left.y + MIN_HEIGHT
+ Math.ceil(Math.random() * h),
random_right = top_left.y + MIN_HEIGHT
+ Math.ceil(Math.random() * h);
var rectangle_1 = [top_left
,{x: random_top, y: random_left}],
rectangle_2 = [{x: top_left.x, y: random_left}
,{x: random_top, y: bottom_right.y}],
rectangle_3 = [{x: random_top, y: top_left.y}
,{x: bottom_right.x, y: random_right}],
rectangle_4 = [{x: random_top, y: random_right}
,bottom_right];
return [rectangle_1, rectangle_2, rectangle_3, rectangle_4];
}
console.log(JSON.stringify(f({x: 0, y: 0}, {x: 200, y: 200})))
第一次将整个画布大小输入f
。然后,将四个结果矩形中的每一个都输入f
,这样每个矩形都会再次细分,依此类推。在递归中添加一个随机停止,这样一些矩形将比其他矩形更大(因为那些被赢得的细分)。将气泡放在矩形空间内。他们不会碰撞,但由此产生的安排可能会错过更有机的感觉 - 也许可以通过随机调整#34;轻推&#34;他们一点点。