vibe coding an orbital mechanics simulation to try out claude code
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.
 
 
 
 
 

433 lines
16 KiB

#include "ui_renderer.h"
#include "spacecraft.h"
#include "maneuver.h"
#include <cstdio>
#include <cstring>
#include <cmath>
// raygui implementation (header-only library)
#define RAYGUI_IMPLEMENTATION
#include "raygui.h"
#include "amber/style_amber.h"
// UI Panel constants
#define INFO_PANEL_WIDTH 300
#define INFO_PANEL_HEIGHT 300
#define BODY_LIST_WIDTH 200
#define BODY_LIST_HEIGHT 400
#define BODY_INFO_WIDTH 250
#define BODY_INFO_HEIGHT 300
#define MANEUVER_LIST_WIDTH 300
#define MANEUVER_LIST_HEIGHT 400
#define MANEUVER_LIST_Y_OFFSET 320
void gui_init() {
GuiLoadStyleAmber();
}
// Render simulation information overlay
void render_info(SimulationState* sim) {
int panel_width = INFO_PANEL_WIDTH;
int panel_height = INFO_PANEL_HEIGHT;
int panel_x = 10;
int panel_y = GetScreenHeight() - panel_height - 10;
Rectangle panel_bounds = { (float)panel_x, (float)panel_y, (float)panel_width, (float)panel_height };
// Draw window
if (GuiWindowBox(panel_bounds, "Info")) {
// Window box close button doesn't close this panel
}
// Prepare info text
char info_text[1024];
char temp_buffer[512];
snprintf(info_text, sizeof(info_text), "Orbital Mechanics Simulation\n\n");
// Config name
if (sim->config_name[0] != '\0') {
snprintf(temp_buffer, sizeof(temp_buffer), "Config: %s\n\n", sim->config_name);
strcat(info_text, temp_buffer);
}
// Simulation time (in days)
double days = sim->time / 86400.0; // seconds to days
snprintf(temp_buffer, sizeof(temp_buffer), "Time: %.2f days\n", days);
strcat(info_text, temp_buffer);
// Body count
snprintf(temp_buffer, sizeof(temp_buffer), "Bodies: %d\n", sim->body_count);
strcat(info_text, temp_buffer);
// FPS
snprintf(temp_buffer, sizeof(temp_buffer), "FPS: %d\n\n", GetFPS());
strcat(info_text, temp_buffer);
// Controls
strcat(info_text, "Controls:\n");
strcat(info_text, " Arrows: Rotate/Zoom\n");
strcat(info_text, " Space: Pause/Resume\n");
strcat(info_text, " +/-: Speed control\n");
strcat(info_text, " ESC: Quit\n");
// Draw info text (inside window, leave space for title bar)
Rectangle text_bounds = {
(float)panel_x + 8,
(float)panel_y + 25,
(float)panel_width - 16,
(float)panel_height - 29
};
GuiLabel(text_bounds, info_text);
}
// Render body list UI panel
void render_body_list_ui(SimulationState* sim, RenderState* render_state, UIState* ui_state) {
int panel_width = BODY_LIST_WIDTH;
int panel_height = BODY_LIST_HEIGHT;
int panel_x = 10;
int panel_y = 10;
Rectangle panel_bounds = { (float)panel_x, (float)panel_y, (float)panel_width, (float)panel_height };
// Create list string for raygui (NULL-terminated)
// Include both bodies and spacecraft
int total_items = sim->body_count + sim->craft_count;
int buffer_size = total_items * (64 + 3 + 1) + 1; // Extra space for "🚀 " prefix
char* list_text = (char*)malloc(buffer_size);
if (!list_text) return;
list_text[0] = '\0';
int offset = 0;
int item_index = 0;
// Add bodies to list
for (int i = 0; i < sim->body_count; i++) {
if (item_index > 0) {
offset += snprintf(list_text + offset, buffer_size - offset, ";");
}
offset += snprintf(list_text + offset, buffer_size - offset, "%s", sim->bodies[i].name);
item_index++;
}
// Add spacecraft to list with "🚀 " prefix
for (int i = 0; i < sim->craft_count; i++) {
offset += snprintf(list_text + offset, buffer_size - offset, ";");
offset += snprintf(list_text + offset, buffer_size - offset, "🚀 %s", sim->spacecraft[i].name);
item_index++;
}
// Draw window (no close button - panels always shown)
GuiWindowBox(panel_bounds, "Objects");
// Draw list view (inside window, leave space for title bar)
Rectangle list_bounds = {
(float)panel_x + 4,
(float)panel_y + 25,
(float)panel_width - 8,
(float)panel_height - 29
};
int previous_active = ui_state->body_list_active;
GuiListView(list_bounds, list_text, &ui_state->body_list_scroll, &ui_state->body_list_active);
// Check if an item was selected
if (ui_state->body_list_active >= 0 && ui_state->body_list_active != previous_active) {
if (ui_state->body_list_active < sim->body_count) {
render_state->selected_body_index = ui_state->body_list_active;
ui_state->selected_craft_index = -1;
render_state->camera_target_enabled = true;
printf("Camera follow enabled for body: %s\n", sim->bodies[render_state->selected_body_index].name);
} else {
int craft_index = ui_state->body_list_active - sim->body_count;
if (craft_index >= 0 && craft_index < sim->craft_count) {
Spacecraft* craft = &sim->spacecraft[craft_index];
render_state->selected_body_index = craft->parent_index;
ui_state->selected_craft_index = craft_index;
render_state->camera_target_enabled = true;
printf("Camera follow enabled for spacecraft: %s\n", craft->name);
}
}
}
free(list_text);
}
// Render body information UI panel
void render_body_info_ui(SimulationState* sim, RenderState* render_state, UIState* ui_state) {
int panel_width = BODY_INFO_WIDTH;
int panel_height = BODY_INFO_HEIGHT;
int panel_x = GetScreenWidth() - panel_width - 10;
int panel_y = 10;
if ((render_state->selected_body_index < 0 || render_state->selected_body_index >= sim->body_count) &&
(ui_state->selected_craft_index < 0 || ui_state->selected_craft_index >= sim->craft_count)) {
// No body or spacecraft selected - render empty panel
Rectangle panel_bounds = { (float)panel_x, (float)panel_y, (float)panel_width, (float)panel_height };
GuiWindowBox(panel_bounds, "Info");
return;
}
Rectangle panel_bounds = { (float)panel_x, (float)panel_y, (float)panel_width, (float)panel_height };
// Check if a body is selected
if (render_state->selected_body_index >= 0 && render_state->selected_body_index < sim->body_count) {
CelestialBody* body = &sim->bodies[render_state->selected_body_index];
// Draw window (no close button - panels always shown)
GuiWindowBox(panel_bounds, body->name);
// Prepare info text
char info_text[1024];
char temp_buffer[256];
snprintf(info_text, sizeof(info_text), "Name: %s", body->name);
snprintf(temp_buffer, sizeof(temp_buffer), "Mass: %.2e kg", body->mass);
strcat(info_text, "\n");
strcat(info_text, temp_buffer);
snprintf(temp_buffer, sizeof(temp_buffer), "Radius: %.2e m", body->radius);
strcat(info_text, "\n");
strcat(info_text, temp_buffer);
if (body->parent_index <= 0) {
// Position
snprintf(temp_buffer, sizeof(temp_buffer), "Position: (%.2e, %.2e, %.2e)",
body->global_position.x, body->global_position.y, body->global_position.z);
strcat(info_text, "\n");
strcat(info_text, temp_buffer);
// Velocity
double vel_mag = vec3_magnitude(body->global_velocity);
snprintf(temp_buffer, sizeof(temp_buffer), "Velocity: %.2f m/s", vel_mag);
strcat(info_text, "\n");
strcat(info_text, temp_buffer);
} else {
// Position (Local)
snprintf(temp_buffer, sizeof(temp_buffer), "local_position: (%.2e, %.2e, %.2e)",
body->local_position.x, body->local_position.y, body->local_position.z);
strcat(info_text, "\n");
strcat(info_text, temp_buffer);
// Velocity (Local)
double parent_vel_mag = vec3_magnitude(sim->bodies[body->parent_index].global_velocity);
double vel_mag = vec3_magnitude(body->global_velocity) - parent_vel_mag;
snprintf(temp_buffer, sizeof(temp_buffer), "local_velocity: %.2f m/s", vel_mag);
strcat(info_text, "\n");
strcat(info_text, temp_buffer);
}
// Orbital elements
snprintf(temp_buffer, sizeof(temp_buffer), "Eccentricity: %.3f", body->orbit.eccentricity);
strcat(info_text, "\n");
strcat(info_text, temp_buffer);
snprintf(temp_buffer, sizeof(temp_buffer), "Semi-major axis: %.2e m", body->orbit.semi_major_axis);
strcat(info_text, "\n");
strcat(info_text, temp_buffer);
// Parent body
if (body->parent_index >= 0 && body->parent_index < sim->body_count) {
snprintf(temp_buffer, sizeof(temp_buffer), "Parent: %s", sim->bodies[body->parent_index].name);
} else {
snprintf(temp_buffer, sizeof(temp_buffer), "Parent: None");
}
strcat(info_text, "\n");
strcat(info_text, temp_buffer);
// SOI
snprintf(temp_buffer, sizeof(temp_buffer), "SOI radius: %.2e m", body->soi_radius);
strcat(info_text, "\n");
strcat(info_text, temp_buffer);
// Draw info text (inside window, leave space for title bar)
Rectangle text_bounds = {
(float)panel_x + 8,
(float)panel_y + 25,
(float)panel_width - 16,
(float)panel_height - 29
};
GuiLabel(text_bounds, info_text);
return;
}
// Check if a spacecraft is selected
if (ui_state->selected_craft_index >= 0 && ui_state->selected_craft_index < sim->craft_count) {
Spacecraft* craft = &sim->spacecraft[ui_state->selected_craft_index];
// Draw window (no close button - panels always shown)
GuiWindowBox(panel_bounds, craft->name);
// Prepare info text
char info_text[1024];
char temp_buffer[256];
snprintf(info_text, sizeof(info_text), "Name: %s", craft->name);
snprintf(temp_buffer, sizeof(temp_buffer), "Mass: %.2e kg", craft->mass);
strcat(info_text, "\n");
strcat(info_text, temp_buffer);
// Position (Local)
snprintf(temp_buffer, sizeof(temp_buffer), "local_position: (%.2e, %.2e, %.2e)",
craft->local_position.x, craft->local_position.y, craft->local_position.z);
strcat(info_text, "\n");
strcat(info_text, temp_buffer);
// Velocity (Local)
double vel_mag = vec3_magnitude(craft->local_velocity);
snprintf(temp_buffer, sizeof(temp_buffer), "local_velocity: %.2f m/s", vel_mag);
strcat(info_text, "\n");
strcat(info_text, temp_buffer);
// Parent body
if (craft->parent_index >= 0 && craft->parent_index < sim->body_count) {
snprintf(temp_buffer, sizeof(temp_buffer), "Parent: %s", sim->bodies[craft->parent_index].name);
} else {
snprintf(temp_buffer, sizeof(temp_buffer), "Parent: None");
}
strcat(info_text, "\n");
strcat(info_text, temp_buffer);
// Maneuver info
int pending_maneuvers = 0;
int executed_maneuvers = 0;
for (int i = 0; i < sim->maneuver_count; i++) {
if (sim->maneuvers[i].craft_index == ui_state->selected_craft_index) {
if (sim->maneuvers[i].executed) {
executed_maneuvers++;
} else {
pending_maneuvers++;
}
}
}
snprintf(temp_buffer, sizeof(temp_buffer), "Pending maneuvers: %d", pending_maneuvers);
strcat(info_text, "\n");
strcat(info_text, temp_buffer);
snprintf(temp_buffer, sizeof(temp_buffer), "Executed maneuvers: %d", executed_maneuvers);
strcat(info_text, "\n");
strcat(info_text, temp_buffer);
// Draw info text (inside window, leave space for title bar)
Rectangle text_bounds = {
(float)panel_x + 8,
(float)panel_y + 25,
(float)panel_width - 16,
(float)panel_height - 29
};
GuiLabel(text_bounds, info_text);
return;
}
}
// Render maneuver list UI panel
void render_maneuver_list_ui(SimulationState* sim, RenderState* render_state, UIState* ui_state) {
if (ui_state->selected_craft_index < 0 || ui_state->selected_craft_index >= sim->craft_count) {
return; // No spacecraft selected
}
int panel_width = MANEUVER_LIST_WIDTH;
int panel_height = MANEUVER_LIST_HEIGHT;
int panel_x = GetScreenWidth() - panel_width - 10;
int panel_y = MANEUVER_LIST_Y_OFFSET; // Below info panel
Rectangle panel_bounds = { (float)panel_x, (float)panel_y, (float)panel_width, (float)panel_height };
Spacecraft* craft = &sim->spacecraft[ui_state->selected_craft_index];
// Create title with spacecraft name
char title[128];
snprintf(title, sizeof(title), "Maneuvers: %s", craft->name);
// Draw window
GuiWindowBox(panel_bounds, title);
// Count maneuvers for this spacecraft
int craft_maneuver_count = 0;
for (int i = 0; i < sim->maneuver_count; i++) {
if (sim->maneuvers[i].craft_index == ui_state->selected_craft_index) {
craft_maneuver_count++;
}
}
if (craft_maneuver_count == 0) {
// No maneuvers for this spacecraft
Rectangle text_bounds = {
(float)panel_x + 8,
(float)panel_y + 25,
(float)panel_width - 16,
(float)panel_height - 29
};
GuiLabel(text_bounds, "No maneuvers defined");
return;
}
// Create maneuver list string for raygui
int buffer_size = craft_maneuver_count * (64 + 10 + 1) + 1;
char* maneuver_list_text = (char*)malloc(buffer_size);
if (!maneuver_list_text) return;
maneuver_list_text[0] = '\0';
int offset = 0;
for (int i = 0; i < sim->maneuver_count; i++) {
if (sim->maneuvers[i].craft_index == ui_state->selected_craft_index) {
if (offset > 0) {
offset += snprintf(maneuver_list_text + offset, buffer_size - offset, ";");
}
const char* status_char = sim->maneuvers[i].executed ? "[DONE]" : "[PENDING]";
offset += snprintf(maneuver_list_text + offset, buffer_size - offset, "%s %s",
status_char, sim->maneuvers[i].name);
}
}
// Draw list view (inside window, leave space for title bar)
Rectangle list_bounds = {
(float)panel_x + 4,
(float)panel_y + 25,
(float)panel_width - 8,
(float)panel_height - 100 // Leave space for details below
};
int maneuver_scroll = 0;
int maneuver_active = -1;
GuiListView(list_bounds, maneuver_list_text, &maneuver_scroll, &maneuver_active);
// Show details for first pending or last executed maneuver
char details_text[512];
details_text[0] = '\0';
for (int i = 0; i < sim->maneuver_count; i++) {
if (sim->maneuvers[i].craft_index == ui_state->selected_craft_index) {
if (sim->maneuvers[i].executed) {
snprintf(details_text, sizeof(details_text),
"Last executed:\n%s\nΔv: %.1f m/s\nTime: %.1f s",
sim->maneuvers[i].name, sim->maneuvers[i].delta_v, sim->maneuvers[i].executed_time);
} else {
snprintf(details_text, sizeof(details_text),
"Next pending:\n%s\nΔv: %.1f m/s",
sim->maneuvers[i].name, sim->maneuvers[i].delta_v);
break; // Show first pending
}
}
}
if (details_text[0] != '\0') {
Rectangle details_bounds = {
(float)panel_x + 8,
(float)panel_y + (float)panel_height - 70,
(float)panel_width - 16,
65
};
GuiLabel(details_bounds, details_text);
}
free(maneuver_list_text);
}