diff options
| -rw-r--r-- | balls.c | 40 | ||||
| -rw-r--r-- | balls.h | 15 | ||||
| -rw-r--r-- | collision.c | 22 | ||||
| -rw-r--r-- | graphics.c | 13 |
4 files changed, 58 insertions, 32 deletions
@@ -21,7 +21,7 @@ enum { DEFAULT_NBALLS = 3, VMAX = 3, - MASS = 10, + G_PER_KG = 1000, FPS = 60, NS_PER_SEC = 1000000000, @@ -44,6 +44,7 @@ typedef struct { } BallArg; static int ballcolors[] = { DRed, DGreen, DBlue }; +static uint radii[] = { 20, 30, 50 }; static Rectangle bounds = {{PAD, PAD}, {PAD+WIDTH, PAD+HEIGHT}}; int init(char *label, Mousectl **mctl, Keyboardctl **kctl); @@ -52,9 +53,11 @@ void spawnballs(int n); Channel **allocchans(int nchans, int elsize, int nel); void mcopycolskip(Channel *vec[], Channel **matrix[], int n, int col, int skip); void vcopyskip(Channel *dst[], Channel *src[], int n, int skip); -void nooverlapcircles(Point centers[], int n); +void nooverlapcircles(Point centers[], int n, uint radius); Point randptinrect(Rectangle r); int randint(int lo, int hi); +uint maxelem(uint arr[], uint n); +double mass(uint radius); void ball(void *arg); void broadcast(Point p, Channel *cs[], int n); void frametick(void *arg); @@ -143,7 +146,7 @@ spawnballs(int n) { if ((ps = malloc(n*sizeof(Point))) == nil) sysfatal("failed to allocate position array"); - nooverlapcircles(ps, n); + nooverlapcircles(ps, n, maxelem(radii, NELEMS(radii))); if ((cs = malloc(n*sizeof(Channel **))) == nil) sysfatal("failed to allocate channel matrix"); @@ -164,7 +167,8 @@ spawnballs(int n) { arg->b.p = ps[i]; arg->b.v = V((double) randint(-VMAX, VMAX+1), (double) randint(-VMAX, VMAX+1)); - arg->b.m = MASS; + arg->b.r = radii[randint(0, NELEMS(radii))]; + arg->b.m = mass(arg->b.r); arg->color = ballcolors[randint(0, NELEMS(ballcolors))]; @@ -234,14 +238,14 @@ vcopyskip(Channel *dst[], Channel *src[], int n, int skip) { } void -nooverlapcircles(Point centers[], int n) { +nooverlapcircles(Point centers[], int n, uint radius) { int i, j; srand(time(0)); for (i = 0; i < n; i++) { - centers[i] = randptinrect(insetrect(bounds, RADIUS)); + centers[i] = randptinrect(insetrect(bounds, radius)); for (j = 0; j < i; j++) - if (iscollision(centers[j], centers[i])) + if (iscollision(centers[j], radius, centers[i], radius)) break; if (j < i) { /* overlapping */ i--; @@ -260,6 +264,24 @@ randint(int lo, int hi) { return (rand() % (hi-lo)) + lo; } +uint +maxelem(uint arr[], uint n) { + uint max; + + if (n == 0) + return 0; + max = arr[--n]; + while (n-- > 0) + if (arr[n] > max) + max = arr[n]; + return max; +} + +double +mass(uint radius) { + return (double) radius / 10 / G_PER_KG; +} + void ball(void *arg) { BallArg *barg; @@ -273,8 +295,8 @@ ball(void *arg) { barg = (BallArg *) arg; b = (Ball) barg->b; - fill = alloccircle(barg->color, DTransparent); - erase = alloccircle(BG, DTransparent); + fill = alloccircle(barg->color, DTransparent, b.r); + erase = alloccircle(BG, DTransparent, b.r); if (fill == nil ||erase == nil) sysfatal("failed to allocate image"); @@ -4,10 +4,6 @@ #include <draw.h> #include <cursor.h> -enum { - RADIUS = 20, -}; - typedef struct { double x, y; } Vec; @@ -17,9 +13,10 @@ typedef struct { } Line; typedef struct { - Point p; /* position */ - Vec v; /* velocity */ - double m; /* mass */ + Point p; /* position [pixels] */ + Vec v; /* velocity [m/s] */ + uint r; /* radius [pixels] */ + double m; /* mass [kg] */ } Ball; Vec vsub(Vec v1, Vec v2); @@ -33,9 +30,9 @@ Vec V(double x, double y); Vec Vpt(Point p, Point q); void drawbg(Image *walls, Image *bg); -Image *alloccircle(int fg, int bg); +Image *alloccircle(int fg, int bg, uint radius); void drawcircle(Image *m, Point pos); -int iscollision(Point p, Point q); +int iscollision(Point p1, uint r1, Point p2, uint r2); void collideball(Ball *b1, const Ball *b2); void collidewall(Ball *b, Rectangle wall); diff --git a/collision.c b/collision.c index 1fa083b..bf359c0 100644 --- a/collision.c +++ b/collision.c @@ -5,12 +5,12 @@ static int min(int a, int b); static int max(int a, int b); int -iscollision(Point p, Point q) { +iscollision(Point p1, uint r1, Point p2, uint r2) { int dx, dy; - dx = p.x - q.x; - dy = p.y - q.y; - return (dx*dx + dy*dy) <= 4*RADIUS*RADIUS; + dx = p1.x - p2.x; + dy = p1.y - p2.y; + return (dx*dx + dy*dy) <= (r1+r2)*(r1+r2); } void @@ -19,11 +19,11 @@ collideball(Ball *b1, const Ball *b2) { Vec d, n; double magnitude; - if (!iscollision(b1->p, b2->p)) + if (!iscollision(b1->p, b1->r, b2->p, b2->r)) return; midpoint = divpt(addpt(b1->p, b2->p), 2); d = Vpt(b2->p, b1->p); - b1->p = ptaddv(midpoint, vmuls(unitnorm(d), RADIUS)); + b1->p = ptaddv(midpoint, vmuls(unitnorm(d), b1->r)); printf("collision (%d,%d), (%d,%d)\n", b1->p.x, b1->p.y, b2->p.x, b2->p.y); @@ -41,13 +41,15 @@ collideball(Ball *b1, const Ball *b2) { void collidewall(Ball *b, Rectangle wall) { - if (b->p.x < wall.min.x+RADIUS || b->p.x > wall.max.x-RADIUS) { - b->p.x = clamp(b->p.x, wall.min.x+RADIUS, wall.max.x-RADIUS); + wall = insetrect(wall, b->r); + + if (b->p.x < wall.min.x || b->p.x > wall.max.x) { + b->p.x = clamp(b->p.x, wall.min.x, wall.max.x); printf("clamped to %d\n", b->p.x); b->v.x = -b->v.x; } - if (b->p.y < wall.min.y+RADIUS || b->p.y > wall.max.y-RADIUS) { - b->p.y = clamp(b->p.y, wall.min.y+RADIUS, wall.max.y-RADIUS); + if (b->p.y < wall.min.y || b->p.y > wall.max.y) { + b->p.y = clamp(b->p.y, wall.min.y, wall.max.y); b->v.y = -b->v.y; } } @@ -8,10 +8,13 @@ drawbg(Image *walls, Image *bg) { } Image * -alloccircle(int fg, int bg) { +alloccircle(int fg, int bg, uint radius) { Image *m, *fill; + uint d; - m = allocimage(display, Rect(0, 0, 2*RADIUS, 2*RADIUS), RGBA32, 0, bg); + d = 2*radius; /* diameter */ + printf("alloccircle: d=%u\n", d); + m = allocimage(display, Rect(0, 0, d, d), RGBA32, 0, bg); if (m == nil) return nil; @@ -21,15 +24,17 @@ alloccircle(int fg, int bg) { return nil; } - fillellipse(m, Pt(RADIUS, RADIUS), RADIUS, RADIUS, fill, ZP); + fillellipse(m, Pt(radius, radius), radius, radius, fill, ZP); freeimage(fill); return m; } void drawcircle(Image *m, Point pos) { + uint radius; Rectangle r; - r = Rpt(subpt(pos, Pt(RADIUS, RADIUS)), addpt(pos, Pt(RADIUS, RADIUS))); + radius = Dx(m->r)/2; + r = Rpt(subpt(pos, Pt(radius, radius)), addpt(pos, Pt(radius, radius))); draw(screen, r, m, nil, ZP); } |