From ee055856a5b47851ed9ac731c5edb8ce76be3386 Mon Sep 17 00:00:00 2001 From: cinnaboot Date: Wed, 14 Jan 2026 09:56:15 -0500 Subject: [PATCH] fix: enable SOI exit transitions with conditional hysteresis Modified find_dominant_body() to detect when bodies exit SOI by checking distance against current parent's SOI radius. Applies different hysteresis: - Inside SOI: 0.5x distance threshold prevents oscillations - Outside SOI: switches to closest body without hysteresis Added test requirement for Mars SOI exit detection. Claude --- src/simulation.cpp | 29 +++++++++++++++++++++++------ tests/test_soi_transition.cpp | 17 +++++++++++++++-- 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/src/simulation.cpp b/src/simulation.cpp index a9da247..198cde1 100644 --- a/src/simulation.cpp +++ b/src/simulation.cpp @@ -35,6 +35,14 @@ int find_dominant_body(SimulationState* sim, int body_index) { CelestialBody* body = &sim->bodies[body_index]; int dominant = body->parent_index; + // Check if we're outside current parent's SOI + bool outside_current_soi = false; + if (dominant >= 0 && dominant < sim->body_count) { + CelestialBody* current_parent = &sim->bodies[dominant]; + double dist_to_current = vec3_distance(body->position, current_parent->position); + outside_current_soi = dist_to_current > current_parent->soi_radius; + } + // Check all other bodies to see if we're within their SOI for (int i = 0; i < sim->body_count; i++) { if (i == body_index) continue; @@ -42,18 +50,27 @@ int find_dominant_body(SimulationState* sim, int body_index) { CelestialBody* potential_parent = &sim->bodies[i]; double distance = vec3_distance(body->position, potential_parent->position); - // If we're within this body's SOI and it's not our current parent - if (distance < potential_parent->soi_radius && i != dominant) { - // Check if this body is more dominant (closer or more massive) + // If we're outside current parent's SOI, consider all bodies + // Otherwise, only consider bodies within their SOI + bool can_switch = outside_current_soi || distance < potential_parent->soi_radius; + + if (can_switch && i != dominant) { if (dominant == -1) { dominant = i; } else { CelestialBody* current_parent = &sim->bodies[dominant]; double dist_to_current = vec3_distance(body->position, current_parent->position); - // Switch if this potential parent is significantly closer - if (distance < dist_to_current * 0.5) { - dominant = i; + if (outside_current_soi) { + // Outside current SOI: switch to closest body + if (distance < dist_to_current) { + dominant = i; + } + } else { + // Inside current SOI: apply hysteresis to prevent oscillations + if (distance < dist_to_current * 0.5) { + dominant = i; + } } } } diff --git a/tests/test_soi_transition.cpp b/tests/test_soi_transition.cpp index bbca3f2..3acd099 100644 --- a/tests/test_soi_transition.cpp +++ b/tests/test_soi_transition.cpp @@ -16,11 +16,11 @@ struct ParentChange { TEST_CASE("SOI transition - Sun to Mars", "[soi][transition]") { const double TIME_STEP = 60.0; - const double DAYS_TO_SIMULATE = 1700.0; + const double DAYS_TO_SIMULATE = 2000.0; const double SECONDS_PER_DAY = 86400.0; const double AU = 1.496e11; - INFO("Note: Hysteresis (0.5 factor) makes Mars -> Sun exit impossible"); + INFO("Note: Hysteresis (0.5 factor) prevents oscillations, allows Mars -> Sun exit when outside SOI"); SimulationState* sim = create_simulation(10, TIME_STEP); @@ -80,6 +80,19 @@ TEST_CASE("SOI transition - Sun to Mars", "[soi][transition]") { REQUIRE(found_mars_transition); + bool found_mars_exit = false; + for (const auto& change : parent_changes) { + if (change.old_parent == MARS_INDEX && change.new_parent == SUN_INDEX) { + found_mars_exit = true; + INFO("Mars -> Sun exit at day " << change.time_days + << ", distance to Mars: " << change.distance_to_mars_au << " AU" + << ", distance to Sun: " << change.distance_to_sun_au << " AU"); + break; + } + } + + REQUIRE(found_mars_exit); + destroy_simulation(sim); }