No Description

main.c 8.2KB

    #include <stdio.h> #include <stdlib.h> #include <math.h> #include <time.h> #include <string.h> #include <unistd.h> #include "qdbmp.h" typedef struct RigidMass { long mass; double vx, vy; double x, y; double dvx, dvy; double tick_time; } RigidMass_t; double G = 0.00667408; //Calculates delta V on body, due to other. void calcGravity(RigidMass_t *body, RigidMass_t other) { double dx = body->x - other.x; double dy = body->y - other.y; double dist_squared = dx * dx + dy * dy; double v = -G * other.mass / dist_squared * body->tick_time; double hyp = sqrt(dist_squared); body->dvx += v * (dx) / hyp; body->dvy += v * (dy) / hyp; } void processTick(RigidMass_t *body) { //technically, we could just remove dvx/dvy //and work with vx/vy directly, since they don't change anything other than when //processing the tick here body->vx += body->dvx; body->vy += body->dvy; body->dvx = 0; body->dvy = 0; body->x += body->vx * body->tick_time; body->y += body->vy * body->tick_time; } //END PHYSICS ENGINE //stackoverflow stuff /* reverse: reverse string s in place */ void reverse(char s[]) { int i, j; char c; for (i = 0, j = strlen(s)-1; i<j; i++, j--) { c = s[i]; s[i] = s[j]; s[j] = c; } } /* itoa: convert n to characters in s */ void itoa(int n, char s[]) { int i, sign; if ((sign = n) < 0) /* record sign */ n = -n; /* make n positive */ i = 0; do { /* generate digits in reverse order */ s[i++] = n % 10 + '0'; /* get next digit */ } while ((n /= 10) > 0); /* delete it */ if (sign < 0) s[i++] = '-'; s[i] = '\0'; reverse(s); } //END SO //Data that needs to be saved and loaded typedef struct config { int nStars; int imgW, imgH; int savePeriod; int iteration; char saveFile[255]; } config_t; /* DEFAULT CONFIGURATION OPTIONS */ int nStars = 100; int w = 1920; int h = 1080; int spawnSize = 500; double maxComponentV = 10; long m = 500; int a = 100; int iteration = 0; char s[255]; //save filename char l[255]; //load filename /* END DEFAULT CONFIGURATION OPTIONS */ //booleans are cool typedef int bool; #define true 1 #define false 0 //Allocate this array later! RigidMass_t *stars; void generateStars() { int randAddonX = w/2 - spawnSize/2; int randAddonY = h/2 - spawnSize/2; RigidMass_t new; new.mass = m; new.dvx = 0; new.dvy = 0; new.tick_time = 0.05; for(int i = 0; i < nStars; i++) { new.x = rand() % spawnSize + randAddonX; new.y = rand() % spawnSize + randAddonY; double dx = new.x - w / 2; double dy = new.y - h / 2; if(abs(dx) <= abs(dy)) { new.vx = (double)rand()/(double)(RAND_MAX/maxComponentV); new.vy = -new.vx * dx / dy; } else { new.vy = (double)rand()/(double)(RAND_MAX/maxComponentV); new.vx = -new.vy * dy / dx; } if(dx > dy) { new.vx *= -1; new.vy *= -1; } stars[i] = new; } } void iterate() { #pragma omp parallel for for(int a = 0; a < nStars; a++) { for(int b = 0; b < nStars; b++) { //If stars are closer than 0.1 units apart, we don't calculate the force on each other //this is important since we're stepping through time. If we end up with two stars ridiculously //close together, the math will act as if they were in that position for the entire tick, //whereas they would likely only be there for a small fraction of that. //we don't want any stars to be flung away at a fraction of the speed of light! if(abs(stars[a].x - stars[b].x) > 0.1 && abs(stars[a].y - stars[b].y) > 0.1) { //printf("%d, %d \r\n", stars[a].dvx, stars[a].dvy); calcGravity(stars + a, stars[b]); } } } //'commit' the deltaV for(int a = 0; a < nStars; a++) { processTick(stars + a); //printf("%f, %f \r\n", stars[a].x, stars[a].y); } } BMP *out; void addToImage() { for(int a = 0; a < nStars; a++) { BMP_SetPixelRGB(out, stars[a].x, stars[a].y, 0, 150, 0); } } void saveImage(double frame) { char filename[50]; char buffer[46]; strcpy(filename, "img/"); itoa(frame, buffer); strcat(filename, buffer); strcat(filename, ".bmp"); BMP_WriteFile(out, filename); BMP_Free(out); out = BMP_Create(w, h, 24); } void saveState(int it) { FILE *fp; char filename[300]; //lots of room char buffer[15]; strcpy(filename, "saves/"); strcat(filename, s); itoa(it, buffer); strcat(filename, buffer); strcat(filename, "-ID.sgs"); fp = fopen(filename, "wb"); //first, write the config parameters config_t conf; conf.nStars = nStars; conf.imgW = w; conf.imgH = h; conf.savePeriod = a; conf.iteration = it; strcpy(conf.saveFile, s); fwrite(&conf, sizeof(conf), 1, fp); //then, write all the stars fwrite(stars, sizeof(RigidMass_t), nStars, fp); //close the file fclose(fp); } void prnHelp(char *name) { //TODO: iterations per frame //ticktime? printf("Usage: %s [OPTION]\r\n", name); printf("Available options:\r\n"); printf("N\t New simulation\r\n"); printf("n\t Number of stars\r\n"); printf("w WDTH\t Image width\r\n"); printf("h HGHT\t Image height\r\n"); printf("p SIZE\t Spawn size - Where new stars are put\r\n"); printf("v VEL\t Maximum component velocity of new stars\r\n"); printf("m MASS\t Mass of stars\r\n"); printf("s FILE\t Filename for autosaves\r\n"); printf("a PER\t How often to autosave, in iterations\r\n"); printf("L FILE\t Load previous simulation\r\n"); exit(0); } int main(int argc, char **argv) { printf("\t\tStephen's Gravitational Engine\r\n"); printf("\t\t\twww.scd31.com\r\n"); //Process commandline arguments bool success = false; bool new = false; bool load = false; strcpy(s, "sim"); //default autosave filename char c; while((c = getopt(argc, argv, "Nn:w:h:p:v:m:a:L:s:")) != -1) { success = true; switch(c) { case 'N': new = true; break; case 'n': nStars = atoi(optarg); break; case 'w': w = atoi(optarg); break; case 'h': h = atoi(optarg); break; case 'p': spawnSize = atoi(optarg); break; case 'v': maxComponentV = atof(optarg); break; case 'm': m = atol(optarg); break; case 'a': a = atoi(optarg); break; case 's': if(strlen(optarg) >= 255) { printf("ERROR: Save filename too long.\r\n"); } else strcpy(s, optarg); break; case 'L': load = true; if(strlen(optarg) >= 255) { printf("ERROR: Load filename too long.\r\n"); } else strcpy(l, optarg); break; default: prnHelp(argv[0]); } } if(!success) { prnHelp(argv[0]); } //Command validation if(!new && !load) { printf("ERROR: Either start a new simulation or load an old one.\r\n"); exit(1); } if(new && load) { printf("ERROR: Cannot start a new simulation and load an old one at the same time!\r\n"); exit(1); } if(new) { printf("Starting a new simulation with the following parameters:\r\n"); printf("\t N = %d\r\n", nStars); printf("\t W = %d, H = %d\r\n", w, h); printf("\t spawn = %d\r\n", spawnSize); printf("\t vComponentMax = %f\r\n", maxComponentV); printf("\t m = %ldE8 kg\r\n", m); printf("\t Autosave: %s-ID.sgs every %d iterations\r\n", s, a); //allocate array stars = malloc(sizeof(RigidMass_t) * nStars); srand(time(NULL)); generateStars(); } else if(load) //technically redundant. can never be too sure { printf("Continuing %s...\r\n", l); FILE *fp; fp = fopen(l, "r"); //first, read the config parameters config_t conf; fread(&conf, sizeof(conf), 1, fp); nStars = conf.nStars; w = conf.imgW; h = conf.imgH; a = conf.savePeriod; iteration = conf.iteration; strcpy(s, conf.saveFile); //second, read the stars stars = malloc(sizeof(RigidMass_t) * nStars); fread(stars, sizeof(RigidMass_t), nStars, fp); //close the file fclose(fp); printf("%d stars loaded.\r\n", nStars); printf("Continuing from i=%d\r\n", iteration); } out = BMP_Create(w, h, 24); //Runs until CTRL+C //TODO: CTRL+C should force autosave bool justStarted = true; while(1) { iterate(); addToImage(); //gives the next frame some iterations to build up //without justStarted, the loaded iteration is rerendered, but only from //a single iteration, which stands out quite a bit. if(justStarted) { justStarted = false; } else { if(iteration % 10 == 0) { saveImage(iteration / 10); } if(iteration % a == 0) { saveState(iteration); } } iteration++; } return 0; }