rework geometry so displaystring position is always topleft screen coord

This commit is contained in:
saji 2024-10-14 11:15:03 -05:00
parent 6b034b0176
commit 261dedc4dc
2 changed files with 61 additions and 16 deletions

View file

@ -110,17 +110,17 @@ class DisplayRotation(Enum):
Generally, prefer LEFTRIGHT/UPDOWN over other rotations. Generally, prefer LEFTRIGHT/UPDOWN over other rotations.
""" """
LEFTRIGHT = 0 R0 = 0
UPDOWN = 1 R90 = 1
DOWNUP = 2 R180 = 2
RIGHTLEFT = 3 # why are you like this. R270 = 3 # why are you like this.
@dataclass(frozen=True) @dataclass(frozen=True)
class DisplayString: class DisplayString:
"""Internal class to represent a string of HUB75 displays. """Internal class to represent a string of HUB75 displays.
position: (X,Y) coordinates of the local top-left of the display position: (X,Y) screen coordinates of the top-left of the display.
dimensions: (length, height) local-coordinate dimensions of the display. dimensions: (length, height) local-coordinate dimensions of the display.
@ -129,8 +129,7 @@ class DisplayString:
position: Coord position: Coord
dimensions: DisplayDimensions dimensions: DisplayDimensions
rotation: DisplayRotation rotation: DisplayRotation = DisplayRotation.R0
# TODO: encode muxing
@property @property
def bbox(self) -> BBox: def bbox(self) -> BBox:
@ -140,14 +139,10 @@ class DisplayString:
l = self.dimensions.length l = self.dimensions.length
h = self.dimensions.height h = self.dimensions.height
match self.rotation: match self.rotation:
case DisplayRotation.LEFTRIGHT: case DisplayRotation.R0 | DisplayRotation.R270:
return BBox(Coord(x, y), Coord(x + l, y + h)) return BBox(Coord(x, y), Coord(x + l, y + h))
case DisplayRotation.UPDOWN: case DisplayRotation.R90 | DisplayRotation.R180:
return BBox(Coord(x - h, y), Coord(x, y + l)) return BBox(Coord(x, y), Coord(x + h, y + l))
case DisplayRotation.DOWNUP:
return BBox(Coord(x, y + l), Coord(x + h, y))
case DisplayRotation.RIGHTLEFT:
return BBox(Coord(x - l, y - h), Coord(x, y))
def contains_pix(self, coord: Coord) -> bool: def contains_pix(self, coord: Coord) -> bool:
"""Checks if the given coordinate is inside this display.""" """Checks if the given coordinate is inside this display."""
@ -157,6 +152,38 @@ class DisplayString:
"""Checks if the given BBox intersects with this display""" """Checks if the given BBox intersects with this display"""
return self.bbox.intersects(box) return self.bbox.intersects(box)
def translate_coord(self, pixnum, addr, mux):
"""Helper function to translate string coordinates to screen coordinates"""
assert mux < self.dimensions.mux, "Mux must be within the mux of the display"
x = self.position.x
y = self.position.y
addrshift = addr + (self.dimensions.height // self.dimensions.mux) * mux
#
match self.rotation:
case DisplayRotation.R0:
# x is linear
return {
"x": x + pixnum,
"y": y + addrshift,
}
case DisplayRotation.R90:
# x is height - addrshift
return {
"x": x + self.dimensions.height - addrshift - 1,
"y": y + pixnum,
}
case DisplayRotation.R180:
pass
case DisplayRotation.R270:
# x and y are both length - pixnum
return {
"x": x + self.dimensions.length - 1 - pixnum,
"y": y + addrshift,
}
class DisplayGeometry: class DisplayGeometry:
"""Represents a display based on several strings in different positions.""" """Represents a display based on several strings in different positions."""
@ -185,7 +212,7 @@ class DisplayGeometry:
return sum return sum
def add_string(self, s: DisplayString): def add_string(self, s: DisplayString):
"""Add a new string to the display. """Add a new string to the display.
When in strict mode, this method will throw an exception if this new string When in strict mode, this method will throw an exception if this new string
will overlap with an existing string. will overlap with an existing string.
""" """

View file

@ -1,4 +1,4 @@
from ..geom import Coord, BBox from ..geom import Coord, BBox, DisplayString, DisplayDimensions, DisplayRotation
import pytest import pytest
@ -37,3 +37,21 @@ def test_bbox():
BBox(Coord(0, 0), Coord(1, 0)) BBox(Coord(0, 0), Coord(1, 0))
with pytest.raises(RuntimeError): with pytest.raises(RuntimeError):
BBox(Coord(1, 1), Coord(0, 0)) BBox(Coord(1, 1), Coord(0, 0))
ds_testdata = [
(DisplayRotation.R0, (0, 0, 0), {"x": 3, "y": 0}),
(DisplayRotation.R0, (40, 2, 0), {"x": 43, "y": 2}),
(DisplayRotation.R0, (40, 2, 1), {"x": 43, "y": 34}),
(DisplayRotation.R90, (40, 0, 0), {"x": 66, "y": 40}),
(DisplayRotation.R90, (120, 2, 0), {"x": 64, "y": 120}),
]
@pytest.mark.parametrize("rot, inp, expected", ds_testdata)
def test_displaystring(rot, inp, expected):
ds = DisplayString(Coord(3, 0), DisplayDimensions(128, 64), rot)
res = ds.translate_coord(*inp)
assert res == expected