Source code for kicad_sch_api.core.junctions

"""
Junction collection and management for KiCAD schematics.

This module provides enhanced junction management for wire intersections
and connection points with performance optimization and validation.
"""

import logging
import uuid as uuid_module
from typing import Any, Dict, List, Optional, Tuple, Union

from .collections import BaseCollection
from .types import Junction, Point

logger = logging.getLogger(__name__)


[docs] class JunctionCollection(BaseCollection[Junction]): """ Professional junction collection with enhanced management features. Inherits from BaseCollection for standard operations and adds junction-specific functionality. Features: - Fast UUID-based lookup and indexing (inherited) - Position-based junction queries - Bulk operations for performance (inherited) - Validation and conflict detection """
[docs] def __init__(self, junctions: Optional[List[Junction]] = None) -> None: """ Initialize junction collection. Args: junctions: Initial list of junctions """ super().__init__(junctions, collection_name="junctions")
[docs] def add( self, position: Union[Point, Tuple[float, float]], diameter: float = 0, color: Tuple[int, int, int, int] = (0, 0, 0, 0), uuid: Optional[str] = None, grid_units: bool = False, grid_size: float = 1.27, ) -> str: """ Add a junction to the collection. Args: position: Junction position in mm (or grid units if grid_units=True) diameter: Junction diameter (0 is KiCAD default) color: RGBA color tuple (0,0,0,0 is default) uuid: Optional UUID (auto-generated if not provided) grid_units: If True, interpret position as grid units instead of mm grid_size: Grid size in mm (default 1.27mm = 50 mil KiCAD standard) Returns: UUID of the created junction Raises: ValueError: If UUID already exists """ # Generate UUID if not provided if uuid is None: uuid = str(uuid_module.uuid4()) elif uuid in self._uuid_index: raise ValueError(f"Junction with UUID '{uuid}' already exists") # Convert grid units to mm if requested if grid_units: if isinstance(position, tuple): position = Point(position[0] * grid_size, position[1] * grid_size) else: position = Point(position.x * grid_size, position.y * grid_size) # Convert position elif isinstance(position, tuple): position = Point(position[0], position[1]) # Create junction junction = Junction(uuid=uuid, position=position, diameter=diameter, color=color) # Add to collection using base class method self._add_item(junction) logger.debug(f"Added junction at {position}, UUID={uuid}") return uuid
[docs] def get_at_position( self, position: Union[Point, Tuple[float, float]], tolerance: float = 0.01 ) -> Optional[Junction]: """ Find junction at or near a specific position. Args: position: Position to search tolerance: Distance tolerance for matching Returns: Junction if found, None otherwise """ if isinstance(position, tuple): position = Point(position[0], position[1]) for junction in self._items: if junction.position.distance_to(position) <= tolerance: return junction return None
[docs] def get_by_point( self, point: Union[Point, Tuple[float, float]], tolerance: float = 0.01 ) -> List[Junction]: """ Find all junctions near a point. Args: point: Point to search near tolerance: Distance tolerance Returns: List of junctions near the point """ if isinstance(point, tuple): point = Point(point[0], point[1]) matching_junctions = [] for junction in self._items: if junction.position.distance_to(point) <= tolerance: matching_junctions.append(junction) return matching_junctions
[docs] def get_statistics(self) -> Dict[str, Any]: """Get junction collection statistics (extends base statistics).""" base_stats = super().get_statistics() if not self._items: return {**base_stats, "total_junctions": 0, "avg_diameter": 0, "positions": []} avg_diameter = sum(j.diameter for j in self._items) / len(self._items) positions = [(j.position.x, j.position.y) for j in self._items] return { **base_stats, "total_junctions": len(self._items), "avg_diameter": avg_diameter, "positions": positions, "unique_diameters": len(set(j.diameter for j in self._items)), "unique_colors": len(set(j.color for j in self._items)), }
@property def modified(self) -> bool: """Check if collection has been modified.""" return self.is_modified()
[docs] def mark_saved(self) -> None: """Mark collection as saved (reset modified flag).""" self.reset_modified_flag()