Source code for kicadfiles.board_layout

"""Board layout elements for KiCad S-expressions - PCB/board design and routing."""

from dataclasses import dataclass, field
from typing import Any, List, Optional

from .advanced_graphics import GrText
from .base_element import (
    KiCadFloat,
    KiCadInt,
    KiCadObject,
    KiCadStr,
    OptionalFlag,
    ParseStrictness,
)
from .base_types import (
    At,
    End,
    Layer,
    Layers,
    Property,
    Start,
    Uuid,
)
from .footprint_library import Footprint
from .pad_and_drill import Net
from .zone_system import Zone


[docs] @dataclass class Nets(KiCadObject): """Nets section definition token. The 'nets' token defines nets for the board in the format:: (net ORDINAL "NET_NAME" ) Args: net_definitions: List of net definitions (ordinal, net_name) """ __token_name__ = "nets" net_definitions: List[tuple[Any, ...]] = field( default_factory=list, metadata={"description": "List of net definitions (ordinal, net_name)"}, )
[docs] @dataclass class PrivateLayers(KiCadObject): """Private layers definition token. The 'private_layers' token defines layers private to specific elements in the format:: (private_layers "LAYER_LIST") Args: layers: List of private layer names """ __token_name__ = "private_layers" layers: List[str] = field( default_factory=list, metadata={"description": "List of private layer names"} )
[docs] @dataclass class Segment(KiCadObject): """Track segment definition token. The 'segment' token defines a track segment in the format:: (segment (start X Y) (end X Y) (width WIDTH) (layer LAYER_DEFINITION) [(locked)] (net NET_NUMBER) (tstamp UUID) ) Args: start: Coordinates of the beginning of the line end: Coordinates of the end of the line width: Line width layer: Layer the track segment resides on locked: Whether the line cannot be edited (optional) net: Net ordinal number from net section tstamp: Unique identifier of the line object (optional) uuid: Unique identifier """ __token_name__ = "segment" start: Start = field( default_factory=lambda: Start(), metadata={"description": "Coordinates of the beginning of the line"}, ) end: End = field( default_factory=lambda: End(), metadata={"description": "Coordinates of the end of the line"}, ) width: KiCadFloat = field( default_factory=lambda: KiCadFloat("width", 0.0), metadata={"description": "Line width"}, ) layer: Layer = field( default_factory=lambda: Layer(), metadata={"description": "Layer the track segment resides on"}, ) locked: Optional[OptionalFlag] = field( default_factory=lambda: OptionalFlag.create_bool_flag("locked"), metadata={ "description": "Whether the line cannot be edited", "required": False, }, ) net: int = field( default=0, metadata={"description": "Net ordinal number from net section"} ) tstamp: Optional[KiCadStr] = field( default_factory=lambda: KiCadStr("tstamp", "", required=False), metadata={ "description": "Unique identifier of the line object", "required": False, }, ) # NEW Variant uuid: Optional[Uuid] = field( default_factory=lambda: Uuid(), metadata={"description": "Unique identifier"} ) # Old Variant
[docs] @dataclass class Setup(KiCadObject): """Board setup definition token. The 'setup' token stores current settings and options used by the board in the format:: (setup [(STACK_UP_SETTINGS)] (pad_to_mask_clearance CLEARANCE) [(solder_mask_min_width MINIMUM_WIDTH)] [(pad_to_paste_clearance CLEARANCE)] [(pad_to_paste_clearance_ratio RATIO)] [(aux_axis_origin X Y)] [(grid_origin X Y)] (PLOT_SETTINGS) ) Args: stackup: Stackup definition (optional) pad_to_mask_clearance: Pad to mask clearance solder_mask_min_width: Minimum solder mask width (optional) pad_to_paste_clearance: Pad to paste clearance (optional) pad_to_paste_clearance_ratio: Pad to paste clearance ratio (0-100%) (optional) aux_axis_origin: Auxiliary axis origin (X, Y) (optional) grid_origin: Grid origin (X, Y) (optional) plot_settings: Plot settings (optional) """ __token_name__ = "setup" stackup: Optional[dict[Any, Any]] = field( default=None, metadata={"description": "Stackup definition", "required": False} ) pad_to_mask_clearance: float = field( default=0.0, metadata={"description": "Pad to mask clearance"} ) solder_mask_min_width: Optional[float] = field( default=None, metadata={"description": "Minimum solder mask width", "required": False}, ) pad_to_paste_clearance: Optional[float] = field( default=None, metadata={"description": "Pad to paste clearance", "required": False}, ) pad_to_paste_clearance_ratio: Optional[float] = field( default=None, metadata={ "description": "Pad to paste clearance ratio (0-100%)", "required": False, }, ) aux_axis_origin: Optional[tuple[float, float]] = field( default=None, metadata={"description": "Auxiliary axis origin (X, Y)", "required": False}, ) grid_origin: Optional[tuple[float, float]] = field( default=None, metadata={"description": "Grid origin (X, Y)", "required": False}, ) plot_settings: Optional[dict[Any, Any]] = field( default=None, metadata={"description": "Plot settings", "required": False} )
[docs] @dataclass class General(KiCadObject): """General board settings definition token. The 'general' token defines general board settings in the format:: (general (thickness THICKNESS) [(legacy_teardrops yes|no)] ) Args: thickness: Board thickness legacy_teardrops: Whether to use legacy teardrops (optional) """ __token_name__ = "general" thickness: KiCadFloat = field( default_factory=lambda: KiCadFloat("thickness", 1.6), metadata={"description": "Board thickness"}, ) legacy_teardrops: Optional[OptionalFlag] = field( default_factory=lambda: OptionalFlag.create_bool_flag("legacy_teardrops"), metadata={ "description": "Whether to use legacy teardrops", "required": False, }, )
[docs] @dataclass class Tracks(KiCadObject): """Tracks container definition token. The 'tracks' token defines a container for track segments in the format:: (tracks (segment ...) ... ) Args: segments: List of track segments """ __token_name__ = "tracks" segments: List[Segment] = field( default_factory=list, metadata={"description": "List of track segments"} )
[docs] @dataclass class Via(KiCadObject): """Via definition token. The 'via' token defines a track via in the format:: (via [TYPE] [(locked)] (at X Y) (size DIAMETER) (drill DIAMETER) (layers LAYER1 LAYER2) [(remove_unused_layers)] [(keep_end_layers)] [(free)] (net NET_NUMBER) (tstamp UUID) ) Args: type: Via type (blind | micro) (optional) locked: Whether the line cannot be edited (optional) at: Coordinates of the center of the via size: Diameter of the via annular ring drill: Drill diameter of the via layers: Layer set the via connects remove_unused_layers: Remove unused layers flag (optional) keep_end_layers: Keep end layers flag (optional) free: Whether via is free to move outside assigned net (optional) net: Net ordinal number from net section tstamp: Unique identifier of the line object (optional) uuid: Unique identifier """ __token_name__ = "via" type: Optional[str] = field( default=None, metadata={"description": "Via type (blind | micro)", "required": False}, ) locked: Optional[OptionalFlag] = field( default_factory=lambda: OptionalFlag.create_bool_flag("locked"), metadata={ "description": "Whether the line cannot be edited", "required": False, }, ) at: At = field( default_factory=lambda: At(), metadata={"description": "Coordinates of the center of the via"}, ) size: KiCadFloat = field( default_factory=lambda: KiCadFloat("size", 0.0), metadata={"description": "Diameter of the via annular ring"}, ) drill: KiCadFloat = field( default_factory=lambda: KiCadFloat("drill", 0.0), metadata={"description": "Drill diameter of the via"}, ) layers: Layers = field( default_factory=lambda: Layers(), metadata={"description": "Layer set the via connects"}, ) remove_unused_layers: Optional[OptionalFlag] = field( default_factory=lambda: OptionalFlag.create_bool_flag("remove_unused_layers"), metadata={"description": "Remove unused layers flag", "required": False}, ) keep_end_layers: Optional[OptionalFlag] = field( default_factory=lambda: OptionalFlag.create_bool_flag("keep_end_layers"), metadata={"description": "Keep end layers flag", "required": False}, ) free: Optional[OptionalFlag] = field( default_factory=lambda: OptionalFlag.create_bool_flag("free"), metadata={ "description": "Whether via is free to move outside assigned net", "required": False, }, ) net: int = field( default=0, metadata={"description": "Net ordinal number from net section"} ) tstamp: Optional[KiCadStr] = field( default_factory=lambda: KiCadStr("tstamp", "", required=False), metadata={ "description": "Unique identifier of the line object", "required": False, }, ) # NEW Variant uuid: Optional[Uuid] = field( default_factory=lambda: Uuid(), metadata={"description": "Unique identifier"} ) # Old Variant
[docs] @dataclass class Vias(KiCadObject): """Vias container definition token. The 'vias' token defines a container for vias in the format:: (vias (via ...) ... ) Args: vias: List of vias """ __token_name__ = "vias" vias: List[Via] = field( default_factory=list, metadata={"description": "List of vias"} )
[docs] @dataclass class KicadPcb(KiCadObject): """KiCad PCB board file definition. The 'kicad_pcb' token defines a complete PCB board file in the format:: (kicad_pcb (version VERSION) (generator GENERATOR) (general ...) (paper "SIZE") (page ...) (layers ...) (setup ...) [(property ...)] [(net ...)] [(footprint ...)] [(gr_text ...)] [(segment ...)] [(via ...)] [(zone ...)] ) Args: version: File format version generator: Generator application generator_version: Generator version (optional) general: General board settings (optional) page: Page settings (optional) paper: Paper size specification (optional) layers: Layer definitions (optional) setup: Board setup (optional) properties: Board properties nets: Net definitions footprints: Footprint instances gr_texts: Graphical text elements segments: Track segments vias: Via definitions zones: Zone definitions """ __token_name__ = "kicad_pcb" # Required header fields version: KiCadInt = field( default_factory=lambda: KiCadInt("version", 20240101), metadata={"description": "File format version"}, ) generator: KiCadStr = field( default_factory=lambda: KiCadStr("generator", ""), metadata={"description": "Generator application"}, ) generator_version: Optional[KiCadStr] = field( default_factory=lambda: KiCadStr("generator_version", "", required=False), metadata={"description": "Generator version", "required": False}, ) # Optional sections general: Optional[General] = field( default=None, metadata={"description": "General board settings", "required": False}, ) page: Optional[KiCadStr] = field( default_factory=lambda: KiCadStr("page", "", required=False), metadata={"description": "Page settings", "required": False}, ) paper: Optional[KiCadStr] = field( default_factory=lambda: KiCadStr("paper", "A4", required=False), metadata={"description": "Paper size specification", "required": False}, ) layers: Optional[Layers] = field( default=None, metadata={"description": "Layer definitions", "required": False} ) setup: Optional[Setup] = field( default=None, metadata={"description": "Board setup", "required": False} ) # Multiple elements (lists) properties: List[Property] = field( default_factory=list, metadata={"description": "Board properties"} ) nets: List[Net] = field( default_factory=list, metadata={"description": "Net definitions"} ) footprints: List[Footprint] = field( default_factory=list, metadata={"description": "Footprint instances"} ) gr_texts: List[GrText] = field( default_factory=list, metadata={"description": "Graphical text elements"} ) segments: List[Segment] = field( default_factory=list, metadata={"description": "Track segments"} ) vias: List[Via] = field( default_factory=list, metadata={"description": "Via definitions"} ) zones: List[Zone] = field( default_factory=list, metadata={"description": "Zone definitions"} )
[docs] @classmethod def from_file( cls, file_path: str, strictness: ParseStrictness = ParseStrictness.STRICT, encoding: str = "utf-8", ) -> "KicadPcb": """Parse from S-expression file - convenience method for PCB operations.""" if not file_path.endswith(".kicad_pcb"): raise ValueError("Unsupported file extension. Expected: .kicad_pcb") with open(file_path, "r", encoding=encoding) as f: content = f.read() return cls.from_str(content, strictness)
[docs] def save_to_file(self, file_path: str, encoding: str = "utf-8") -> None: """Save to .kicad_pcb file format. Args: file_path: Path to write the .kicad_pcb file encoding: File encoding (default: utf-8) """ if not file_path.endswith(".kicad_pcb"): raise ValueError("Unsupported file extension. Expected: .kicad_pcb") content = self.to_sexpr_str() with open(file_path, "w", encoding=encoding) as f: f.write(content)