Guest User

Numerical wave equation "solver" written in C

a guest
Jul 16th, 2026
77
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 7.38 KB | Source Code | 0 0
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <stdint.h>
  4. #include <stdbool.h>
  5. #include <math.h>
  6. #include <ncurses.h>
  7.  
  8. #define WORLD_SIZE 512
  9.  
  10. typedef struct {
  11.   double velocity;
  12.   double position;
  13. } Cell;
  14.  
  15. Cell cell_get(const Cell* cells, int32_t i) {
  16.   if (i < 0) return (Cell){0, 0};
  17.   if (i > WORLD_SIZE - 1) return (Cell){0, 0};
  18.   return cells[i];
  19. }
  20.  
  21. void cell_set(Cell cell, Cell* cells, int32_t i) {
  22.   if (i < 0 || i > WORLD_SIZE - 1) return;
  23.   cells[i] = cell;
  24. }
  25.                                                                                  inline Cell cell_make(double position) { return (Cell){0, position}; }
  26.  
  27. double dif2_num(double nx, double x, double px, double dx) {
  28.   return (nx - 2.0*x + px) / (dx * dx);
  29. }
  30.  
  31. double dif2_cell(Cell nc, Cell c, Cell pc) {
  32.   return dif2_num(nc.position, c.position, pc.position, 1.0);
  33. }
  34.  
  35. double dif2(const Cell* cells, int32_t x) {
  36.   return dif2_num(cell_get(cells, x-1).position,
  37.                   cell_get(cells, x).position,
  38.                   cell_get(cells, x+1).position, 1);
  39. }
  40.  
  41. void world_simulate(const Cell* world, Cell* output, double dt) {
  42.   for (size_t i = 0; i < WORLD_SIZE; ++i) {
  43.     Cell l = cell_get(world, (int32_t)i - 1);
  44.     Cell r  = cell_get(world, (int32_t)i + 1);
  45.     Cell c = cell_get(world, (int32_t)i);
  46.  
  47.     double a = dif2_cell(l, c, r);
  48.     output[i] = c;
  49.     output[i].velocity += a * dt;
  50.     output[i].position += output[i].velocity * dt;
  51.   }
  52. }
  53.  
  54. void step_simulation(Cell** back, Cell** front, double dt) {
  55.   world_simulate(*back, *front, dt);
  56.  
  57.   Cell* t = *back;
  58.   *back = *front;
  59.   *front = t;
  60. }
  61.  
  62. typedef struct {
  63.   double x;
  64.   double y;
  65.   double range_x; // same
  66.   double range_y; // means from -y to y
  67. } Camera;
  68.  
  69. // haha funny numbers go brr
  70. double smootherstep(double a, double b, double x) {
  71.   return a + (b - a) * (x * x * x * (x * (6.0 * x - 15.0) + 10.0));
  72. }
  73.  
  74. double lerp(double a, double b, double x) {
  75.   return a + (b - a) * x;
  76. }
  77.  
  78. int gradient(double velocity) {
  79.   double v = fabs(velocity);
  80.   if (v > 0.5) {
  81.     return '@';
  82.   } else if (v > 0.4) {
  83.     return '#';
  84.   } else if (v > 0.3) {
  85.     return '=';
  86.   } else if (v > 0.2) {
  87.     return '-';
  88.   } else if (v > 0.1) {
  89.     return '.';
  90.   }
  91.  
  92.   return ' ';
  93. }
  94.  
  95. void world_draw(const Cell* world, Camera camera, bool should_lerp) {
  96.   double from_x = -camera.range_x + camera.x;
  97.   double to_x =    camera.range_x + camera.x;
  98.   double from_y = -camera.range_y + camera.y;
  99.   double to_y =    camera.range_y + camera.y;
  100.  
  101.   for (size_t x = 0; x < (size_t)COLS; x++) {
  102.     for (size_t y = 0; y < (size_t)LINES; y++) {
  103.       double threshold = lerp(from_y, to_y, (double)y/(double)LINES);
  104.       double world_x = lerp(from_x, to_x, (double)x/(double)COLS);
  105.  
  106.       int32_t at = floor(world_x);
  107.       double t = world_x - (double)at;
  108.  
  109.       double height;
  110.       double velocity;
  111.  
  112.       if (should_lerp) {
  113.         Cell c0 = cell_get(world, at);
  114.         Cell c1 = cell_get(world, at + 1);
  115.         height = smootherstep(c0.position, c1.position, t);
  116.         velocity = smootherstep(c0.velocity, c1.velocity, t);
  117.       } else {
  118.         Cell c = cell_get(world, at);
  119.         height = c.position;
  120.         velocity = c.velocity;
  121.       }
  122.  
  123.       if (at > WORLD_SIZE-1) {
  124.         if (at - 1 <= WORLD_SIZE-1)
  125.           mvaddch(y, x, '|');
  126.         continue;
  127.       }
  128.       if (at < 0) {
  129.         if (at + 1 >= 0)
  130.           mvaddch(y, x, '|');
  131.         continue;
  132.       }
  133.  
  134.       if (height >= threshold && threshold > 0.0)
  135.         mvaddch(y, x, gradient(velocity) | A_REVERSE);
  136.  
  137.       if (height <= threshold && threshold < 0.0)
  138.         mvaddch(y, x, gradient(velocity) | A_REVERSE);
  139.  
  140.     }
  141.   }
  142. }
  143.  
  144. void ui_draw(Camera camera, bool fast_mode, bool paused, double dt, double sim_speed) {
  145.   mvprintw(LINES/2, 0, "%.2f", -camera.range_x + camera.x); // from x
  146.   mvprintw(LINES/2, COLS-6, "%.2f", camera.range_x + camera.x); // to x
  147.   mvprintw(LINES-1, COLS/2, "%.2f", -camera.range_y + camera.y); // from y
  148.   mvprintw(0, COLS/2, "%.2f", camera.range_y + camera.y); // to y
  149.  
  150.   mvprintw(0, 0, "%s", fast_mode ? "" : "(fast mode active)");
  151.   mvprintw(1, 0, "%s", paused ? "" : "(running the simulation)");
  152.   mvprintw(2, 0, "dt: %f (%.2f FPS)", dt, 1.0/dt);
  153.   mvprintw(3, 0, "simulation speed: %.2fx", sim_speed);
  154. }
  155.  
  156. void redraw(const Cell* cells, Camera* camera, bool fast_mode, bool paused, double dt, double sim_speed, bool should_lerp) {
  157.   erase();
  158.   world_draw(cells, *camera, should_lerp);
  159.   ui_draw(*camera, fast_mode, paused, dt, sim_speed);
  160.   refresh();
  161. }
  162.  
  163. double dt_from_timestamps(struct timespec start, struct timespec end) {
  164.   return (double)(end.tv_sec - start.tv_sec) +
  165.     (double)(end.tv_nsec - start.tv_nsec) * 1e-9;
  166. }
  167.  
  168. #define FAST_MODE_SPEED 2.0
  169. #define SPEED 0.25
  170. void events(
  171.   bool* paused,
  172.   bool* running,
  173.   bool* fast_mode,
  174.   bool* should_lerp,
  175.   Camera* camera,
  176.   Cell** world_back,
  177.   Cell** world_front,
  178.   double* sim_speed
  179. ) {
  180.   switch (getch()) {
  181.   case 'h':
  182.     camera->range_x *= 0.8;
  183.     break;
  184.   case 'l':
  185.     camera->range_x *= 1.2;
  186.     break;
  187.   case 'j':
  188.     camera->range_y *= 0.8;
  189.     break;
  190.   case 'k':
  191.     camera->range_y *= 1.2;
  192.     break;
  193.  
  194.   case KEY_LEFT:
  195.     camera->x -= (!*fast_mode ? FAST_MODE_SPEED : SPEED) * camera->range_x;
  196.     break;
  197.   case KEY_RIGHT:
  198.     camera->x += (!*fast_mode ? FAST_MODE_SPEED : SPEED) * camera->range_x;
  199.     break;
  200.   case KEY_UP:
  201.     camera->y -= (!*fast_mode ? FAST_MODE_SPEED : SPEED) * camera->range_y;
  202.     break;
  203.   case KEY_DOWN:
  204.     camera->y += (!*fast_mode ? FAST_MODE_SPEED : SPEED) * camera->range_y;
  205.     break;
  206.  
  207.   case '+':
  208.     *sim_speed *= 1.2;
  209.     break;
  210.   case '-':
  211.     *sim_speed *= 0.8;
  212.     break;
  213.   case '=':
  214.     *sim_speed = 1.0;
  215.     break;
  216.  
  217.   case 'a':
  218.     *should_lerp = !*should_lerp;
  219.     break;
  220.   case 'n':
  221.     camera->range_x = COLS/2.0;
  222.     camera->x = WORLD_SIZE/2.0;
  223.     camera->range_y = 1;
  224.     camera->y = 0;
  225.     break;
  226.   case 's':
  227.     step_simulation(world_back, world_front, 0.5 * *sim_speed);
  228.     break;
  229.   case 'x':
  230.     *fast_mode = !*fast_mode;
  231.     break;
  232.   case 'q':
  233.     *running = false;
  234.     break;
  235.   case ' ':
  236.     *paused = !*paused;
  237.     break;
  238.   }
  239. }
  240.  
  241. int main(void) {
  242.   Cell* world_front = malloc(sizeof(Cell) * WORLD_SIZE);
  243.   Cell* world_back  = malloc(sizeof(Cell) * WORLD_SIZE);
  244.  
  245.   world_back[WORLD_SIZE/2 + 1] = cell_make(1);
  246.   world_back[WORLD_SIZE/2 - 1] = cell_make(1);
  247.   memcpy(world_front, world_back, sizeof(Cell) * WORLD_SIZE);
  248.  
  249.   bool running = true;
  250.   bool paused  = true;
  251.   bool fast_mode = false;
  252.   bool should_lerp = true;
  253.  
  254.   struct timespec start = {0};
  255.   struct timespec end = {0};
  256.   double dt = 0.001;
  257.   double simulation_speed = 1.0;
  258.  
  259.   initscr();
  260.   noecho();
  261.   nodelay(stdscr, true);
  262.   keypad(stdscr, true);
  263.  
  264.   Camera camera = (Camera){WORLD_SIZE/2.0, 0.0, COLS/2.0, 1.0};
  265.  
  266.   world_draw(world_front, camera, should_lerp);
  267.   clock_gettime(CLOCK_MONOTONIC, &start);
  268.   while (running) {
  269.     clock_gettime(CLOCK_MONOTONIC, &end);
  270.     dt = dt_from_timestamps(start, end);
  271.     start = end;
  272.     events(&paused, &running, &fast_mode, &should_lerp, &camera, &world_front, &world_back, &simulation_speed);
  273.  
  274.     if (!paused) {
  275.       step_simulation(&world_back, &world_front, dt * simulation_speed);
  276.     }
  277.     redraw(world_front, &camera, fast_mode, paused, dt, simulation_speed, should_lerp);
  278.   }
  279.  
  280.   endwin();
  281.   free(world_front);
  282.   free(world_back);
  283. }
Advertisement
Add Comment
Please, Sign In to add comment