Skip to content

SPIM head shows a stale frame after selecting a different embryo #127

Description

@pskeshu

Selecting a different embryo on the SPIM head changes the caption and nothing else. The view keeps showing the previous embryo while claiming another one is selected.

From the recording, three attempts in a row (18:45, 19:00, 19:45): Selected: embryo 3Selected: embryo 1Selected: embryo 2, with SAMPLE → OBJECTIVE 28.2 µm, the banner and the image pixels identical every time.

Ryan, 18:49: "so right now it seems like we're just looking at embryo one… but the image itself isn't changing." Magdalena, 19:56: "if you click a different embryo, I would like maybe an empty image to show up, even if you don't move the objective. Because right now I think it might be confusing, because you might think that this is the embryo — like the selected embryo is the image that's shown."

Cause

There is no predicate anywhere in Operate relating the live stage position to the selected embryo's position, even though both facts are in hand: _xy (fed from SharedState stageXY, operate.js:1304-1306) and resolveXY(emb) (:126-133).

  • selectEmbryo (operate.js:817-826) calls four renderers, none of which touch the image.
  • The caption is the only thing that moves — renderSpimTarget (:809-814) writes Selected: embryo N into index.html:637.
  • onSpimFrame (:1159-1166) paints every arriving frame unconditionally.

Caption and pixels are two independent channels that can disagree indefinitely. Commit 0120487 (fix(lightbox): never leave a stale frame) does not cover this surface — it touched the lightbox, not the Operate SPIM pane.

Done when

The light-sheet view does not present a frame as belonging to the selected embryo unless the stage is at that embryo — blank or explicitly mark it otherwise.

Smallest fix: add one pure predicate and use it at the only two points that can make caption and pixels disagree. In operate-math.js (which exists for exactly this and is covered by tests/js/operate-math.test.mjs), add atPosition(xy, target, tol) with AT_TOL_UM = 50 as the tunable knob. In operate.js, atSelected() over _embryos/_selected/_xy, then gate onSpimFrame (:1159) and renderSpimTarget (:809).

Leave the tolerance as a named constant — 50 µm is a guess about stage repeatability and embryo spacing that wants tuning against the real scope, not a derived value.

Distinct from #111: Go to is the capability, this is the correctness bug. Fixing #111 does not fix this — you would still show a stale frame between issuing a move and arriving.


Evidence: 2026-08-07 walkthrough, 18:45–20:11. Not previously captured.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workinguiUser interfaceuser-feedbackTraces to a named user demo or feedback session

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions