You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

435 lines
9.1 KiB

  1. #include <png.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <math.h>
  5. #include <time.h>
  6. #include <string.h>
  7. #include <stdbool.h>
  8. #include <unistd.h>
  9. #include <stdint.h>
  10. //#include "qdbmp.h"
  11. #include "engine.h"
  12. typedef struct
  13. {
  14. uint8_t red;
  15. uint8_t green;
  16. uint8_t blue;
  17. } pixel_t;
  18. void reverse(char s[])
  19. {
  20. int i, j;
  21. char c;
  22. for (i = 0, j = strlen(s)-1; i<j; i++, j--) {
  23. c = s[i];
  24. s[i] = s[j];
  25. s[j] = c;
  26. }
  27. }
  28. /* itoa: convert n to characters in s */
  29. void itoa(int n, char s[])
  30. {
  31. int i, sign;
  32. if ((sign = n) < 0) /* record sign */
  33. n = -n; /* make n positive */
  34. i = 0;
  35. do { /* generate digits in reverse order */
  36. s[i++] = n % 10 + '0'; /* get next digit */
  37. } while ((n /= 10) > 0); /* delete it */
  38. if (sign < 0)
  39. s[i++] = '-';
  40. s[i] = '\0';
  41. reverse(s);
  42. }
  43. //END SO
  44. //Data that needs to be saved and loaded
  45. typedef struct config
  46. {
  47. int nStars;
  48. int imgW, imgH;
  49. int savePeriod;
  50. int iteration;
  51. char saveFile[255];
  52. } config_t;
  53. /* DEFAULT CONFIGURATION OPTIONS */
  54. int nStars = 100;
  55. int w = 1920;
  56. int h = 1080;
  57. int spawnSize = 500;
  58. double maxComponentV = 10;
  59. long m = 500;
  60. int a = 100;
  61. int iteration = 0;
  62. char s[255]; //save filename
  63. char l[255]; //load filename
  64. /* END DEFAULT CONFIGURATION OPTIONS */
  65. //Allocate these arrays later!
  66. RigidMass_t *stars;
  67. int *starsAtPixel;
  68. void generateStars()
  69. {
  70. int maxRadius = spawnSize / 2;
  71. int randAddonX = w/2;
  72. int randAddonY = h/2;
  73. RigidMass_t new;
  74. new.mass = m;
  75. new.dvx = 0;
  76. new.dvy = 0;
  77. new.tick_time = 0.05;
  78. for(int i = 0; i < nStars; i++)
  79. {
  80. //Generate radius between 0 and maxRadius
  81. double radius = (double)rand() / RAND_MAX * maxRadius;
  82. //Generate random x - must be less than r
  83. //Otherwise we end up with sqrt(<0) which makes a big mess
  84. double x = ((double)rand() / RAND_MAX * radius * 2) - radius;
  85. //Use x to make y which satisfies x and r
  86. double yRange = sqrt(radius * radius - x * x);
  87. double y = (double)rand() / RAND_MAX * 2 * yRange - yRange;
  88. new.x = x + randAddonX;
  89. new.y = y + randAddonY;
  90. new.vx = y / maxRadius * maxComponentV; //Scale velocities
  91. new.vy = -x / maxRadius * maxComponentV;
  92. stars[i] = new;
  93. }
  94. }
  95. void hsl_to_rgb(int h, double s, double l, char *rOut, char *gOut, char *bOut)
  96. {
  97. if(h == 360) h = 0;
  98. if(s > 1) s = 1;
  99. if(l > 1) l = 1;
  100. double c = (1 - abs(2 * l - 1)) * s;
  101. double x = c * (1 - fabs(fmod(((double)h)/60, 2) - 1));
  102. double m = l - c / 2;
  103. double r, g, b;
  104. int q = h / 60;
  105. switch(q)
  106. {
  107. case(0):
  108. r = c;
  109. g = x;
  110. b = 0;
  111. break;
  112. case(1):
  113. r = x;
  114. g = c;
  115. b = 0;
  116. break;
  117. case(2):
  118. r = 0;
  119. g = c;
  120. b = x;
  121. break;
  122. case(3):
  123. r = 0;
  124. g = x;
  125. b = c;
  126. break;
  127. case(4):
  128. r = x;
  129. g = 0;
  130. b = c;
  131. break;
  132. case(5):
  133. r = c;
  134. g = 0;
  135. b = x;
  136. }
  137. *rOut = 255 * (r + m);
  138. *gOut = 255 * (g + m);
  139. *bOut = 255 * (b + m);
  140. }
  141. pixel_t *out;
  142. void addToImage()
  143. {
  144. memset(out, 0, sizeof(pixel_t) * w * h);
  145. memset(starsAtPixel, 0, sizeof(int) * w * h);
  146. #define IMAGE_INC_AMOUNT 50
  147. for(int a = 0; a < nStars; a++)
  148. {
  149. int starX = stars[a].x;
  150. int starY = stars[a].y;
  151. if(starX > 0 && starX < w && starY > 0 && starY < h)
  152. {
  153. starsAtPixel[starX * h + starY]++;
  154. }
  155. }
  156. for(int a = 0; a < nStars; a++)
  157. {
  158. //pixel colour depends on how many stars are at that location
  159. int starX = stars[a].x;
  160. int starY = stars[a].y;
  161. if(starX > 0 && starX < w && starY > 0 && starY < h)
  162. {
  163. int nStarsAtLoc = starsAtPixel[starX * h + starY] * 10;
  164. //if(nStarsAtLoc > 360) nStarsAtLoc = 360;
  165. pixel_t p = {.red = 0, .green = 0, .blue = 0};
  166. if(nStarsAtLoc > 50) nStarsAtLoc = 50;
  167. p.red = nStarsAtLoc * 5;
  168. p.green = p.red;
  169. p.blue = p.red;
  170. //hsl_to_rgb(nStarsAtLoc, 1, nStarsAtLoc / (double)100, &curR, &curG, &curB);
  171. out[starX + starY * w] = p;
  172. }
  173. }
  174. }
  175. void saveImage(double frame)
  176. {
  177. char filename[50];
  178. char buffer[46];
  179. strcpy(filename, "img/");
  180. itoa(frame, buffer);
  181. strcat(filename, buffer);
  182. strcat(filename, ".png");
  183. FILE *fp;
  184. fp = fopen(filename, "wb");
  185. if(!fp)
  186. {
  187. printf("ERROR: Could not save image!\r\n");
  188. return;
  189. }
  190. png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
  191. png_infop info_ptr = png_create_info_struct(png_ptr);
  192. png_set_IHDR (png_ptr,
  193. info_ptr,
  194. w, //image width
  195. h, //height
  196. 8, //bit depth
  197. PNG_COLOR_TYPE_RGB,
  198. PNG_INTERLACE_NONE,
  199. PNG_COMPRESSION_TYPE_DEFAULT,
  200. PNG_FILTER_TYPE_DEFAULT);
  201. png_byte **row_pointers = png_malloc (png_ptr, h * sizeof (png_byte *));
  202. for(int y = 0; y < h; y++)
  203. {
  204. png_byte *row = png_malloc (png_ptr, sizeof(uint8_t) * w * 3);
  205. row_pointers[y] = row;
  206. for(int x = 0; x < w; x++)
  207. {
  208. pixel_t pixel = out[x + w*y];
  209. *row++ = pixel.red;
  210. *row++ = pixel.green;
  211. *row++ = pixel.blue;
  212. }
  213. }
  214. png_init_io (png_ptr, fp);
  215. png_set_rows (png_ptr, info_ptr, row_pointers);
  216. png_write_png (png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
  217. fclose(fp);
  218. //Free memory
  219. for(int y = 0; y < h; y++)
  220. {
  221. png_free(png_ptr, row_pointers[y]);
  222. }
  223. png_free(png_ptr, row_pointers);
  224. }
  225. void saveState(int it)
  226. {
  227. FILE *fp;
  228. char filename[300]; //lots of room
  229. char buffer[15];
  230. strcpy(filename, "saves/");
  231. strcat(filename, s);
  232. itoa(it, buffer);
  233. strcat(filename, buffer);
  234. strcat(filename, "-ID.sgs");
  235. fp = fopen(filename, "wb");
  236. //first, write the config parameters
  237. config_t conf;
  238. conf.nStars = nStars;
  239. conf.imgW = w;
  240. conf.imgH = h;
  241. conf.savePeriod = a;
  242. conf.iteration = it;
  243. strcpy(conf.saveFile, s);
  244. fwrite(&conf, sizeof(conf), 1, fp);
  245. //then, write all the stars
  246. fwrite(stars, sizeof(RigidMass_t), nStars, fp);
  247. //close the file
  248. fclose(fp);
  249. }
  250. void prnHelp(char *name)
  251. {
  252. //TODO: iterations per frame
  253. //ticktime?
  254. printf("Usage: %s [OPTION]\r\n", name);
  255. printf("Available options:\r\n");
  256. printf("N\t New simulation\r\n");
  257. printf("n\t Number of stars\r\n");
  258. printf("w WDTH\t Image width\r\n");
  259. printf("h HGHT\t Image height\r\n");
  260. printf("p SIZE\t Spawn size - Where new stars are put\r\n");
  261. printf("v VEL\t Maximum component velocity of new stars\r\n");
  262. printf("m MASS\t Mass of stars\r\n");
  263. printf("s FILE\t Filename for autosaves\r\n");
  264. printf("a PER\t How often to autosave, in iterations\r\n");
  265. printf("L FILE\t Load previous simulation\r\n");
  266. exit(0);
  267. }
  268. int main(int argc, char **argv)
  269. {
  270. printf("\t\tStephen's Gravitational Engine\r\n");
  271. printf("\t\t\twww.scd31.com\r\n");
  272. //Process commandline arguments
  273. bool success = false;
  274. bool new = false;
  275. bool load = false;
  276. strcpy(s, "sim"); //default autosave filename
  277. char c;
  278. while((c = getopt(argc, argv, "Nn:w:h:p:v:m:a:L:s:")) != -1)
  279. {
  280. success = true;
  281. switch(c)
  282. {
  283. case 'N': new = true; break;
  284. case 'n': nStars = atoi(optarg); break;
  285. case 'w': w = atoi(optarg); break;
  286. case 'h': h = atoi(optarg); break;
  287. case 'p': spawnSize = atoi(optarg); break;
  288. case 'v': maxComponentV = atof(optarg); break;
  289. case 'm': m = atol(optarg); break;
  290. case 'a': a = atoi(optarg); break;
  291. case 's':
  292. if(strlen(optarg) >= 255)
  293. {
  294. printf("ERROR: Save filename too long.\r\n");
  295. }
  296. else strcpy(s, optarg);
  297. break;
  298. case 'L':
  299. load = true;
  300. if(strlen(optarg) >= 255)
  301. {
  302. printf("ERROR: Load filename too long.\r\n");
  303. }
  304. else strcpy(l, optarg);
  305. break;
  306. default:
  307. prnHelp(argv[0]);
  308. }
  309. }
  310. if(!success)
  311. {
  312. prnHelp(argv[0]);
  313. }
  314. //Command validation
  315. if(!new && !load)
  316. {
  317. printf("ERROR: Either start a new simulation or load an old one.\r\n");
  318. exit(1);
  319. }
  320. if(new && load)
  321. {
  322. printf("ERROR: Cannot start a new simulation and load an old one at the same time!\r\n");
  323. exit(1);
  324. }
  325. if(new)
  326. {
  327. printf("Starting a new simulation with the following parameters:\r\n");
  328. printf("\t N = %d\r\n", nStars);
  329. printf("\t W = %d, H = %d\r\n", w, h);
  330. printf("\t spawn = %d\r\n", spawnSize);
  331. printf("\t vComponentMax = %f\r\n", maxComponentV);
  332. printf("\t m = %ldE8 kg\r\n", m);
  333. printf("\t Autosave: %s-ID.sgs every %d iterations\r\n", s, a);
  334. //allocate arrays
  335. stars = malloc(sizeof(RigidMass_t) * nStars);
  336. starsAtPixel = malloc(sizeof(int) * w * h);
  337. srand(time(NULL));
  338. generateStars();
  339. }
  340. else if(load) //technically redundant. can never be too sure
  341. {
  342. printf("Continuing %s...\r\n", l);
  343. FILE *fp;
  344. fp = fopen(l, "r");
  345. //first, read the config parameters
  346. config_t conf;
  347. fread(&conf, sizeof(conf), 1, fp);
  348. nStars = conf.nStars;
  349. w = conf.imgW;
  350. h = conf.imgH;
  351. a = conf.savePeriod;
  352. iteration = conf.iteration;
  353. strcpy(s, conf.saveFile);
  354. //second, read the stars
  355. stars = malloc(sizeof(RigidMass_t) * nStars);
  356. fread(stars, sizeof(RigidMass_t), nStars, fp);
  357. starsAtPixel = malloc(sizeof(int) * w * h);
  358. //close the file
  359. fclose(fp);
  360. printf("%d stars loaded.\r\n", nStars);
  361. printf("Continuing from i=%d\r\n", iteration);
  362. }
  363. //malloc some space for our output image
  364. out = malloc(w * h * sizeof(pixel_t));
  365. //Runs until CTRL+C
  366. //TODO: CTRL+C should force autosave
  367. while(1)
  368. {
  369. //iterate();
  370. printf("Started gentree\n");
  371. barnesIterate(stars, nStars, w, h);
  372. printf("Gentree finished\n");
  373. //exit(1);
  374. addToImage();
  375. saveImage(iteration);
  376. if(iteration % a == 0)
  377. {
  378. saveState(iteration);
  379. }
  380. iteration++;
  381. }
  382. return 0;
  383. }