From fded16c19a26ecd10a422d3ad751b971d9e49e5f Mon Sep 17 00:00:00 2001 From: GlassOnTin Date: Thu, 2 Apr 2026 11:35:44 +0100 Subject: [PATCH] Fix bubble level: sub-step spring-damper for stability at 500ms GUI rate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The spring-damper (k=40, c=12) was unstable at dt=0.5s — position overshot max_r on every frame, got clamped, then overshot again, keeping the bubble stuck at the ring edge. Fix: sub-step the physics at 20ms intervals (25 steps per 500ms frame). The integration is now stable at any GUI update rate. --- Gui.h | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/Gui.h b/Gui.h index 4be7478..6c716dc 100644 --- a/Gui.h +++ b/Gui.h @@ -816,13 +816,18 @@ static void gui_update_data() { float target_y = -tilt_dir_y * mapped_r; // Spring-damper: overdamped for viscous fluid feel - // spring=40 damping=12 → settles in ~0.3s, no oscillation + // Sub-step at 20ms to keep integration stable at any frame rate const float spring = 40.0f; const float damping = 12.0f; - vel_x += (spring * (target_x - bub_x) - damping * vel_x) * dt; - vel_y += (spring * (target_y - bub_y) - damping * vel_y) * dt; - bub_x += vel_x * dt; - bub_y += vel_y * dt; + const float max_sub = 0.02f; + int steps = (int)(dt / max_sub) + 1; + float sdt = dt / steps; + for (int si = 0; si < steps; si++) { + vel_x += (spring * (target_x - bub_x) - damping * vel_x) * sdt; + vel_y += (spring * (target_y - bub_y) - damping * vel_y) * sdt; + bub_x += vel_x * sdt; + bub_y += vel_y * sdt; + } // Clamp to ring boundary (bubble can't escape the fluid) float dist = sqrtf(bub_x * bub_x + bub_y * bub_y);