Ir ao conteúdo
  • Cadastre-se

C Colisões de Partículas (Allegro4)


Midori
Ir à solução Resolvido por Midori,

Posts recomendados

Com a função 'colidiu' identifico a colisão, mas em alguns casos a colisão é ignorada e uma partícula acaba passando sobre a outra.

 

Qual é a melhor forma de evitar isso? Para resolver tentei incrementar a posição (x/y) logo após a colisão para já se afastarem (p[a].x+=p[a].vx..., etc), mas nem sempre funciona.

 

#include <allegro.h>

#define QUANTIDADE 15

typedef struct particula{
    float x;
    float y;
    int raio;
    int massa;
    float vx;
    float vy;
    int cor;
}Particula;

int colidiu(Particula *p1, Particula *p2){
    float qx = (p1->x - p2->x)*(p1->x - p2->x);
    float qy = (p1->y - p2->y)*(p1->y - p2->y);
    float qr = (p1->raio + p2->raio)*(p1->raio + p2->raio);
    return qx + qy <= qr;
}

float vfinal(int m1, int m2, float v1,float v2){
    return (v1 * (m1 - m2) + 2 * v2 * m2)/(m1 + m2);
}

int main(){
    BITMAP *buffer;
    Particula p[QUANTIDADE];
    int a = 0;
    int iniciou = 0;

    allegro_init();
    install_keyboard();
    set_color_depth(32);
    set_gfx_mode(GFX_AUTODETECT_WINDOWED,300,300,0,0);
    buffer = create_bitmap(SCREEN_W,SCREEN_H);
    srand(time(0));

    do{
        if(!iniciou){
            int r = 50 + (rand() % 256);
            int g = 50 + (rand() % 256);
            int b = 50 + (rand() % 256);
            p[a].raio = 20;//3 + rand() % 15;
            p[a].massa = p[a].raio;
            p[a].vx = rand() % 3;
            p[a].vy = rand() % 3;
            p[a].y = p[a].raio + (rand() % (SCREEN_W-100));
            p[a].x = p[a].raio + (rand() % (SCREEN_H-100));
            p[a].cor = makecol(r,g,b);
        }else{
            int b;
            for(b=0;b<QUANTIDADE;b++){
                if(b!=a){
                    if(p[a].vx && colidiu(&p[a],&p[b])){
                        float vx1 = p[a].vx;
                        float vx2 = p[b].vx;
                        float vy1 = p[a].vy;
                        float vy2 = p[b].vy;
                        p[a].vx = vfinal(p[a].massa,p[b].massa,vx1,vx2);
                        p[b].vx = vfinal(p[b].massa,p[a].massa,vx2,vx1);
                        p[a].vy = vfinal(p[a].massa,p[b].massa,vy1,vy2);
                        p[b].vy = vfinal(p[b].massa,p[a].massa,vy2,vy1);
                        p[a].x+=p[a].vx;
                        p[b].x+=p[b].vx;
                        p[a].y+=p[a].vy;
                        p[b].y+=p[b].vy;
                    }
                }
            }
        }		
        if((p[a].x>=SCREEN_W-p[a].raio)||(p[a].x<=p[a].raio)){p[a].vx*=-1;}
        if((p[a].y>=SCREEN_H-p[a].raio)||(p[a].y<=p[a].raio)){p[a].vy*=-1;}
        p[a].x+=p[a].vx;
        p[a].y+=p[a].vy;

        circlefill(buffer,p[a].x,p[a].y,p[a].raio,p[a].cor);

        if(++a >= QUANTIDADE){
            draw_sprite(screen,buffer,0,0);
            clear_bitmap(buffer);
            a = 0;
            iniciou = 1;
        }
    }while(!key[KEY_ESC]);

    destroy_bitmap(buffer);
    allegro_exit();
    return 0;
}
END_OF_MAIN();

 

  • Curtir 1
Link para o comentário
Compartilhar em outros sites

Entendi que a suspeita na função colidiu falhar durante inspeção espacial [distância] das partículas? Ok! Pode ser erro de arredondamento propagado pela imprecisão de tipos flutuantes.

 

"qx + qy <= qr;

Então, por conta de uma imprecisão de tipos flutuantes a soma que é a distância ao quadrada dos centros pode resultar em valor com pequena diferença, porém maior que o quadrado da soma de raios.

 

Logo sugiro tratar de diferença, ou seja:   "qx + qy - qr <=  0.00(...)x

Boa Sorte

  • Curtir 1
Link para o comentário
Compartilhar em outros sites

@Mauro Britivaldo Criei um arquivo de log para registrar as variáveis da função 'colidiu' e o cálculo está sendo feito corretamente com ponto flutuante.

 

Fiz um teste com 50 partículas de diversos tamanhos e o problema ficou mais claro. Na hora de atribuir as posições inicias x/y aleatórias das partículas acaba acontecendo casos de ficar uma sobre a outra.

 

Tentei corrigir isso com este loop para verificar se já há colisão na hora dessa atribuição, mas não deu certo.

 

for(b=0;b<a;b++){
    if(b!=a){
        while(colidiu(&p[a],&p[b])){
            p[a].y = (rand() % (SCREEN_W-p[a].raio*2))+p[a].raio*2;
            p[a].x = (rand() % (SCREEN_H-p[a].raio*2))+p[a].raio*2;
        }
    }
}

 

Esse loop coloquei logo após "p[a].vy = rand() % 3" (removi a atribuição anterior de x/y).

 

Atribui desta forma para evitar criar uma partícula fora da janela,

p[a].y = (rand() % (SCREEN_W-p[a].raio*2))+p[a].raio*2;
p[a].x = (rand() % (SCREEN_H-p[a].raio*2))+p[a].raio*2;

 

  • Curtir 1
Link para o comentário
Compartilhar em outros sites

  • Solução

Resolvi o problema das sobreposições iniciais com um goto,

 

do{
    if(!iniciou){
        int r = 50 + (rand() % 200);
        int g = 50 + (rand() % 200);
        int b = 50 + (rand() % 200);
        p[a].cor = makecol(r,g,b);
NPART:
        p[a].raio = 3 + rand() % 18;
        p[a].massa = p[a].raio;
        p[a].vx = rand() % 2;
        p[a].vy = rand() % 2;
        p[a].x = (rand() % (SCREEN_W-p[a].raio));
        p[a].y = (rand() % (SCREEN_H-p[a].raio));			
        if(p[a].x<p[a].raio)p[a].x = p[a].raio;
        if(p[a].y<p[a].raio)p[a].y = p[a].raio;

        for(b = 0; b < a; b++){
            if(colidiu(&p[b], &p[a])) goto NPART;
        }
...

 

Link para o comentário
Compartilhar em outros sites

:) Sei que resolveu isso, mas fiquei curioso em ver como seria isso rodando por uns minutos e escrevi uma parte. Não conheço esse 🚝 então não fui nada longe 

 

Mas fiquei pensando nessas coisas então vou comentar aqui

  • não seria melhor (mais fácil de programar e controlar) usar coordenadas polares e uma estrutura linear? Eu copiei seu código para começar, mas não sei s e faria de novo assim
  • eu escrevi uma estrutura Nuvem para por as partículas e uma Config para controlar a geração 
  • e aí usei duas funções: primeira_nuvem() e proxima_nuvem() para controlar o sistema
  • acabei achando que é mais divertido criar as partículas on-purpose do que aleatoriamente também. É mais divertido apontar umas para as outras :D
  • desisti das colisões elásticas. Achei mais interessante quando tem uma colisão parar a menor e criar uma nova partícula descendente da maior e desviar uma para cada lado por um certo ângulo
  • mas separei o tratamento da colisão numa rotina impacto() separada

Veja uma primeira_nuvem()

Nuvem* primeira_nuvem(Config* cfg)
{
    Nuvem* const nv = (Nuvem*)malloc(sizeof(Nuvem));
    nv->p = (Particula**)malloc(cfg->n_Part * sizeof(Particula*));
    // primeira
    nv->p[0] = gera_particula(cfg);
    nv->nPart = 1;
    // as outras
    while (nv->nPart < cfg->n_Part)
    {   // cria um vetor de particulas, a nuvem
        // coloca a nova na proxima posicao livre
        nv->p[nv->nPart] = gera_particula(cfg);
        nv->nPart += 1; // conta a nova
        while (colide_com_nuvem(nv->nPart - 1, nv) >= 0)
        {   // enquanto colidir tenta outra posicao
            nv->p[nv->nPart]->x = nv->p[nv->nPart]->raio +
                (rand() % cfg->largura - nv->p[nv->nPart]->raio);
            nv->p[nv->nPart]->y = nv->p[nv->nPart]->raio +
                (rand() % cfg->altura - nv->p[nv->nPart]->raio);
        };  // while()
        // ok: sem colisao
    };  // while()
    return nv;
};  // primeira_nuvem()


Esse foi o jeito que arrumei para a geração inicial.


E cada próxima geração  algo assim

Tela* proxima_nuvem(const Tela* prev, const Config* cfg)
{   char n_colisoes = 0;
    do
    {   // move uma particula pA
        for (int a = 0; a < prev->nuvem->nPart; a += 1)
        {
            Particula* pA = prev->nuvem->p[a];
            if ((pA->vx == 0) && (pA->vy == 0)) continue;
            for (int b = a + 1; b < prev->nuvem->nPart; b += 1)
            {
                Particula* pB = prev->nuvem->p[b]; // a outra
                n_colisoes = impacto(a, b, prev->nuvem, cfg);
            };  // for()

            pA->x += pA->vx;
            pA->y += pA->vy; // pA andou
            // se esta nas bordas inverte o sentido
            if ((pA->x >= prev->W - pA->raio) ||
                (pA->x <= pA->raio)) pA->vx *= -1;
            if ((pA->y >= prev->H - pA->raio) ||
                (pA->y <= pA->raio)) pA->vy *= -1;
            if (n_colisoes > 0) break;
        };  // for()
    } while (n_colisoes != 0);
    return (Tela*)prev; // retorna a nova Tela
};  // proxima_nuvem()

impacto() devolve a Nuvem corrigida se a bateu em b e assim é mais fácil alterar "políticas de colisão"

 

Mas vou parar porque é como um game :) e gasta muito tempo.

Veja um movimento de uma nuvem de 40 delas


image.thumb.png.aa90fbfe19cb70e86bd1f9183cedfa2c.png


E depois de uns 30s
 

image.thumb.png.0c531c73b9b044895b3b49cbbb2d6b26.png

  • Obrigado 1
Link para o comentário
Compartilhar em outros sites

5 horas atrás, arfneto disse:

não seria melhor (mais fácil de programar e controlar) usar coordenadas polares e uma estrutura linear? Eu copiei seu código para começar, mas não sei s e faria de novo assim

Para esse tipo de colisão (unidimensional) tenho que considerar só a massa e a velocidade inicial para calcular a velocidade final. Com a função vfinal acho que essa foi uma forma simples de atribuir a velocidade e o sentido no momento da colisão.

 

Se eu tivesse que considerar o ângulo para colisão obliqua, eu usaria funções trigonométricas. Algo assim,

arco = atan2(p[a].y - p[b].y, p[a].x - p[b].x);
Px = cos(arco);
Py = sin(arco);

 

5 horas atrás, arfneto disse:

Veja um movimento de uma nuvem de 40 delas

Você usou allegro para programar? Pode postar o código?

 

  • Obrigado 1
Link para o comentário
Compartilhar em outros sites

48 minutos atrás, Midori disse:

Se eu tivesse que considerar o ângulo para colisão obliqua, eu usaria funções trigonométricas

 

É como temos desde o ensino médio afinal. Por isso acho que usar coordenadas polares e um espaço linear para os pixels facilitaria as contas todas. E é mais intuitivo para pensar no modelo porque as partículas se movimentam em módulo, direção e sentido. Uma direção. E decompor a cada momento a direção em deslocamento x e deslocamento y é chato. 

 

51 minutos atrás, Midori disse:

Você usou allegro para programar? Pode postar o código?

 

Usei porque era o que o tópico citava, mas eu nunca tinha visto e então não andei quase nada. Meu programa não deve servir como exemplo do uso da API :( 

Mas é divertido de ficar olhando. 
Eu ia postar mas como estava já resolvido deixei pra lá. Mas ontem eu lembrei disso de novo


Se tiver alguma ideia e quiser alterar pode propor lá direto e eu incorporo. Lá tem um botão de download também. Deixei em um arquivo ou dois porque eu ia postar no forum.

 

Citação

Poste o código atual e algumas telas com partículas

 

Eu não tenho esse ambiente para programar porque eu nem conhecia. E não tenho assim muito interesse para instalar e compilar pacotes grandes de coisas que não uso e nem conheço. Só que eu uso vcpkg no meu ambiente Windows e allegro foi portada para vcpkg então eu instalei. É outra versão mas também aí seria pedir muito ;) ter a versão certinha. Mas

vcpkg install allegro5

E estava ok minutos depois. E tinha um link para um manual de referência em PDF e o seu programa então...

 

O programa está aqui.  Basicamente o que eu mudei foi a estrutura para ficar mais controlável: o programa roda em torno dessa configuração

typedef struct
{
    uint16_t        altura;
    uint16_t        largura;
    double          min_velocidade;
    uint16_t        n_Part;
    unsigned        semente;
    double          time_out;
    const char*     titulo;

    ALLEGRO_COLOR           fundo;
    ALLEGRO_DISPLAY*        display;
    ALLEGRO_EVENT_QUEUE*    eventos;
    ALLEGRO_KEYBOARD_STATE  teclado;
    ALLEGRO_TIMEOUT         timeout;

}   Config;

Mais flexível para eu alterar.  Eu reprogramei a parte de saída pra sair com ESCAPE ou clicando no X para fechar a janela. 

 

E usei uma Nuvem para as partículas

typedef struct
{
    float           x;
    float           y;
    float           raio;
    float           massa;
    float           vx;
    float           vy;
    ALLEGRO_COLOR   cor;

}   Particula;

typedef struct
{
    uint16_t    nPart; // contagem
    Particula** p;  // as próprias

}   Nuvem;

E uma Tela pra por a Nuvem.

Assim é mais flexível e dá pra ter mais telas e nuvens no mesmo programa. Achei que ia ficar mas colorido e festivo ;) 

typedef struct
{
    ALLEGRO_BITMAP* buffer;
    uint16_t        H; // altura em pixels
    Nuvem* nuvem;
    uint16_t        W; // largura

}   Tela;

A lógica eu acho que não mudei. Apenas dividi em primeira_nuvem() e proxima_nuvem()

com o sentido óbvio.

 

  • Curtir 1
  • Obrigado 1
Link para o comentário
Compartilhar em outros sites

15 horas atrás, arfneto disse:

or isso acho que usar coordenadas polares e um espaço linear para os pixels facilitaria as contas todas. E é mais intuitivo para pensar no modelo porque as partículas se movimentam em módulo, direção e sentido. Uma direção. E decompor a cada momento a direção em deslocamento x e deslocamento y é chato. 

 

Além da direção e sentido, após a colisão tenho que atualizar a velocidade da colisão elástica (função vfinal).

 

Mantendo as atribuições aleatórias e considerando o cálculo da velocidade vfinal, de que forma o código ficaria mais fácil de controlar?

 

No bloco if da colisão tive que usar as variáveis vx1/vy1 para guardar a velocidade e depois fiz algumas correções com incremento nas posições para evitar sobreposição.

 

O código está rodando sem problemas.

#include <allegro.h>

#define QPART 50

typedef struct particula{
    float x;
    float y;
    int raio;
    int massa;
    float vx;
    float vy;
    int cor;
}Particula;

int colidiu(Particula *p1, Particula *p2){
    float qx = (p1->x - p2->x)*(p1->x - p2->x);
    float qy = (p1->y - p2->y)*(p1->y - p2->y);
    float qr = (p1->raio + p2->raio)*(p1->raio + p2->raio);
    return qx + qy <= qr;
}

float vfinal(int m1, int m2, float v1,float v2){
    return (v1 * (m1 - m2) + 2 * v2 * m2)/(m1 + m2);
}

int main(){
    BITMAP *buffer;
    Particula p[QPART];
    int a = 0;
    int b = 0;
    int iniciou = 0;

    allegro_init();
    install_keyboard();
    set_color_depth(32);
    set_gfx_mode(GFX_AUTODETECT_WINDOWED,640,480,0,0);
    buffer = create_bitmap(SCREEN_W,SCREEN_H);
    srand(time(0));

    do{
        if(!iniciou){
            int r = 50 + (rand() % 200);
            int g = 50 + (rand() % 200);
            int b = 50 + (rand() % 200);
            p[a].cor = makecol(r,g,b);

NPART:	
            p[a].raio = 3 + rand() % 18;
            p[a].massa = p[a].raio;
            p[a].vx = rand() % 2;
            p[a].vy = rand() % 2;
            p[a].x = (rand() % (SCREEN_W-p[a].raio));
            p[a].y = (rand() % (SCREEN_H-p[a].raio));
            if(p[a].x<p[a].raio){p[a].x = p[a].raio;}
            if(p[a].y<p[a].raio){p[a].y = p[a].raio;}

            for(b=0;b<a;b++){
                if(colidiu(&p[b],&p[a])){goto NPART;}
            }
        }else{
            for(b=0;b<QPART;b++){
                if(b!=a){
                    if(colidiu(&p[a],&p[b])){
                        float vx1 = p[a].vx;
                        float vy1 = p[a].vy;

                        if(p[a].x>p[b].x){p[a].x+=0.5;p[b].x-=0.5;}
                        if(p[a].y>p[b].y){p[a].y+=0.5;p[b].y-=0.5;}

                        p[a].vx = vfinal(p[a].massa,p[b].massa,vx1,p[b].vx);
                        p[a].vy = vfinal(p[a].massa,p[b].massa,vy1,p[b].vy);
                        p[b].vx = vfinal(p[b].massa,p[a].massa,p[b].vx,vx1);
                        p[b].vy = vfinal(p[b].massa,p[a].massa,p[b].vy,vy1);
                        p[a].x+=p[a].vx;
                        p[a].y+=p[a].vy;
                        p[b].x+=p[b].vx;
                        p[b].y+=p[b].vy;
                    }
                }
            }
        }
        if(p[a].x>=SCREEN_W-p[a].raio){
            p[a].x=(SCREEN_W-p[a].raio)-1;p[a].vx*=-1;
        }
        if(p[a].y>=SCREEN_H-p[a].raio){
            p[a].y=(SCREEN_H-p[a].raio)-1;p[a].vy*=-1;
        }		
        if(p[a].x<=p[a].raio){p[a].x=p[a].raio+1;p[a].vx*=-1;}
        if(p[a].y<=p[a].raio){p[a].y=p[a].raio+1;p[a].vy*=-1;}
		
        p[a].x+=p[a].vx;
        p[a].y+=p[a].vy;

        circlefill(buffer,p[a].x,p[a].y,p[a].raio,p[a].cor);

        if(++a >= QPART){
            draw_sprite(screen,buffer,0,0);
            clear_bitmap(buffer);
            a = 0;
            iniciou = 1;
        }
    }while(!key[KEY_ESC]);

    destroy_bitmap(buffer);
    allegro_exit();
    return 0;
}
END_OF_MAIN();

 

  • Curtir 1
Link para o comentário
Compartilhar em outros sites

2 horas atrás, Midori disse:

Além da direção e sentido, após a colisão tenho que atualizar a velocidade da colisão elástica (função vfinal)

 

como eu te disse, optei por não usar colisão elástica e usei um desvio das partículas porque eu só queria ver o efeito. E colisão elástica nem existe afinal :) 

 

2 horas atrás, Midori disse:

Mantendo as atribuições aleatórias e considerando o cálculo da velocidade vfinal, de que forma o código ficaria mais fácil de controlar?

 

Deixei a atribuições aleatórias mas separei a carga em primeira_nuvem() porque acho muito mais legal programar as nuvens de partículas não aleatórias, tipo em pares de partículas em rota de colisão, ou convergindo em grupo. É mais colorido. Ou algo como o game life, onde as partículas ao colidir podem desaparecer ou se multiplicarem. Experimente.

 

Usando as estruturas como te mostrei fica bem mais fácil de controlar e de ler. Uma configuração, Nuvem, Particula e Tela. Teve oportunidade de ler o código que eu postei?

 

E as colisões em impacto() são tratadas internamente porque a função recebe o endereço da Nuvem e as partículas a verificar:

int         impacto(
    const uint16_t a,
    const uint16_t b,
    const Nuvem* N, const Config* cfg)
{

e a rotina devolve a Nuvem atualizada. Bem mais simples. Não faz diferença se vai calcular vfinal() usando colisão elástica ou extinguir uma das partículas ou explodir em um grupo mantendo a massa pra simular uma explosão ;) Só altera em um ponto.

 

E estamos em C então pode mudar o protótipo de impacto() e acrescentar o endereço de uma função de cálculo, embora eu ache que mudar isso durante uma simulação pode ter um efeito ruim na eventual transição de uma função para outra durante uma simulação

 

Note que as colisões podem ser revistas usando um algoritmo como o bubble sort, e no lugar daquela "otimização" 1d10t@ que sai quando não faz nenhuma troca no loop --- o tal algoritmo otimizado, tipo não-***** --- você sai quando não tem nenhuma colisão no loop. É o mesmo algoritmo e acho que foi assim que eu escrevi no exemplo.
 

2 horas atrás, Midori disse:

No bloco if da colisão tive que usar as variáveis vx1/vy1 para guardar a velocidade e depois fiz algumas correções com incremento nas posições para evitar sobreposição

 

Pode usar o próprio valor avaliado na colisão e recuar as partículas para o momento imediatamente anterior à colisão, e aí deixar o algoritmo aplicar o desvio calculado com as velocidades corrigidas. Bem simples.

 

Mas acho mesmo mais intuitivo e simples para calcular usar velocidade linear e ângulo ao invés de vx e vy

  • Curtir 1
Link para o comentário
Compartilhar em outros sites

@arfneto Lendo o seu código me veio algumas ideias. Agora em vez de vetor estou usando lista encadeada. Está rodando ok, mas quando tento incluir uma função para destruir uma partícula o programa trava e fecha.

 

Comentei a linha com esse problema.

 

Tentei resolver com break no loop da função para evitar deletar várias de uma vez, mas continuou travando.

 

#include <allegro.h>

struct _particula{
    float x;
    float y;
    int raio;
    int massa;
    float vx;
    float vy;
    int cor;
    struct _particula *proxima;
};

struct _config{
    BITMAP *buffer;
    int gfxm;
    int qpart;
    int largura;
    int altura;
};

typedef struct _particula PARTICULA;
typedef struct _config CONFIG;

PARTICULA *cria_particula(PARTICULA *,CONFIG *);
PARTICULA *destroi_particula(PARTICULA *);
void atualiza_posicao(PARTICULA *,CONFIG *);
int colidiu(PARTICULA *,PARTICULA *);
float vfinal(int,int,float,float);
void iniciar(CONFIG *);
void sair(CONFIG *);

int main(){
    PARTICULA *particula = NULL;
    PARTICULA *pa = NULL;
    CONFIG config;

    config.qpart = 50;
    config.largura = 640;
    config.altura = 480;
    config.gfxm = GFX_AUTODETECT_WINDOWED;
    iniciar(&config);
    config.buffer = create_bitmap(config.largura,config.altura);

    while(config.qpart--){
        pa = cria_particula(pa,&config);
    }
    particula = pa;

    do{
        atualiza_posicao(particula, &config);

        circlefill(config.buffer,
            particula->x,
            particula->y,
            particula->raio,
            particula->cor);

        //particula = destroi_particula(particula);

        if((particula = particula->proxima) == NULL){
            particula = pa;
            draw_sprite(screen,config.buffer,0,0);
            clear_bitmap(config.buffer);
        }
    }while(!key[KEY_ESC]);

    sair(&config);

    return 0;
}
END_OF_MAIN();

int colidiu(PARTICULA *p1, PARTICULA *p2){
    float qx = (p1->x - p2->x)*(p1->x - p2->x);
    float qy = (p1->y - p2->y)*(p1->y - p2->y);
    float qr = (p1->raio + p2->raio)*(p1->raio + p2->raio);
    return qx + qy <= qr;
}

float vfinal(int m1, int m2, float v1,float v2){
	return (v1 * (m1 - m2) + 2 * v2 * m2)/(m1 + m2);
}

PARTICULA *cria_particula(PARTICULA *particula, CONFIG *config){
    PARTICULA *pa = (PARTICULA*)malloc(sizeof(PARTICULA));
    PARTICULA *pb;
    int r = 50 + (rand() % 200);
    int g = 50 + (rand() % 200);
    int b = 50 + (rand() % 200);

    pa->cor = makecol(r,g,b);

NPART:
    pa->raio = pa->massa = 3 + rand() % 18;
    pa->vx = rand() % 3;
    pa->vy = rand() % 3;
    pa->x = (rand() % (config->largura-pa->raio));
    pa->y = (rand() % (config->altura-pa->raio));

    if(pa->x<pa->raio){pa->x = pa->raio;}
    if(pa->y<pa->raio){pa->y = pa->raio;}

    for(pb=particula;pb!=NULL;pb=pb->proxima){
        if(colidiu(pa,pb)){goto NPART;}
    }
    pa->proxima = particula;
    return pa;
}

void atualiza_posicao(PARTICULA *particula, CONFIG *config){
    PARTICULA *pb = particula;

    while(pb!=NULL){
        if(pb != particula){
            if(colidiu(pb,particula)){
                float vx1 = particula->vx;
                float vy1 = particula->vy;

                if(particula->x > pb->x){
                    particula->x+=0.5;pb->x-=0.5;
                }
                if(particula->y > pb->y){
                    particula->y+=0.5;pb->y-=0.5;
                }

                particula->vx = vfinal(
                    particula->massa,pb->massa,vx1,pb->vx);
                particula->vy = vfinal(
                    particula->massa,pb->massa,vy1,pb->vy);
                pb->vx = vfinal(
                    pb->massa,particula->massa,pb->vx,vx1);
                pb->vy = vfinal(
                    pb->massa,particula->massa,pb->vy,vy1);

                particula->x+=particula->vx;
                particula->y+=particula->vy;
                pb->x+=pb->vx;
                pb->y+=pb->vy;
            }
		}
        pb = pb->proxima;
    }

    if(particula->x>=config->largura-particula->raio){
        particula->x=(config->largura-particula->raio)-1;
        particula->vx*=-1;
    }
    if(particula->y>=config->altura-particula->raio){
        particula->y=(config->altura-particula->raio)-1;
        particula->vy*=-1;
    }		
    if(particula->x<=particula->raio){
        particula->x=particula->raio+1;
        particula->vx*=-1;
    }
    if(particula->y<=particula->raio){
        particula->y=particula->raio+1;
        particula->vy*=-1;
    }
    particula->x += particula->vx;
    particula->y += particula->vy;
}

PARTICULA *destroi_particula(PARTICULA *particula){
    PARTICULA	*pa = NULL;
    PARTICULA	*pb = particula;

    while(pb != NULL){
        if(pb->vx && pb->vx < 1) break;
        pa = pb;
        pb = pb->proxima;
    }

    if(pb == NULL) return particula;

    if(pa == NULL){
        particula = pb->proxima;
    }else{
        pa->proxima = pb->proxima;
    }
    free(pb);

    return particula;
}

void iniciar(CONFIG *config){
    allegro_init();
    install_keyboard();
    set_color_depth(32);
    set_gfx_mode(
        config->gfxm,
        config->largura,
        config->altura,0,0);
    srand(time(0));
}

void sair(CONFIG *config){
    destroy_bitmap(config->buffer);
    allegro_exit();
}

 

Link para o comentário
Compartilhar em outros sites

34 minutos atrás, Midori disse:

Agora em vez de vetor estou usando lista encadeada

 

Não é uma boa ideia, acredito.

 

Entenda que está tratando de uma coleção de partículas. Tudo que fizer roda milhões de vezes num minuto. E é essencial manter num mínimo as chamadas de função e qualquer mudança de contexto, como o uso de funções ou ponteiros para estruturas complexas que quase certamente vão comprometer o uso do cache. Deve ficar como 50x mais lento, fácil.

 

Um vetor linear é a opção mais eficiente. É o que os processadores modernos fazem melhor. E coordenadas polares.

 

Vou ler o código, mas não recomendo seguir por esse caminho. Se medir os tempos vai ficar surpreso.

 

 

adicionado 23 minutos depois

Li seu programa.
 

Não posso rodar o programa de fato porque como te disse não entendo nada disso e só escrevi aquele programa porque achei que ia ser bacana de ver na tela, e é :) e porque meu sistema de build consegue instalar essas bibliotecas grandes sem esforço nenhum. Já usei SDL, Qt e wsWidgets, mas essa Allegro eu não conhecia. Mas são muito similares todas.

 

No entanto só tenho acesso fácil à versão 5. Tenho os dois manuais, das versões 4.4 e 5.X, porque eu na verdade usei o seu programa e não sabia por onde começar. E fiz o simples: usei o seu programa, os manuais e o meu modelo, com uma estrutura de Tela com Nuvem, e uma configuração.

 

De volta ao programa

 

Está usando uma lista para vincular coisas que não tem relação. Só vai ter trabalho e vai ficar muito, mas muito lento se aumentar o número de partículas.

 

Entenda que uma lista ligada NÃO é um Node. E sempre que declarar como se fosse vai perder algo. Veja uma estrutura de lista comum:


struct no
{
    void*      item;
    struct no* proxima;
    struct no* anterior;
};  // no
typedef struct no Node;

struct a_propria_lista
{
    char*     nome;
    unsigned  quantos;
    unsigned  maximo;
    Node*     inicio;
    Node*     fim;
};
typedef struct a_propria_lista Lista;

E entenda a diferença: é muito conveniente manter os metadados da lista juntos. Isso sem falar no pesadelo que seria ter mais de uma lista no mesmo programa sem isso. 
 

A lista é um objeto complexo, e se você encapsular os atributos dela DENTRO dela própria a vida fica muito mais fácil.

 

Se você precisa apagar partículas --- e eu não sei se entendo porque --- devia manter ponteiros para trás na lista. Assim na hora de apagar não teria que ir de novo e de novo ao início da lista. Onde salvou o início da lista para poder procurar a partícula? Ficar varrendo essa lista durante uma simulação vai levar eras em um caso de muitas partículas

 

Faz mais sentido criar uma Nuvem, e tem a ver com a abstração normal do modelo. E seria bacana ter grupos de partículas interagindo. Imagine um grupo delas indo numa direção e aí você ativa um outro em direção ao mesmo ponto :) E muda as cores na colisão.

  • Curtir 1
Link para o comentário
Compartilhar em outros sites

Crie uma conta ou entre para comentar

Você precisa ser um usuário para fazer um comentário

Criar uma conta

Crie uma nova conta em nossa comunidade. É fácil!

Crie uma nova conta

Entrar

Já tem uma conta? Faça o login.

Entrar agora

Sobre o Clube do Hardware

No ar desde 1996, o Clube do Hardware é uma das maiores, mais antigas e mais respeitadas comunidades sobre tecnologia do Brasil. Leia mais

Direitos autorais

Não permitimos a cópia ou reprodução do conteúdo do nosso site, fórum, newsletters e redes sociais, mesmo citando-se a fonte. Leia mais

×
×
  • Criar novo...