How to insert objects using canvas (javascript) and avoid 2 objects in the same position

Asked

Viewed 177 times

3

I’m doing a canvas test to randomly insert various polka dots into a rectangle.

I use a random position for each ball, as long as the limit is less than the width of the rectangle. So far so good, but sometimes some balls are superimposed on others.

How can I prevent one little ball from overlapping the other?

Follow my code below

//SETUP
var tx = 400; //LARGURA TOTAL
var ty = 300; //ALTURA TOTAL
var t = 1; //TAMANHO DE CADA PONTO

//CARINHA
var tam = 4; //TAMANHO DA BOLINHA
var tamPopulacao = 150; //TAMANHO DA POPULAÇÃO
var visao = 5;

function desenha() {
    var canvas = document.getElementById("canvas");
    var desenho = canvas.getContext("2d");

    //MAPA *************************************************
    desenho.beginPath();
    desenho.rect(0, 0, tx, ty);
    desenho.fillStyle = "white";
    desenho.fill();  
    //MAPA *************************************************

    //BOLINA ***********************************************
    for(p=1;p<=tamPopulacao;p++){
        //POSIÇÃO
        var px = Math.floor(Math.random() * tx-tam);
        var py = Math.floor(Math.random() * ty-tam);
        var startAngle = 0;
        var endAngle = 3.5 * Math.PI;
        //DESENHAR
        desenho.beginPath();
        desenho.arc(px, py, tam, 0, Math.PI * 2, true);
        desenho.fillStyle = "#e87e04";
        desenho.fill();
    }
    //BOLINA ***********************************************
}
<html>
    <head>
        <script type="text/javascript" src="desenha.js"></script>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
        <title>Movimento</title>
        <style type="text/css">
            body{
                padding:20px;
                background:#ccc;
                margin:0;
            }
        </style>
    </head>
    <body onload="desenha()">
        <canvas id="canvas" width="400" height="300" style="border:1px solid #aaa;"></canvas>
    </body>
</html>

  • I want to see who commented down solve.

2 answers

7


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...

inserir a descrição da imagem aqui

...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>

  • Perfect, it was exactly what I needed, thank you!!

0

What you need is a collision detector:

What I did:

  • saved the position of each drawn ball in an array;
  • I created a method that traverses the array of the drawn balls and tests the positions of all until I find one that does not collide.

See the example on Jsfiddle.

//SETUP
var tx = 400; //LARGURA TOTAL
var ty = 300; //ALTURA TOTAL
var t = 1; //TAMANHO DE CADA PONTO

//CARINHA
var tam = 4; //TAMANHO DA BOLINHA
var tamPopulacao = 150; //TAMANHO DA POPULAÇÃO
var visao = 5;

var bolinhaDesenhadas = [];

function getP() {
  var x = Math.floor(Math.random() * tx - tam);
  var y = Math.floor(Math.random() * ty - tam);
  var x1 = x - tam;
  var x2 = x1 + tam * 2;
  var y1 = y - tam;
  var y2 = y1 + tam * 2;

  for (var i = 0; i < bolinhaDesenhadas.length; i++) {
    var b = bolinhaDesenhadas[i];
    var colisaox = x1 > b.x1 && x1 < b.x2 || x2 > b.x1 && x2 < b.x2;
    var colisaoy = y1 > b.y1 && y1 < b.y2 || y2 > b.y1 && y2 < b.y2;
    if (colisaox && colisaoy) {
      return getP();
    }

  }

  bolinhaDesenhadas.push({
    x1,
    x2,
    y1,
    y2
  });

  return {
    x,
    y
  };
}

function desenha() {
  var canvas = document.getElementById("canvas");
  var desenho = canvas.getContext("2d");

  //MAPA *************************************************
  desenho.beginPath();
  desenho.rect(0, 0, tx, ty);
  desenho.fillStyle = "white";
  desenho.fill();
  //MAPA *************************************************

  //BOLINA ***********************************************
  for (i = 1; i <= tamPopulacao; i++) {
    //POSIÇÃO
    var p = getP();
    var startAngle = 0;
    var endAngle = 3.5 * Math.PI;
    //DESENHAR
    desenho.beginPath();
    desenho.arc(p.x, p.y, tam, 0, Math.PI * 2, true);
    desenho.fillStyle = "#e87e04";
    desenho.fill();
  }
  //BOLINA ***********************************************
}
body {
  padding: 20px;
  background: #ccc;
  margin: 0;
}
<body onload="desenha()">
  <canvas id="canvas" width="400" height="300" style="border:1px solid #aaa;"></canvas>
</body>

Browser other questions tagged

You are not signed in. Login or sign up in order to post.