How to check for collision between particles?

9

I need to know how to make each circle collide with the other along with what I'm going to do to follow that direction after that collision.

This is the program's main window class:

class canvas : public QWidget {
    Q_OBJECT
public:
    explicit canvas(QWidget *parent) : QWidget(parent) {
        m_particulas.resize(30);
        int n = m_particulas.size();
        for(int i = 0 ; i<n ; ++i) {
            m_particulas[i].init();
        }
        startTimer(5);
    }
protected:
    void paintEvent(QPaintEvent *) {
        QPainter painter(this);
        painter.setWindow(0,0,1000,1000);
        painter.setRenderHint(QPainter::Antialiasing, true);
        painter.setPen(QPen(Qt::black, 5, Qt::SolidLine, Qt::RoundCap));
        painter.setBrush(QBrush(Qt::darkCyan, Qt::SolidPattern));
        int n = m_particulas.size();
        for(int i = 0 ; i<n ; ++i) {
            double x = m_particulas[i].x();
            double y = m_particulas[i].y();
            double L = 2*m_particulas[i].r();
            painter.drawEllipse(QPointF(x, y), L, L);
        }
    }
    void timerEvent(QTimerEvent *) {
        int n = m_particulas.size();
        for(int i = 0 ; i<n ; ++i) {
            m_particulas[i].andar();
        }
        update();
    }
signals:
public slots:
private:
    std::vector<Particula> m_particulas;
};

And that's the class of every particle on the screen:

class Particula {
private:
    double m_x;
    double m_y;
    double m_vx;
    double m_vy;
    double m_r;
public:
    Particula() {};
    void init() {
        m_r = 10;
        m_x = 900.0*rand()/RAND_MAX + 50;
        m_y = 900.0*rand()/RAND_MAX + 50;
        m_vx = 2.0 * rand()/RAND_MAX - 1;
        m_vy = 2.0 * rand()/RAND_MAX - 1;
        double norma = sqrt(m_vx*m_vx + m_vy*m_vy);
        m_vx /= norma;
        m_vy /= norma;
    }
    double x() const { return m_x; }
    double y() const { return m_y; }
    double r() const { return m_r; }
    void andar() {
        m_x += m_vx;
        m_y += m_vy;
        if(m_x > 990-m_r) m_vx *= -1; //inferior - multiplicado por -1 para inverter a direção...
        if(m_y > 990-m_r) m_vy *= -1; //direita
        if(m_x < 30-m_r) m_vx *= -1; //esquerda
        if(m_y < 30-m_r) m_vy *= -1; //superior
    }
};
    
asked by anonymous 26.04.2014 / 01:42

2 answers

12

So it seems the problem is more to check for collisions. A simple way is to check that the distance between the particles is less than the sum of their rays:

Somethinglike:

dist<(p1.r()+p2.r())

Ifthisistrue,theparticlep1collidedwithp2.

Fromthereyoucalculatethecollisionbetweentheparticles.Ifyou'regoingtodosomethingrealistic,youshouldtakealookat Linear Moment . If you consider that they have the same mass, it becomes simpler.

Based on the code you posted you can check the collision like this:

float temp_x, temp_y, dist;

for(int i = 0 ; i<n ; ++i)
{
    Particula& p1 = m_particulas[i];

    for(int j = 0; j < n; ++j)
    {
        if(i == j)
        {
            // Não verifica colisão entre a mesma partícula.
            continue;
        }
        else
        {
            // Verifica se a distância entre duas partículas é menor que 2.diametro,
            // se for é porque colidiu.
            Particula& p2 = m_particulas[j];

            temp_x = p2.x() - p1.x();
            temp_y = p2.y() - p1.y();

            dist = std::sqrt(temp_x * temp_x + temp_y * temp_y);

            if(dist < (p1.r() + p2.r()))
            {
                // Colidiram.
                // Nesse caso, elas devem mudar de direção.

                // Inverta a velocidade ou faça um metodo para colisão mais realístico.

                if(temp_x > temp_y)
                {
                    p1.invVx();
                    p2.invVx();
                }
                else
                {
                    p1.invVy();
                    p2.invVy();
                }                    

                break;
            }
        }
    }

    m_particulas[i].andar();
}

There are more efficient ways to check for collision between particles, but this is a simple and should help solve your problem.

If you want to delve deeper, this link has a similar but more realistic implementation, considering the mass of the particles and everything.

In C ++, the version presented by the link looks like this:

void Particula::colidir(Particula& p1, Particula& p2)
{
    // diferença entre as distâncias em x e y
    double dx = p1.x() - p2.x();
    double dy = p1.y() - p2.y();

    // angulo de colisão
    double col_angle = std::atan2(dy, dx);

    // módulo da velocidade
    double V1 = p1.velocity();
    double V2 = p2.velocity();

    // ângulo da direção
    double direction1 = std::atan2(p1.vy(), p1.vx());
    double direction2 = std::atan2(p2.vy(), p2.vx());

    // joga os cálculos para uma única dimesão para realizar
    // os cálculos.
    double vx_1 = V1 * std::cos(direction1 - col_angle);
    double vy_1 = V1 * std::sin(direction1 - col_angle);
    double vx_2 = V2 * std::cos(direction2 - col_angle);
    double vy_2 = V2 * std::sin(direction2 - col_angle);

    // calcula a velocidade final, considerando a massa das partículas.
    double final_vx_1 = ((p1.mass() - p2.mass()) * vx_1 + (p2.mass() + p2.mass()) * vx_2)/(p1.mass() + p2.mass());
    double final_vx_2 = ((p1.mass() + p1.mass()) * vx_1 + (p2.mass() - p1.mass()) * vx_2)/(p1.mass() + p2.mass());
    double final_vy_1 = vy_1;
    double final_vy_2 = vy_2;

    // retorna os cálculos para o 2d
    const double PI2 = 1.57079632679489661923132169163;
    p1.m_vx = std::cos(col_angle) * final_vx_1 + std::cos(col_angle + PI2) * final_vy_1;
    p1.m_vy = std::sin(col_angle) * final_vx_1 + std::sin(col_angle + PI2) * final_vy_1;
    p2.m_vx = std::cos(col_angle) * final_vx_2 + std::cos(col_angle + PI2) * final_vy_2;
    p2.m_vy = std::sin(col_angle) * final_vx_2 + std::sin(col_angle + PI2) * final_vy_2;
}

However, it should be noted that for this algorithm it still does not cover two situations:

  • Particles that move very fast: at each step of the calculation, they can move more than the collision detection calculations predict and thereby "cross" the others (this problem is handled by box2d , with a special algorithm (only search for bullet ).

  • When the particles are randomly (during loading) placed inside one another.

  • Another detail: In your program, when you draw the particle, you use a 5-pixel border. Remove this border or add it to the radius because it influences the collision visualization.

        
    26.04.2014 / 02:30
    9

    You can investigate the following Qt classes to accomplish this task for you:

    The two classes together offer collision detection capabilities, as demonstrated in the Colliding Mice :

    The example uses QGraphicsScene::collidingItems() to check for collisions between the mice.

        
    26.04.2014 / 19:41