Source code for jitx.feature

"""
Feature definitions
===================

This module provides feature classes for representing board elements
like cutouts, keepouts, silkscreen, and other board features. Notably, this
does not include copper shapes, see the :py:mod:`~jitx.copper` module for that.
"""

from dataclasses import dataclass, replace
from typing import Self
import os
import warnings

# using fq names here to prevent reexport
import jitx._structural
import jitx.shapes
import jitx.layerindex


_warn_skips = (os.path.dirname(__file__),)


[docs] @dataclass class Feature(jitx._structural.Critical): """Base class for board features.""" shape: jitx.shapes.Shape """Shape of this feature."""
[docs] def invert(self) -> Self: """Return a copy of this feature with the layers inverted.""" return replace(self)
[docs] @dataclass class Cutout(Feature): """Cutout element for holes and slots. Through-hole pads will include their hole definition using a Cutout. >>> cutout = Cutout(Circle(radius=1.0)) """
[docs] @dataclass class MultiLayerFeature(Feature): """Feature that spans multiple board layers.""" layers: jitx.layerindex.LayerSet """The set of layers this feature applies to."""
[docs] def invert(self) -> Self: return replace(self, layers=self.layers.invert())
[docs] @dataclass(kw_only=True) class KeepOut(MultiLayerFeature): """Construct keepout regions on a range of layers. >>> # Keep out pours and vias on the top layer >>> keepout = KeepOut(layers=LayerSet(0), pour=True, via=True, route=False) """ pour: bool = False """Keep pours from covering this area.""" via: bool = False """Avoid auto-placing vias in this area.""" route: bool = False """Disallow auto-router traces in this area.""" def __post_init__(self): if not (self.pour or self.via or self.route): warnings.warn( "KeepOut has no effect: all of pour, via, and route are False. " "Set at least one to True for the keepout to have any effect.", skip_file_prefixes=_warn_skips, stacklevel=2, )
# @dataclass # class BoardEdge(Feature): # """Board edge alignment feature."""
[docs] @dataclass class SurfaceFeature(Feature): """Surface features are features that apply to either top or bottom side of the board, but not to internal layers. This class should not be used directly, instead use one of the subclasses.""" side: jitx.layerindex.Side = jitx.layerindex.Side.Top """Side of this surface feature. Note that a "bottom" side simply means the opposite side of where the landpattern / module it's associated with is placed, and not necessarily the bottom side."""
[docs] def invert(self) -> Self: return replace(self, side=self.side.flip())
[docs] @dataclass class Silkscreen(SurfaceFeature): """Add a shape to the silkscreen layer >>> silkscreen = Silkscreen(rectangle(2, 3), side=Side.Bottom) """
[docs] @dataclass class Soldermask(SurfaceFeature): """Add a shape to the solder mask layer >>> soldermask = Soldermask(rectangle(2, 1), side=Side.Top) """
[docs] @dataclass class Paste(SurfaceFeature): """Add a shape to the paste application layer >>> paste = Paste(circle(diameter=1)) """
[docs] @dataclass class Glue(SurfaceFeature): """Add a shape to the glue application layer >>> glue = Glue(circle(diameter=1), side=Side.Bottom) """
[docs] @dataclass class Finish(SurfaceFeature): """Add a shape to the glue application layer >>> finish = Finish(rectangle(1, 1)) """
[docs] @dataclass class Courtyard(SurfaceFeature): """Courtyards are used to indicate land pattern bounds. >>> courtyard = Courtyard(rectangle(3, 3)) """ def __post_init__(self): # lines forming a rectangle are often erroneously used to represent a # rectangular courtyard, trap it here and have them convert it to a # polygon if they really want to keep it. from jitx.shapes.primitive import Polyline, ArcPolyline if isinstance(self.shape.geometry, Polyline | ArcPolyline): raise ValueError( "Courtyard must be a closed shape, but got a polyline " "which is unlikely to actually represent the courtyard shape. " "To keep using this shape, convert it to a polygon." )
[docs] @dataclass(kw_only=True) class Custom(SurfaceFeature): """Custom surface feature with user-defined name. >>> custom = Custom(rectangle(2, 2), name="Fab") """ name: str """Name of the custom feature layer."""