Skip to content

vector_2d

vector_2d

Simple of 2D vector container class.

Vector2D

Bases: FrozenGeneralModel

Tuple wrapper for representing size with custom accessors.

Source code in src/pygerber/gerberx3/math/vector_2d.py
class Vector2D(FrozenGeneralModel):
    """Tuple wrapper for representing size with custom accessors."""

    NULL: ClassVar[Vector2D]
    UNIT_X: ClassVar[Vector2D]
    UNIT_Y: ClassVar[Vector2D]

    x: Offset
    y: Offset

    def as_pixels(self, dpi: int) -> tuple[int, int]:
        """Return size as pixels using given DPI for conversion."""
        return (self.x.as_pixels(dpi), self.y.as_pixels(dpi))

    def __eq__(self, other: object) -> bool:
        if isinstance(other, Vector2D):
            return self.x == other.x and self.y == other.y
        return NotImplemented

    def _operator(
        self,
        other: object,
        op: Callable,
    ) -> Vector2D:
        if isinstance(other, Offset):
            return Vector2D(
                x=op(self.x, other),
                y=op(self.y, other),
            )
        if isinstance(other, Vector2D):
            return Vector2D(
                x=op(self.x, other.x),
                y=op(self.y, other.y),
            )

        if isinstance(other, (Decimal, int, float, str)):
            return Vector2D(
                x=op(self.x, Decimal(other)),
                y=op(self.y, Decimal(other)),
            )
        return NotImplemented  # type: ignore[unreachable]

    def __add__(self, other: object) -> Vector2D:
        return self._operator(other, operator.add)

    def __sub__(self, other: object) -> Vector2D:
        return self._operator(other, operator.sub)

    def __mul__(self, other: object) -> Vector2D:
        return self._operator(other, operator.mul)

    def __truediv__(self, other: object) -> Vector2D:
        return self._operator(other, operator.truediv)

    def __neg__(self) -> Vector2D:
        return Vector2D(x=-self.x, y=-self.y)

    def _i_operator(
        self,
        other: object,
        op: Callable,
    ) -> Vector2D:
        if isinstance(other, Vector2D):
            return self.model_copy(
                update={
                    "x": op(self.x, other.x),
                    "y": op(self.y, other.y),
                },
            )
        if isinstance(other, Offset):
            return self.model_copy(
                update={
                    "x": op(self.x, other),
                    "y": op(self.y, other),
                },
            )
        if isinstance(other, (Decimal, int, float, str)):
            return self.model_copy(
                update={
                    "x": op(self.x, Decimal(other)),
                    "y": op(self.y, Decimal(other)),
                },
            )
        return NotImplemented  # type: ignore[unreachable]

    def __iadd__(self, other: object) -> Vector2D:
        return self._i_operator(other, operator.add)

    def __isub__(self, other: object) -> Vector2D:
        return self._i_operator(other, operator.sub)

    def __imul__(self, other: object) -> Vector2D:
        return self._i_operator(other, operator.mul)

    def __itruediv__(self, other: object) -> Vector2D:
        return self._i_operator(other, operator.truediv)

    def __str__(self) -> str:
        return f"{self.__class__.__qualname__}(x={self.x}, y={self.y})"

    def length(self) -> Offset:
        """Return length of vector."""
        return Offset(value=((self.x * self.x).value + (self.y * self.y).value).sqrt())

    def angle_between_clockwise(self, other: Vector2D) -> float:
        """Calculate angle between two vectors in degrees clockwise."""
        self_norm = self / self.length()
        other_norm = other / other.length()

        dot = other_norm.dot(self_norm)
        determinant = self_norm.determinant(other_norm)

        theta = math.atan2(float(dot.value), float(determinant.value))

        return math.degrees(theta)

    def dot(self, other: Vector2D) -> Offset:
        """Calculate dot product of two vectors."""
        return self.x * other.x + self.y * other.y

    def determinant(self, other: Vector2D) -> Offset:
        """Calculate determinant of matrix constructed from self and other."""
        return self.x * other.y - self.y * other.x

as_pixels

as_pixels(dpi: int) -> tuple[int, int]

Return size as pixels using given DPI for conversion.

Source code in src/pygerber/gerberx3/math/vector_2d.py
def as_pixels(self, dpi: int) -> tuple[int, int]:
    """Return size as pixels using given DPI for conversion."""
    return (self.x.as_pixels(dpi), self.y.as_pixels(dpi))

length

length() -> Offset

Return length of vector.

Source code in src/pygerber/gerberx3/math/vector_2d.py
def length(self) -> Offset:
    """Return length of vector."""
    return Offset(value=((self.x * self.x).value + (self.y * self.y).value).sqrt())

angle_between_clockwise

angle_between_clockwise(other: Vector2D) -> float

Calculate angle between two vectors in degrees clockwise.

Source code in src/pygerber/gerberx3/math/vector_2d.py
def angle_between_clockwise(self, other: Vector2D) -> float:
    """Calculate angle between two vectors in degrees clockwise."""
    self_norm = self / self.length()
    other_norm = other / other.length()

    dot = other_norm.dot(self_norm)
    determinant = self_norm.determinant(other_norm)

    theta = math.atan2(float(dot.value), float(determinant.value))

    return math.degrees(theta)

dot

dot(other: Vector2D) -> Offset

Calculate dot product of two vectors.

Source code in src/pygerber/gerberx3/math/vector_2d.py
def dot(self, other: Vector2D) -> Offset:
    """Calculate dot product of two vectors."""
    return self.x * other.x + self.y * other.y

determinant

determinant(other: Vector2D) -> Offset

Calculate determinant of matrix constructed from self and other.

Source code in src/pygerber/gerberx3/math/vector_2d.py
def determinant(self, other: Vector2D) -> Offset:
    """Calculate determinant of matrix constructed from self and other."""
    return self.x * other.y - self.y * other.x