diff --git a/src/groovylight/geom.py b/src/groovylight/geom.py index dcf384d..d658c7c 100644 --- a/src/groovylight/geom.py +++ b/src/groovylight/geom.py @@ -3,6 +3,8 @@ from enum import Enum from dataclasses import dataclass +from typing import Self +from math import ceil, log2 @dataclass(frozen=True, order=True) @@ -13,7 +15,7 @@ class Coord: x: int y: int - + def __post_init__(self): if self.x < 0 or self.y < 0: raise RuntimeError("x and y must both be >= 0") @@ -34,6 +36,26 @@ class BBox: def contains(self, c: Coord) -> bool: return c > self.topleft and c < self.bottomright + @property + def width(self) -> int: + return self.bottomright.x - self.topleft.x + + @property + def height(self) -> int: + return self.bottomright.y - self.topleft.y + + def intersects(self, other: Self) -> bool: + ## other leftmost edge is right of our rightmost edge + x1 = other.topleft.x > self.bottomright.x + # our leftmost edge is to the right of other rightmost edge + x2 = self.topleft.x > other.bottomright.x + # other top edge is below (greater than!) our bottom edge + y1 = other.topleft.y > self.bottomright.y + # our top edge is below other bottom edge. + y2 = self.topleft.y > other.bottomright.y + + return not (x1 or x2 or y1 or y2) + @dataclass(frozen=True) class DisplayDimensions: @@ -44,6 +66,10 @@ class DisplayDimensions: length: int height: int + mux: int = 2 # number of lines driven at once. + + def addr_bits(self) -> int: + return ceil(log2(self.height / self.mux)) class DisplayRotation(Enum): @@ -68,7 +94,7 @@ class DisplayRotation(Enum): @dataclass(frozen=True) -class _DisplayString: +class DisplayString: """Internal class to represent a string of HUB75 displays. position: (X,Y) coordinates of the local top-left of the display @@ -81,6 +107,7 @@ class _DisplayString: position: Coord dimensions: DisplayDimensions rotation: DisplayRotation + # TODO: encode muxing @property def bbox(self) -> BBox: @@ -103,10 +130,13 @@ class _DisplayString: """Checks if the given coordinate is inside this display.""" return self.bbox.contains(coord) + def intersects(self, box: BBox) -> bool: + """Checks if the given BBox intersects with this display""" + return self.bbox.intersects(box) + class DisplayGeometry: - """Represents a display based on several strings in different positions. - """ + """Represents a display based on several strings in different positions.""" def __init__(self, *, strict: bool = False): self.strict = strict