Source code for jitxlib.landpatterns.package
from __future__ import annotations
from typing import override, final, Self
from dataclasses import dataclass
from jitx.shapes import Shape
from jitx.shapes.primitive import Circle
from jitx.shapes.composites import rectangle
from jitx.toleranced import Toleranced
from jitx.placement import Kinematic
from .ipc import DensityLevel
[docs]
@dataclass(kw_only=True)
class PackageBody(Kinematic):
"""Component package body base class
This class is the representation of the 3D footprint of a component. Do not
instantiate directly, but use on of the subclasses instead.
"""
height: Toleranced
"""Height of the package body"""
[docs]
@final
def envelope(self, density_level: DensityLevel) -> Shape:
"""Compute the envelope of the package body
This method returns the 2D envelope of the package body at the
given density level.
Args:
density_level: density level to compute the envelope for
Returns:
The envelope of the package body at the given density level
"""
shape = self._envelope(density_level)
if self.transform:
shape = shape.at(self.transform)
return shape
def _envelope(self, density_level: DensityLevel) -> Shape:
raise NotImplementedError
@property
def dims(self) -> tuple[Toleranced, Toleranced]:
"""Get the axis aligned bounding box dimensions of the package body."""
raise NotImplementedError(f"{self.__class__.__name__}.dims")
[docs]
@dataclass
class RectanglePackage(PackageBody):
"""Rectangular PackageBody
This class represents a rectangular prism bounding box. For a cylindrical
package body, use :py:class:`~CylinderPackage` instead.
"""
width: Toleranced
"""Width of the package body"""
length: Toleranced
"""Length of the package body"""
@override
def _envelope(self, density_level: DensityLevel):
width = self.width.typ
length = self.length.typ
match density_level:
case DensityLevel.A:
width = self.width.max_value
length = self.length.max_value
case DensityLevel.C:
width = self.width.min_value
length = self.length.min_value
return rectangle(width, length)
@property
def dims(self) -> tuple[Toleranced, Toleranced]:
return self.width, self.length
[docs]
@dataclass
class CylinderPackage(PackageBody):
"""Cylindrical PackageBody
This class represents a cylindrical bounding box. For a rectangular package
body, use :py:class:`~RectanglePackage` instead.
"""
diameter: Toleranced
"""Diameter of the package body"""
@override
def _envelope(self, density_level: DensityLevel):
diameter = self.diameter.typ
match density_level:
case DensityLevel.A:
diameter = self.diameter.max_value
case DensityLevel.C:
diameter = self.diameter.min_value
return Circle(diameter=diameter)
@property
def dims(self) -> tuple[Toleranced, Toleranced]:
return self.diameter, self.diameter
[docs]
class PackageBodyMixin:
"""Mixin class for components that have a package body"""
__package_body: PackageBody | None = None
"""Package body of the component"""
[docs]
def package_body(self, body: PackageBody) -> Self:
"""Set the package body of the component"""
self.__package_body = body
return self
@property
def _package_body_optional(self) -> PackageBody | None:
"""Get the package body of the component"""
return self.__package_body
def _package_body(self) -> PackageBody:
"""Get the package body of the component"""
if self.__package_body is None:
raise RuntimeError("Package body not set")
return self.__package_body