Make the visualizer rotation-aware (closes #1056)#1062
Merged
Conversation
Closes PyLabRobot#1056. The Visualizer dropped rotation updates because `Resource.rotate()` had no notification channel and `Resource.draw()` never passed `rotation` to its Konva.Group. Rotation becomes part of every resource's serialized state and rides the existing `set_state` channel: `rotate()` fires `_state_updated()`; base `serialize_state()` / `load_state()` include `"rotation"`; `Container` / `TipSpot` chain through `super()`. On the JS side, `Resource.draw()` and `Resource.setState()` apply rotation to the Konva.Group, and `Container` / `TipSpot` / `LiquidHandler` chain to `super().setState(state)`. Visualizer overlay markers follow rotation now. Outlines that should rotate with the resource (click and hover highlights) nest inside `resource.group`. Axis-aligned markers that must track a chosen anchor on a rotated resource (bullseye, delta-line endpoints, coords-tool readouts) go through three small rotation-aware helpers that mirror Python's `Resource.get_absolute_*` for the 2D z-only case; at zero rotation they reduce to the legacy math byte-for-byte, so the unrotated path is provably unchanged. No public API change; pre-patch saved state JSONs still load. Verified: 21 patch invariants, full `pytest pylabrobot/ --ignore=pylabrobot/liquid_handling/backends` (1405 tests), and the visualizer demo at 0°, 45°, 90°, 180°, 270° - plate body, both highlights, bullseye, and delta lines all track rotation. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Makes the web visualizer correctly render and update resource rotation by propagating rotation through the existing Python set_state channel and applying it in the Konva render tree, including rotation-aware overlay/measurement markers.
Changes:
- Python: rotation is now included in base
Resource.serialize_state()/load_state(), andResource.rotate()notifies via_state_updated(). - JS visualizer:
Resource.draw()andResource.setState()applyrotation.zto the KonvaGroup; overlay marker math is updated to be rotation-aware. - State chaining: updated JS
setState()overrides (e.g.,Container,TipSpot,LiquidHandler) and Python tracker-based resources (e.g.,Container,TipSpot) to incorporatesuper()rotation state.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
pylabrobot/visualizer/lib.js |
Applies rotation to Konva groups; updates highlights and overlay/coords helpers to be rotation-aware. |
pylabrobot/resources/resource.py |
Adds rotation to base state serialization/loading and emits state updates on rotate(). |
pylabrobot/resources/container.py |
Merges base state (rotation) with volume tracker state; calls super().load_state(). |
pylabrobot/resources/tip_rack.py |
Merges base state (rotation) with tip tracker state; calls super().load_state(). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Mirrors the Container/TipSpot pattern from this PR so `lh.rotate(...)` reaches the visualizer via the existing `set_state` channel. Per Copilot review on PyLabRobot#1062. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…adout Follow-up to the main rotation work on this PR. Three further `getAbsoluteLocation()` callsites in lib.js were rotation-blind for the same reason as the bullseye / highlights / delta lines: - Tooltip position (`Resource.draw` mouseover) now centres on the rotated visual centre via `getResourceWorldReferencePoint`, so the tooltip floats above rotated resources instead of off to one side. - `focusOnResource` (double-click zoom) now fits and centres on the rotated world AABB (small `getResourceWorldAABB` helper added) so the visible plate is what the viewport zooms to. - Attribute panel `abs_location` readout now uses `getResourceWorldLocation`, so the value matches Python's `get_absolute_location()` for resources inside a rotated parent. Unrotated path provably unchanged in all three: same `!xRefStr ||` fall-through and at `totalRot=0` the cos/sin terms vanish. Verified: 21 invariants + 1405 broader tests + 38 stress assertions all green. Co-Authored-By: Claude Opus 4.7 (1M context) <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.
Closes #1056. The Visualizer dropped rotation updates because
Resource.rotate()had no notification channel andResource.draw()never passedrotationto its Konva.Group.Rotation becomes part of every resource's serialized state and rides the existing
set_statechannel:rotate()fires_state_updated(); baseserialize_state()/load_state()include"rotation";Container/TipSpotchain throughsuper(). On the JS side,Resource.draw()andResource.setState()apply rotation to the Konva.Group, andContainer/TipSpot/LiquidHandlerchain tosuper().setState(state).Visualizer overlay markers follow rotation now. Outlines that should rotate with the resource (click and hover highlights) nest inside
resource.group. Axis-aligned markers that must track a chosen anchor on a rotated resource (bullseye, delta-line endpoints, coords-tool readouts) go through three small rotation-aware helpers that mirror Python'sResource.get_absolute_*for the 2D z-only case; at zero rotation they reduce to the legacy math byte-for-byte, so the unrotated path is provably unchanged.No public API change; pre-patch saved state JSONs still load.
Verified: 21 patch invariants, full
pytest pylabrobot/ --ignore=pylabrobot/liquid_handling/backends(1405 tests), and the visualizer demo at 0°, 45°, 90°, 180°, 270° - plate body, both highlights, bullseye, and delta lines all track rotation.🤖 Generated with Claude Code