boids-playground/src/ui.cpp
2023-08-14 23:29:49 +03:00

128 lines
6.0 KiB
C++

#include "boid-playground.hpp"
#include "raygui.h"
#include "world.hpp"
struct VerticalLayout {
float x, y;
float gap;
};
static Rectangle rect_with_offset(Rectangle rect, Vector2 offset) {
return { rect.x + offset.x, rect.y + offset.y, rect.width, rect.height };
}
static Rectangle rect_with_offset(Rectangle rect, float x, float y) {
return { rect.x + x, rect.y + y, rect.width, rect.height };
}
static int gui_valuebox_float(Rectangle rect, const char *text, float *value, float min_value, float max_value, bool edit_mode) {
int int_value = *value;
int result = GuiValueBox(rect, text, &int_value, min_value, max_value, edit_mode);
*value = int_value;
return result;
}
static void gui_valuebox_float(Rectangle rect, const char *text, float *value, float min_value, float max_value, bool *edit_mode) {
if (gui_valuebox_float(rect, text, value, min_value, max_value, *edit_mode)) {
*edit_mode = !*edit_mode;
}
}
static Rectangle next_in_layout(VerticalLayout *layout, float width, float height, float offset_x = 0) {
Rectangle rect = { layout->x + offset_x, layout->y, width, height };
layout->y += height + layout->gap;
return rect;
}
static void ui_draw_control_panel(World *world, Visuals *visuals, UI *ui, Rectangle rect) {
float panel_height = rect.height;
ui->show_panel = !GuiWindowBox(rect, "Control panel");
float group_height = panel_height - 45;
GuiGroupBox({ 30, 55, 180, group_height }, "Visuals");
{
VerticalLayout layout = { .x = 40, .y = 65, .gap = 8 };
GuiCheckBox(next_in_layout(&layout, 15, 15), "Show world outline", &visuals->draw_world_outline);
GuiCheckBox(next_in_layout(&layout, 15, 15), "Show direction", &visuals->draw_boid_direction);
GuiCheckBox(next_in_layout(&layout, 15, 15), "Show view cone", &visuals->draw_view_cone);
GuiCheckBox(next_in_layout(&layout, 15, 15), "Show separation radius", &visuals->draw_separation_radius);
GuiCheckBox(next_in_layout(&layout, 15, 15), "Show collision rays", &visuals->draw_collision_avoidance_rays);
// TODO: Visualize cohesion, alignment and separation forces
// GuiCheckBox(next_in_layout(&layout, 15, 15), "Show pulling forces", &visuals->draw_pulling_forces);
GuiSlider(next_in_layout(&layout, 100, 15), NULL, "Boid size", &visuals->boid_edge_size, 2.5, 50);
Rectangle boid_color_rect = next_in_layout(&layout, 50, 50);
GuiColorPicker(boid_color_rect, NULL, &visuals->boid_color);
GuiLabel(rect_with_offset({ 80, 10, 80, 16 }, boid_color_rect.x, boid_color_rect.y), "Boid color");
Rectangle bg_color_rect = next_in_layout(&layout, 50, 50);
GuiColorPicker(bg_color_rect, NULL, &visuals->bg_color);
GuiLabel(rect_with_offset({ 80, 10, 80, 16 }, bg_color_rect.x, bg_color_rect.y), "BG color");
// TODO: Add showing out of bounds boids
}
GuiGroupBox({ 220, 55, 220, group_height }, "Boid");
{
VerticalLayout layout = { .x = 230, .y = 65, .gap = 8 };
GuiCheckBox(next_in_layout(&layout, 15, 15), "Looping walls", &world->looping_walls);
GuiSlider(next_in_layout(&layout, 100, 15), NULL, "Separation radius", &world->separation_radius, 2.5, 150);
GuiSlider(next_in_layout(&layout, 100, 15), NULL, "View radius", &world->view_radius, 2.5, 150);
GuiSlider(next_in_layout(&layout, 100, 15), NULL, "View angle", &world->view_angle, 0, 2*PI);
gui_valuebox_float(next_in_layout(&layout, 50, 15, 60), "Min speed", &world->min_speed, 0, 1000, &ui->min_speed_edit);
gui_valuebox_float(next_in_layout(&layout, 50, 15, 60), "Max speed", &world->max_speed, 0, 1000, &ui->max_speed_edit);
gui_valuebox_float(next_in_layout(&layout, 50, 15, 60), "Steer speed", &world->max_steer_speed, 0, 1000, &ui->steer_speed_edit);
GuiSlider(next_in_layout(&layout, 100, 15), NULL, "Collision distance", &world->collision_avoidance_distance, 5, 100);
GuiSlider(next_in_layout(&layout, 100, 15), NULL, "Collision ray angle", &world->collision_avoidance_ray_angle, 0, PI);
GuiSpinner(next_in_layout(&layout, 100, 15, 95), "Collision ray count", &world->collision_avoidance_ray_count, 1, 10, false);
}
GuiGroupBox({ 450, 55, 220, group_height }, "Flock");
{
VerticalLayout layout = { .x = 605, .y = 65, .gap = 8 };
GuiCheckBox(next_in_layout(&layout, 15, 15, -145), "Freeze", &world->freeze);
GuiSlider(next_in_layout(&layout, 100, 15, -145), NULL, "Boid count", &ui->target_boid_count, 10, MAX_BOIDS);
world_adjust_boid_count(world, (int)ui->target_boid_count);
gui_valuebox_float(next_in_layout(&layout, 50, 15), "Alignment strength", &world->alignment_strength, 0, 100, &ui->alignment_strength_edit);
gui_valuebox_float(next_in_layout(&layout, 50, 15), "Cohesion strength", &world->cohesion_strength, 0, 100, &ui->cohesion_strength_edit);
gui_valuebox_float(next_in_layout(&layout, 50, 15), "Separation strength", &world->separation_strength, 0, 100, &ui->separation_strength_edit);
gui_valuebox_float(next_in_layout(&layout, 50, 15), "Collision avoidance strength", &world->collision_avoidance_strength, 0, 100, &ui->collision_avoidance_strength_edit);
}
}
static void ui_draw_controls_guide(Rectangle rect) {
GuiPanel(rect, "Controls");
VerticalLayout layout = { .x = rect.x + 10, .y = rect.y + 30, .gap = 6 };
GuiLabel(next_in_layout(&layout, 250, 12), "R - reset camera view");
GuiLabel(next_in_layout(&layout, 250, 12), "Scroll middle mouse - zoom camera");
GuiLabel(next_in_layout(&layout, 250, 12), "Hold middle mouse & move - move camera");
}
static void ui_draw(World *world, Visuals *visuals, UI *ui) {
if (!ui->show_panel) {
ui->show_panel = GuiButton({ 20, 20, 30, 30 }, GuiIconText(ICON_PENCIL_BIG, ""));
return;
}
ui_draw_control_panel(world, visuals, ui, { 20, 20, 660, 315 });
ui_draw_controls_guide({ 20, 340, 275, 120 });
float window_width = GetScreenWidth();
float window_height = GetScreenHeight();
DrawFPS(window_width - 90, 10);
char boid_label[128] = { 0 };
snprintf(boid_label, sizeof(boid_label), "%lu boids", world->boids.size());
DrawText(boid_label, window_width - 125, 35, 20, GREEN);
DrawText("Beware!", 20, window_height - 50, 20, RED);
DrawText("May crash sometimes", 20, window_height - 30, 20, RED);
}