generated from saji/ecp5-template
added geometry module
This commit is contained in:
parent
66b492e147
commit
fbb39a85e5
124
src/groovylight/geom.py
Normal file
124
src/groovylight/geom.py
Normal file
|
@ -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.
|
||||||
|
"""
|
Loading…
Reference in a new issue