Feature/second system - #3
Open
ecaterina-sur wants to merge 12 commits into
Open
Conversation
…m visualization A browser-based, server-free (JS-ported physics) app for exploring the instability of rotation about the intermediate axis, with adjustable racket geometry, free/controlled/wind scenarios, and live 2D/3D views of angular velocity, energy, and the Casimir sphere phase portrait. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
src/models/3D_SO3_Windy_Pendulum/ph_gp_ode_v2/JaxJD has been a bare gitlink with no corresponding .gitmodules entry since the repo's first commit, on every branch. Any tool that does a full submodule-recursive clone (e.g. Cloudflare Pages' build step) fails outright with "error occurred while updating repository submodules" before it even gets to checking out files. Scoped to this branch only, not touching main. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The Cloudflare project was created via the newer unified Workers+Pages flow, which makes it a Worker with static assets under the hood, not a classic Pages project -- so `wrangler pages deploy` (the old Pages-only API) was failing with an authentication/resource error, since no Pages project by that name exists for the account (only a Worker does). This config lets `wrangler deploy` pick up the name and static assets directory directly instead. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The energy panel's y-axis ticks were a fixed step (major every 20, minor
every 10) tuned to the default geometry's H range (~220) -- any geometry
with a much smaller H range (e.g. ~14) showed only a single "0" tick, since
the next gridline at 20 fell off the top of the chart. Replaced with
computeNiceStep(), a standard "nice numbers" rounding algorithm (as in
D3/matplotlib) that picks a sensible major/minor step from the actual
on-screen range -- targetCount=8 was chosen specifically because it
reproduces both previously hand-tuned defaults exactly (omega's ~16 rad/s
range -> major=2/minor=1; the default geometry's ~220 H range ->
major=20/minor=10) while generalizing correctly to any other range.
Also relabeled imin/imid/imax as "short/intermediate/long axis" throughout
the user-facing UI (dropdowns, legends, inertia readout) -- "intermediate
axis" doubles as the popular-science name for the whole phenomenon. Only
display text changed; the underlying value="imin" etc. data values (and
all internal variable/property names) are untouched, so this is purely
cosmetic. Spelled out "Hamiltonian energy (H)" for the free-mode panel
title and "Hamiltonian (H)" for the y-axis label; left the controlled-mode
title ("Current vs. desired (H)") as is, since it doesn't use the word
"Hamiltonian" at all.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…y sphere legend
The short/long axis naming was backwards: moment of inertia is smallest
about an object's LONGEST physical axis (mass sits close to it, like
spinning a pencil along its length) and largest about its SHORTEST axis
(mass swings far around it, like flipping a book like a frisbee) --
confirmed against racket_geometry.py, where face_normal_body (the racket's
thinnest dimension) is exactly the imax axis. So imin is the long axis and
imax is the short one, the opposite of what was labeled. Swapped throughout
(dropdowns, H(...) legend, omega legend).
Also reformatted the moments-of-inertia readout from a single run-on
sentence into a labeled 4-line block, and clarified the Casimir sphere
legend ("current" -> "current state", "stable"/"unstable" -> "stable/
unstable fixed point").
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Each panel now has an "Explain" switch (top-left, matching the existing "3D" toggle's corner-switch style) that flips the whole panel 180deg via a CSS 3D transform to reveal a plain-language explanation of what's plotted, while the simulation keeps running underneath exactly as before -- the canvas never stops rendering just because its face is turned away, the same reason the existing 3D Hamiltonian view already keeps drawing while hidden. Structure: .flip-outer (the sizing box, replaces the old plain .panel) -> .flip-inner (the element that actually rotates) -> two .flip-face children (front = the live canvas, back = the explanation text), plus a persistent .explain-toggle sibling that isn't part of either face, so the control that flips the panel back doesn't disappear along with the front face. One generic main.js listener wires all 4 switches by data-flip id instead of four near-identical ones. Explanation text for all 4 panels drafted collaboratively and fact-checked against the actual physics (e.g. the Casimir invariant is total angular momentum, not the Hamiltonian; there are exactly 6 equilibrium points, 4 stable + 2 unstable). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…Casimir text The toggle now sits in a label row above each panel, outside the flip-rotating element, so it stays crisp instead of blurring mid-flip. Racket/Hamiltonian/omega explanations are centered and slightly larger; the Casimir explanation is condensed so it fits without scrolling.
Native checkbox inputs ignore backface-visibility in some browsers, so the front face's 3D toggle bled through onto the flipped-to explanation.
Ports the gravity-compensated IDA-PBC controller physics (Python + JS, cross-validated against shared fixtures) so the pendulum can swing freely, get pushed by wind, or be actively steered to any target angle -- alongside the existing Tennis Racket, switchable via a new sliding carousel (app.js/#system-viewport) built to support further systems later. - Controller gains auto-scale from the pendulum's own live inertia rather than exposing raw K_R/K_p sliders; continuous starting-angle and target-angle sliders (any angle, not just fixed presets) with live pose previews while paused. - Distinguishes "achieved" (reached the exact target) from "settled" (came to rest off-target under a constant wind disturbance), reporting the steady-state holding torque and whether the target is a stable or unstable equilibrium either way. - All four panel explanations rewritten for a general audience; Title Case adopted as the standing convention for every UI label/heading/legend/ button across both systems. - Fixes a y-axis unit-label layout bug (a fixed pixel offset that could overlap wide tick values) shared by every 2D panel in the app. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
racket-visualization | c382089 | Aug 13 2026, 08:55 PM |
Hamiltonian-formulation cart-pole (state (x, theta, p_x, p_theta), momenta
not velocities, theta=0 upright) alongside the Tennis Racket and Windy
Pendulum, with a genuinely different per-step stochastic wind model (a
Heun predictor-corrector SDE on p_theta, not the other systems' "sample
once, hold constant" gust). Three Controller modes -- None (free fall),
Manual (arrow keys), and ct_sac -- a real trained SAC checkpoint from a
coworker's own live demo, ported to a hand-written JS/Python forward pass
and verified against 24 golden (observation -> action) test vectors
(2.13e-6 N worst-case error), not a placeholder.
- Chase-cam rendering: the camera pans to keep the cart centered rather
than framing the whole track, so the panel can sit in the same plain 2x2
grid every other system uses instead of needing an oversized banner;
fixed-world-position diagonal ground marks and rolling wheel spokes
(rotation = -distance/radius) give a sense of motion since the cart
itself never visibly approaches a screen edge.
- Per-mode failure thresholds, independent of ct_sac's own +-12deg
training/task boundary: None can fall the full +-90deg, Manual/ct_sac end
at +-45deg so a failure past the trained envelope is actually watchable
instead of insta-terminating. A live Rail Length slider and a
termination-cause readout ("left the rail" vs. "pole exceeded the
threshold") make both boundaries visible and adjustable rather than
silent fixed constants.
- Fragility Diagnostics panel: closed-form gain/instability-time
cross-checks (against the coworker's own demo numbers, and independently
against a 4x4 Jacobian) for the upright equilibrium's linearized
dynamics, shown alongside the survival/termination readout.
- Shared fixes that benefit every system: adaptive axis-tick decimal
capping (garbled labels on near-zero ranges), a torque-panel domain
fallback for genuinely-all-zero data, and the y-axis label overlap fix
already applied to the pendulum's own panels.
258 JS (Vitest) + 81 Python (pytest) tests passing. Full decision history,
including several rounds of hands-on visual/UX correction and the physics
discussion behind the failure-threshold design, in DECISIONS.md.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary:
Adds the Windy Pendulum as a second visualized system alongside the Tennis Racket, switchable via a new sliding carousel (app.js / #system-viewport) built to support further systems later.
Ports the gravity-compensated IDA-PBC controller physics (Python + JS, cross-validated against shared fixtures) so the pendulum can swing freely, get pushed by wind, or be actively steered to any target angle.
Controller gains auto-scale from the pendulum's own live inertia rather than exposing raw K_R/K_p sliders; continuous starting-angle and target-angle sliders (any angle, not just fixed presets) with live pose previews while paused.
Distinguishes "achieved" (reached the exact target) from "settled" (came to rest off-target under a constant wind disturbance), reporting the steady-state holding torque and whether the target is a stable or unstable equilibrium either way.
All four panel explanations rewritten for a general audience; Title Case adopted as the standing convention for every UI label/heading/legend/button across both systems.
Fixes a y-axis unit-label layout bug (a fixed pixel offset that could overlap wide tick values) shared by every 2D panel in the app.
Test plan:
npm test (vitest) — 173/173 passing
pytest — 49/49 passing
Click through both systems' free and controlled (with/without wind) states on the deployed preview link
(not done)
Confirm no console errors in the deployed build