star-sim

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

commit f258d192d0be5a79c9c4c28bf5010eec187dd33f
parent f9f43f41a6f40a590147854369aeb5faff9b338a
Author: amin <dev@aminmesbah.com>
Date:   Sat,  5 Aug 2017 07:40:15 +0000

Add simulation bounding box.

The simulation no longer ends at the borders of the window. A bounding
square is calculated during every update. That box defines the
dimensions of the cell of the quad tree's root node.

There is now no need to respawn stars that go off the edge of the screen
as they are still contained within the Barnes-Hut tree.

Making the simulation independent of window and screen size is the first
step in adding view controls.

FossilOrigin-Name: 39d19429c1ffab94454d3c483e09e3f1e5f0d7fcca856efce37af8afab249b05
Diffstat:
MREADME.md | 3++-
Msrc/platform_sdl.c | 5+++++
Msrc/star_garden.c | 153+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
Msrc/star_garden.h | 19+++++++++++++++----
4 files changed, 160 insertions(+), 20 deletions(-)

diff --git a/README.md b/README.md @@ -10,7 +10,8 @@ efficiently calculate the forces acting on each star. ### Keyboard Shortcuts -- `b`: Toggle brute-force mode. Stars turn blue when this mode is active, and +- `b`: Toggle bounding box rendering. +- `f`: Toggle brute-force mode. Stars turn blue when this mode is active, and gravitational attraction is calculated for every single pair of stars. The Barnes-Hut grid is not updated. - `g`: Toggle Barnes-Hut grid rendering. diff --git a/src/platform_sdl.c b/src/platform_sdl.c @@ -12,6 +12,7 @@ extern bool PAUSED; extern bool BRUTE_FORCE; extern bool RENDER_GRID; +extern bool RENDER_BOUNDING_BOX; extern bool RENDER_TRAILS; static struct SDLOffscreenBuffer global_back_buffer; @@ -92,6 +93,10 @@ bool handle_event(SDL_Event *event) { if (key_code == SDLK_b) { + RENDER_BOUNDING_BOX = !RENDER_BOUNDING_BOX; + } + if (key_code == SDLK_f) + { BRUTE_FORCE = !BRUTE_FORCE; } if (key_code == SDLK_g) diff --git a/src/star_garden.c b/src/star_garden.c @@ -5,6 +5,7 @@ bool PAUSED = false; bool BRUTE_FORCE = false; bool RENDER_GRID = false; +bool RENDER_BOUNDING_BOX = false; bool RENDER_TRAILS = false; @@ -17,22 +18,70 @@ void sim_init(struct SimState *sim_state, int field_width, int field_height) } sim_state->num_stars = NUM_STARS; + sim_state->bounding_box.center_x = field_width / 2; + sim_state->bounding_box.center_y = field_height / 2; + sim_state->bounding_box.side_length_x = 0; + sim_state->bounding_box.side_length_y = 0; + + float min_x = field_width / 2; + float max_x = field_width / 2; + float min_y = field_height / 2; + float max_y = field_height / 2; + for (int i = 0; i < sim_state->num_stars; ++i) { + int star_x = rand() % field_width; + int star_y = rand() % field_height; + struct Star *star = &sim_state->stars[i]; - star->x = rand() % field_width; - star->y = rand() % field_height; + star->x = star_x; + star->y = star_y; star->angle = ((float)rand()/(float)(RAND_MAX)) * 2 * M_PI; star->speed = 0; star->mass = 5; star->size = star_calc_size(star->mass); star->color = COLOR_WHITE; //printf("%f, %f, %f\n", star->angle, star->speed, star->mass); + + if (star_x <= min_x) + { + min_x = star_x; + } + else if (star_x >= max_x) + { + max_x = star_x; + } + if (star_y <= min_y) + { + min_y = star_y; + } + else if (star_y >= max_y) + { + max_y = star_y; + } } + sim_bounding_box_update(&sim_state->bounding_box, min_x, min_y, max_x, max_y); + sim_state->qt = quad_tree_init(); } +void sim_bounding_box_update(struct SimBounds *bounds, float min_x, float min_y, float max_x, float max_y) +{ + if (!bounds) + { + // TODO: handle invalid pointer error + return; + } + + float bounding_square_side_length = fmaxf(max_x - min_x, max_y - min_y); + bounds->side_length_x = bounding_square_side_length; + bounds->side_length_y = bounding_square_side_length; + bounds->center_x = (min_x + max_x) / 2; + bounds->center_y = (min_y + max_y) / 2; +} + + void sim_update(struct SimState *sim_state, int field_width, int field_height) { if (!sim_state) @@ -41,17 +90,16 @@ void sim_update(struct SimState *sim_state, int field_width, int field_height) return; } - // TODO: either limit the bounds of the simulation, or base these values on - // the smallest bounding rectangle - int center_x = field_width / 2; - int center_y = field_height / 2; - int dist_x = field_width / 2; - int dist_y = field_height / 2; + float min_x = sim_state->bounding_box.center_x; + float max_x = sim_state->bounding_box.center_x; + float min_y = sim_state->bounding_box.center_y; + float max_y = sim_state->bounding_box.center_y; int num_stars = sim_state->num_stars; struct Star *stars = sim_state->stars; struct QuadTree *qt = sim_state->qt; + if (BRUTE_FORCE) { for (int i = 0; i < num_stars; ++i) @@ -65,7 +113,12 @@ void sim_update(struct SimState *sim_state, int field_width, int field_height) else { quad_tree_node_free(qt->root); - qt->root = quad_tree_node_init(center_x, center_y, dist_x, dist_y); + qt->root = quad_tree_node_init( + sim_state->bounding_box.center_x, + sim_state->bounding_box.center_y, + sim_state->bounding_box.side_length_x / 2, + sim_state->bounding_box.side_length_y / 2); + for (int i = 0; i < num_stars; ++i) { quad_tree_node_insert_star(qt->root, &(stars[i])); @@ -82,6 +135,7 @@ void sim_update(struct SimState *sim_state, int field_width, int field_height) stars[i].x += sinf(stars[i].angle) * stars[i].speed; stars[i].y -= cosf(stars[i].angle) * stars[i].speed; stars[i].speed *= DRAG; + #ifdef RESPAWN_STARS if (stars[i].x < 0 || stars[i].x > field_width || stars[i].y < 0 || stars[i].y > field_height) @@ -92,7 +146,26 @@ void sim_update(struct SimState *sim_state, int field_width, int field_height) stars[i].speed = 0; } #endif + + if (stars[i].x <= min_x) + { + min_x = stars[i].x; + } + else if (stars[i].x >= max_x) + { + max_x = stars[i].x; + } + if (stars[i].y <= min_y) + { + min_y = stars[i].y; + } + else if (stars[i].y >= max_y) + { + max_y = stars[i].y; + } } + + sim_bounding_box_update(&sim_state->bounding_box, min_x, min_y, max_x, max_y); } @@ -133,7 +206,11 @@ void sim_render(struct OffscreenBuffer *buffer, float dt, struct SimState *sim_s if (RENDER_GRID) { - sim_render_grid(buffer, qt->root, COLOR_GREEN); + sim_grid_render(buffer, qt->root, COLOR_GREEN); + } + if (RENDER_BOUNDING_BOX) + { + sim_bounding_box_render(buffer, &sim_state->bounding_box, COLOR_RED); } } @@ -160,7 +237,7 @@ void sim_set_pixel(struct OffscreenBuffer *buffer, uint32_t x, uint32_t y, uint3 } -void sim_render_grid(struct OffscreenBuffer *buffer, struct QuadTreeNode *node, uint32_t color) +void sim_grid_render(struct OffscreenBuffer *buffer, struct QuadTreeNode *node, uint32_t color) { if (!buffer) { @@ -184,10 +261,56 @@ void sim_render_grid(struct OffscreenBuffer *buffer, struct QuadTreeNode *node, sim_set_pixel(buffer, node->cell->center_x, y, color); } - sim_render_grid(buffer, node->ne, color); - sim_render_grid(buffer, node->nw, color); - sim_render_grid(buffer, node->sw, color); - sim_render_grid(buffer, node->se, color); + sim_grid_render(buffer, node->ne, color); + sim_grid_render(buffer, node->nw, color); + sim_grid_render(buffer, node->sw, color); + sim_grid_render(buffer, node->se, color); + } +} + + +void sim_bounding_box_render(struct OffscreenBuffer *buffer, struct SimBounds *bounding_box, uint32_t color) +{ + if (!buffer) + { + // TODO: handle invalid pointer error + return; + } + + if (bounding_box) + { + int reticle_radius = 3; + for (int x = bounding_box->center_x - reticle_radius; + x <= bounding_box->center_x + reticle_radius; + x++) + { + sim_set_pixel(buffer, x, bounding_box->center_y, color); + } + for (int y = bounding_box->center_y - reticle_radius; + y <= bounding_box->center_y + reticle_radius; + y++) + { + sim_set_pixel(buffer, bounding_box->center_x, y, color); + } + + float half_length_x = bounding_box->side_length_x / 2; + float half_length_y = bounding_box->side_length_y / 2; + + for (int x = (bounding_box->center_x - half_length_x); + x <= (bounding_box->center_x + half_length_x); + ++x) + { + sim_set_pixel(buffer, x, bounding_box->center_y - half_length_y, color); + sim_set_pixel(buffer, x, bounding_box->center_y + half_length_y, color); + } + + for (int y = (bounding_box->center_y - half_length_y); + y <= (bounding_box->center_y + half_length_y); + ++y) + { + sim_set_pixel(buffer, bounding_box->center_x - half_length_x, y, color); + sim_set_pixel(buffer, bounding_box->center_x + half_length_x, y, color); + } } } diff --git a/src/star_garden.h b/src/star_garden.h @@ -8,7 +8,7 @@ #include <stdlib.h> #define FULLSCREEN -#define RESPAWN_STARS +//#define RESPAWN_STARS #define USE_TEST_SEED #define TITLE "Stars" @@ -54,11 +54,20 @@ typedef enum color_t NUM_COLORS, } color_t; +struct SimBounds +{ + float center_x; + float center_y; + float side_length_x; + float side_length_y; +}; + struct SimState { - struct Star stars[NUM_STARS]; - struct QuadTree *qt; int num_stars; + struct SimBounds bounding_box; + struct QuadTree *qt; + struct Star stars[NUM_STARS]; }; struct OffscreenBuffer @@ -72,9 +81,11 @@ struct OffscreenBuffer void sim_init(struct SimState *sim_state, int field_width, int field_height); void sim_update(struct SimState *sim_state, int field_width, int field_height); +void sim_bounding_box_update(struct SimBounds *bounds, float min_x, float min_y, float max_x, float max_y); void sim_render(struct OffscreenBuffer *buffer, float dt, struct SimState *sim_state); void sim_set_pixel(struct OffscreenBuffer *buffer, uint32_t x, uint32_t y, uint32_t color); -void sim_render_grid(struct OffscreenBuffer *buffer, struct QuadTreeNode *node, uint32_t color); +void sim_bounding_box_render(struct OffscreenBuffer *buffer, struct SimBounds *bounding_box, uint32_t color); +void sim_grid_render(struct OffscreenBuffer *buffer, struct QuadTreeNode *node, uint32_t color); void sim_cleanup(struct SimState *sim_state); #define STAR_GARDEN_H