From fbb39a85e51fb81b3adafc0f87778c8a002a8967 Mon Sep 17 00:00:00 2001 From: saji Date: Thu, 19 Sep 2024 14:32:02 -0500 Subject: [PATCH] added geometry module --- src/groovylight/geom.py | 124 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 src/groovylight/geom.py diff --git a/src/groovylight/geom.py b/src/groovylight/geom.py new file mode 100644 index 0000000..dcf384d --- /dev/null +++ b/src/groovylight/geom.py @@ -0,0 +1,124 @@ +# Geometry-related classes and functions. Manipulations and +# generation of panel-layout metadata for use in the HDL. + +from enum import Enum +from dataclasses import dataclass + + +@dataclass(frozen=True, order=True) +class Coord: + """Coordinate class. Uses computer-graphics standard coordinate system, + where X=0, Y=0 is top left. +X goes right. +Y goes down. + """ + + 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") + + +@dataclass(frozen=True) +class BBox: + """Bounding box class. Captures the top left coordinate and bottom right coordinate + of an object""" + + topleft: Coord + bottomright: Coord + + def __post_init__(self): + if not self.topleft < self.bottomright: + raise RuntimeError("topleft must be strictly less than bottomright") + + def contains(self, c: Coord) -> bool: + return c > self.topleft and c < self.bottomright + + +@dataclass(frozen=True) +class DisplayDimensions: + """Represents the dimensions of a display string, in length x height. Notably + this is in local coordinates to the display. The display top left is 0,0. + Uses length/height notation to separate it from coord + """ + + length: int + height: int + + +class DisplayRotation(Enum): + """Display rotation enums. The names indicate the general direction + of data flow. most normal displays (LCDs, CRTs) are LEFTRIGHT, + since they scan left-to-right, top to bottom. + + Note that the direction of subsequent lines is not always clear. + They are enumerated below: + LEFTRIGHT -> next line is below it (-Y) + UPDOWN -> next line is to the LEFT (-X) + DOWNUP -> next line is to the RIGHT (+X) + RIGHTLEFT -> next line is above it (+Y) + + Generally, prefer LEFTRIGHT/UPDOWN over other rotations. + """ + + LEFTRIGHT = 0 + UPDOWN = 1 + DOWNUP = 2 + RIGHTLEFT = 3 # why are you like this. + + +@dataclass(frozen=True) +class _DisplayString: + """Internal class to represent a string of HUB75 displays. + + position: (X,Y) coordinates of the local top-left of the display + + dimensions: (length, height) local-coordinate dimensions of the display. + + rotation: DisplayRotation: the orientation of the display. + """ + + position: Coord + dimensions: DisplayDimensions + rotation: DisplayRotation + + @property + def bbox(self) -> BBox: + """Returns the bounding box of the display based on the dimensions, position, and rotation.""" + x = self.position.x + y = self.position.y + l = self.dimensions.length + h = self.dimensions.height + match self.rotation: + case DisplayRotation.LEFTRIGHT: + return BBox(Coord(x, y), Coord(x + l, y + h)) + case DisplayRotation.UPDOWN: + return BBox(Coord(x - h, y), Coord(x, 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: + """Checks if the given coordinate is inside this display.""" + return self.bbox.contains(coord) + + +class DisplayGeometry: + """Represents a display based on several strings in different positions. + """ + + def __init__(self, *, strict: bool = False): + self.strict = strict + pass + + def add_string(self, position: (int, int), rot: int, dimensions: (int, int)): + """Add a new string to the display. This new string is located at + a specific point, and has a direction, along with dimension that reveal + the number of address lines (typically 64, with 1:32 selection so 5 address + bits) and the total length of the string which is used to size the line + buffers. + + When in strict mode, this method may throw an exception if this new string + will overlap with an existing string. + """