#include "ui_renderer.h" #include "spacecraft.h" #include "maneuver.h" #include #include #include // raygui implementation (header-only library) #define RAYGUI_IMPLEMENTATION #include "raygui.h" #include "amber/style_amber.h" void gui_init() { GuiLoadStyleAmber(); } // Render simulation information overlay void render_info(SimulationState* sim) { // Panel dimensions int panel_width = 300; int panel_height = 300; 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) { // Panel dimensions int panel_width = 200; int panel_height = 400; 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) { // A body was selected render_state->selected_body_index = ui_state->body_list_active; render_state->selected_craft_index = -1; render_state->camera_follow_body = true; printf("Camera follow enabled for body: %s\n", sim->bodies[render_state->selected_body_index].name); } else { // A spacecraft was selected int craft_index = ui_state->body_list_active - sim->body_count; if (craft_index >= 0 && craft_index < sim->craft_count) { render_state->selected_body_index = -1; render_state->selected_craft_index = craft_index; render_state->camera_follow_body = true; printf("Camera follow enabled for spacecraft: %s\n", sim->spacecraft[craft_index].name); } } } free(list_text); } // Render body information UI panel void render_body_info_ui(SimulationState* sim, RenderState* render_state) { // Panel dimensions int panel_width = 250; int panel_height = 300; 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) && (render_state->selected_craft_index < 0 || render_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 (render_state->selected_craft_index >= 0 && render_state->selected_craft_index < sim->craft_count) { Spacecraft* craft = &sim->spacecraft[render_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 == render_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) { if (render_state->selected_craft_index < 0 || render_state->selected_craft_index >= sim->craft_count) { return; // No spacecraft selected } int panel_width = 300; int panel_height = 400; int panel_x = GetScreenWidth() - panel_width - 10; int panel_y = 320; // Below info panel Rectangle panel_bounds = { (float)panel_x, (float)panel_y, (float)panel_width, (float)panel_height }; Spacecraft* craft = &sim->spacecraft[render_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 == render_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 == render_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 == render_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); }