Ir ao conteúdo
  • Cadastre-se

C Colisão de Partículas (Motif/X11)


Posts recomendados

Tenho um projeto para o ambiente X envolvendo movimentos com particulas. Esse por enquanto é um caso de controlar a direção, colisoes nas bordas entre particulas, etc. Ainda quero criar mais funções com relações físicas como uma particula caindo, considerando a gravidade, enquanto ganha aceleração durante a queda e quica até parar.

 

Para este código tem coisas que preciso melhorar, no anexo p.ex dá para ver que acontece casos de sobreposição.

 

O código para quem se interessar ou quiser fazer sugestões ou melhorias.

 

Para compilar estes links para as libs do Motif e X11 são necesários:  -lXm -lXt -lXext -lX11

 

forum.png.af67a7935eef25d9b89cea3a712bdf83.png

 

 

#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/extensions/Xdbe.h>
#include <Xm/Xm.h>
#include <Xm/DrawingA.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>

#define DEFAULT_WIDTH   300
#define DEFAULT_HEIGHT  300
#define DEFAULT_PART    3
#define	MAX_PART        50

#define RAND(max) (rand() % (max))
#define RGB(r, g, b) ((r) * 65536 + (g) * 256 + (b))

static XdbeBackBuffer   buffer;
static Display*         display;
static Window           window;
static GC               gc;
static Dimension        width;
static Dimension        height;

typedef struct{
    double x;
    double y;
}Point;

typedef struct{
    Point   position;
    Point   velocity;
    Point   acceleration;
    int     radius;
    double  mass;
    long    color;
}Particle;

XtAppContext init_app(int* argc, char* argv[]);
Particle* alloc_particles(int count);
Bool event_app(XtAppContext app);
Particle* draw(Particle* particle);
Particle* collision(Particle* particle, int count);
Particle* move(Particle* particle, int count);

int main(int argc, char* argv[]){
    int opt;
    int count = DEFAULT_PART;
    width = DEFAULT_WIDTH;
    height = DEFAULT_HEIGHT;

    while((opt = getopt(argc, argv, "w:h:c:")) != -1){
        switch(opt){
            case 'h':
                height = atoi(optarg);
                break;
            case 'w':
                width = atoi(optarg);
                break;
            case 'c':
                count = atoi(optarg);
                break;
        }
    }
	
    if(count < 1 || count > MAX_PART){
        fprintf(stderr, "Error, count cannot be < 1 or > %d\n", MAX_PART);
        exit(1);
    }
	
    XtAppContext app = init_app(&argc, argv);
	
    if(app != NULL){
        srand(time(NULL));
        Particle* particle = alloc_particles(count);
		
        if(particle != NULL){
            while(event_app(app) == False){
                move(particle, count);				
            }
            free(particle);
        }else{
            fprintf(stderr, "Cannot alloc particle\n");
            exit(1);
        }		
    }else{
        fprintf(stderr, "Invalid %dx%d dimension\n", width, height);
        exit(1);
    }
    return 0;
}

/******************************************************************************
 *  - operações basicas do Motif/X11 para criar o ambiente.
 *  - AppClass: xparticle (shell-widget) e area (area-widget)
 *  - Chama funcao para alocar o buffer onde as particulas serao diretamente
 *      criadas. Metodo para evitar XClearWindow com efeito flickering
 *      na atualizacao da janela para os movimentos das particulas.
 *****************************************************************************/
XtAppContext init_app(int* argc, char* argv[]){
    XtAppContext    app;
    Widget          shell;
    Widget          area;
    Dimension       display_width;
    Dimension       display_height;
	
    shell = XtVaAppInitialize(&app, "xparticle", NULL, 0, argc, argv, NULL, NULL);
    XtVaSetValues(shell, XmNtitle, "Particle Collision", XmNwidth, width, XmNheight, height, NULL);
    area = XtVaCreateManagedWidget("area", xmDrawingAreaWidgetClass, shell, NULL);
    display = XtDisplay(area);
    display_width = DisplayWidth(display, DefaultScreen(display));
    display_height = DisplayHeight(display, DefaultScreen(display));
	
    if(width > display_width || width < DEFAULT_WIDTH 
        || height > display_height || height < DEFAULT_HEIGHT){
            return NULL;
    }	

    gc = XCreateGC(display, RootWindowOfScreen(XtScreen(area)), 0, NULL);
    XtRealizeWidget(shell);
    window = XtWindow(area);
    XSelectInput(display, window, KeyPressMask);
    buffer = XdbeAllocateBackBufferName(display, window, XdbeBackground);	
    return app;
}

/******************************************************************************
 * - Atribui valores aleatorios a estrutura da particula alocada dinamicamente
 * - Aceleracao ainda com valor neutro para o calculo da posicao em move
 * - Ainda sem teste de sobreposicao e relação de raio-quantidade-dimensoes 
 *****************************************************************************/
Particle* alloc_particles(int count){
    Particle*	particle = (Particle*)malloc(sizeof(Particle) * count);
	
    if(particle != NULL){
        for(int i = 0; i < count; i += 1){
            Particle* A = &particle[i];
            A->radius = RAND(40) + 5;
            A->position.x = RAND(width - A->radius);
            A->position.y = RAND(height - A->radius) + A->radius + 1;
			
            if(A->position.x < A->radius) A->position.x = A->radius + 1;
            if(A->position.y < A->radius) A->position.y = A->radius + 1;
			
            A->velocity.x = (RAND(95) + 5)/100.0;
            A->velocity.y = (RAND(95) + 5)/100.0;
            A->mass = A->radius;
            A->acceleration.x = A->acceleration.y = 1;
            A->color = RGB(RAND(128), RAND(128), RAND(128));
        }
    }
    return particle;
}

/*****************************************************************************
 * - Funcao Bool testa a entrada da tecla q que marca a saida da aplicacao
 * - Chama as funcoes Event.
 * - Chama a funcao XSendEvent:
 *  para Exposure para realizar o movimento da particula durante o loop de main
 ****************************************************************************/
Bool event_app(XtAppContext app){
    XEvent	event;
	
    XtAppNextEvent(app, &event);
    XtDispatchEvent(&event);		
	
    if(event.type == KeyPress){
        if(XLookupKeysym(&event.xkey, 0) == XK_q){
            return True;
        }
    }
    XSendEvent(display, window, 1, ExposureMask, &event);
    return False;
}

/******************************************************************************
 *  - Atribui a cor e desenha particula com X*Arc.
 *  - O angulo inicia o desenho do arco a partir do "horario" 3 no sentido
 *      anti-horario. A posicao/coordenada x/y não fica centralizada.
 *  - A operacao 360 * 64 e padrão para o ultimo argumento para criar o circulo
 *  - correção feita com operações no raio para acertar/centralizar a posicao
 *****************************************************************************/
Particle* draw(Particle* particle){
    XSetForeground(display, gc, particle->color);
    XFillArc(display, buffer, gc, 
        particle->position.x - particle->radius, 
        particle->position.y - particle->radius,
        particle->radius * 2,
        particle->radius * 2,
        0, 360 * 64);
    return particle;
}

/******************************************************************************
 * - Bloco For removido do loop da funcao move. 
 * - Testa a particula "atual" (count recebida do indice i de move) e as demais.
 * - Atualiza a velocidade e direcao x/y apos a colisao
 *****************************************************************************/
Particle* collision(Particle* particle, int count){
    Particle* A = &particle[count];
	
    for(int i = 0; i < count; i += 1){
        Particle* B = &particle[i];
		
        double dx = A->position.x - B->position.x;
        double dy = A->position.y - B->position.y;
        double area = dx * dx + dy * dy;
		
        if(area <= A->radius * A->radius + B->radius * B->radius){
            double dot = 
                dx * (B->velocity.x - A->velocity.x)
                + dy * (B->velocity.y - A->velocity.y);

            if(dot > 0){
                double sx = dx * dot / area;
                double sy = dy * dot / area;
                double aweight = 2 * B->mass/(A->mass + B->mass);
                double bweight = 2 * A->mass/(A->mass + B->mass);

                A->velocity.x += aweight * sx;
                A->velocity.y += aweight * sy;
                B->velocity.x -= bweight * sx;
                B->velocity.y -= bweight * sy;
            }
        }
    }
    return A;
}

/******************************************************************************
 * - O loop seleciona as particulas com seus respectivos valores, testa as
 *  colisoes, desenha no buffer e aplica a substituicao pela area/janela.
 *****************************************************************************/
Particle* move(Particle* particle, int count){
    XdbeSwapInfo    swinfo = {window, XdbeBackground};
	
    for(int i = 0; i < count; i += 1){
        Particle* A = collision(particle, i);
        draw(A);

        if(A->position.x < A->radius || A->position.x > width - A->radius){
            A->velocity.x = -A->velocity.x;
        }
		
        if(A->position.y < A->radius || A->position.y > height - A->radius){
            A->velocity.y = -A->velocity.y;
        }
		
        A->position.x += A->velocity.x * A->acceleration.x;
        A->position.y += A->velocity.y * A->acceleration.y;
    }
    XdbeSwapBuffers(display, &swinfo, 1);
    return particle;
}

 

Para o cálculo que apliquei na função collision, crédito para uma resposta do user desta página,

 

https://gamedev.stackexchange.com/questions/20516/ball-collisions-sticking-together

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

  • Midori alterou o título para Colisão de Partículas (Motif/X11)

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

Ebook grátis: Aprenda a ler resistores e capacitores!

EBOOK GRÁTIS!

CLIQUE AQUI E BAIXE AGORA MESMO!