star-sim

Barnes-Hut gravity simulation.
git clone git://git.amin.space/star-sim.git
Log | Files | Refs | README | LICENSE

sim.c (11009B)


      1 #include "sim.h"
      2 
      3 #include <stdbool.h>
      4 
      5 bool PAUSED = false;
      6 bool BRUTE_FORCE = false;
      7 bool RENDER_GRID = false;
      8 bool RENDER_BOUNDING_BOX = false;
      9 bool RENDER_TRAILS = false;
     10 
     11 
     12 void sim_init(struct SimState *sim_state, int field_width, int field_height)
     13 {
     14     if (!sim_state)
     15     {
     16         // TODO: handle invalid pointer error
     17         return;
     18     }
     19 
     20     sim_state->num_stars = NUM_STARS;
     21 
     22     sim_state->bounding_box.center_x = field_width / 2;
     23     sim_state->bounding_box.center_y = field_height / 2;
     24     sim_state->bounding_box.side_length_x = 0;
     25     sim_state->bounding_box.side_length_y = 0;
     26 
     27     sim_state->view.dx = 0;
     28     sim_state->view.dy = 0;
     29     sim_state->view.zoom = 1;
     30 
     31     float min_x = field_width / 2;
     32     float max_x = field_width / 2;
     33     float min_y = field_height / 2;
     34     float max_y = field_height / 2;
     35 
     36     for (int i = 0; i < sim_state->num_stars; ++i)
     37     {
     38         int star_x = rand() % field_width;
     39         int star_y = rand() % field_height;
     40 
     41         struct Star *star = &sim_state->stars[i];
     42         star->x = star_x;
     43         star->y = star_y;
     44         star->angle = ((float)rand()/(float)(RAND_MAX)) * 2 * M_PI;
     45         star->speed = 0;
     46         star->mass = 5;
     47         star->size = star_calc_size(star->mass);
     48         star->color = COLOR_WHITE;
     49         //printf("%f, %f, %f\n", star->angle, star->speed, star->mass);
     50 
     51         if (star_x <= min_x)
     52         {
     53             min_x = star_x;
     54         }
     55         else if (star_x >= max_x)
     56         {
     57             max_x = star_x;
     58         }
     59         if (star_y <= min_y)
     60         {
     61             min_y = star_y;
     62         }
     63         else if (star_y >= max_y)
     64         {
     65             max_y = star_y;
     66         }
     67     }
     68     sim_bounding_box_update(&sim_state->bounding_box, min_x, min_y, max_x, max_y);
     69 
     70     sim_state->qt = quad_tree_init();
     71 }
     72 
     73 
     74 void sim_bounding_box_update(struct SimBounds *bounds, float min_x, float min_y, float max_x, float max_y)
     75 {
     76     if (!bounds)
     77     {
     78         // TODO: handle invalid pointer error
     79         return;
     80     }
     81 
     82     float bounding_square_side_length = fmaxf(max_x - min_x, max_y - min_y);
     83     bounds->side_length_x = bounding_square_side_length;
     84     bounds->side_length_y = bounding_square_side_length;
     85     bounds->center_x = (min_x + max_x) / 2;
     86     bounds->center_y = (min_y + max_y) / 2;
     87 }
     88 
     89 
     90 void sim_update(struct SimState *sim_state, int field_width, int field_height)
     91 {
     92     if (!sim_state)
     93     {
     94         // TODO: handle invalid pointer error
     95         return;
     96     }
     97 
     98     float min_x = sim_state->bounding_box.center_x;
     99     float max_x = sim_state->bounding_box.center_x;
    100     float min_y = sim_state->bounding_box.center_y;
    101     float max_y = sim_state->bounding_box.center_y;
    102 
    103     int num_stars = sim_state->num_stars;
    104     struct Star *stars = sim_state->stars;
    105     struct QuadTree *qt = sim_state->qt;
    106 
    107 
    108     if (BRUTE_FORCE)
    109     {
    110         for (int i = 0; i < num_stars; ++i)
    111         {
    112             for (int j = i + 1; j < num_stars; ++j)
    113             {
    114                 star_attract(&stars[i], &stars[j]);
    115             }
    116         }
    117     }
    118     else
    119     {
    120         quad_tree_node_free(qt->root);
    121         qt->root = quad_tree_node_init(
    122                 sim_state->bounding_box.center_x,
    123                 sim_state->bounding_box.center_y,
    124                 sim_state->bounding_box.side_length_x / 2,
    125                 sim_state->bounding_box.side_length_y / 2);
    126 
    127         for (int i = 0; i < num_stars; ++i)
    128         {
    129             quad_tree_node_insert_star(qt->root, &(stars[i]));
    130         }
    131         for (int i = 0; i < num_stars; ++i)
    132         {
    133             // TODO: Will this result in stars being attracted to each other
    134             // twice?
    135             quad_tree_calc_force_on_star(qt->root, &(stars[i]));
    136         }
    137     }
    138     for (int i = 0; i < num_stars; ++i)
    139     {
    140         stars[i].x += sinf(stars[i].angle) * stars[i].speed;
    141         stars[i].y -= cosf(stars[i].angle) * stars[i].speed;
    142         stars[i].speed *= DRAG;
    143 
    144 #ifdef RESPAWN_STARS
    145         if (stars[i].x < 0 || stars[i].x > field_width
    146             || stars[i].y < 0 || stars[i].y > field_height)
    147         {
    148             stars[i].x = rand() % field_width;
    149             stars[i].y = rand() % field_height;
    150             stars[i].angle = 0;
    151             stars[i].speed = 0;
    152         }
    153 #endif
    154 
    155         if (stars[i].x <= min_x)
    156         {
    157             min_x = stars[i].x;
    158         }
    159         else if (stars[i].x >= max_x)
    160         {
    161             max_x = stars[i].x;
    162         }
    163         if (stars[i].y <= min_y)
    164         {
    165             min_y = stars[i].y;
    166         }
    167         else if (stars[i].y >= max_y)
    168         {
    169             max_y = stars[i].y;
    170         }
    171     }
    172 
    173     sim_bounding_box_update(&sim_state->bounding_box, min_x, min_y, max_x, max_y);
    174 }
    175 
    176 
    177 void sim_render(struct OffscreenBuffer *buffer, float dt, struct SimState *sim_state)
    178 {
    179     if (!buffer || !sim_state)
    180     {
    181         // TODO: handle invalid pointer error
    182         return;
    183     }
    184 
    185     int num_stars = sim_state->num_stars;
    186     struct Star *stars = sim_state->stars;
    187     struct QuadTree *qt = sim_state->qt;
    188 
    189     float dx = sim_state->view.dx;
    190     float dy = sim_state->view.dy;
    191     float zoom = sim_state->view.zoom;
    192 
    193     for (int i = 0; i < num_stars; ++i)
    194     {
    195         uint32_t color;
    196         if (BRUTE_FORCE)
    197         {
    198             color = COLOR_CYAN;
    199         }
    200         else
    201         {
    202             color = stars[i].color;
    203         }
    204         sim_set_pixel(
    205             buffer,
    206             sim_calc_render_offset(zoom, dx, stars[i].x + (sinf(stars[i].angle) * stars[i].speed) * dt, buffer->width/2),
    207             sim_calc_render_offset(zoom, dy, stars[i].y - (cosf(stars[i].angle) * stars[i].speed) * dt, buffer->height/2),
    208             color);
    209     }
    210 
    211     if (RENDER_GRID)
    212     {
    213         sim_grid_render(buffer, qt->root, COLOR_GREEN, &sim_state->view);
    214     }
    215     if (RENDER_BOUNDING_BOX)
    216     {
    217         sim_bounding_box_render(buffer, &sim_state->bounding_box, COLOR_RED, &sim_state->view);
    218     }
    219 }
    220 
    221 
    222 void sim_grid_render(struct OffscreenBuffer *buffer, struct QuadTreeNode *node, uint32_t color, struct SimView *view)
    223 {
    224     if (!buffer)
    225     {
    226         // TODO: handle invalid pointer error
    227         return;
    228     }
    229 
    230     if (node && node->ne && node->nw && node->sw && node->se)
    231     {
    232         for (int x = (node->cell->center_x - node->cell->distance_x);
    233             x <= (node->cell->center_x + node->cell->distance_x);
    234             ++x)
    235         {
    236             sim_set_pixel(
    237                     buffer,
    238                     sim_calc_render_offset(view->zoom, view->dx, x, buffer->width/2),
    239                     sim_calc_render_offset(view->zoom, view->dy, node->cell->center_y, buffer->height/2),
    240                     color);
    241         }
    242 
    243         for (int y = (node->cell->center_y - node->cell->distance_y);
    244             y <= (node->cell->center_y + node->cell->distance_y);
    245             ++y)
    246         {
    247             sim_set_pixel(
    248                     buffer,
    249                     sim_calc_render_offset(view->zoom, view->dx, node->cell->center_x, buffer->width/2),
    250                     sim_calc_render_offset(view->zoom, view->dy, y, buffer->height/2),
    251                     color);
    252         }
    253 
    254         sim_grid_render(buffer, node->ne, color, view);
    255         sim_grid_render(buffer, node->nw, color, view);
    256         sim_grid_render(buffer, node->sw, color, view);
    257         sim_grid_render(buffer, node->se, color, view);
    258     }
    259 }
    260 
    261 
    262 void sim_bounding_box_render(struct OffscreenBuffer *buffer, struct SimBounds *bounding_box, uint32_t color, struct SimView *view)
    263 {
    264     if (!buffer)
    265     {
    266         // TODO: handle invalid pointer error
    267         return;
    268     }
    269 
    270     if (bounding_box)
    271     {
    272         int reticle_radius = 3;
    273         for (int x = bounding_box->center_x - reticle_radius;
    274             x <= bounding_box->center_x + reticle_radius;
    275             x++)
    276         {
    277             sim_set_pixel(
    278                     buffer,
    279                     sim_calc_render_offset(view->zoom, view->dx, x, buffer->width/2),
    280                     sim_calc_render_offset(view->zoom, view->dy, bounding_box->center_y, buffer->height/2),
    281                     color);
    282         }
    283         for (int y = bounding_box->center_y - reticle_radius;
    284             y <= bounding_box->center_y + reticle_radius;
    285             y++)
    286         {
    287             sim_set_pixel(
    288                     buffer,
    289                     sim_calc_render_offset(view->zoom, view->dx, bounding_box->center_x, buffer->width/2),
    290                     sim_calc_render_offset(view->zoom, view->dy, y, buffer->height/2),
    291                     color);
    292         }
    293 
    294         float half_length_x = bounding_box->side_length_x / 2;
    295         float half_length_y = bounding_box->side_length_y / 2;
    296 
    297         for (int x = (bounding_box->center_x - half_length_x);
    298             x <= (bounding_box->center_x + half_length_x);
    299             ++x)
    300         {
    301             sim_set_pixel(
    302                     buffer,
    303                     sim_calc_render_offset(view->zoom, view->dx, x, buffer->width/2),
    304                     sim_calc_render_offset(view->zoom, view->dy, bounding_box->center_y - half_length_y, buffer->height/2),
    305                     color);
    306             sim_set_pixel(
    307                     buffer,
    308                     sim_calc_render_offset(view->zoom, view->dx, x, buffer->width/2),
    309                     sim_calc_render_offset(view->zoom, view->dy, bounding_box->center_y + half_length_y, buffer->height/2),
    310                     color);
    311         }
    312 
    313         for (int y = (bounding_box->center_y - half_length_y);
    314             y <= (bounding_box->center_y + half_length_y);
    315             ++y)
    316         {
    317             sim_set_pixel(
    318                     buffer,
    319                     sim_calc_render_offset(view->zoom, view->dx, bounding_box->center_x - half_length_x, buffer->width/2),
    320                     sim_calc_render_offset(view->zoom, view->dy, y, buffer->height/2),
    321                     color);
    322             sim_set_pixel(
    323                     buffer,
    324                     sim_calc_render_offset(view->zoom, view->dx, bounding_box->center_x + half_length_x, buffer->width/2),
    325                     sim_calc_render_offset(view->zoom, view->dy, y, buffer->height/2),
    326                     color);
    327         }
    328     }
    329 }
    330 
    331 
    332 float sim_calc_render_offset(float zoom, float delta, float pos, float center)
    333 {
    334     return ((1 - zoom) * center) + (pos + delta) * zoom;
    335 }
    336 
    337 
    338 void sim_set_pixel(struct OffscreenBuffer *buffer, uint32_t x, uint32_t y, uint32_t color)
    339 {
    340     if (!buffer)
    341     {
    342         // TODO: handle invalid pointer error
    343         return;
    344     }
    345 
    346     /* Origin is (0, 0) on the upper left.
    347      * To go one pixel right, increment by 32 bits.
    348      * To go one pixel down, increment by (buffer.width * 32) bits.
    349      */
    350     if (x < buffer->width && y < buffer->height)
    351     {
    352         uint8_t *pixel_pos = (uint8_t *)buffer->memory;
    353         pixel_pos += ((BYTES_PER_PIXEL*x) + (buffer->pitch * y));
    354         uint32_t *pixel = (uint32_t *)pixel_pos;
    355         *pixel = color;
    356     }
    357 }
    358 
    359 
    360 void sim_cleanup(struct SimState *sim_state)
    361 {
    362     if (!sim_state)
    363     {
    364         // TODO: handle invalid pointer error
    365         return;
    366     }
    367 
    368     quad_tree_free(sim_state->qt);
    369 }