/* Touched by: * sah@coraid.com, 06/02 * * To do: * Decrease the mass of the galaxy as the stars leave. * Add fullscreen option on right button menu. * Give stars tiny mass and make them react to each other. * Give stars size based on their mass. */ #include #include #include #include #define MIN_GALS 2 #define MAX_GALS 4 #define MAX_STARS 500 #define MAX_IDELTAT 50 #define MIN_GALSZ 1e-1 #define MAX_GALSZ 1.5e-1 #define COLORBASE 16 #define EPSILON 1e-8 #define SQRT_EPSILON 1e-4 #define DELTAT (MAX_IDELTAT * SQRT_EPSILON) #define QCONS 1e-3 #define EPS 1/(EPSILON * SQRT_EPSILON * DELTAT * DELTAT * QCONS); #define M_PI 3.141592653589793238462643383 typedef struct Star Star; struct Star { double pos[3], vel[3]; }; typedef struct Galaxy Galaxy; struct Galaxy { int mass, nstars, galcol, swappoints, col; double pos[3], vel[3], opos[3], ovel[3]; Star stars[MAX_STARS]; Point oldpoints[MAX_STARS], newpoints[MAX_STARS]; }; typedef struct Uni Uni; struct Uni { int midx, midy, ngals, f_hititerations, step; double mat[3][3], scale, diff[3], rot_x, rot_y; Galaxy gals[MAX_GALS]; }; enum { WHITE, PALEGREYGREEN, PALEGREYBLUE, GREYGREEN, GREYBLUE, RED, GREEN, BLUE, CYAN, MAGENTA, YELLOW, PALEYELLOW, DARKYELLOW, DARKGREEN, PALEGREEN, MEDGREEN, DARKBLUE, PALEBLUEGREEN, PALEBLUE, BLUEGREEN, YELLOWGREEN, MEDBLUE, PURPLEBLUE, RANDY, TWINKLE, SPARKLE, FWORKS, NCOLORS, }; int colors[NCOLORS] = { [WHITE] DWhite, [RED] DRed, [GREEN] DGreen, [BLUE] DBlue, [CYAN] DCyan, [MAGENTA] DMagenta, [YELLOW] DYellow, [PALEYELLOW] DPaleyellow, [DARKYELLOW] DDarkyellow, [DARKGREEN] DDarkgreen, [PALEGREEN] DPalegreen, [MEDGREEN] DMedgreen, [DARKBLUE] DDarkblue, [PALEBLUEGREEN] DPalebluegreen, [PALEBLUE] DPaleblue, [BLUEGREEN] DBluegreen, [GREYGREEN] DGreygreen, [PALEGREYGREEN] DPalegreygreen, [YELLOWGREEN] DYellowgreen, [MEDBLUE] DMedblue, [GREYBLUE] DGreyblue, [PALEGREYBLUE] DPalegreyblue, [PURPLEBLUE] DPurpleblue }; char *mbtns[] = { [WHITE] "White", [RED] "Red", [GREEN] "Green", [BLUE] "Blue", [CYAN] "Cyan", [MAGENTA] "Magenta", [YELLOW] "Yellow", [PALEYELLOW] "Paleyellow", [DARKYELLOW] "Darkyellow", [DARKGREEN] "Darkgreen", [PALEGREEN] "Palegreen", [MEDGREEN] "Medgreen", [DARKBLUE] "Darkblue", [PALEBLUEGREEN] "Palebluegreen", [PALEBLUE] "Paleblue", [BLUEGREEN] "Bluegreen", [GREYGREEN] "Greygreen", [PALEGREYGREEN] "Palegreygreen", [YELLOWGREEN] "Yellowgreen", [MEDBLUE] "Medblue", [GREYBLUE] "Greyblue", [PALEGREYBLUE] "Palegreyblue", [PURPLEBLUE] "PurpleBlue", [RANDY] "Random", [FWORKS] "Fireworks", [SPARKLE] "Sparkle", [TWINKLE] "Twinkle", [NCOLORS] 0 }; char *rbtns[] = { "exit", 0 }; Image *images[NCOLORS]; Uni univ; uint color; int alloc_colors(void) { int i; for(i=0; ichan, 0, colors[i]); return 1; } void free_colors(void) { int i; for(i=0; ivel[0] = g->vel[1] = g->vel[2] = frand() * 2.0 - 1.0; g->ovel[0] = g->ovel[1] = g->ovel[2] = g->vel[0]; g->pos[0] = g->opos[0] = -g->vel[0] * DELTAT * univ.f_hititerations + frand() - 0.5; g->pos[1] = g->opos[0] = -g->vel[1] * DELTAT * univ.f_hititerations + frand() - 0.5; g->pos[2] = g->opos[0] = -g->vel[2] * DELTAT * univ.f_hititerations + frand() - 0.5; g->mass = (int) (frand() * 1000.0) + 1; g->nstars = nrand(MAX_STARS / 2) + MAX_STARS / 2; if(color == RANDY) g->col = rand()%RANDY; } void init_stars(Galaxy *g) { int i; double galsz, sinw, cosw, d, h, v; Star *st; Point *oldp, *newp; galsz = MAX_GALSZ*frand() + MIN_GALSZ; st = g->stars; oldp = g->oldpoints; newp = g->newpoints; for (i=0; i < g->nstars; i++, st++, oldp++, newp++) { d = 2.0 * M_PI * frand(); sinw = sin(d); cosw = cos(d); d = frand() * galsz; h = frand() * exp(-2.0 * (d / galsz)) / 5.0 * galsz; if(frand() < 0.5) h = -h; st->pos[0] = univ.mat[0][0] * d * cosw + univ.mat[1][0] * d * sinw + univ.mat[2][0] * h + g->pos[0]; st->pos[1] = univ.mat[0][1] * d * cosw + univ.mat[1][1] * d * sinw + univ.mat[2][1] * h + g->pos[1]; st->pos[2] = univ.mat[0][2] * d * cosw + univ.mat[1][2] * d * sinw + univ.mat[2][2] * h + g->pos[2]; v = sqrt(g->mass * QCONS / sqrt(d * d + h * h)); st->vel[0] = -univ.mat[0][0] * v * sinw + univ.mat[1][0] * v * cosw + g->vel[0]; st->vel[1] = -univ.mat[0][1] * v * sinw + univ.mat[1][1] * v * cosw + g->vel[1]; st->vel[2] = -univ.mat[0][2] * v * sinw + univ.mat[1][2] * v * cosw + g->vel[2]; st->vel[0] *= DELTAT; st->vel[1] *= DELTAT; st->vel[2] *= DELTAT; *oldp = Pt(0, 0); *newp = Pt(0, 0);; } } static void startover(void) { int i; Galaxy *g; univ.step = 0; univ.rot_y = 0; univ.rot_x = 0; univ.ngals = MIN_GALS + (frand()*(MAX_GALS-MIN_GALS+1)); for(i=0, g=univ.gals; i < univ.ngals; i++, g++) { init_mat(); init_gal(g); init_stars(g); } draw(screen, screen->r, display->black, nil, ZP); } void init(void) { srand(time(0)); if(initdraw(nil, nil, "bez") < 0) sysfatal("initdraw failed: %r"); if(!alloc_colors()) sysfatal("failed to init colors\n"); univ.f_hititerations = 100; univ.scale = (double) (Dx(screen->r) + Dy(screen->r)) / 8.0; univ.midx = screen->r.min.x + Dx(screen->r) / 2; univ.midy = screen->r.min.y +Dy(screen->r) / 2; } /* calculate star pos,vel relative to the gravity of the other galaxies */ void calc_star_rel_gal(Galaxy *g, Star *s) { double d, d0, d1, d2, v0, v1, v2; v0 = s->vel[0], v1 = s->vel[1], v2 = s->vel[2]; d0 = g->pos[0] - s->pos[0]; d1 = g->pos[1] - s->pos[1]; d2 = g->pos[2] - s->pos[2]; d = d0 * d0 + d1 * d1 + d2 * d2; if (d > EPSILON) d = g->mass / (d * sqrt(d)) * DELTAT * DELTAT * QCONS; else d = g->mass * EPS; v0 += d0 * d; v1 += d1 * d; v2 += d2 * d; s->vel[0] = v0, s->vel[1] = v1, s->vel[2] = v2; s->pos[0] += v0, s->pos[1] += v1, s->pos[2] += v2; } /* calculate galaxy pos/vel relative to other galaxies */ void calc_gal_rel_gals(Galaxy *thisg) { double d, d0, d1, d2; int i; Galaxy *g; for (i=0, g=univ.gals; i < univ.ngals; i++, g++) { if(g == thisg) continue; d0 = g->opos[0] - thisg->pos[0]; d1 = g->opos[1] - thisg->pos[1]; d2 = g->opos[2] - thisg->pos[2]; d = d0 * d0 + d1 * d1 + d2 * d2; if(d > EPSILON) d = thisg->mass * thisg->mass / (d * sqrt(d)); else d = thisg->mass * thisg->mass / (EPSILON * SQRT_EPSILON); d *= DELTAT * QCONS; d0 *= d; d1 *= d; d2 *= d; thisg->vel[0] += d0 / thisg->mass; thisg->vel[1] += d1 / thisg->mass; thisg->vel[2] += d2 / thisg->mass; g->vel[0] -= d0 / g->mass; g->vel[1] -= d1 / g->mass; g->vel[2] -= d2 / g->mass; } thisg->pos[0] += thisg->vel[0] * DELTAT; thisg->pos[1] += thisg->vel[1] * DELTAT; thisg->pos[2] += thisg->vel[2] * DELTAT; } void redraw_stars(Galaxy *g) { uint i; Rectangle r; Point *oldp, *newp; if((g->swappoints ^= 1)) { oldp = g->newpoints; newp = g->oldpoints; } else { oldp=g->oldpoints; newp=g->newpoints; } if(color == FWORKS) g->col = rand()%RANDY; for(i=0; i < g->nstars; i++, oldp++, newp++) { if(color == SPARKLE) g->col = rand()%RANDY; else if(color == TWINKLE) g->col = rand()%PALEGREYBLUE; r = Rpt(*oldp, addpt(*oldp, Pt(2, 2))); draw(screen, r, display->black, nil, ZP); r = Rpt(*newp, addpt(*newp, Pt(2, 2))); draw(screen, r, images[g->col], nil, ZP); } } void redraw_gal(Galaxy *g) { int i; Point *p; Star *s; s=g->stars; // try one of these instead for nifty effects ;) // p = g->newpoints; // p = g->swappoints ? g->oldpoints : g->newpoints; p = g->swappoints ? g->newpoints : g->oldpoints; for(i=0; i < g->nstars; i++, s++, p++) { calc_star_rel_gal(g, s); p->x = (((s->pos[0]) - (s->pos[2])) * univ.scale) + univ.midx; p->y = (((s->pos[1]) - (((s->pos[0]) + (s->pos[2])))) * univ.scale) + univ.midy; } calc_gal_rel_gals(g); redraw_stars(g); } void redraw_univ(void) { int i; Galaxy *g; univ.rot_y += 0.01; univ.rot_x += 0.004; for(i=0, g=univ.gals; i < univ.ngals; i++, g++) redraw_gal(g); for(i=0, g=univ.gals; i < univ.ngals; i++, g++) { g->ovel[0] = g->vel[0]; g->ovel[1] = g->vel[1]; g->ovel[2] = g->vel[2]; g->opos[0] = g->pos[0]; g->opos[1] = g->pos[1]; g->opos[2] = g->pos[2]; } if(++univ.step > univ.f_hititerations * 4) startover(); } void eresized(int new) { if(new && getwindow(display, Refnone) < 0) sysfatal("can't reattach to window"); draw(screen, screen->r, display->black, nil, ZP); } void set_galcolor(int col) { int i; Galaxy *g; if(col < RANDY) for(i=0, g=univ.gals; i < univ.ngals; i++, g++) g->col = col; else if(col == RANDY) for(i=0, g=univ.gals; i < univ.ngals; i++, g++) g->col = rand()%RANDY; } Menu rmenu = { rbtns }, mmenu = { mbtns }; Mouse m; void main(void) { vlong tbegin, tend; ulong frames=0; void init(void); int col; init(); einit(Emouse); eresized(0); tbegin = nsec(); for(;;) { if(ecanmouse()) { m = emouse(); if(m.buttons&4) if(!emenuhit(3, &m, &rmenu)) { tend = nsec(); print("fps: %uf\n", frames/((tend - tbegin)/1000000000.0)); exits(0); } if(m.buttons&2) if((col=emenuhit(2, &m, &mmenu)) >= 0) set_galcolor(color=col); } redraw_univ(); frames++; } }