inire/inire/geometry/primitives.py

65 lines
1.6 KiB
Python

from __future__ import annotations
import numpy
# 1nm snap (0.001 µm)
GRID_SNAP_UM = 0.001
def snap_nm(value: float) -> float:
"""
Snap a coordinate to the nearest 1nm (0.001 um).
"""
return round(value * 1000) / 1000
class Port:
"""
A port defined by (x, y, orientation) in micrometers.
"""
__slots__ = ('x', 'y', 'orientation')
def __init__(
self,
x: float,
y: float,
orientation: float,
) -> None:
self.x = snap_nm(x)
self.y = snap_nm(y)
self.orientation = float(orientation % 360)
def __repr__(self) -> str:
return f'Port(x={self.x}, y={self.y}, orientation={self.orientation})'
def __eq__(self, other: object) -> bool:
if not isinstance(other, Port):
return False
return (self.x == other.x and
self.y == other.y and
self.orientation == other.orientation)
def __hash__(self) -> int:
return hash((self.x, self.y, self.orientation))
def translate_port(port: Port, dx: float, dy: float) -> Port:
"""
Translate a port by (dx, dy).
"""
return Port(port.x + dx, port.y + dy, port.orientation)
def rotate_port(port: Port, angle: float, origin: tuple[float, float] = (0, 0)) -> Port:
"""
Rotate a port by a multiple of 90 degrees around an origin.
"""
ox, oy = origin
px, py = port.x, port.y
rad = numpy.radians(angle)
qx = snap_nm(ox + numpy.cos(rad) * (px - ox) - numpy.sin(rad) * (py - oy))
qy = snap_nm(oy + numpy.sin(rad) * (px - ox) + numpy.cos(rad) * (py - oy))
return Port(qx, qy, port.orientation + angle)