From 6f6dc29aff2ee4a3264e0975f9b4cbbe57cc87ee Mon Sep 17 00:00:00 2001 From: Sriram Devadas Date: Fri, 13 Nov 2026 09:05:45 -0500 Subject: [PATCH] `Visualizer3D`: a turned deck has the track marks it has upright `describe_grid` measures how far a deck's marks run along the deck's own y axis, the frame the page draws them in: the deck's own depth, and the depth of each carrier seated on the track line, turned as it stands on the deck. Turning the deck turns its marks without changing their length. --- pylabrobot/visualizer3D/grids.py | 20 ++++++++++++----- pylabrobot/visualizer3D/grids_tests.py | 30 ++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 5 deletions(-) create mode 100644 pylabrobot/visualizer3D/grids_tests.py diff --git a/pylabrobot/visualizer3D/grids.py b/pylabrobot/visualizer3D/grids.py index 46b84d5fd1b..b1d629cc36a 100644 --- a/pylabrobot/visualizer3D/grids.py +++ b/pylabrobot/visualizer3D/grids.py @@ -48,6 +48,17 @@ def describe_bands(resource: Resource) -> Optional[List[Dict[str, Any]]]: return described or None +def _depth_in(child: Resource, frame: Resource) -> float: + """How far `child` reaches along `frame`'s y axis, in mm: its box, turned as it stands there.""" + ys = [ + child.get_location_wrt(frame, x, y, z).y + for x in ("l", "r") + for y in ("f", "b") + for z in ("b", "t") + ] + return max(ys) - min(ys) + + def describe_grid(resource: Resource) -> Optional[Dict[str, Any]]: """The repeated grid this resource lays positions on, or None if it lays none. @@ -102,10 +113,9 @@ def describe_grid(resource: Resource) -> Optional[Dict[str, Any]]: # A mark runs to the back of what the positions carry, not of the resource: the deepest child # seated on the line (sharing its y), or the resource's own depth when nothing stands on it. - try: - depth = resource.get_absolute_size_y() - except Exception: - return None + # Both along the resource's own y, the frame the marks are drawn in, so turning the resource + # turns its marks without changing them. + depth = resource.get_size_y() seated: List[float] = [] for child in getattr(resource, "children", []): @@ -113,7 +123,7 @@ def describe_grid(resource: Resource) -> Optional[Dict[str, Any]]: if location is None or abs(location.y - first.y) > SEATED_TOLERANCE: continue try: - seated.append(child.get_absolute_size_y()) + seated.append(_depth_in(child, resource)) except Exception: continue diff --git a/pylabrobot/visualizer3D/grids_tests.py b/pylabrobot/visualizer3D/grids_tests.py new file mode 100644 index 00000000000..dcac972e2b5 --- /dev/null +++ b/pylabrobot/visualizer3D/grids_tests.py @@ -0,0 +1,30 @@ +"""The grids a resource lays its positions on, as the page is given them to draw.""" + +import unittest + +from pylabrobot.resources.hamilton import STARLetDeck, hamilton_plate_carrier_L5_ac +from pylabrobot.visualizer3D.grids import describe_grid + + +class DescribeGridTests(unittest.TestCase): + """A deck's grid is measured in the deck's own frame, the frame its marks are drawn in.""" + + def test_a_turned_deck_has_the_grid_it_has_upright(self): + """The page draws a deck's marks in the deck's own frame and turns them with the deck, so the + grid is in that frame too. From a STARlet's front edge, its marks end at its back, 653.5 mm, + or at the back of a carrier on track 1, 560 mm: an `extent` of 590.5 or 497 mm from the first + track's line at 63 mm. A STARlet turned 90 degrees and measured along the world's y would end + its marks 1005 mm from its front edge, or 198 mm beside the carrier.""" + for turn in (90, 37.5): + for with_carrier, extent in ((False, 590.5), (True, 497.0)): + upright, turned = STARLetDeck(), STARLetDeck() + if with_carrier: + for deck in (upright, turned): + deck.assign_child_resource(hamilton_plate_carrier_L5_ac("carrier"), track=1) + turned.rotate(z=turn) + with self.subTest(turn=turn, with_carrier=with_carrier): + upright_grid, turned_grid = describe_grid(upright), describe_grid(turned) + assert upright_grid is not None and turned_grid is not None + self.assertAlmostEqual(upright_grid.pop("extent"), extent, places=3) + self.assertAlmostEqual(turned_grid.pop("extent"), extent, places=3) + self.assertEqual(turned_grid, upright_grid)