Source code for jitxlib.symbols.net_symbols.ground

"""
Ground symbol for JITX Standard Library

This module defines the Ground symbol, often used as a net symbol, and associated construction functions.
"""

from __future__ import annotations
from dataclasses import dataclass, replace
from typing import TYPE_CHECKING

from jitx.compat.altium import AltiumSymbol, AltiumSymbolProperty
from jitx.shapes import Shape
from jitx.shapes.primitive import Polyline
from jitx.symbol import Direction, Pin, SymbolOrientation

from ..common import DEF_LINE_WIDTH
from ..label import LabelConfigurable, LabelledSymbol

if TYPE_CHECKING:
    from ..context import SymbolStyleContext


# Ground symbol constants
DEF_GND_PORCH_WIDTH = 1.0
DEF_GND_SPACING = 0.4
DEF_GND_LINE_COUNT = 3
DEF_GND_MAX_LEN = 2.0
DEF_GND_MIN_LEN = 0.4


[docs] @dataclass class GroundConfig(LabelConfigurable): """ Configuration for ground symbols Defines the geometric and visual parameters for ground symbols. """ line_width: float = DEF_LINE_WIDTH """Width of the ground symbol lines""" porch_width: float = DEF_GND_PORCH_WIDTH """Distance from pin to first horizontal line""" spacing: float = DEF_GND_SPACING """Vertical spacing between horizontal lines""" max_len: float = DEF_GND_MAX_LEN """Length of the longest line in the ground symbol""" min_len: float = DEF_GND_MIN_LEN """Length of the shortest line in the ground symbol""" line_count: int = DEF_GND_LINE_COUNT """Number of lines in the ground symbol""" def __post_init__(self): """Validate configuration parameters""" if self.line_width <= 0: raise ValueError("Ground symbol line_width must be positive") if self.porch_width <= 0: raise ValueError("Ground symbol porch_width must be positive") if self.spacing <= 0: raise ValueError("Ground symbol spacing must be positive") if self.max_len <= 0: raise ValueError("Ground symbol max_len must be positive") if self.min_len <= 0: raise ValueError("Ground symbol min_len must be positive") if self.max_len < self.min_len: raise ValueError("Ground symbol max_len cannot be less than min_len") if self.line_count < 1: raise ValueError("Ground symbol must have at least one line")
[docs] class GroundSymbol(LabelledSymbol): """ Ground symbol with graphics and pin. Creates a typical power ground net symbol consisting of horizontal lines of evenly decreasing length, evenly spaced in the -Y dimension. """ config: GroundConfig vertical_line: Shape[Polyline] horizontal_lines: tuple[Shape[Polyline], ...] gnd: Pin def _lookup_config( self, config: GroundConfig | None = None, context: SymbolStyleContext | None = None, ) -> GroundConfig: """Lookup the config for this symbol.""" if config is None: if context is None: return GroundConfig() return context.ground_config return config def __init__(self, config: GroundConfig | None = None, **kwargs): """ Initialize ground symbol Args: config: GroundConfig, or None to use defaults **kwargs: Individual parameters to override defaults """ # Apparently this needs to be imported here, even though SymbolStyleContext is imported in TYPE_CHECKING. from ..context import SymbolStyleContext context = SymbolStyleContext.get() config = self._lookup_config(config, context) self.config = replace(config, **kwargs) self._build_ground_symbol() self._build_pins() self._build_labels(value=Direction.Down) self.orientation = SymbolOrientation(0) AltiumSymbolProperty(AltiumSymbol.PowerGndPower).assign(self) def _build_pins(self) -> None: """Build 'gnd' symbol pin for the ground.""" self.gnd = Pin(at=(0, 0), direction=Direction.Up) def _build_ground_symbol(self) -> None: """Build the ground symbol artwork.""" # Vertical line from pin to first horizontal line self.vertical_line = Polyline( self.config.line_width, [(0.0, 0.0), (0.0, -self.config.porch_width)], ) # Generate line lengths that decrease evenly from max_len to min_len line_lengths = self._generate_line_lengths() # Horizontal lines of decreasing length horizontal_lines = [] for i, line_length in enumerate(line_lengths): half_width = line_length / 2.0 y_pos = -self.config.porch_width - (i * self.config.spacing) line = Polyline( self.config.line_width, [(-half_width, y_pos), (half_width, y_pos)], ) horizontal_lines.append(line) self.horizontal_lines = tuple(horizontal_lines) def _generate_line_lengths(self) -> tuple[float, ...]: """Generate evenly spaced line lengths from max_len to min_len.""" if self.config.line_count == 1: return (self.config.max_len,) # Calculate the step size for even distribution step = (self.config.max_len - self.config.min_len) / ( self.config.line_count - 1 ) # Generate lengths from max to min lengths = [] for i in range(self.config.line_count): length = self.config.max_len - (i * step) lengths.append(length) return tuple(lengths) # Convenience properties to access config values @property def line_width(self) -> float: """See :attr:`~.GroundConfig.line_width`.""" return self.config.line_width @property def porch_width(self) -> float: """See :attr:`~.GroundConfig.porch_width`.""" return self.config.porch_width @property def spacing(self) -> float: """See :attr:`~.GroundConfig.spacing`.""" return self.config.spacing @property def max_len(self) -> float: """See :attr:`~.GroundConfig.max_len`.""" return self.config.max_len @property def min_len(self) -> float: """See :attr:`~.GroundConfig.min_len`.""" return self.config.min_len @property def line_count(self) -> int: """See :attr:`~.GroundConfig.line_count`.""" return self.config.line_count @property def line_lengths(self) -> tuple[float, ...]: """Generated line lengths from max_len to min_len.""" return self._generate_line_lengths() @property def label_config(self) -> LabelConfigurable: """Configuration object that provides label configuration""" return self.config