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.
 
 
 
 
 

263 lines
9.1 KiB

#include "config_loader.h"
#include <cstdio>
#include <cmath>
#include <cstring>
#include "simulation.h"
static bool parse_toml_spacecraft(toml_datum_t craft_table, Spacecraft* craft);
static bool load_spacecraft_from_toml(SimulationState* sim, toml_result_t result);
static bool extract_vec3_from_table(toml_datum_t table, Vec3* pos) {
toml_datum_t x = toml_get(table, "x");
toml_datum_t y = toml_get(table, "y");
toml_datum_t z = toml_get(table, "z");
// Accept both INT64 and FP64 for coordinates (TOML may parse integers as floats)
if ((x.type != TOML_FP64 && x.type != TOML_INT64) ||
(y.type != TOML_FP64 && y.type != TOML_INT64) ||
(z.type != TOML_FP64 && z.type != TOML_INT64)) {
return false;
}
pos->x = x.type == TOML_FP64 ? x.u.fp64 : (double)x.u.int64;
pos->y = y.type == TOML_FP64 ? y.u.fp64 : (double)y.u.int64;
pos->z = z.type == TOML_FP64 ? z.u.fp64 : (double)z.u.int64;
return true;
}
static bool extract_color_from_table(toml_datum_t table, float* color) {
toml_datum_t r = toml_get(table, "r");
toml_datum_t g = toml_get(table, "g");
toml_datum_t b = toml_get(table, "b");
// Accept both INT64 and FP64 for color components
if ((r.type != TOML_FP64 && r.type != TOML_INT64) ||
(g.type != TOML_FP64 && g.type != TOML_INT64) ||
(b.type != TOML_FP64 && b.type != TOML_INT64)) {
return false;
}
color[0] = (float)(r.type == TOML_FP64 ? r.u.fp64 : (double)r.u.int64);
color[1] = (float)(g.type == TOML_FP64 ? g.u.fp64 : (double)g.u.int64);
color[2] = (float)(b.type == TOML_FP64 ? b.u.fp64 : (double)b.u.int64);
return true;
}
static bool parse_toml_body(toml_datum_t body_table, CelestialBody* body) {
// Extract string fields
toml_datum_t name = toml_get(body_table, "name");
if (name.type != TOML_STRING) {
return false;
}
strncpy(body->name, name.u.s, 63);
body->name[63] = '\0';
// Extract numeric fields
toml_datum_t mass = toml_get(body_table, "mass");
toml_datum_t radius = toml_get(body_table, "radius");
toml_datum_t parent_idx = toml_get(body_table, "parent_index");
toml_datum_t eccentricity = toml_get(body_table, "eccentricity");
toml_datum_t semi_major = toml_get(body_table, "semi_major_axis");
if (mass.type != TOML_FP64 || radius.type != TOML_FP64 ||
parent_idx.type != TOML_INT64 ||
eccentricity.type != TOML_FP64 ||
semi_major.type != TOML_FP64) {
return false;
}
body->mass = mass.u.fp64;
body->radius = radius.u.fp64;
body->parent_index = (int)(parent_idx.type == TOML_INT64 ? parent_idx.u.int64 : (int)parent_idx.u.fp64);
body->eccentricity = eccentricity.u.fp64;
body->semi_major_axis = semi_major.u.fp64;
// Extract position vector
toml_datum_t position = toml_get(body_table, "position");
if (position.type != TOML_TABLE || !extract_vec3_from_table(position, &body->position)) {
return false;
}
// Extract color
toml_datum_t color = toml_get(body_table, "color");
if (color.type != TOML_TABLE || !extract_color_from_table(color, body->color)) {
return false;
}
// Initialize velocity (will be calculated later)
body->velocity = {0.0, 0.0, 0.0};
body->soi_radius = 0.0;
return true;
}
bool load_system_config(SimulationState* sim, const char* filepath) {
toml_result_t result = toml_parse_file_ex(filepath);
if (!result.ok) {
printf("Error: Could not parse TOML config file: %s\n", filepath);
printf("TOML Error: %s\n", result.errmsg);
return false;
}
// Get bodies array
toml_datum_t bodies = toml_get(result.toptab, "bodies");
if (bodies.type != TOML_ARRAY) {
printf("Error: Expected 'bodies' array in config file: %s\n", filepath);
toml_free(result);
return false;
}
// Count bodies first
int body_count = bodies.u.arr.size;
if (body_count == 0) {
printf("Error: No bodies found in config file: %s\n", filepath);
toml_free(result);
return false;
}
if (body_count > sim->max_bodies) {
printf("Error: Too many bodies (%d) for simulation (max: %d)\n",
body_count, sim->max_bodies);
toml_free(result);
return false;
}
for (int i = 0; i < body_count; i++) {
toml_datum_t body_table = bodies.u.arr.elem[i];
if (!parse_toml_body(body_table, &sim->bodies[i])) {
printf("Error: Failed to parse body at index %d\n", i);
toml_free(result);
return false;
}
if (sim->bodies[i].parent_index != -1 && sim->bodies[i].parent_index >= i) {
printf("Error: Body '%s' (index %d) has invalid parent_index %d - must be < %d or -1\n",
sim->bodies[i].name, i, sim->bodies[i].parent_index, i);
toml_free(result);
return false;
}
}
// Validate parent-child distances
for (int i = 0; i < body_count; i++) {
if (sim->bodies[i].parent_index >= 0) {
CelestialBody* body = &sim->bodies[i];
CelestialBody* parent = &sim->bodies[body->parent_index];
double distance = vec3_distance(body->position, parent->position);
double min_distance = parent->radius + body->radius;
if (distance < min_distance) {
printf("Error: Body '%s' (index %d) too close to parent '%s' (index %d)\n",
body->name, i, parent->name, body->parent_index);
printf(" Distance: %.2e m\n", distance);
printf(" Minimum required: %.2e m (parent radius + body radius)\n", min_distance);
toml_free(result);
return false;
}
}
}
sim->body_count = body_count;
strncpy(sim->config_name, filepath, sizeof(sim->config_name) - 1);
sim->config_name[sizeof(sim->config_name) - 1] = '\0';
// Load spacecraft from the same config file (before freeing TOML data)
if (!load_spacecraft_from_toml(sim, result)) {
toml_free(result);
return false;
}
toml_free(result);
initialize_bodies(sim);
printf("Loaded %d bodies from %s\n", body_count, filepath);
return true;
}
static bool load_spacecraft_from_toml(SimulationState* sim, toml_result_t result) {
toml_datum_t spacecraft = toml_get(result.toptab, "spacecraft");
if (spacecraft.type != TOML_ARRAY) {
printf("No spacecraft array found in config file: %s (optional)\n", sim->config_name);
return true;
}
int craft_count = spacecraft.u.arr.size;
if (craft_count == 0) {
printf("No spacecraft found in config file: %s\n", sim->config_name);
return true;
}
if (craft_count > sim->max_craft) {
printf("Error: Too many spacecraft (%d) for simulation (max: %d)\n",
craft_count, sim->max_craft);
return false;
}
for (int i = 0; i < craft_count; i++) {
Spacecraft craft;
toml_datum_t craft_table = spacecraft.u.arr.elem[i];
if (!parse_toml_spacecraft(craft_table, &craft)) {
printf("Error: Failed to parse spacecraft at index %d\n", i);
return false;
}
if (craft.parent_index < 0 || craft.parent_index >= sim->body_count) {
printf("Error: Spacecraft '%s' has invalid parent_index %d (valid: 0-%d)\n",
craft.name, craft.parent_index, sim->body_count - 1);
return false;
}
CelestialBody* parent = &sim->bodies[craft.parent_index];
craft.position = vec3_add(parent->position, craft.local_position);
craft.velocity = vec3_add(parent->velocity, craft.local_velocity);
int idx = add_spacecraft(sim, &craft);
if (idx < 0) {
printf("Error: Failed to add spacecraft to simulation\n");
return false;
}
}
printf("Loaded %d spacecraft from %s\n", craft_count, sim->config_name);
return true;
}
static bool parse_toml_spacecraft(toml_datum_t craft_table, Spacecraft* craft) {
toml_datum_t name = toml_get(craft_table, "name");
if (name.type != TOML_STRING) {
return false;
}
strncpy(craft->name, name.u.s, 63);
craft->name[63] = '\0';
toml_datum_t mass = toml_get(craft_table, "mass");
toml_datum_t parent_idx = toml_get(craft_table, "parent_index");
if (mass.type != TOML_FP64 || parent_idx.type != TOML_INT64) {
return false;
}
craft->mass = mass.u.fp64;
craft->parent_index = (int)(parent_idx.type == TOML_INT64 ? parent_idx.u.int64 : (int)parent_idx.u.fp64);
toml_datum_t position = toml_get(craft_table, "position");
if (position.type != TOML_TABLE || !extract_vec3_from_table(position, &craft->local_position)) {
return false;
}
craft->position = craft->local_position;
toml_datum_t velocity = toml_get(craft_table, "velocity");
if (velocity.type == TOML_TABLE) {
if (!extract_vec3_from_table(velocity, &craft->local_velocity)) {
return false;
}
} else {
craft->local_velocity = {0.0, 0.0, 0.0};
}
craft->velocity = craft->local_velocity;
return true;
}