To detect the collision between two geometries it is necessary to implement a collision test.
There is a specific algorithm for the collision test between two figures of arbitrary geometry called SAT and is based on Stein’s theorem that for this case is a very complex tool to implement.
So I opted for a simpler solution, since all figures are circles.
First I created a model of the circles to be drawn that constitutes the point (x,y)
which is its center and a climb, its raio
. Before each circle is drawn I compare its model as the models of all the other circles already drawn checking in pairs if the distance between them is less than the sum of its rays using the formula...
...for calculate the distance between the centers, if the distance is less than the sum of the rays implies a collision which causes the position of the ball to be recomputed. If there is no collision the model of the circle is stored in a list of circles already drawn and then the drawing is concluded.
To apply the mass collision test I used the method Array.prototype.some()
that tests if any of the element in the array passes the test implemented by callback.
PS: I flushed the algorithm taking out some unnecessary parts of the crash test and switched the for
by a while
to facilitate when necessary recalculation of the ball position.
//Diminui o tamanho do canvas para aumentar as chances de colisão
var tx = 150;
var ty = 150;
var tam = 4;
var tamPopulacao = 125; //Aumentei o tamanho da população para aumentar as chances de colisão
//Onde serão armazenadas as informações geométricas dos cículos
let circulos = [];
//Testa a colisão entre dois circulos
function colisão(a, b) {
let dx = b.x - a.x;
let dy = b.y - a.y;
let distancia = Math.sqrt(dx * dx + dy * dy);
return distancia < (a.raio + b.raio);
}
function desenha() {
var canvas = document.getElementById("canvas");
var desenho = canvas.getContext("2d");
desenho.clearRect(0, 0, tx, ty);
let p = 1;
while (p <= tamPopulacao) {
var px = Math.floor(Math.random() * tx - tam);
var py = Math.floor(Math.random() * ty - tam);
//Modela geometricamente um círculo
let circulo = {
x: px,
y: py,
raio: tam
};
//Faz o teste de colisão do circulo atual com os seus predecessores
if (
circulos.some(item => {
return colisão(circulo, item);
})
) continue; //Se houver colisão computa nova posição
p++;
circulos.push(circulo);
desenho.beginPath();
desenho.arc(px, py, tam, 0, Math.PI * 2);
desenho.fillStyle = "black";
desenho.fill();
}
}
desenha();
<canvas id="canvas" width="150" height="150"></canvas>
In case you want to do a test to verify the effectiveness of the collision detector, just add an id and vector direction to each circle and animate the result.
//Diminui o tamanho do canvas para aumentar as chances de colisão
var tx = 150;
var ty = 150;
var tam = 4;
var tamPopulacao = 25;
let circulos = [];
function colisão(a, b) {
if (a.id == b.id) return false; //Se são o memso elemento não testa
let dx = b.x - a.x;
let dy = b.y - a.y;
let distancia = Math.sqrt(dx * dx + dy * dy);
return distancia < (a.raio + b.raio);
}
function desenha() {
var canvas = document.getElementById("canvas");
var desenho = canvas.getContext("2d");
desenho.clearRect(0, 0, tx, ty);
let p = 1;
while (p <= tamPopulacao) {
var px = Math.floor(Math.random() * tx - tam);
var py = Math.floor(Math.random() * ty - tam);
let circulo = {
id: p,
x: px,
y: py,
vx: Math.random(),
vy: Math.random(),
raio: tam
};
if (
circulos.some(item => {
return colisão(circulo, item);
})
) continue;
p++;
circulos.push(circulo);
desenho.beginPath();
desenho.arc(px, py, tam, 0, Math.PI * 2);
desenho.fillStyle = "black";
desenho.fill();
}
}
desenha();
function anima() {
var canvas = document.getElementById("canvas");
var desenho = canvas.getContext("2d");
desenho.clearRect(0, 0, tx, ty);
for (let p = 1; p < circulos.length; p++) {
let a = circulos[p];
//Testes para detectar se saiu fora da tela..
if (a.x < 0 || a.x > tx) a.vx *= -1; //Se escapou na horizontal inverte o vetor direção na coordenada X
if (a.y < 0 || a.y > tx) a.vy *= -1; //Se escapou na horizontal inverte o vetor direção na coordenada Y
//Adiciona movimento vetorial
a.x += a.vx;
a.y += a.vy;
circulos.some(b => {
//Se houver uma colisão apenas reflita o vetor direção
if (colisão(a, b)) {
a.vx *= -1;
a.vy *= -1;
}
});
desenho.beginPath();
desenho.arc(a.x, a.y, a.raio, 0, Math.PI * 2);
desenho.fillStyle = "black";
desenho.fill();
}
window.requestAnimationFrame(anima)
}
window.requestAnimationFrame(anima)
<canvas id="canvas" width="150" height="150"></canvas>
I want to see who commented down solve.
– TiagoA