How to verify collision between particles?

Asked

Viewed 990 times

9

I need to know how to make each circle collide with each other along with the part of how I’m going to go in such a direction after this collision.

This is the class of the main program window:

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
    }
};
  • 3
    1. If your question needs an external link to be understood, it is too extensive for the OS format. It is important that all the elements for the question are in the question and what code, when necessary, nay be the whole project, just the relevant part. 2. What is the question? What problem and difficulty do you have in trying to solve your problem? Where is the question mark of your post?
  • I think you didn’t read what I wrote, and certainly don’t know how QT Creator works, because it’s not just one code for the program to work but several, so add the link containing the compressed file with the codes.

  • I have read your question and use Qtcreator as the main IDE. I still don’t know what your question is. What problem do you have? In the current format there is no good answer without solving all the activity for you.

  • 1

    Yes, the question seems to be very broad. If you don’t have a specific problem, it’s hard to answer. And @Guilhermebernal knows a lot about Qt Creator. I didn’t really understand the problem, but I doubt it’s related to Qt Creator. And QT Creator isn’t even the correct name for the Qt IDE. QT is short for Quicktime.

  • In the current code, the circles pass through each other, and I need to make them collide with each other and go back in opposite directions.

  • 1

    Right. And what exactly is the problem you had? Didn’t you have a way to get the position of the circles? Can’t you calculate if two circles collide? Not sure what to do if they collide? Have you no idea how to change their direction of movement? Or did you manage to do all this and the result was different than expected? We need to know what you tried and where is the callus.

  • 1

    The callus is in the part where I don’t know how to calculate the exact moment that each circle will collide with each other along with the part of how I’m going to go in that direction.

  • 2

    @Marpd144 Edit the question and add it there, if possible explaining better what you have done with code, already tried and etc. This helps people understand your problem and help you.

Show 3 more comments

2 answers

12

From the looks of it, the problem is more of a collision check. A way simple, is to check that the distance between the particles is less than the sum of their radii:

Distância entre Centros

Something like:

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

If that’s true, it’s because the particle p1 collided with p2.

From there you calculate the collision between the particles. If you are going to do something realistic, you should take a look at Linear Momentum. If you consider that they have the same mass, it becomes simpler.


Basing, in the code you posted you can check the collision this way:

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 verify the collision between particles, but this is a good simple and that should help solve your problem.

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

In C++, the version presented by the link is 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 does not yet cover two situations:

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

  2. When particles are randomly (during loading) placed inside each other.


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

  • I will try to do what you suggested and come back with a reply. Thank you very much.

  • I edited by adding the way I tested it here. But I removed the 5px edge as it changes the radius and influences the calculations. And invVx() is the method that reverses the speed in x.

  • It would be nice to translate the text of the image and, mainly, use the same value references (p1.r() instead of r1, for example).

  • 1

    I agree. I’ll do it.

9

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

The two classes together offer features for collision detection, as shown in the example of Colliding Mice:

inserir a descrição da imagem aqui

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

  • It helped a lot, @karlphillip. Brawl, man.

Browser other questions tagged

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