diff --git a/.coveragerc b/.coveragerc index dacb1757..4cffb768 100644 --- a/.coveragerc +++ b/.coveragerc @@ -4,4 +4,4 @@ exclude_lines = if TYPE_CHECKING: omit = - src/pygerber/gerberx3/language_server/* + src/pygerber/gerber/language_server/* diff --git a/README.md b/README.md index 11b583ff..4811ff83 100644 --- a/README.md +++ b/README.md @@ -107,12 +107,12 @@ mentioned in documentation, as this may inflict suffering and damnation upon you Below you can find list of available APIs: -- `pygerber.gerberx3.api` -- `pygerber.gerberx3.ast` -- `pygerber.gerberx3.ast.nodes` -- `pygerber.gerberx3.compiler` -- `pygerber.gerberx3.parser` -- `pygerber.gerberx3.formatter` +- `pygerber.gerber.api` +- `pygerber.gerber.ast` +- `pygerber.gerber.ast.nodes` +- `pygerber.gerber.compiler` +- `pygerber.gerber.parser` +- `pygerber.gerber.formatter` - `pygerber.vm` - `pygerber.vm.commands` - `pygerber.vm.pillow` diff --git a/pyproject.toml b/pyproject.toml index 4c7b9f11..deb68383 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -108,7 +108,7 @@ build-backend = "poetry.core.masonry.api" [tool.poetry.scripts] pygerber = "pygerber.__main__:main" -pygerber_language_server = "pygerber.gerberx3.language_server.__main__:main" +pygerber_language_server = "pygerber.gerber.language_server.__main__:main" [tool.poe.tasks] # ------------------------------------------------------------------------------------- diff --git a/src/pygerber/backend/__init__.py b/src/pygerber/backend/__init__.py deleted file mode 100644 index e110edab..00000000 --- a/src/pygerber/backend/__init__.py +++ /dev/null @@ -1,31 +0,0 @@ -"""Drawing backends for Gerber files rendering.""" - -from __future__ import annotations - -from enum import Enum -from typing import TYPE_CHECKING - -from pygerber.backend.abstract.errors import BackendNotSupportedError -from pygerber.backend.rasterized_2d.backend_cls import Rasterized2DBackend - -if TYPE_CHECKING: - from pygerber.backend.abstract.backend_cls import Backend - - -class BackendName(Enum): - """Available rendering modes.""" - - Rasterized2D = "rasterized_2d" - Vector2D = "vector_2d" - Model3D = "model_3d" - - @staticmethod - def get_backend_class(backend: str | BackendName) -> type[Backend]: - """Return backend class.""" - if str(backend) == BackendName.Rasterized2D.value: - return Rasterized2DBackend - - raise BackendNotSupportedError(str(backend)) - - def __str__(self) -> str: - return self.value diff --git a/src/pygerber/backend/abstract/__init__.py b/src/pygerber/backend/abstract/__init__.py deleted file mode 100644 index be65aedf..00000000 --- a/src/pygerber/backend/abstract/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Abstract classes for building drawing implementations.""" diff --git a/src/pygerber/backend/abstract/aperture_handle.py b/src/pygerber/backend/abstract/aperture_handle.py deleted file mode 100644 index c4e0abdf..00000000 --- a/src/pygerber/backend/abstract/aperture_handle.py +++ /dev/null @@ -1,134 +0,0 @@ -"""Module contains classes-handles to drawing apertures.""" - -from __future__ import annotations - -from abc import abstractmethod -from functools import cached_property -from typing import TYPE_CHECKING, Optional - -from pygerber.backend.abstract.backend_cls import Backend -from pygerber.backend.abstract.draw_commands.draw_circle import ( - DrawCircle, -) -from pygerber.backend.abstract.drawing_target import DrawingTarget -from pygerber.common.frozen_general_model import FrozenGeneralModel -from pygerber.gerberx3.math.bounding_box import BoundingBox -from pygerber.gerberx3.math.offset import Offset -from pygerber.gerberx3.math.vector_2d import Vector2D -from pygerber.gerberx3.tokenizer.tokens.dnn_select_aperture import ApertureID - -if TYPE_CHECKING: - from pathlib import Path - from types import TracebackType - - from pygerber.backend.abstract.draw_commands.draw_command import DrawCommand - - -class PrivateApertureHandle: - """Base class for creating Gerber X3 apertures.""" - - drawing_target: DrawingTarget - bounding_box: BoundingBox - - def __init__( - self, - aperture_id: ApertureID, - private_id: int, - backend: Backend, - ) -> None: - """Initialize aperture handle.""" - self.aperture_id = aperture_id - self.private_id = private_id - self.backend = backend - self.aperture_draws: list[DrawCommand] = [] - self.is_plain_circle = True - - def add_draw(self, draw: DrawCommand) -> None: - """Add circle to aperture.""" - if self.is_plain_circle and ( - not isinstance(draw, DrawCircle) or len(self.aperture_draws) > 1 - ): - self.is_plain_circle = False - self.aperture_draws.append(draw) - - def __enter__(self) -> None: - pass - - def __exit__( - self, - exc_type: Optional[type[BaseException]], - exc_value: Optional[BaseException], - traceback: Optional[TracebackType], - ) -> None: - if exc_type is None: - self.bounding_box = self.get_bounding_box() - self.coordinate_origin = self._get_coordinate_origin() - self.drawing_target = self._create_drawing_target() - - def finalize_aperture_creation(self) -> None: - """Draw aperture and store result.""" - with self.drawing_target: - for aperture_draw in self.aperture_draws: - aperture_draw.draw(self.drawing_target) - - self._post_drawing_hook() - - def get_bounding_box(self) -> BoundingBox: - """Return bounding box of draw operation.""" - return self._bounding_box - - @cached_property - def _bounding_box(self) -> BoundingBox: - bbox: Optional[BoundingBox] = None - - for aperture_draw in self.aperture_draws: - if bbox is not None: - bbox += aperture_draw.get_bounding_box() - else: - bbox = aperture_draw.get_bounding_box() - - if bbox is not None: - return bbox - - return BoundingBox.NULL - - def _get_coordinate_origin(self) -> Vector2D: - return self.bounding_box.get_min_vector() - - @abstractmethod - def _create_drawing_target(self) -> DrawingTarget: - """Create drawing target object.""" - - def _post_drawing_hook(self) -> None: - """Perform custom actions after drawing.""" - - def get_public_handle(self) -> PublicApertureHandle: - """Return immutable aperture handle.""" - return PublicApertureHandle( - aperture_id=self.aperture_id, - private_id=self.private_id, - ) - - def get_line_width(self) -> Offset: - """Width of line made with this aperture.""" - box = self.get_bounding_box() - return (box.height + box.width) / 2 - - @abstractmethod - def dump_aperture(self, dest: Path) -> None: - """Save aperture to local file, mainly for debugging purposes.""" - - def __str__(self) -> str: - return ( - f"{self.__class__.__qualname__}(aperture_id={self.aperture_id}, " - f"private_id={self.private_id})" - ) - - __repr__ = __str__ - - -class PublicApertureHandle(FrozenGeneralModel): - """Immutable handle to drawing aperture.""" - - aperture_id: ApertureID - private_id: int diff --git a/src/pygerber/backend/abstract/backend_cls.py b/src/pygerber/backend/abstract/backend_cls.py deleted file mode 100644 index bf3b2adf..00000000 --- a/src/pygerber/backend/abstract/backend_cls.py +++ /dev/null @@ -1,172 +0,0 @@ -"""Class interface for visualizing gerber files.""" - -from __future__ import annotations - -from abc import ABC, abstractmethod -from typing import TYPE_CHECKING, ClassVar, List, Optional, Type - -from pygerber.gerberx3.math.bounding_box import BoundingBox -from pygerber.gerberx3.math.vector_2d import Vector2D - -if TYPE_CHECKING: - from pathlib import Path - - from pygerber.backend.abstract.aperture_handle import ( - PrivateApertureHandle, - PublicApertureHandle, - ) - from pygerber.backend.abstract.draw_commands.draw_arc import DrawArc - from pygerber.backend.abstract.draw_commands.draw_circle import ( - DrawCircle, - ) - from pygerber.backend.abstract.draw_commands.draw_command import DrawCommand - from pygerber.backend.abstract.draw_commands.draw_paste import DrawPaste - from pygerber.backend.abstract.draw_commands.draw_polygon import ( - DrawPolygon, - ) - from pygerber.backend.abstract.draw_commands.draw_rectangle import ( - DrawRectangle, - ) - from pygerber.backend.abstract.draw_commands.draw_region import DrawRegion - from pygerber.backend.abstract.draw_commands.draw_vector_line import DrawVectorLine - from pygerber.backend.abstract.draw_commands_handle import DrawCommandsHandle - from pygerber.backend.abstract.drawing_target import DrawingTarget - from pygerber.backend.abstract.result_handle import ResultHandle - from pygerber.gerberx3.tokenizer.tokens.dnn_select_aperture import ApertureID - - -class BackendOptions: - """Additional configuration which can be passed to backend.""" - - def __init__( - self, - dump_apertures: Optional[Path] = None, - *, - draw_region_outlines: bool = False, - ) -> None: - """Initialize options.""" - self.dump_apertures = dump_apertures - self.draw_region_outlines = draw_region_outlines - - -class Backend(ABC): - """Drawing backend interface.""" - - handles: list[PrivateApertureHandle] - drawing_target: DrawingTarget - bounding_box: BoundingBox - coordinate_origin: Vector2D - - options_class: ClassVar[type[BackendOptions]] = BackendOptions - - def __init__(self, options: Optional[BackendOptions] = None) -> None: - """Initialize backend.""" - self.options = self.options_class() if options is None else options - self.handles = [] - - def create_aperture_handle(self, aperture_id: ApertureID) -> PrivateApertureHandle: - """Create new aperture handle.""" - handle = self.get_aperture_handle_cls()( - aperture_id=aperture_id, - private_id=len(self.handles), - backend=self, - ) - self.handles.append(handle) - return handle - - def get_private_aperture_handle( - self, - public_aperture_handle: PublicApertureHandle, - ) -> PrivateApertureHandle: - """Get private aperture handle.""" - return self.handles[public_aperture_handle.private_id] - - def draw(self, draws: List[DrawCommand]) -> ResultHandle: - """Execute all draw actions to create visualization.""" - self.draws = draws - - self.finalize_aperture_creation() - self.bounding_box = self._get_draws_bounding_box(draws) - self.coordinate_origin = self._get_coordinate_origin() - self.drawing_target = self._create_drawing_target() - self._pre_drawing_hook() - - with self.drawing_target: - for draw_action in draws: - draw_action.draw(self.drawing_target) - - self._post_drawing_hook() - - return self.get_result_handle() - - def finalize_aperture_creation(self) -> None: - """Apply draw operations to aperture handles.""" - for handle in self.handles: - handle.finalize_aperture_creation() - - def _get_draws_bounding_box(self, draws: List[DrawCommand]) -> BoundingBox: - bbox: Optional[BoundingBox] = None - - for draw in draws: - if bbox is not None: - bbox += draw.get_bounding_box() - else: - bbox = draw.get_bounding_box() - - if bbox is not None: - return bbox - - return BoundingBox.NULL - - def _get_coordinate_origin(self) -> Vector2D: - return self.bounding_box.get_min_vector() - - @abstractmethod - def _create_drawing_target(self) -> DrawingTarget: - """Create drawing target object.""" - - def _pre_drawing_hook(self) -> None: # noqa: B027 - """Perform custom actions before drawing.""" - - def _post_drawing_hook(self) -> None: # noqa: B027 - """Perform custom actions after drawing.""" - - @abstractmethod - def get_result_handle(self) -> ResultHandle: - """Return result handle to visualization.""" - - @abstractmethod - def get_aperture_handle_cls(self) -> Type[PrivateApertureHandle]: - """Get backend-specific implementation of aperture handle class.""" - - @abstractmethod - def get_draw_circle_cls(self) -> Type[DrawCircle]: - """Get backend-specific implementation of aperture circle component class.""" - - @abstractmethod - def get_draw_rectangle_cls(self) -> Type[DrawRectangle]: - """Get backend-specific implementation of aperture rectangle component class.""" - - @abstractmethod - def get_draw_polygon_cls(self) -> Type[DrawPolygon]: - """Get backend-specific implementation of aperture polygon component class.""" - - @abstractmethod - def get_draw_commands_handle_cls(self) -> type[DrawCommandsHandle]: - """Return backend-specific implementation of draw actions handle.""" - - @abstractmethod - def get_draw_paste_cls(self) -> type[DrawPaste]: - """Return backend-specific implementation of draw paste.""" - - @abstractmethod - def get_draw_region_cls(self) -> type[DrawRegion]: - """Return backend-specific implementation of draw action region.""" - - @abstractmethod - def get_draw_vector_line_cls(self) -> type[DrawVectorLine]: - """Return backend-specific implementation of draw action line.""" - - @abstractmethod - def get_draw_arc_cls(self) -> type[DrawArc]: - """Return backend-specific implementation of draw action arc.""" diff --git a/src/pygerber/backend/abstract/draw_commands/__init__.py b/src/pygerber/backend/abstract/draw_commands/__init__.py deleted file mode 100644 index 8212f8b5..00000000 --- a/src/pygerber/backend/abstract/draw_commands/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Draw operations for constructing apertures.""" diff --git a/src/pygerber/backend/abstract/draw_commands/draw_arc.py b/src/pygerber/backend/abstract/draw_commands/draw_arc.py deleted file mode 100644 index 40a9a450..00000000 --- a/src/pygerber/backend/abstract/draw_commands/draw_arc.py +++ /dev/null @@ -1,120 +0,0 @@ -"""Base class for creating components for aperture creation.""" - -from __future__ import annotations - -import math -from functools import cached_property -from typing import Generator - -from pygerber.backend.abstract.backend_cls import Backend -from pygerber.backend.abstract.draw_commands.draw_command import DrawCommand -from pygerber.gerberx3.math.bounding_box import BoundingBox -from pygerber.gerberx3.math.offset import Offset -from pygerber.gerberx3.math.vector_2d import Vector2D -from pygerber.gerberx3.state_enums import Polarity - - -class DrawArc(DrawCommand): - """Description of aperture component.""" - - start_position: Vector2D - dx_dy_center: Vector2D - end_position: Vector2D - width: Offset - - is_clockwise: bool - is_multi_quadrant: bool - - def __init__( - self, - backend: Backend, - polarity: Polarity, - start_position: Vector2D, - dx_dy_center: Vector2D, - end_position: Vector2D, - width: Offset, - *, - is_clockwise: bool, - is_multi_quadrant: bool, - ) -> None: - """Initialize draw command.""" - super().__init__(backend, polarity) - self.start_position = start_position - self.dx_dy_center = dx_dy_center - self.end_position = end_position - self.width = width - self.is_clockwise = is_clockwise - self.is_multi_quadrant = is_multi_quadrant - - @property - def arc_center_absolute(self) -> Vector2D: - """Return absolute coordinates of arc center point.""" - return self.start_position + self.dx_dy_center - - @property - def arc_space_arc_center(self) -> Vector2D: - """Return arc center coordinates relative to arc center.""" - return self.arc_center_absolute - self.arc_center_absolute - - @property - def arc_space_start_position(self) -> Vector2D: - """Return arc start coordinates relative to arc center.""" - return self.start_position - self.arc_center_absolute - - @property - def arc_space_end_position(self) -> Vector2D: - """Return arc end coordinates relative to arc center.""" - return self.end_position - self.arc_center_absolute - - @property - def arc_radius(self) -> Offset: - """Return arc radius.""" - return self.dx_dy_center.length() - - def get_bounding_box(self) -> BoundingBox: - """Return bounding box of draw operation.""" - return self._bounding_box - - @cached_property - def _bounding_box(self) -> BoundingBox: - vertex_box = BoundingBox.from_diameter(self.width) - radius = self.arc_radius - return (vertex_box + (self.arc_center_absolute + radius)) + ( - vertex_box + (self.arc_center_absolute - radius) - ) - - def _calculate_angles(self) -> tuple[float, float]: - angle_start = self.arc_space_start_position.angle_between_clockwise( - Vector2D.UNIT_Y, - ) - angle_end = self.arc_space_end_position.angle_between_clockwise(Vector2D.UNIT_Y) - - if self.is_multi_quadrant and angle_start == angle_end: - angle_start = 0 - angle_end = 360 - - elif self.is_clockwise: - angle_start, angle_end = angle_end, angle_start - - return angle_start, angle_end - - def calculate_arc_points(self) -> Generator[Vector2D, None, None]: - """Calculate points on arc.""" - angle_start, angle_end = self._calculate_angles() - - angle_step = 1 - angle_min = min(angle_start, angle_end) - angle_max = max(angle_start, angle_end) - - angle_current = angle_min - - yield self.arc_center_absolute - - while angle_current < (angle_max + angle_step): - yield self.arc_center_absolute + Vector2D( - x=self.arc_radius * math.cos(math.radians(angle_current)), - y=self.arc_radius * math.sin(math.radians(angle_current)), - ) - angle_current += angle_step - - yield self.arc_center_absolute diff --git a/src/pygerber/backend/abstract/draw_commands/draw_bounding_box.py b/src/pygerber/backend/abstract/draw_commands/draw_bounding_box.py deleted file mode 100644 index 9092e41f..00000000 --- a/src/pygerber/backend/abstract/draw_commands/draw_bounding_box.py +++ /dev/null @@ -1,38 +0,0 @@ -"""BoundingBox component for creating apertures.""" - -from __future__ import annotations - -from functools import cached_property - -from pygerber.backend.abstract.backend_cls import Backend -from pygerber.backend.abstract.draw_commands.draw_command import DrawCommand -from pygerber.gerberx3.math.bounding_box import BoundingBox -from pygerber.gerberx3.math.offset import Offset -from pygerber.gerberx3.state_enums import Polarity - - -class DrawBoundingBox(DrawCommand): - """Description of BoundingBox component.""" - - bounding_box: BoundingBox - outline_padding: Offset - - def __init__( - self, - backend: Backend, - polarity: Polarity, - bounding_box: BoundingBox, - outline_padding: Offset, - ) -> None: - """Initialize draw command.""" - super().__init__(backend, polarity) - self.bounding_box = bounding_box - self.outline_padding = outline_padding - - def get_bounding_box(self) -> BoundingBox: - """Return bounding box of draw operation.""" - return self._bounding_box - - @cached_property - def _bounding_box(self) -> BoundingBox: - return self.bounding_box + self.outline_padding diff --git a/src/pygerber/backend/abstract/draw_commands/draw_circle.py b/src/pygerber/backend/abstract/draw_commands/draw_circle.py deleted file mode 100644 index 73f28a19..00000000 --- a/src/pygerber/backend/abstract/draw_commands/draw_circle.py +++ /dev/null @@ -1,39 +0,0 @@ -"""Circle component for creating apertures.""" - -from __future__ import annotations - -from functools import cached_property - -from pygerber.backend.abstract.backend_cls import Backend -from pygerber.backend.abstract.draw_commands.draw_command import DrawCommand -from pygerber.gerberx3.math.bounding_box import BoundingBox -from pygerber.gerberx3.math.offset import Offset -from pygerber.gerberx3.math.vector_2d import Vector2D -from pygerber.gerberx3.state_enums import Polarity - - -class DrawCircle(DrawCommand): - """Description of circle aperture component.""" - - center_position: Vector2D - diameter: Offset - - def __init__( - self, - backend: Backend, - polarity: Polarity, - center_position: Vector2D, - diameter: Offset, - ) -> None: - """Initialize draw command.""" - super().__init__(backend, polarity) - self.center_position = center_position - self.diameter = diameter - - def get_bounding_box(self) -> BoundingBox: - """Return bounding box of draw operation.""" - return self._bounding_box - - @cached_property - def _bounding_box(self) -> BoundingBox: - return BoundingBox.from_diameter(self.diameter) + self.center_position diff --git a/src/pygerber/backend/abstract/draw_commands/draw_command.py b/src/pygerber/backend/abstract/draw_commands/draw_command.py deleted file mode 100644 index 205516a0..00000000 --- a/src/pygerber/backend/abstract/draw_commands/draw_command.py +++ /dev/null @@ -1,33 +0,0 @@ -"""Base class for creating components for aperture creation.""" - -from __future__ import annotations - -from abc import ABC, abstractmethod - -from pygerber.backend.abstract.backend_cls import Backend -from pygerber.backend.abstract.drawing_target import DrawingTarget -from pygerber.gerberx3.math.bounding_box import BoundingBox -from pygerber.gerberx3.state_enums import Polarity - - -class DrawCommand(ABC): - """Description of aperture component.""" - - backend: Backend - polarity: Polarity - - def __init__(self, backend: Backend, polarity: Polarity) -> None: - """Initialize draw command.""" - self.backend = backend - self.polarity = polarity - - @abstractmethod - def draw(self, target: DrawingTarget) -> None: - """Apply aperture draw component to handle.""" - - @abstractmethod - def get_bounding_box(self) -> BoundingBox: - """Return bounding box of draw operation.""" - - def __str__(self) -> str: - return f"{self.__class__.__qualname__}({self.polarity})" diff --git a/src/pygerber/backend/abstract/draw_commands/draw_paste.py b/src/pygerber/backend/abstract/draw_commands/draw_paste.py deleted file mode 100644 index ad8765d3..00000000 --- a/src/pygerber/backend/abstract/draw_commands/draw_paste.py +++ /dev/null @@ -1,39 +0,0 @@ -"""Base class for creating components for aperture creation.""" - -from __future__ import annotations - -from functools import cached_property - -from pygerber.backend.abstract.backend_cls import Backend -from pygerber.backend.abstract.draw_commands.draw_command import DrawCommand -from pygerber.backend.abstract.drawing_target import DrawingTarget -from pygerber.gerberx3.math.bounding_box import BoundingBox -from pygerber.gerberx3.math.vector_2d import Vector2D -from pygerber.gerberx3.state_enums import Polarity - - -class DrawPaste(DrawCommand): - """Description of aperture component.""" - - other: DrawingTarget - center_position: Vector2D - - def __init__( - self, - backend: Backend, - polarity: Polarity, - other: DrawingTarget, - center_position: Vector2D, - ) -> None: - """Initialize draw command.""" - super().__init__(backend, polarity) - self.other = other - self.center_position = center_position - - def get_bounding_box(self) -> BoundingBox: - """Return bounding box of draw operation.""" - return self._bounding_box - - @cached_property - def _bounding_box(self) -> BoundingBox: - return self.other.bounding_box + self.center_position diff --git a/src/pygerber/backend/abstract/draw_commands/draw_polygon.py b/src/pygerber/backend/abstract/draw_commands/draw_polygon.py deleted file mode 100644 index 69d745b6..00000000 --- a/src/pygerber/backend/abstract/draw_commands/draw_polygon.py +++ /dev/null @@ -1,49 +0,0 @@ -"""Polygon component for creating apertures.""" - -from __future__ import annotations - -from functools import cached_property -from typing import TYPE_CHECKING - -from pygerber.backend.abstract.backend_cls import Backend -from pygerber.backend.abstract.draw_commands.draw_command import DrawCommand -from pygerber.gerberx3.math.bounding_box import BoundingBox -from pygerber.gerberx3.math.offset import Offset -from pygerber.gerberx3.math.vector_2d import Vector2D -from pygerber.gerberx3.state_enums import Polarity - -if TYPE_CHECKING: - from decimal import Decimal - - -class DrawPolygon(DrawCommand): - """Description of polygon aperture component.""" - - center_position: Vector2D - outer_diameter: Offset - number_of_vertices: int - rotation: Decimal - - def __init__( - self, - backend: Backend, - polarity: Polarity, - center_position: Vector2D, - outer_diameter: Offset, - number_of_vertices: int, - rotation: Decimal, - ) -> None: - """Initialize draw command.""" - super().__init__(backend, polarity) - self.center_position = center_position - self.outer_diameter = outer_diameter - self.number_of_vertices = number_of_vertices - self.rotation = rotation - - def get_bounding_box(self) -> BoundingBox: - """Return bounding box of draw operation.""" - return self._bounding_box - - @cached_property - def _bounding_box(self) -> BoundingBox: - return BoundingBox.from_diameter(self.outer_diameter) + self.center_position diff --git a/src/pygerber/backend/abstract/draw_commands/draw_rectangle.py b/src/pygerber/backend/abstract/draw_commands/draw_rectangle.py deleted file mode 100644 index d5cb61d5..00000000 --- a/src/pygerber/backend/abstract/draw_commands/draw_rectangle.py +++ /dev/null @@ -1,44 +0,0 @@ -"""Rectangle component for creating apertures.""" - -from __future__ import annotations - -from functools import cached_property - -from pygerber.backend.abstract.backend_cls import Backend -from pygerber.backend.abstract.draw_commands.draw_command import DrawCommand -from pygerber.gerberx3.math.bounding_box import BoundingBox -from pygerber.gerberx3.math.offset import Offset -from pygerber.gerberx3.math.vector_2d import Vector2D -from pygerber.gerberx3.state_enums import Polarity - - -class DrawRectangle(DrawCommand): - """Description of rectangle aperture component.""" - - center_position: Vector2D - x_size: Offset - y_size: Offset - - def __init__( - self, - backend: Backend, - polarity: Polarity, - center_position: Vector2D, - x_size: Offset, - y_size: Offset, - ) -> None: - """Initialize draw command.""" - super().__init__(backend, polarity) - self.center_position = center_position - self.x_size = x_size - self.y_size = y_size - - def get_bounding_box(self) -> BoundingBox: - """Return bounding box of draw operation.""" - return self._bounding_box - - @cached_property - def _bounding_box(self) -> BoundingBox: - return ( - BoundingBox.from_rectangle(self.x_size, self.y_size) + self.center_position - ) diff --git a/src/pygerber/backend/abstract/draw_commands/draw_region.py b/src/pygerber/backend/abstract/draw_commands/draw_region.py deleted file mode 100644 index 88e80574..00000000 --- a/src/pygerber/backend/abstract/draw_commands/draw_region.py +++ /dev/null @@ -1,46 +0,0 @@ -"""Region component for creating apertures.""" - -from __future__ import annotations - -from functools import cached_property -from typing import Optional - -from pygerber.backend.abstract.backend_cls import Backend -from pygerber.backend.abstract.draw_commands.draw_command import DrawCommand -from pygerber.gerberx3.math.bounding_box import BoundingBox -from pygerber.gerberx3.math.vector_2d import Vector2D -from pygerber.gerberx3.state_enums import Polarity - - -class DrawRegion(DrawCommand): - """Description of Region aperture component.""" - - region_boundary_points: list[Vector2D] - - def __init__( - self, - backend: Backend, - polarity: Polarity, - region_boundary_points: list[Vector2D], - ) -> None: - """Initialize draw command.""" - super().__init__(backend, polarity) - self.region_boundary_points = region_boundary_points - - def get_bounding_box(self) -> BoundingBox: - """Return bounding box of draw operation.""" - return self._bounding_box - - @cached_property - def _bounding_box(self) -> BoundingBox: - box: Optional[BoundingBox] = None - for point in self.region_boundary_points: - if box is not None: - box = box.include_point(point) - else: - box = BoundingBox.NULL + point - - if box is not None: - return box - - return BoundingBox.NULL diff --git a/src/pygerber/backend/abstract/draw_commands/draw_vector_line.py b/src/pygerber/backend/abstract/draw_commands/draw_vector_line.py deleted file mode 100644 index ccdc8cac..00000000 --- a/src/pygerber/backend/abstract/draw_commands/draw_vector_line.py +++ /dev/null @@ -1,50 +0,0 @@ -"""# DrawVectorLine Module. - -This module defines the base class for creating vector line components -used in drawing creation. The main class, `DrawVectorLine`, represents -a vector line defined by its start and end positions and width. -""" - -from __future__ import annotations - -from pygerber.backend.abstract.backend_cls import Backend -from pygerber.backend.abstract.draw_commands.draw_command import DrawCommand -from pygerber.gerberx3.math.bounding_box import BoundingBox -from pygerber.gerberx3.math.offset import Offset -from pygerber.gerberx3.math.vector_2d import Vector2D -from pygerber.gerberx3.state_enums import Polarity - - -class DrawVectorLine(DrawCommand): - """Represents a vector line component used in drawing creation. - This class is defined by its start position, end position, and width. - """ - - start_position: Vector2D - end_position: Vector2D - width: Offset - - def __init__( - self, - backend: Backend, - polarity: Polarity, - start_position: Vector2D, - end_position: Vector2D, - width: Offset, - ) -> None: - """Initialize draw command.""" - super().__init__(backend, polarity) - self.start_position = start_position - self.end_position = end_position - self.width = width - - def get_bounding_box(self) -> BoundingBox: - """Return bounding box of draw operation.""" - vertex_box = BoundingBox.from_diameter(self.width) - return (vertex_box + self.start_position) + (vertex_box + self.end_position) - - def __str__(self) -> str: - return ( - f"{self.__class__.__qualname__}({self.polarity}) start: " - "{self.start_position} end: {self.end_position}" - ) diff --git a/src/pygerber/backend/abstract/draw_commands_handle.py b/src/pygerber/backend/abstract/draw_commands_handle.py deleted file mode 100644 index 06443681..00000000 --- a/src/pygerber/backend/abstract/draw_commands_handle.py +++ /dev/null @@ -1,22 +0,0 @@ -"""Contains class wrapping list of draw operations created by Gerber parser.""" - -from __future__ import annotations - -from typing import List - -from pygerber.backend.abstract.backend_cls import Backend -from pygerber.backend.abstract.draw_commands.draw_command import DrawCommand -from pygerber.backend.abstract.result_handle import ResultHandle - - -class DrawCommandsHandle: - """List of drawing operations produced by Gerber parser.""" - - def __init__(self, draw_actions: List[DrawCommand], backend: Backend) -> None: - """Initialize drawing instructions.""" - self.draw_commands = draw_actions - self.backend = backend - - def draw(self) -> ResultHandle: - """Create visualization based on drawing instructions.""" - return self.backend.draw(self.draw_commands) diff --git a/src/pygerber/backend/abstract/drawing_target.py b/src/pygerber/backend/abstract/drawing_target.py deleted file mode 100644 index 74b01310..00000000 --- a/src/pygerber/backend/abstract/drawing_target.py +++ /dev/null @@ -1,41 +0,0 @@ -"""Target for Draw commands to draw into.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING, Optional - -from pygerber.gerberx3.math.bounding_box import BoundingBox -from pygerber.gerberx3.math.vector_2d import Vector2D - -if TYPE_CHECKING: - from types import TracebackType - - -class DrawingTarget: - """Target for Draw commands to draw into.""" - - coordinate_origin: Vector2D - bounding_box: BoundingBox - - def __init__(self, coordinate_origin: Vector2D, bounding_box: BoundingBox) -> None: - """Initialize drawing target.""" - self.coordinate_origin = coordinate_origin - self.bounding_box = bounding_box - - def __enter__(self) -> None: - pass - - def __exit__( - self, - exc_type: Optional[type[BaseException]], - exc_value: Optional[BaseException], - traceback: Optional[TracebackType], - ) -> None: - if exc_type is None: - self._finalize() - - def _finalize(self) -> None: - """Call at the end of image modification. - - After this call no modifications to image are allowed. - """ diff --git a/src/pygerber/backend/abstract/errors.py b/src/pygerber/backend/abstract/errors.py deleted file mode 100644 index 19278e82..00000000 --- a/src/pygerber/backend/abstract/errors.py +++ /dev/null @@ -1,11 +0,0 @@ -"""Base error classes used in this module.""" - -from __future__ import annotations - - -class BackendError(ValueError): - """Base class for backend errors.""" - - -class BackendNotSupportedError(BackendError): - """Raised when requesting backend which is not officially supported.""" diff --git a/src/pygerber/backend/abstract/result_handle.py b/src/pygerber/backend/abstract/result_handle.py deleted file mode 100644 index 132ae0ba..00000000 --- a/src/pygerber/backend/abstract/result_handle.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Module contains handle class to drawing instructions visualization.""" - -from __future__ import annotations - -from abc import ABC, abstractmethod -from typing import TYPE_CHECKING, Any - -if TYPE_CHECKING: - from io import BytesIO - from pathlib import Path - - -class ResultHandle(ABC): - """Handle to drawing instructions visualization.""" - - @abstractmethod - def save( - self, - dest: Path | str | BytesIO, - **kwargs: Any, - ) -> None: - """Save result to destination. - - All additional parameters are passed to underlying saving system. - For more details see documentation of concrete implementations. - """ diff --git a/src/pygerber/backend/rasterized_2d/__init__.py b/src/pygerber/backend/rasterized_2d/__init__.py deleted file mode 100644 index d11c26b7..00000000 --- a/src/pygerber/backend/rasterized_2d/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Backend implementation for rendering 2D rasterized images.""" diff --git a/src/pygerber/backend/rasterized_2d/aperture_handle.py b/src/pygerber/backend/rasterized_2d/aperture_handle.py deleted file mode 100644 index 0247fe76..00000000 --- a/src/pygerber/backend/rasterized_2d/aperture_handle.py +++ /dev/null @@ -1,66 +0,0 @@ -"""Aperture Handle class which represents Gerber X3 aperture.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from PIL import Image - -from pygerber.backend.abstract.aperture_handle import PrivateApertureHandle -from pygerber.backend.abstract.drawing_target import DrawingTarget -from pygerber.backend.rasterized_2d.drawing_target import Rasterized2DDrawingTarget -from pygerber.gerberx3.state_enums import Polarity - -if TYPE_CHECKING: - from pygerber.backend.rasterized_2d.backend_cls import Rasterized2DBackend - - -class Rasterized2DPrivateApertureHandle(PrivateApertureHandle): - """Base class for creating Gerber X3 apertures.""" - - backend: Rasterized2DBackend - drawing_target: Rasterized2DDrawingTarget - - def _create_drawing_target(self) -> DrawingTarget: - """Draw aperture and store result.""" - bbox = self.bounding_box - size = bbox.get_size().as_pixels(self.backend.dpi) - - # Image must be at least 1x1, otherwise Pillow crashes while saving. - x, y = size - size = (max(x, 0) + 1, max(y, 0) + 1) - - return Rasterized2DDrawingTarget( - coordinate_origin=self.coordinate_origin, - bounding_box=self.bounding_box, - target_image=Image.new( - mode="L", - size=size, - color=Polarity.Background.get_2d_rasterized_color(), - ), - ) - - def _post_drawing_hook(self) -> None: - dest = self.backend.options.dump_apertures - if dest is not None: - dest_aperture_subdir = dest / f"{self.aperture_id}_{self.private_id}" - dest_aperture_subdir.mkdir(0o777, parents=True, exist_ok=True) - - self.drawing_target.target_image.save( - dest_aperture_subdir / "target.png", - ) - self.drawing_target.mask_image.save( - dest_aperture_subdir / "mask.png", - ) - self.drawing_target.image_polarity_clear.save( - dest_aperture_subdir / "clear.png", - ) - self.drawing_target.image_polarity_dark.save( - dest_aperture_subdir / "dark.png", - ) - self.drawing_target.image_polarity_region_clear.save( - dest_aperture_subdir / "clear_region.png", - ) - self.drawing_target.image_polarity_region_dark.save( - dest_aperture_subdir / "dark_region.png", - ) diff --git a/src/pygerber/backend/rasterized_2d/backend_cls.py b/src/pygerber/backend/rasterized_2d/backend_cls.py deleted file mode 100644 index f70a3f93..00000000 --- a/src/pygerber/backend/rasterized_2d/backend_cls.py +++ /dev/null @@ -1,227 +0,0 @@ -"""Backend for rasterized rendering of Gerber files.""" - -from __future__ import annotations - -from decimal import Decimal -from enum import Enum -from typing import TYPE_CHECKING, ClassVar, Optional - -import numpy as np -import numpy.typing as npt -from PIL import Image - -from pygerber.backend.abstract.backend_cls import Backend, BackendOptions -from pygerber.backend.rasterized_2d.aperture_handle import ( - Rasterized2DPrivateApertureHandle, -) -from pygerber.backend.rasterized_2d.color_scheme import ColorScheme -from pygerber.backend.rasterized_2d.draw_commands.draw_arc import Rasterized2DDrawArc -from pygerber.backend.rasterized_2d.draw_commands.draw_bounding_box import ( - Rasterized2DApertureDrawBoundingBox, -) -from pygerber.backend.rasterized_2d.draw_commands.draw_circle import ( - Rasterized2DApertureDrawCircle, -) -from pygerber.backend.rasterized_2d.draw_commands.draw_paste import ( - Rasterized2DDrawPaste, -) -from pygerber.backend.rasterized_2d.draw_commands.draw_polygon import ( - Rasterized2DApertureDrawPolygon, -) -from pygerber.backend.rasterized_2d.draw_commands.draw_rectangle import ( - Rasterized2DApertureDrawRectangle, -) -from pygerber.backend.rasterized_2d.draw_commands.draw_region import ( - Rasterized2DDrawRegion, -) -from pygerber.backend.rasterized_2d.draw_commands.draw_vector_line import ( - Rasterized2DDrawVectorLine, -) -from pygerber.backend.rasterized_2d.draw_commands_handle import ( - Rasterized2DDrawActionsHandle, -) -from pygerber.backend.rasterized_2d.drawing_target import Rasterized2DDrawingTarget -from pygerber.backend.rasterized_2d.result_handle import Rasterized2DResultHandle -from pygerber.gerberx3.math.offset import Offset -from pygerber.gerberx3.state_enums import Polarity - -if TYPE_CHECKING: - from pathlib import Path - - from pygerber.backend.abstract.aperture_handle import PrivateApertureHandle - from pygerber.backend.abstract.draw_commands.draw_arc import DrawArc - from pygerber.backend.abstract.draw_commands.draw_circle import ( - DrawCircle, - ) - from pygerber.backend.abstract.draw_commands.draw_paste import DrawPaste - from pygerber.backend.abstract.draw_commands.draw_polygon import ( - DrawPolygon, - ) - from pygerber.backend.abstract.draw_commands.draw_rectangle import ( - DrawRectangle, - ) - from pygerber.backend.abstract.draw_commands.draw_region import DrawRegion - from pygerber.backend.abstract.draw_commands.draw_vector_line import DrawVectorLine - from pygerber.backend.abstract.draw_commands_handle import DrawCommandsHandle - from pygerber.backend.abstract.drawing_target import DrawingTarget - from pygerber.backend.abstract.result_handle import ResultHandle - - -class ColorMode(Enum): - """Enum which can be used to specify color mode.""" - - RGBA = "RGBA" - RGB = "RGB" - - -class Rasterized2DBackendOptions(BackendOptions): - """Additional configuration which can be passed to backend.""" - - def __init__( - self, - dpi: int = 300, - color_scheme: ColorScheme = ColorScheme.DEFAULT_GRAYSCALE, - color_mode: ColorMode = ColorMode.RGBA, - dump_apertures: Optional[Path] = None, - *, - include_debug_padding: bool = False, - include_bounding_boxes: bool = False, - draw_region_outlines: bool = False, - ) -> None: - """Initialize options.""" - self.dpi = dpi - self.color_scheme = color_scheme - self.color_mode = color_mode - self.include_debug_padding = include_debug_padding - self.include_bounding_boxes = include_bounding_boxes - super().__init__( - dump_apertures=dump_apertures, - draw_region_outlines=draw_region_outlines, - ) - - -class Rasterized2DBackend(Backend): - """Drawing backend interface.""" - - options: Rasterized2DBackendOptions - drawing_target: Rasterized2DDrawingTarget - - options_class: ClassVar[type[BackendOptions]] = Rasterized2DBackendOptions - - def __init__(self, options: Rasterized2DBackendOptions | None = None) -> None: - """Initialize backend.""" - if options is not None and not isinstance(options, Rasterized2DBackendOptions): - msg = ( # type: ignore[unreachable] - "Expected Rasterized2DBackendOptions or None as options, got " - + str( - type(options), - ) - ) - raise TypeError(msg) - super().__init__(options) - - @property - def dpi(self) -> int: - """Return image DPI.""" - return self.options.dpi - - def _create_drawing_target(self) -> DrawingTarget: - """Execute all draw actions to create visualization.""" - raw_bbox = self.bounding_box - - if self.options.include_debug_padding: - bbox = raw_bbox + Decimal(1.0) - else: - bbox = raw_bbox - - size = bbox.get_size() - coordinate_origin = bbox.get_min_vector() - - image_size = size.as_pixels(self.dpi) - # Image must be at least 1x1, otherwise Pillow crashes while saving. - x, y = image_size - image_size = (max(x, 0) + 1, max(y, 0) + 1) - - return Rasterized2DDrawingTarget( - coordinate_origin=coordinate_origin, - bounding_box=bbox, - target_image=Image.new( - mode="L", - size=image_size, - color=Polarity.Background.get_2d_rasterized_color(), - ), - ) - - def _pre_drawing_hook(self) -> None: - """Perform custom actions after drawing.""" - if self.options.include_bounding_boxes: - self.draws.append( - Rasterized2DApertureDrawBoundingBox( - backend=self, - polarity=Polarity.DEBUG, - bounding_box=self.bounding_box, - outline_padding=Offset.from_pixels(1, self.dpi), - ), - ) - - def _post_drawing_hook(self) -> None: - self._replace_image_colors() - - def _replace_image_colors(self) -> None: - img = self.drawing_target.target_image - color_map = self.options.color_scheme.get_grayscale_to_rgba_color_map() - - np_img = np.array(img) - - # Create an empty RGBA image with the same size as the original image - rgba_img: npt.NDArray[np.uint8] = np.zeros( - (img.height, img.width, 4), - dtype=np.uint8, - ) - - # For each grayscale value, set the corresponding RGBA value in the new image - for gray_value, rgba in color_map.items(): - rgba_img[np_img == gray_value] = rgba - - # Convert the resulting NumPy array back to a Pillow image - self.drawing_target.target_image = Image.fromarray(rgba_img, "RGBA") - - def get_result_handle(self) -> ResultHandle: - """Return result handle to visualization.""" - return Rasterized2DResultHandle(self.drawing_target.target_image) - - def get_aperture_handle_cls(self) -> type[PrivateApertureHandle]: - """Get backend-specific implementation of aperture handle class.""" - return Rasterized2DPrivateApertureHandle - - def get_draw_circle_cls(self) -> type[DrawCircle]: - """Get backend-specific implementation of aperture circle component class.""" - return Rasterized2DApertureDrawCircle - - def get_draw_rectangle_cls(self) -> type[DrawRectangle]: - """Get backend-specific implementation of aperture rectangle component class.""" - return Rasterized2DApertureDrawRectangle - - def get_draw_polygon_cls(self) -> type[DrawPolygon]: - """Get backend-specific implementation of aperture polygon component class.""" - return Rasterized2DApertureDrawPolygon - - def get_draw_commands_handle_cls(self) -> type[DrawCommandsHandle]: - """Return backend-specific implementation of draw actions handle.""" - return Rasterized2DDrawActionsHandle - - def get_draw_paste_cls(self) -> type[DrawPaste]: - """Return backend-specific implementation of draw action flash.""" - return Rasterized2DDrawPaste - - def get_draw_region_cls(self) -> type[DrawRegion]: - """Return backend-specific implementation of draw action region.""" - return Rasterized2DDrawRegion - - def get_draw_vector_line_cls(self) -> type[DrawVectorLine]: - """Return backend-specific implementation of draw action line.""" - return Rasterized2DDrawVectorLine - - def get_draw_arc_cls(self) -> type[DrawArc]: - """Return backend-specific implementation of draw action arc.""" - return Rasterized2DDrawArc diff --git a/src/pygerber/backend/rasterized_2d/color_scheme.py b/src/pygerber/backend/rasterized_2d/color_scheme.py deleted file mode 100644 index 7ef36c72..00000000 --- a/src/pygerber/backend/rasterized_2d/color_scheme.py +++ /dev/null @@ -1,213 +0,0 @@ -"""ColorScheme class - utility for describing color schemes.""" - -from __future__ import annotations - -from typing import ClassVar - -from pygerber.common.frozen_general_model import FrozenGeneralModel -from pygerber.common.rgba import RGBA -from pygerber.gerberx3.state_enums import Polarity - - -class ColorScheme(FrozenGeneralModel): - r"""Set of colors which should be used for rendering. - - ColorScheme class contains set of colors which should be used for different parts - of rendered image. At the same time it also works as a container for predefined - color schemes commonly used for parts of PCB. - - !!! info "Predefined colors" - - All predefined colors have two variants - normal one and one with "\*_ALPHA" - suffix. Those without suffix have solid background and are not intended for - constructing multi-layer images out of them ie. they are not suitable for - rendering a project consisting of separate copper, silk, pase mask and composing - them into single image. For cases when rendered images are intended for stacking - "\*_ALPHA" schemes should be used, as background and transparent parts of image - will be truly transparent. - - """ - - SILK: ClassVar[ColorScheme] - """Default color of silk layer. - - This schema provided non-transparent background, which results in images which - can not be used for stacking on top of other layers, as they would completely - obscure them.""" - - SILK_ALPHA: ClassVar[ColorScheme] - """Default color of silk layer with alpha channel. - - This schema provides transparent background. Images using this schema can be - stacked on top of each other without obscuring layers below.""" - - COPPER: ClassVar[ColorScheme] - """Default color of copper layer. - - This schema provided non-transparent background, which results in images which - can not be used for stacking on top of other layers, as they would completely - obscure them.""" - - COPPER_ALPHA: ClassVar[ColorScheme] - """Default color of copper layer with alpha channel. - - This schema provides transparent background. Images using this schema can be - stacked on top of each other without obscuring layers below.""" - - PASTE_MASK: ClassVar[ColorScheme] - """Default color of paste mask layer. - - This schema provided non-transparent background, which results in images which - can not be used for stacking on top of other layers, as they would completely - obscure them.""" - - PASTE_MASK_ALPHA: ClassVar[ColorScheme] - """Default color of paste mask layer with alpha channel. - - This schema provides transparent background. Images using this schema can be - stacked on top of each other without obscuring layers below.""" - - SOLDER_MASK: ClassVar[ColorScheme] - """Default color of solder mask layer. - - This schema provided non-transparent background, which results in images which - can not be used for stacking on top of other layers, as they would completely - obscure them.""" - - SOLDER_MASK_ALPHA: ClassVar[ColorScheme] - """Default color of solder mask layer with alpha channel. - - This schema provides transparent background. Images using this schema can be - stacked on top of each other without obscuring layers below.""" - - DEFAULT_GRAYSCALE: ClassVar[ColorScheme] - """Default color scheme for files which were not assigned other color scheme.""" - - DEBUG_1: ClassVar[ColorScheme] - """Debug color scheme.""" - - DEBUG_1_ALPHA: ClassVar[ColorScheme] - """Debug color scheme with alpha channel.""" - - background_color: RGBA - """Color used as empty image background.""" - - clear_color: RGBA - """Color used for clear draws.""" - - solid_color: RGBA - """Color used for solid draws.""" - - clear_region_color: RGBA - """Color used for clear region draws.""" - - solid_region_color: RGBA - """Color used for solid region draws.""" - - debug_1_color: RGBA = RGBA.from_hex("#ababab") - """Color used for debug elements.""" - - debug_2_color: RGBA = RGBA.from_hex("#7d7d7d") - """Color used for debug elements.""" - - def get_grayscale_to_rgba_color_map(self) -> dict[int, tuple[int, int, int, int]]: - """Return grayscale to RGBA color map.""" - return { - Polarity.Dark.get_2d_rasterized_color(): self.solid_color.as_rgba_int(), - Polarity.Clear.get_2d_rasterized_color(): self.clear_color.as_rgba_int(), - Polarity.DarkRegion.get_2d_rasterized_color(): self.solid_region_color.as_rgba_int(), # noqa: E501 - Polarity.ClearRegion.get_2d_rasterized_color(): self.clear_region_color.as_rgba_int(), # noqa: E501 - Polarity.Background.get_2d_rasterized_color(): self.background_color.as_rgba_int(), # noqa: E501 - Polarity.DEBUG.get_2d_rasterized_color(): self.debug_1_color.as_rgba_int(), - Polarity.DEBUG2.get_2d_rasterized_color(): self.debug_2_color.as_rgba_int(), - } - - -ColorScheme.SILK = ColorScheme( - background_color=RGBA.from_hex("#000000"), - clear_color=RGBA.from_hex("#000000"), - solid_color=RGBA.from_hex("#FFFFFF"), - clear_region_color=RGBA.from_hex("#000000"), - solid_region_color=RGBA.from_hex("#FFFFFF"), -) -ColorScheme.SILK_ALPHA = ColorScheme( - background_color=RGBA.from_hex("#00000000"), - clear_color=RGBA.from_hex("#00000000"), - solid_color=RGBA.from_hex("#FFFFFFFF"), - clear_region_color=RGBA.from_hex("#00000000"), - solid_region_color=RGBA.from_hex("#FFFFFFFF"), -) - -ColorScheme.COPPER = ColorScheme( - background_color=RGBA.from_rgba(0, 0, 0, 255), - clear_color=RGBA.from_rgba(60, 181, 60, 255), - solid_color=RGBA.from_rgba(40, 143, 40, 255), - clear_region_color=RGBA.from_rgba(60, 181, 60, 255), - solid_region_color=RGBA.from_rgba(40, 143, 40, 255), -) -ColorScheme.COPPER_ALPHA = ColorScheme( - background_color=RGBA.from_rgba(0, 0, 0, 0), - clear_color=RGBA.from_rgba(60, 181, 60, 255), - solid_color=RGBA.from_rgba(40, 143, 40, 255), - clear_region_color=RGBA.from_rgba(60, 181, 60, 255), - solid_region_color=RGBA.from_rgba(40, 143, 40, 255), -) - -ColorScheme.PASTE_MASK = ColorScheme( - background_color=RGBA.from_rgba(0, 0, 0, 255), - clear_color=RGBA.from_rgba(0, 0, 0, 255), - solid_color=RGBA.from_rgba(117, 117, 117, 255), - clear_region_color=RGBA.from_rgba(0, 0, 0, 255), - solid_region_color=RGBA.from_rgba(117, 117, 117, 255), -) -ColorScheme.PASTE_MASK_ALPHA = ColorScheme( - background_color=RGBA.from_rgba(0, 0, 0, 0), - clear_color=RGBA.from_rgba(0, 0, 0, 0), - solid_color=RGBA.from_rgba(117, 117, 117, 255), - clear_region_color=RGBA.from_rgba(0, 0, 0, 0), - solid_region_color=RGBA.from_rgba(117, 117, 117, 255), -) - -ColorScheme.SOLDER_MASK = ColorScheme( - background_color=RGBA.from_rgba(0, 0, 0, 255), - clear_color=RGBA.from_rgba(0, 0, 0, 255), - solid_color=RGBA.from_rgba(117, 117, 117, 255), - clear_region_color=RGBA.from_rgba(0, 0, 0, 255), - solid_region_color=RGBA.from_rgba(117, 117, 117, 255), -) -ColorScheme.SOLDER_MASK_ALPHA = ColorScheme( - background_color=RGBA.from_rgba(0, 0, 0, 0), - clear_color=RGBA.from_rgba(0, 0, 0, 0), - solid_color=RGBA.from_rgba(153, 153, 153, 255), - clear_region_color=RGBA.from_rgba(0, 0, 0, 0), - solid_region_color=RGBA.from_rgba(153, 153, 153, 255), -) - -ColorScheme.DEFAULT_GRAYSCALE = ColorScheme( - background_color=RGBA.from_rgba(0, 0, 0, 0), - clear_color=RGBA.from_rgba(0, 0, 0, 0), - solid_color=RGBA.from_rgba(255, 255, 255, 255), - clear_region_color=RGBA.from_rgba(0, 0, 0, 0), - solid_region_color=RGBA.from_rgba(255, 255, 255, 255), - debug_1_color=RGBA.from_hex("#ababab"), - debug_2_color=RGBA.from_hex("#7d7d7d"), -) - -ColorScheme.DEBUG_1 = ColorScheme( - background_color=RGBA.from_rgba(0, 0, 0, 0), - clear_color=RGBA.from_rgba(187, 8, 65, 255), - solid_color=RGBA.from_rgba(19, 61, 145, 255), - clear_region_color=RGBA.from_rgba(94, 52, 20, 255), - solid_region_color=RGBA.from_rgba(21, 92, 130, 255), - debug_1_color=RGBA.from_hex("#ababab"), - debug_2_color=RGBA.from_hex("#7d7d7d"), -) -ColorScheme.DEBUG_1_ALPHA = ColorScheme( - background_color=RGBA.from_rgba(0, 0, 0, 0), - clear_color=RGBA.from_rgba(0, 0, 0, 0), - solid_color=RGBA.from_rgba(19, 61, 145, 255), - clear_region_color=RGBA.from_rgba(0, 0, 0, 0), - solid_region_color=RGBA.from_rgba(21, 92, 130, 255), - debug_1_color=RGBA.from_hex("#ababab"), - debug_2_color=RGBA.from_hex("#7d7d7d"), -) diff --git a/src/pygerber/backend/rasterized_2d/draw_commands/__init__.py b/src/pygerber/backend/rasterized_2d/draw_commands/__init__.py deleted file mode 100644 index b22a5e39..00000000 --- a/src/pygerber/backend/rasterized_2d/draw_commands/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Specialized implementations of draw operations for constructing apertures.""" diff --git a/src/pygerber/backend/rasterized_2d/draw_commands/draw_arc.py b/src/pygerber/backend/rasterized_2d/draw_commands/draw_arc.py deleted file mode 100644 index c6106209..00000000 --- a/src/pygerber/backend/rasterized_2d/draw_commands/draw_arc.py +++ /dev/null @@ -1,41 +0,0 @@ -"""Class for drawing 2D rasterized vector lines.""" - -from __future__ import annotations - -import logging -from typing import TYPE_CHECKING - -from pygerber.backend.abstract.draw_commands.draw_arc import DrawArc -from pygerber.backend.abstract.drawing_target import DrawingTarget -from pygerber.backend.rasterized_2d.drawing_target import Rasterized2DDrawingTarget - -if TYPE_CHECKING: - from pygerber.backend.rasterized_2d.backend_cls import Rasterized2DBackend - - -class Rasterized2DDrawArc(DrawArc): - """Draw 2D rasterized vector line.""" - - backend: Rasterized2DBackend - - def draw(self, target: DrawingTarget) -> None: - """Apply aperture draw component to handle.""" - if not isinstance(target, Rasterized2DDrawingTarget): - msg = f"Expected Rasterized2DDrawingTarget got {type(target)}" - raise TypeError(msg) - - bbox = self.get_bounding_box() - target.coordinate_origin - pixel_box = bbox.as_pixel_box(self.backend.dpi) - - angle_start, angle_end = self._calculate_angles() - - width = self.width.as_pixels(self.backend.dpi) - - target.image_draw.arc( - xy=pixel_box, - start=angle_start, - end=angle_end, - fill=self.polarity.get_2d_rasterized_color(), - width=width, - ) - logging.debug("Adding %s to %s", self.__class__.__qualname__, target) diff --git a/src/pygerber/backend/rasterized_2d/draw_commands/draw_bounding_box.py b/src/pygerber/backend/rasterized_2d/draw_commands/draw_bounding_box.py deleted file mode 100644 index 50a0b73d..00000000 --- a/src/pygerber/backend/rasterized_2d/draw_commands/draw_bounding_box.py +++ /dev/null @@ -1,42 +0,0 @@ -"""BoundingBox component for creating apertures.""" - -from __future__ import annotations - -import logging -from typing import TYPE_CHECKING - -from pygerber.backend.abstract.draw_commands.draw_bounding_box import DrawBoundingBox -from pygerber.backend.abstract.drawing_target import DrawingTarget -from pygerber.backend.rasterized_2d.drawing_target import Rasterized2DDrawingTarget - -if TYPE_CHECKING: - from pygerber.backend.rasterized_2d.backend_cls import Rasterized2DBackend - - -class Rasterized2DApertureDrawBoundingBox(DrawBoundingBox): - """Concrete implementation of DrawBoundingBox for rasterized 2D drawing.""" - - backend: Rasterized2DBackend - - def draw(self, target: DrawingTarget) -> None: - """Apply bounding box draw component to handle.""" - if not isinstance(target, Rasterized2DDrawingTarget): - msg = f"Expected Rasterized2DDrawingTarget got {type(target)}" - raise TypeError(msg) - - box = self.get_bounding_box() - image_space_box = box - target.coordinate_origin - pixel_box = image_space_box.as_pixel_box(self.backend.dpi, dx_max=-1, dy_max=-1) - - (min_x, min_y, max_x, max_y) = pixel_box - if (max_x - min_x <= 0) or (max_y - min_y <= 0): - logging.warning("Drawing zero surface bounding box. DPI may be too low.") - return - - target.image_draw.rectangle( - xy=pixel_box, - fill=None, - outline=self.polarity.get_2d_rasterized_color(), - width=1, - ) - logging.debug("Adding %s to %s", self.__class__.__qualname__, target) diff --git a/src/pygerber/backend/rasterized_2d/draw_commands/draw_circle.py b/src/pygerber/backend/rasterized_2d/draw_commands/draw_circle.py deleted file mode 100644 index 598cdbcd..00000000 --- a/src/pygerber/backend/rasterized_2d/draw_commands/draw_circle.py +++ /dev/null @@ -1,52 +0,0 @@ -"""Circle component for creating apertures.""" - -from __future__ import annotations - -import logging -from typing import TYPE_CHECKING - -from pygerber.backend.abstract.draw_commands.draw_circle import ( - DrawCircle, -) -from pygerber.backend.abstract.drawing_target import DrawingTarget -from pygerber.backend.rasterized_2d.drawing_target import Rasterized2DDrawingTarget - -if TYPE_CHECKING: - from pygerber.backend.rasterized_2d.backend_cls import Rasterized2DBackend - - -class Rasterized2DApertureDrawCircle(DrawCircle): - """Description of circle aperture component.""" - - backend: Rasterized2DBackend - - def draw(self, target: DrawingTarget) -> None: - """Apply aperture draw component to handle.""" - if not isinstance(target, Rasterized2DDrawingTarget): - msg = f"Expected Rasterized2DDrawingTarget got {type(target)}" - raise TypeError(msg) - - box = self.get_bounding_box() - image_space_box = box - target.coordinate_origin - pixel_box = image_space_box.as_pixel_box( - self.backend.dpi, - dx_max=-2, - dy_max=-2, - dx_min=+1, - dy_min=+1, - ) - (min_x, min_y, max_x, max_y) = pixel_box - if (max_x - min_x <= 0) or (max_y - min_y <= 0): - logging.warning( - "Drawing zero surface circle. DPI may be too low. %s", - pixel_box, - ) - - else: - target.image_draw.ellipse( - xy=pixel_box, - fill=self.polarity.get_2d_rasterized_color(), - outline=None, - width=0, - ) - logging.debug("Adding %s to %s", self.__class__.__qualname__, target) diff --git a/src/pygerber/backend/rasterized_2d/draw_commands/draw_paste.py b/src/pygerber/backend/rasterized_2d/draw_commands/draw_paste.py deleted file mode 100644 index 23224074..00000000 --- a/src/pygerber/backend/rasterized_2d/draw_commands/draw_paste.py +++ /dev/null @@ -1,59 +0,0 @@ -"""Base class for creating components for aperture creation.""" - -from __future__ import annotations - -import logging -from typing import TYPE_CHECKING - -from pygerber.backend.abstract.draw_commands.draw_paste import DrawPaste -from pygerber.backend.abstract.drawing_target import DrawingTarget -from pygerber.backend.rasterized_2d.drawing_target import Rasterized2DDrawingTarget -from pygerber.backend.rasterized_2d.image_tools import replace_color -from pygerber.gerberx3.state_enums import Polarity - -if TYPE_CHECKING: - from pygerber.backend.rasterized_2d.backend_cls import Rasterized2DBackend - - -class Rasterized2DDrawPaste(DrawPaste): - """Description of aperture component.""" - - other: Rasterized2DDrawingTarget - backend: Rasterized2DBackend - - def draw(self, target: DrawingTarget) -> None: - """Apply aperture draw component to handle.""" - if not isinstance(target, Rasterized2DDrawingTarget): - msg = f"Expected Rasterized2DDrawingTarget got {type(target)}" - raise TypeError(msg) - - box = self.get_bounding_box() - image_space_box = box - target.coordinate_origin - pixel_box = image_space_box.get_min_vector().as_pixels(self.backend.dpi) - - if self.polarity == Polarity.Dark: - im = self.other.image_polarity_dark - - elif self.polarity == Polarity.Clear: - im = self.other.image_polarity_clear - - elif self.polarity == Polarity.DarkRegion: - im = self.other.image_polarity_region_dark - - elif self.polarity == Polarity.ClearRegion: - im = self.other.image_polarity_region_clear - - else: - im = replace_color( - self.other.target_image, - Polarity.Dark.get_2d_rasterized_color(), - self.polarity.get_2d_rasterized_color(), - output_image_mode="L", - ) - - target.target_image.paste( - im=im, - box=pixel_box, - mask=self.other.mask_image, - ) - logging.debug("Adding %s to %s", self.__class__.__qualname__, target) diff --git a/src/pygerber/backend/rasterized_2d/draw_commands/draw_polygon.py b/src/pygerber/backend/rasterized_2d/draw_commands/draw_polygon.py deleted file mode 100644 index 5d883247..00000000 --- a/src/pygerber/backend/rasterized_2d/draw_commands/draw_polygon.py +++ /dev/null @@ -1,66 +0,0 @@ -"""Polygon component for creating apertures.""" - -from __future__ import annotations - -import logging -from decimal import Decimal -from typing import TYPE_CHECKING - -from pygerber.backend.abstract.draw_commands.draw_polygon import ( - DrawPolygon, -) -from pygerber.backend.abstract.drawing_target import DrawingTarget -from pygerber.backend.rasterized_2d.drawing_target import Rasterized2DDrawingTarget - -if TYPE_CHECKING: - from pygerber.backend.rasterized_2d.backend_cls import Rasterized2DBackend - - -NUMBER_OF_VERTICES_IN_TRIANGLE = 3 - - -class Rasterized2DApertureDrawPolygon(DrawPolygon): - """Description of polygon aperture component.""" - - backend: Rasterized2DBackend - - def draw(self, target: DrawingTarget) -> None: - """Apply aperture draw component to handle.""" - if not isinstance(target, Rasterized2DDrawingTarget): - msg = f"Expected Rasterized2DDrawingTarget got {type(target)}" - raise TypeError(msg) - - box = self.get_bounding_box() - image_space_box = box - target.coordinate_origin - center = image_space_box.center.as_pixels(self.backend.dpi) - - bounding_circle = ( - *center, - (self.outer_diameter / 2).as_pixels(self.backend.dpi), - ) - rotation = float(-self.rotation + Decimal("-90.0")) - - if self.number_of_vertices < NUMBER_OF_VERTICES_IN_TRIANGLE: - logging.warning( - "Drawing invalid polygon, number of vertices < 3 (%s)", - self.number_of_vertices, - ) - return - - (_, __, radius) = bounding_circle - if radius == 0: - logging.warning( - "Drawing zero surface polygon. DPI may be too low. %s", - bounding_circle, - ) - return - - target.image_draw.regular_polygon( - bounding_circle=bounding_circle, - n_sides=self.number_of_vertices, - rotation=int(rotation), - fill=self.polarity.get_2d_rasterized_color(), - outline=None, - width=0, - ) - logging.debug("Adding %s to %s", self.__class__.__qualname__, target) diff --git a/src/pygerber/backend/rasterized_2d/draw_commands/draw_rectangle.py b/src/pygerber/backend/rasterized_2d/draw_commands/draw_rectangle.py deleted file mode 100644 index 647a94b3..00000000 --- a/src/pygerber/backend/rasterized_2d/draw_commands/draw_rectangle.py +++ /dev/null @@ -1,49 +0,0 @@ -"""Rectangle component for creating apertures.""" - -from __future__ import annotations - -import logging -from typing import TYPE_CHECKING - -from pygerber.backend.abstract.draw_commands.draw_rectangle import ( - DrawRectangle, -) -from pygerber.backend.abstract.drawing_target import DrawingTarget -from pygerber.backend.rasterized_2d.drawing_target import Rasterized2DDrawingTarget - -if TYPE_CHECKING: - from pygerber.backend.rasterized_2d.backend_cls import Rasterized2DBackend - - -class Rasterized2DApertureDrawRectangle(DrawRectangle): - """Description of rectangle aperture component.""" - - backend: Rasterized2DBackend - - def draw(self, target: DrawingTarget) -> None: - """Apply aperture draw component to handle.""" - if not isinstance(target, Rasterized2DDrawingTarget): - msg = f"Expected Rasterized2DDrawingTarget got {type(target)}" - raise TypeError(msg) - - box = self.get_bounding_box() - image_space_box = box - target.coordinate_origin - pixel_box = image_space_box.as_pixel_box( - self.backend.dpi, - ) - - (min_x, min_y, max_x, max_y) = pixel_box - if (max_x - min_x <= 0) or (max_y - min_y <= 0): - logging.warning( - "Drawing zero surface rectangle. DPI may be too low. %s", - pixel_box, - ) - - else: - target.image_draw.rectangle( - xy=pixel_box, - fill=self.polarity.get_2d_rasterized_color(), - outline=None, - width=0, - ) - logging.debug("Adding %s to %s", self.__class__.__qualname__, target) diff --git a/src/pygerber/backend/rasterized_2d/draw_commands/draw_region.py b/src/pygerber/backend/rasterized_2d/draw_commands/draw_region.py deleted file mode 100644 index 7d56719e..00000000 --- a/src/pygerber/backend/rasterized_2d/draw_commands/draw_region.py +++ /dev/null @@ -1,49 +0,0 @@ -"""Class for drawing 2D rasterized vector lines.""" - -from __future__ import annotations - -import logging -from typing import TYPE_CHECKING - -from pygerber.backend.abstract.draw_commands.draw_region import DrawRegion -from pygerber.backend.abstract.drawing_target import DrawingTarget -from pygerber.backend.rasterized_2d.drawing_target import Rasterized2DDrawingTarget - -if TYPE_CHECKING: - from pygerber.backend.rasterized_2d.backend_cls import Rasterized2DBackend - - -NUMBER_OF_VERTICES_IN_TRIANGLE = 3 - - -class Rasterized2DDrawRegion(DrawRegion): - """Draw 2D rasterized vector line.""" - - backend: Rasterized2DBackend - - def draw(self, target: DrawingTarget) -> None: - """Apply aperture draw component to handle.""" - if not isinstance(target, Rasterized2DDrawingTarget): - msg = f"Expected Rasterized2DDrawingTarget got {type(target)}" - raise TypeError(msg) - - boundary_points: list[tuple[int, int]] = [] - - for point in self.region_boundary_points: - pixel_point = (point - target.coordinate_origin).as_pixels( - self.backend.dpi, - ) - boundary_points.append(pixel_point) - - if len(boundary_points) < NUMBER_OF_VERTICES_IN_TRIANGLE: - logging.warning( - "Drawing invalid region, number of vertices < 3 (%s)", - len(boundary_points), - ) - return - - target.image_draw.polygon( - boundary_points, - fill=self.polarity.get_2d_rasterized_color(), - ) - logging.debug("Adding %s to %s", self.__class__.__qualname__, target) diff --git a/src/pygerber/backend/rasterized_2d/draw_commands/draw_vector_line.py b/src/pygerber/backend/rasterized_2d/draw_commands/draw_vector_line.py deleted file mode 100644 index 6a8d33ba..00000000 --- a/src/pygerber/backend/rasterized_2d/draw_commands/draw_vector_line.py +++ /dev/null @@ -1,40 +0,0 @@ -"""Class for drawing 2D rasterized vector lines.""" - -from __future__ import annotations - -import logging -from typing import TYPE_CHECKING - -from pygerber.backend.abstract.draw_commands.draw_vector_line import DrawVectorLine -from pygerber.backend.abstract.drawing_target import DrawingTarget -from pygerber.backend.rasterized_2d.drawing_target import Rasterized2DDrawingTarget - -if TYPE_CHECKING: - from pygerber.backend.rasterized_2d.backend_cls import Rasterized2DBackend - - -class Rasterized2DDrawVectorLine(DrawVectorLine): - """Draw 2D rasterized vector line.""" - - backend: Rasterized2DBackend - - def draw(self, target: DrawingTarget) -> None: - """Apply aperture draw component to handle.""" - if not isinstance(target, Rasterized2DDrawingTarget): - msg = f"Expected Rasterized2DDrawingTarget got {type(target)}" - raise TypeError(msg) - - start = (self.start_position - target.coordinate_origin).as_pixels( - self.backend.dpi, - ) - end = (self.end_position - target.coordinate_origin).as_pixels( - self.backend.dpi, - ) - width = self.width.as_pixels(self.backend.dpi) - - target.image_draw.line( - (start, end), - fill=self.polarity.get_2d_rasterized_color(), - width=width, - ) - logging.debug("Adding %s to %s", self.__class__.__qualname__, target) diff --git a/src/pygerber/backend/rasterized_2d/draw_commands_handle.py b/src/pygerber/backend/rasterized_2d/draw_commands_handle.py deleted file mode 100644 index 76c47ec4..00000000 --- a/src/pygerber/backend/rasterized_2d/draw_commands_handle.py +++ /dev/null @@ -1,9 +0,0 @@ -"""Contains class wrapping list of draw operations created by Gerber parser.""" - -from __future__ import annotations - -from pygerber.backend.abstract.draw_commands_handle import DrawCommandsHandle - - -class Rasterized2DDrawActionsHandle(DrawCommandsHandle): - """List of drawing operations produced by Gerber parser.""" diff --git a/src/pygerber/backend/rasterized_2d/drawing_target.py b/src/pygerber/backend/rasterized_2d/drawing_target.py deleted file mode 100644 index 978449ee..00000000 --- a/src/pygerber/backend/rasterized_2d/drawing_target.py +++ /dev/null @@ -1,119 +0,0 @@ -"""Target for Draw commands to draw into.""" - -from __future__ import annotations - -from typing import Optional - -from PIL import Image, ImageDraw - -from pygerber.backend.abstract.drawing_target import DrawingTarget -from pygerber.backend.rasterized_2d.errors import ApertureImageNotInitializedError -from pygerber.gerberx3.math.bounding_box import BoundingBox -from pygerber.gerberx3.math.vector_2d import Vector2D -from pygerber.gerberx3.state_enums import Polarity - -GRAYSCALE_CENTER_VALUE: int = 127 - - -class Rasterized2DDrawingTarget(DrawingTarget): - """Target for Draw commands to draw into.""" - - target_image: Image.Image - _target_image_polarity_dark: Optional[Image.Image] = None - _target_image_polarity_clear: Optional[Image.Image] = None - _target_image_polarity_region_dark: Optional[Image.Image] = None - _target_image_polarity_region_clear: Optional[Image.Image] = None - _mask_image: Optional[Image.Image] = None - - def __init__( - self, - coordinate_origin: Vector2D, - bounding_box: BoundingBox, - target_image: Image.Image, - ) -> None: - """Initialize drawing target.""" - super().__init__(coordinate_origin, bounding_box) - self.target_image = target_image - self._target_image_polarity_dark = None - self._target_image_polarity_clear = None - self._target_image_polarity_region_dark = None - self._target_image_polarity_region_clear = None - self._mask_image = None - self._is_finalized = False - - @property - def image_draw(self) -> ImageDraw.ImageDraw: - """Acquire drawing interface.""" - return ImageDraw.Draw(self.target_image) - - def _finalize(self) -> None: - self._is_finalized = True - - @property - def mask_image(self) -> Image.Image: - """Inverted aperture image.""" - if not self._is_finalized: - raise ApertureImageNotInitializedError - - if self._mask_image is None: - self._mask_image = self.target_image.point( - lambda p: 255 if p > GRAYSCALE_CENTER_VALUE else 0, - ) - - return self._mask_image - - @property - def image_polarity_dark(self) -> Image.Image: - """Inverted aperture image.""" - if not self._is_finalized: - raise ApertureImageNotInitializedError - - if self._target_image_polarity_dark is None: - color = Polarity.Dark.get_2d_rasterized_color() - self._target_image_polarity_dark = self.mask_image.point( - lambda p: color if p else 0, - ) - - return self._target_image_polarity_dark - - @property - def image_polarity_clear(self) -> Image.Image: - """Inverted aperture image.""" - if not self._is_finalized: - raise ApertureImageNotInitializedError - - if self._target_image_polarity_clear is None: - color = Polarity.Clear.get_2d_rasterized_color() - self._target_image_polarity_clear = self.mask_image.point( - lambda p: color if p else 0, - ) - - return self._target_image_polarity_clear - - @property - def image_polarity_region_dark(self) -> Image.Image: - """Inverted aperture image.""" - if not self._is_finalized: - raise ApertureImageNotInitializedError - - if self._target_image_polarity_region_dark is None: - color = Polarity.DarkRegion.get_2d_rasterized_color() - self._target_image_polarity_region_dark = self.mask_image.point( - lambda p: color if p else 0, - ) - - return self._target_image_polarity_region_dark - - @property - def image_polarity_region_clear(self) -> Image.Image: - """Inverted aperture image.""" - if not self._is_finalized: - raise ApertureImageNotInitializedError - - if self._target_image_polarity_region_clear is None: - color = Polarity.ClearRegion.get_2d_rasterized_color() - self._target_image_polarity_region_clear = self.mask_image.point( - lambda p: color if p else 0, - ) - - return self._target_image_polarity_region_clear diff --git a/src/pygerber/backend/rasterized_2d/errors.py b/src/pygerber/backend/rasterized_2d/errors.py deleted file mode 100644 index f10b167e..00000000 --- a/src/pygerber/backend/rasterized_2d/errors.py +++ /dev/null @@ -1,17 +0,0 @@ -"""Base error classes used in this module.""" - -from __future__ import annotations - -from pygerber.backend.abstract.errors import BackendError - - -class Rasterized2DBackendError(BackendError): - """Base class for backend errors.""" - - -class ApertureImageNotInitializedError(Rasterized2DBackendError): - """Raised when aperture image is requested before it was initialized.""" - - -class BackendImageNotInitializedError(Rasterized2DBackendError): - """Raised when backend canvas image is requested before initialization.""" diff --git a/src/pygerber/backend/rasterized_2d/image_tools.py b/src/pygerber/backend/rasterized_2d/image_tools.py deleted file mode 100644 index 71c9a570..00000000 --- a/src/pygerber/backend/rasterized_2d/image_tools.py +++ /dev/null @@ -1,29 +0,0 @@ -"""Utils for image operations.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -if TYPE_CHECKING: - from PIL import Image - - -def replace_color( - input_image: Image.Image, - original: tuple[int, ...] | int, - replacement: tuple[int, ...] | int, - *, - output_image_mode: str = "RGBA", -) -> Image.Image: - """Replace `original` color from input image with `replacement` color.""" - if input_image.mode != output_image_mode: - output_image = input_image.convert(output_image_mode) - else: - output_image = input_image.copy() - - for x in range(input_image.width): - for y in range(input_image.height): - if input_image.getpixel((x, y)) == original: - output_image.putpixel((x, y), replacement) - - return output_image diff --git a/src/pygerber/backend/rasterized_2d/result_handle.py b/src/pygerber/backend/rasterized_2d/result_handle.py deleted file mode 100644 index 39504d9b..00000000 --- a/src/pygerber/backend/rasterized_2d/result_handle.py +++ /dev/null @@ -1,56 +0,0 @@ -"""Module contains handle class to drawing instructions visualization.""" - -from __future__ import annotations - -from pathlib import Path -from typing import TYPE_CHECKING, Any - -from PIL import Image - -from pygerber.backend.abstract.result_handle import ResultHandle - -if TYPE_CHECKING: - from io import BytesIO - - -class Rasterized2DResultHandle(ResultHandle): - """Handle to drawing instructions visualization.""" - - def __init__(self, result: Image.Image) -> None: - """Initialize result handle. - - Parameters - ---------- - result : Image.Image - Image object containing finished Gerber image. - - """ - super().__init__() - self.result = result - - def save( - self, - dest: Path | str | BytesIO, - **kwargs: Any, - ) -> None: - """Save result to destination. - - Parameters - ---------- - dest : Path | str | BytesIO - Write target. - **kwargs: Any - Extra parameters which will be passed to `Image.save()`. - For details see [Pillow documentation](https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.save). - - """ - image = self.get_image() - if (isinstance(dest, Path) and dest.suffix in (".jpg", "jpeg")) or ( - isinstance(dest, str) and (dest.endswith((".jpg", ".jpeg"))) - ): - image = image.convert("RGB") - image.save(dest, **kwargs) - - def get_image(self) -> Image.Image: - """Get result image object.""" - return self.result.transpose(Image.Transpose.FLIP_TOP_BOTTOM) diff --git a/src/pygerber/common/position.py b/src/pygerber/common/position.py index d11168b2..95861f5c 100644 --- a/src/pygerber/common/position.py +++ b/src/pygerber/common/position.py @@ -5,7 +5,7 @@ from dataclasses import dataclass from typing import TYPE_CHECKING -from pygerber.gerberx3.language_server.status import ( +from pygerber.gerber.language_server.status import ( is_language_server_available, ) diff --git a/src/pygerber/console/commands.py b/src/pygerber/console/commands.py index 43fa4b77..66f0cb67 100644 --- a/src/pygerber/console/commands.py +++ b/src/pygerber/console/commands.py @@ -5,7 +5,7 @@ import click import pygerber -from pygerber.gerberx3.api import FileTypeEnum +from pygerber.gerber.api import FileTypeEnum @click.group("pygerber") @@ -18,7 +18,7 @@ def main() -> None: @main.command("is-language-server-available") def _is_language_server_available() -> None: - from pygerber.gerberx3.language_server.status import is_language_server_available + from pygerber.gerber.language_server.status import is_language_server_available if is_language_server_available(): click.echo("Language server is available.") diff --git a/src/pygerber/console/raster_2d_style.py b/src/pygerber/console/raster_2d_style.py deleted file mode 100644 index a8dfc857..00000000 --- a/src/pygerber/console/raster_2d_style.py +++ /dev/null @@ -1,93 +0,0 @@ -"""Tool for converting style string to ColorScheme objects.""" - -from __future__ import annotations - -from typing import Optional - -from pygerber.backend.rasterized_2d.color_scheme import ColorScheme -from pygerber.common.rgba import RGBA - -CUSTOM_STYLE_OPTION = "custom" - -STYLE_TO_COLOR_SCHEME = { - "silk": ColorScheme.SILK, - "silk_alpha": ColorScheme.SILK_ALPHA, - "copper": ColorScheme.COPPER, - "copper_alpha": ColorScheme.COPPER_ALPHA, - "paste_mask": ColorScheme.PASTE_MASK, - "paste_mask_alpha": ColorScheme.PASTE_MASK_ALPHA, - "solder_mask": ColorScheme.SOLDER_MASK, - "solder_mask_alpha": ColorScheme.SOLDER_MASK_ALPHA, - "default_grayscale": ColorScheme.DEFAULT_GRAYSCALE, - "debug_1": ColorScheme.DEBUG_1, - CUSTOM_STYLE_OPTION: None, -} -"""Map of known color styles to ColorScheme objects.""" - - -def get_color_scheme_from_style( - style: str, - custom: Optional[str] = None, -) -> ColorScheme: - """Convert style string to ColorScheme object. - - Accepted values for style are any key from STYLE_TO_COLOR_SCHEME. When style is - 'custom' then parameter custom must also be provided. - Custom color should be a single string consisting of 5 or 7 valid hexadecimal colors - separated with commas. Any color which can be parsed by RGBA type is accepted. - Colors are assigned in order: - - - background_color - - clear_color - - solid_color - - clear_region_color - - solid_region_color - - debug_1_color (optional, by default #ABABAB) - - debug_2_color (optional, by default #7D7D7D) - - eg. `"000000,000000,FFFFFF,000000,FFFFFF"` - """ - if style == CUSTOM_STYLE_OPTION: - if custom is None: - msg = ( - f"When style is {CUSTOM_STYLE_OPTION!r} custom have to be a valid " - "custom scheme." - ) - raise TypeError(msg) - return _construct_custom_scheme(custom) - - color_scheme = STYLE_TO_COLOR_SCHEME[style] - if color_scheme is None: - # Can't happen - option 'custom' is picked by if above. - raise TypeError - - return color_scheme - - -def _construct_custom_scheme(custom: str) -> ColorScheme: - ( - background_color, - clear_color, - solid_color, - clear_region_color, - solid_region_color, - *debug_colors, - ) = custom.strip().split(",") - - return ColorScheme( - background_color=RGBA.from_hex(background_color), - clear_color=RGBA.from_hex(clear_color), - solid_color=RGBA.from_hex(solid_color), - clear_region_color=RGBA.from_hex(clear_region_color), - solid_region_color=RGBA.from_hex(solid_region_color), - debug_1_color=( - RGBA.from_hex(debug_colors[0]) - if len(debug_colors) > 0 - else RGBA.from_hex("#ababab") - ), - debug_2_color=( - RGBA.from_hex(debug_colors[0]) - if len(debug_colors) > 0 - else RGBA.from_hex("#7d7d7d") - ), - ) diff --git a/src/pygerber/gerberx3/__init__.py b/src/pygerber/gerber/__init__.py similarity index 100% rename from src/pygerber/gerberx3/__init__.py rename to src/pygerber/gerber/__init__.py diff --git a/src/pygerber/gerberx3/api/__init__.py b/src/pygerber/gerber/api/__init__.py similarity index 63% rename from src/pygerber/gerberx3/api/__init__.py rename to src/pygerber/gerber/api/__init__.py index 27d91b72..f38c4af3 100644 --- a/src/pygerber/gerberx3/api/__init__.py +++ b/src/pygerber/gerber/api/__init__.py @@ -1,22 +1,22 @@ -"""`pygerber.gerberx3.api` module provides simple, high-level API for rendering +"""The `api` module provides simple, high-level API for rendering Gerber X3/X2 files. """ from __future__ import annotations -from pygerber.gerberx3.api._enums import ( +from pygerber.gerber.api._enums import ( DEFAULT_ALPHA_COLOR_MAP, DEFAULT_COLOR_MAP, FileTypeEnum, ) -from pygerber.gerberx3.api._gerber_file import ( +from pygerber.gerber.api._gerber_file import ( GerberFile, Image, ImageSpace, PillowImage, Units, ) -from pygerber.gerberx3.api._project import Project +from pygerber.gerber.api._project import Project __all__ = [ "FileTypeEnum", diff --git a/src/pygerber/gerberx3/api/_enums.py b/src/pygerber/gerber/api/_enums.py similarity index 100% rename from src/pygerber/gerberx3/api/_enums.py rename to src/pygerber/gerber/api/_enums.py diff --git a/src/pygerber/gerberx3/api/_errors.py b/src/pygerber/gerber/api/_errors.py similarity index 100% rename from src/pygerber/gerberx3/api/_errors.py rename to src/pygerber/gerber/api/_errors.py diff --git a/src/pygerber/gerberx3/api/_gerber_file.py b/src/pygerber/gerber/api/_gerber_file.py similarity index 95% rename from src/pygerber/gerberx3/api/_gerber_file.py rename to src/pygerber/gerber/api/_gerber_file.py index b3c1b2b3..051301bc 100644 --- a/src/pygerber/gerberx3/api/_gerber_file.py +++ b/src/pygerber/gerber/api/_gerber_file.py @@ -1,4 +1,4 @@ -"""Module contains implementation details of GerberX3 high level interface of API v2.""" +"""The `_gerber_file` module contains definition of `GerberFile` class.""" from __future__ import annotations @@ -8,16 +8,16 @@ import pyparsing as pp -from pygerber.gerberx3.api._enums import ( +from pygerber.gerber.api._enums import ( COLOR_MAP_T, DEFAULT_ALPHA_COLOR_MAP, FileTypeEnum, ) -from pygerber.gerberx3.ast import State, get_final_state -from pygerber.gerberx3.ast.nodes.attribute.TF import TF_FileFunction -from pygerber.gerberx3.ast.nodes.enums import UnitMode -from pygerber.gerberx3.compiler import compile -from pygerber.gerberx3.parser import parse +from pygerber.gerber.ast import State, get_final_state +from pygerber.gerber.ast.nodes.attribute.TF import TF_FileFunction +from pygerber.gerber.ast.nodes.enums import UnitMode +from pygerber.gerber.compiler import compile +from pygerber.gerber.parser import parse from pygerber.vm import render from pygerber.vm.pillow.vm import PillowResult from pygerber.vm.types.box import Box @@ -26,7 +26,7 @@ if TYPE_CHECKING: import PIL.Image - from pygerber.gerberx3.ast.nodes import File + from pygerber.gerber.ast.nodes import File from pygerber.vm.rvmc import RVMC if TYPE_CHECKING: diff --git a/src/pygerber/gerberx3/api/_project.py b/src/pygerber/gerber/api/_project.py similarity index 97% rename from src/pygerber/gerberx3/api/_project.py rename to src/pygerber/gerber/api/_project.py index dfed19d2..b9dd7b09 100644 --- a/src/pygerber/gerberx3/api/_project.py +++ b/src/pygerber/gerber/api/_project.py @@ -4,7 +4,7 @@ from PIL import Image -from pygerber.gerberx3.api._gerber_file import GerberFile, PillowImage +from pygerber.gerber.api._gerber_file import GerberFile, PillowImage class CompositeImage: diff --git a/src/pygerber/gerberx3/ast/__init__.py b/src/pygerber/gerber/ast/__init__.py similarity index 72% rename from src/pygerber/gerberx3/ast/__init__.py rename to src/pygerber/gerber/ast/__init__.py index 149b89c4..a00f05ff 100644 --- a/src/pygerber/gerberx3/ast/__init__.py +++ b/src/pygerber/gerber/ast/__init__.py @@ -1,12 +1,12 @@ -"""`pygerber.gerberx3.ast` package contains all the node classes used to represent +"""`` package contains all the node classes used to represent the Gerber X3 abstract syntax tree. """ from __future__ import annotations -from pygerber.gerberx3.ast.ast_visitor import AstVisitor -from pygerber.gerberx3.ast.builder import GerberX3Builder -from pygerber.gerberx3.ast.errors import ( +from pygerber.gerber.ast.ast_visitor import AstVisitor +from pygerber.gerber.ast.builder import GerberX3Builder +from pygerber.gerber.ast.errors import ( ApertureNotFoundError, ApertureNotSelectedError, AstError, @@ -17,10 +17,10 @@ SourceNotAvailableError, VisitorError, ) -from pygerber.gerberx3.ast.expression_eval_visitor import ExpressionEvalVisitor -from pygerber.gerberx3.ast.node_finder import NodeFinder -from pygerber.gerberx3.ast.nodes import File -from pygerber.gerberx3.ast.state_tracking_visitor import ( +from pygerber.gerber.ast.expression_eval_visitor import ExpressionEvalVisitor +from pygerber.gerber.ast.node_finder import NodeFinder +from pygerber.gerber.ast.nodes import File +from pygerber.gerber.ast.state_tracking_visitor import ( ApertureStorage, ArcInterpolation, Attributes, diff --git a/src/pygerber/gerberx3/ast/ast_visitor.py b/src/pygerber/gerber/ast/ast_visitor.py similarity index 99% rename from src/pygerber/gerberx3/ast/ast_visitor.py rename to src/pygerber/gerber/ast/ast_visitor.py index b2078e0d..27e209b2 100644 --- a/src/pygerber/gerberx3/ast/ast_visitor.py +++ b/src/pygerber/gerber/ast/ast_visitor.py @@ -4,10 +4,10 @@ from typing import TYPE_CHECKING -from pygerber.gerberx3.ast.nodes.invalid import Invalid +from pygerber.gerber.ast.nodes.invalid import Invalid if TYPE_CHECKING: - from pygerber.gerberx3.ast.nodes import ( + from pygerber.gerber.ast.nodes import ( AB, AD, ADC, diff --git a/src/pygerber/gerberx3/ast/builder.py b/src/pygerber/gerber/ast/builder.py similarity index 98% rename from src/pygerber/gerberx3/ast/builder.py rename to src/pygerber/gerber/ast/builder.py index 0a9722e3..769f06d2 100644 --- a/src/pygerber/gerberx3/ast/builder.py +++ b/src/pygerber/gerber/ast/builder.py @@ -9,7 +9,7 @@ from pydantic import BaseModel, Field -from pygerber.gerberx3.ast.nodes import ( +from pygerber.gerber.ast.nodes import ( ADC, ADO, ADP, @@ -44,7 +44,7 @@ TA_DrillTolerance, TA_UserName, ) -from pygerber.gerberx3.ast.nodes.enums import ( +from pygerber.gerber.ast.nodes.enums import ( AperFunction, CoordinateNotation, Mirroring, @@ -52,9 +52,9 @@ UnitMode, Zeros, ) -from pygerber.gerberx3.ast.nodes.types import ApertureIdStr -from pygerber.gerberx3.ast.state_tracking_visitor import CoordinateFormat -from pygerber.gerberx3.formatter import Formatter +from pygerber.gerber.ast.nodes.types import ApertureIdStr +from pygerber.gerber.ast.state_tracking_visitor import CoordinateFormat +from pygerber.gerber.formatter import Formatter if TYPE_CHECKING: from typing_extensions import Self diff --git a/src/pygerber/gerberx3/ast/errors.py b/src/pygerber/gerber/ast/errors.py similarity index 93% rename from src/pygerber/gerberx3/ast/errors.py rename to src/pygerber/gerber/ast/errors.py index 3c7dbc87..b7bda5b5 100644 --- a/src/pygerber/gerberx3/ast/errors.py +++ b/src/pygerber/gerber/ast/errors.py @@ -1,12 +1,12 @@ -"""`pygerber.gerberx3.ast.errors` module gathers errors raised by visitors.""" +"""The `errors` module contains errors raised by visitor classes.""" from __future__ import annotations from typing import TYPE_CHECKING if TYPE_CHECKING: - from pygerber.gerberx3.ast.nodes import TF_MD5 - from pygerber.gerberx3.ast.nodes.types import ApertureIdStr + from pygerber.gerber.ast.nodes import TF_MD5 + from pygerber.gerber.ast.nodes.types import ApertureIdStr class AstError(Exception): diff --git a/src/pygerber/gerberx3/ast/expression_eval_visitor.py b/src/pygerber/gerber/ast/expression_eval_visitor.py similarity index 92% rename from src/pygerber/gerberx3/ast/expression_eval_visitor.py rename to src/pygerber/gerber/ast/expression_eval_visitor.py index 0a30066e..056c6049 100644 --- a/src/pygerber/gerberx3/ast/expression_eval_visitor.py +++ b/src/pygerber/gerber/ast/expression_eval_visitor.py @@ -1,4 +1,4 @@ -"""`pygerber.gerberx3.expression_eval_visitor` contains definition of +"""The `expression_eval_visitor` module contains definition of `ExpressionEvalVisitor` class. """ @@ -6,11 +6,11 @@ from typing import TYPE_CHECKING, Optional -from pygerber.gerberx3.ast.ast_visitor import AstVisitor -from pygerber.gerberx3.ast.nodes import Double +from pygerber.gerber.ast.ast_visitor import AstVisitor +from pygerber.gerber.ast.nodes import Double if TYPE_CHECKING: - from pygerber.gerberx3.ast.nodes import ( + from pygerber.gerber.ast.nodes import ( Add, Constant, Div, diff --git a/src/pygerber/gerberx3/ast/node_finder.py b/src/pygerber/gerber/ast/node_finder.py similarity index 96% rename from src/pygerber/gerberx3/ast/node_finder.py rename to src/pygerber/gerber/ast/node_finder.py index 740f0eb7..002c0b44 100644 --- a/src/pygerber/gerberx3/ast/node_finder.py +++ b/src/pygerber/gerber/ast/node_finder.py @@ -8,8 +8,8 @@ from pydantic import BaseModel, Field -from pygerber.gerberx3.ast.ast_visitor import AstVisitor -from pygerber.gerberx3.ast.nodes import AB, AM, SR, File, Node +from pygerber.gerber.ast.ast_visitor import AstVisitor +from pygerber.gerber.ast.nodes import AB, AM, SR, File, Node class ZeroBasedPosition(BaseModel): diff --git a/src/pygerber/gerber/ast/nodes/__init__.py b/src/pygerber/gerber/ast/nodes/__init__.py new file mode 100644 index 00000000..5b6aeb91 --- /dev/null +++ b/src/pygerber/gerber/ast/nodes/__init__.py @@ -0,0 +1,256 @@ +"""`nodes` package contains all the node container classes +generated by the Gerber X3 parser. +""" + +from __future__ import annotations + +from pygerber.gerber.ast.nodes.aperture.AB import AB +from pygerber.gerber.ast.nodes.aperture.AB_close import ABclose +from pygerber.gerber.ast.nodes.aperture.AB_open import ABopen +from pygerber.gerber.ast.nodes.aperture.AD import AD +from pygerber.gerber.ast.nodes.aperture.ADC import ADC +from pygerber.gerber.ast.nodes.aperture.ADmacro import ADmacro +from pygerber.gerber.ast.nodes.aperture.ADO import ADO +from pygerber.gerber.ast.nodes.aperture.ADP import ADP +from pygerber.gerber.ast.nodes.aperture.ADR import ADR +from pygerber.gerber.ast.nodes.aperture.AM import AM +from pygerber.gerber.ast.nodes.aperture.AM_close import AMclose +from pygerber.gerber.ast.nodes.aperture.AM_open import AMopen +from pygerber.gerber.ast.nodes.aperture.SR import SR +from pygerber.gerber.ast.nodes.aperture.SR_close import SRclose +from pygerber.gerber.ast.nodes.aperture.SR_open import SRopen +from pygerber.gerber.ast.nodes.attribute.TA import ( + TA, + TA_AperFunction, + TA_DrillTolerance, + TA_FlashText, + TA_UserName, +) +from pygerber.gerber.ast.nodes.attribute.TD import TD +from pygerber.gerber.ast.nodes.attribute.TF import ( + TF, + TF_MD5, + TF_CreationDate, + TF_FileFunction, + TF_FilePolarity, + TF_GenerationSoftware, + TF_Part, + TF_ProjectId, + TF_SameCoordinates, + TF_UserName, +) +from pygerber.gerber.ast.nodes.attribute.TO import ( + TO, + TO_C, + TO_CMNP, + TO_N, + TO_P, + TO_CFtp, + TO_CHgt, + TO_CLbD, + TO_CLbN, + TO_CMfr, + TO_CMnt, + TO_CPgD, + TO_CPgN, + TO_CRot, + TO_CSup, + TO_CVal, + TO_UserName, +) +from pygerber.gerber.ast.nodes.base import Node, SourceInfo +from pygerber.gerber.ast.nodes.d_codes.D01 import D01 +from pygerber.gerber.ast.nodes.d_codes.D02 import D02 +from pygerber.gerber.ast.nodes.d_codes.D03 import D03 +from pygerber.gerber.ast.nodes.d_codes.Dnn import Dnn +from pygerber.gerber.ast.nodes.file import File +from pygerber.gerber.ast.nodes.g_codes.G import G +from pygerber.gerber.ast.nodes.g_codes.G01 import G01 +from pygerber.gerber.ast.nodes.g_codes.G02 import G02 +from pygerber.gerber.ast.nodes.g_codes.G03 import G03 +from pygerber.gerber.ast.nodes.g_codes.G04 import G04 +from pygerber.gerber.ast.nodes.g_codes.G36 import G36 +from pygerber.gerber.ast.nodes.g_codes.G37 import G37 +from pygerber.gerber.ast.nodes.g_codes.G54 import G54 +from pygerber.gerber.ast.nodes.g_codes.G55 import G55 +from pygerber.gerber.ast.nodes.g_codes.G70 import G70 +from pygerber.gerber.ast.nodes.g_codes.G71 import G71 +from pygerber.gerber.ast.nodes.g_codes.G74 import G74 +from pygerber.gerber.ast.nodes.g_codes.G75 import G75 +from pygerber.gerber.ast.nodes.g_codes.G90 import G90 +from pygerber.gerber.ast.nodes.g_codes.G91 import G91 +from pygerber.gerber.ast.nodes.invalid import Invalid +from pygerber.gerber.ast.nodes.load.LM import LM +from pygerber.gerber.ast.nodes.load.LN import LN +from pygerber.gerber.ast.nodes.load.LP import LP +from pygerber.gerber.ast.nodes.load.LR import LR +from pygerber.gerber.ast.nodes.load.LS import LS +from pygerber.gerber.ast.nodes.m_codes.M00 import M00 +from pygerber.gerber.ast.nodes.m_codes.M01 import M01 +from pygerber.gerber.ast.nodes.m_codes.M02 import M02 +from pygerber.gerber.ast.nodes.math.assignment import Assignment +from pygerber.gerber.ast.nodes.math.constant import Constant +from pygerber.gerber.ast.nodes.math.expression import Expression +from pygerber.gerber.ast.nodes.math.operators.binary.add import Add +from pygerber.gerber.ast.nodes.math.operators.binary.div import Div +from pygerber.gerber.ast.nodes.math.operators.binary.mul import Mul +from pygerber.gerber.ast.nodes.math.operators.binary.sub import Sub +from pygerber.gerber.ast.nodes.math.operators.unary.neg import Neg +from pygerber.gerber.ast.nodes.math.operators.unary.pos import Pos +from pygerber.gerber.ast.nodes.math.parenthesis import Parenthesis +from pygerber.gerber.ast.nodes.math.point import Point +from pygerber.gerber.ast.nodes.math.variable import Variable +from pygerber.gerber.ast.nodes.other.coordinate import ( + Coordinate, + CoordinateI, + CoordinateJ, + CoordinateX, + CoordinateY, +) +from pygerber.gerber.ast.nodes.primitives.code_0 import Code0 +from pygerber.gerber.ast.nodes.primitives.code_1 import Code1 +from pygerber.gerber.ast.nodes.primitives.code_2 import Code2 +from pygerber.gerber.ast.nodes.primitives.code_4 import Code4 +from pygerber.gerber.ast.nodes.primitives.code_5 import Code5 +from pygerber.gerber.ast.nodes.primitives.code_6 import Code6 +from pygerber.gerber.ast.nodes.primitives.code_7 import Code7 +from pygerber.gerber.ast.nodes.primitives.code_20 import Code20 +from pygerber.gerber.ast.nodes.primitives.code_21 import Code21 +from pygerber.gerber.ast.nodes.primitives.code_22 import Code22 +from pygerber.gerber.ast.nodes.properties.AS import AS +from pygerber.gerber.ast.nodes.properties.FS import FS +from pygerber.gerber.ast.nodes.properties.IN import IN +from pygerber.gerber.ast.nodes.properties.IP import IP +from pygerber.gerber.ast.nodes.properties.IR import IR +from pygerber.gerber.ast.nodes.properties.MI import MI +from pygerber.gerber.ast.nodes.properties.MO import MO +from pygerber.gerber.ast.nodes.properties.OF import OF +from pygerber.gerber.ast.nodes.properties.SF import SF +from pygerber.gerber.ast.nodes.types import ( + ApertureIdStr, + Double, + Integer, + PackedCoordinateStr, +) + +__all__ = [ + "ABclose", + "ABopen", + "ADC", + "ADmacro", + "AD", + "ADO", + "ADP", + "ADR", + "AMclose", + "AMopen", + "SR", + "SRclose", + "SRopen", + "TA_AperFunction", + "TA_DrillTolerance", + "TA_FlashText", + "TA_UserName", + "TD", + "TF_MD5", + "TF_CreationDate", + "TF_FileFunction", + "TF_FilePolarity", + "TF_GenerationSoftware", + "TF_Part", + "TF_ProjectId", + "TF_SameCoordinates", + "TF_UserName", + "TO_C", + "TO_CMNP", + "TO_N", + "TO_P", + "TO_CFtp", + "TO_CHgt", + "TO_CLbD", + "TO_CLbN", + "TO_CMfr", + "TO_CMnt", + "TO_CPgD", + "TO_CPgN", + "TO_CRot", + "TO_CSup", + "TO_CVal", + "TO_UserName", + "D01", + "D02", + "D03", + "Dnn", + "File", + "G", + "G01", + "G02", + "G03", + "G04", + "G36", + "G37", + "G54", + "G55", + "G70", + "G71", + "G74", + "G75", + "G90", + "G91", + "LM", + "LN", + "LP", + "LR", + "LS", + "M00", + "M01", + "M02", + "Assignment", + "Constant", + "Add", + "Div", + "Mul", + "Sub", + "Neg", + "Pos", + "Point", + "Variable", + "CoordinateI", + "CoordinateJ", + "CoordinateX", + "CoordinateY", + "Code0", + "Code1", + "Code2", + "Code4", + "Code5", + "Code6", + "Code7", + "Code20", + "Code21", + "Code22", + "AS", + "FS", + "IN", + "IP", + "IR", + "MI", + "MO", + "OF", + "SF", + "Double", + "Integer", + "ApertureIdStr", + "PackedCoordinateStr", + "Node", + "TA", + "TO", + "TF", + "Coordinate", + "Expression", + "Parenthesis", + "AB", + "AM", + "SourceInfo", + "Invalid", +] diff --git a/src/pygerber/gerberx3/ast/nodes/aperture/AB.py b/src/pygerber/gerber/ast/nodes/aperture/AB.py similarity index 73% rename from src/pygerber/gerberx3/ast/nodes/aperture/AB.py rename to src/pygerber/gerber/ast/nodes/aperture/AB.py index 4fbd0bb3..646d414e 100644 --- a/src/pygerber/gerberx3/ast/nodes/aperture/AB.py +++ b/src/pygerber/gerber/ast/nodes/aperture/AB.py @@ -4,14 +4,14 @@ from typing import TYPE_CHECKING, Callable, List -from pygerber.gerberx3.ast.nodes.aperture.AB_close import ABclose -from pygerber.gerberx3.ast.nodes.aperture.AB_open import ABopen -from pygerber.gerberx3.ast.nodes.base import Node +from pygerber.gerber.ast.nodes.aperture.AB_close import ABclose +from pygerber.gerber.ast.nodes.aperture.AB_open import ABopen +from pygerber.gerber.ast.nodes.base import Node if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class AB(Node): diff --git a/src/pygerber/gerberx3/ast/nodes/aperture/AB_close.py b/src/pygerber/gerber/ast/nodes/aperture/AB_close.py similarity index 84% rename from src/pygerber/gerberx3/ast/nodes/aperture/AB_close.py rename to src/pygerber/gerber/ast/nodes/aperture/AB_close.py index 2112b069..a8202131 100644 --- a/src/pygerber/gerberx3/ast/nodes/aperture/AB_close.py +++ b/src/pygerber/gerber/ast/nodes/aperture/AB_close.py @@ -4,12 +4,12 @@ from typing import TYPE_CHECKING, Callable -from pygerber.gerberx3.ast.nodes.base import Node +from pygerber.gerber.ast.nodes.base import Node if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class ABclose(Node): diff --git a/src/pygerber/gerberx3/ast/nodes/aperture/AB_open.py b/src/pygerber/gerber/ast/nodes/aperture/AB_open.py similarity index 79% rename from src/pygerber/gerberx3/ast/nodes/aperture/AB_open.py rename to src/pygerber/gerber/ast/nodes/aperture/AB_open.py index e681e683..dec132b7 100644 --- a/src/pygerber/gerberx3/ast/nodes/aperture/AB_open.py +++ b/src/pygerber/gerber/ast/nodes/aperture/AB_open.py @@ -4,13 +4,13 @@ from typing import TYPE_CHECKING, Callable -from pygerber.gerberx3.ast.nodes.base import Node -from pygerber.gerberx3.ast.nodes.types import ApertureIdStr +from pygerber.gerber.ast.nodes.base import Node +from pygerber.gerber.ast.nodes.types import ApertureIdStr if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class ABopen(Node): diff --git a/src/pygerber/gerberx3/ast/nodes/aperture/AD.py b/src/pygerber/gerber/ast/nodes/aperture/AD.py similarity index 67% rename from src/pygerber/gerberx3/ast/nodes/aperture/AD.py rename to src/pygerber/gerber/ast/nodes/aperture/AD.py index 5c77e3fa..91670198 100644 --- a/src/pygerber/gerberx3/ast/nodes/aperture/AD.py +++ b/src/pygerber/gerber/ast/nodes/aperture/AD.py @@ -2,8 +2,8 @@ from __future__ import annotations -from pygerber.gerberx3.ast.nodes.base import Node -from pygerber.gerberx3.ast.nodes.types import ApertureIdStr +from pygerber.gerber.ast.nodes.base import Node +from pygerber.gerber.ast.nodes.types import ApertureIdStr class AD(Node): diff --git a/src/pygerber/gerberx3/ast/nodes/aperture/ADC.py b/src/pygerber/gerber/ast/nodes/aperture/ADC.py similarity index 80% rename from src/pygerber/gerberx3/ast/nodes/aperture/ADC.py rename to src/pygerber/gerber/ast/nodes/aperture/ADC.py index 21c1afd8..1719aa07 100644 --- a/src/pygerber/gerberx3/ast/nodes/aperture/ADC.py +++ b/src/pygerber/gerber/ast/nodes/aperture/ADC.py @@ -6,13 +6,13 @@ from pydantic import Field -from pygerber.gerberx3.ast.nodes.aperture.AD import AD -from pygerber.gerberx3.ast.nodes.types import Double +from pygerber.gerber.ast.nodes.aperture.AD import AD +from pygerber.gerber.ast.nodes.types import Double if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class ADC(AD): diff --git a/src/pygerber/gerberx3/ast/nodes/aperture/ADO.py b/src/pygerber/gerber/ast/nodes/aperture/ADO.py similarity index 81% rename from src/pygerber/gerberx3/ast/nodes/aperture/ADO.py rename to src/pygerber/gerber/ast/nodes/aperture/ADO.py index f1c74d18..fd472218 100644 --- a/src/pygerber/gerberx3/ast/nodes/aperture/ADO.py +++ b/src/pygerber/gerber/ast/nodes/aperture/ADO.py @@ -6,13 +6,13 @@ from pydantic import Field -from pygerber.gerberx3.ast.nodes.aperture.AD import AD -from pygerber.gerberx3.ast.nodes.types import Double +from pygerber.gerber.ast.nodes.aperture.AD import AD +from pygerber.gerber.ast.nodes.types import Double if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class ADO(AD): diff --git a/src/pygerber/gerberx3/ast/nodes/aperture/ADP.py b/src/pygerber/gerber/ast/nodes/aperture/ADP.py similarity index 81% rename from src/pygerber/gerberx3/ast/nodes/aperture/ADP.py rename to src/pygerber/gerber/ast/nodes/aperture/ADP.py index 317c016c..d71e58b3 100644 --- a/src/pygerber/gerberx3/ast/nodes/aperture/ADP.py +++ b/src/pygerber/gerber/ast/nodes/aperture/ADP.py @@ -6,13 +6,13 @@ from pydantic import Field -from pygerber.gerberx3.ast.nodes.aperture.AD import AD -from pygerber.gerberx3.ast.nodes.types import Double, Integer +from pygerber.gerber.ast.nodes.aperture.AD import AD +from pygerber.gerber.ast.nodes.types import Double, Integer if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class ADP(AD): diff --git a/src/pygerber/gerberx3/ast/nodes/aperture/ADR.py b/src/pygerber/gerber/ast/nodes/aperture/ADR.py similarity index 81% rename from src/pygerber/gerberx3/ast/nodes/aperture/ADR.py rename to src/pygerber/gerber/ast/nodes/aperture/ADR.py index 4f80c3f6..6ab58094 100644 --- a/src/pygerber/gerberx3/ast/nodes/aperture/ADR.py +++ b/src/pygerber/gerber/ast/nodes/aperture/ADR.py @@ -6,13 +6,13 @@ from pydantic import Field -from pygerber.gerberx3.ast.nodes.aperture.AD import AD -from pygerber.gerberx3.ast.nodes.types import Double +from pygerber.gerber.ast.nodes.aperture.AD import AD +from pygerber.gerber.ast.nodes.types import Double if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class ADR(AD): diff --git a/src/pygerber/gerberx3/ast/nodes/aperture/ADmacro.py b/src/pygerber/gerber/ast/nodes/aperture/ADmacro.py similarity index 81% rename from src/pygerber/gerberx3/ast/nodes/aperture/ADmacro.py rename to src/pygerber/gerber/ast/nodes/aperture/ADmacro.py index f4cf9272..eea97583 100644 --- a/src/pygerber/gerberx3/ast/nodes/aperture/ADmacro.py +++ b/src/pygerber/gerber/ast/nodes/aperture/ADmacro.py @@ -6,13 +6,13 @@ from pydantic import Field -from pygerber.gerberx3.ast.nodes.aperture.AD import AD -from pygerber.gerberx3.ast.nodes.types import Double +from pygerber.gerber.ast.nodes.aperture.AD import AD +from pygerber.gerber.ast.nodes.types import Double if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class ADmacro(AD): diff --git a/src/pygerber/gerberx3/ast/nodes/aperture/AM.py b/src/pygerber/gerber/ast/nodes/aperture/AM.py similarity index 73% rename from src/pygerber/gerberx3/ast/nodes/aperture/AM.py rename to src/pygerber/gerber/ast/nodes/aperture/AM.py index a5f3fa56..3f8eedd5 100644 --- a/src/pygerber/gerberx3/ast/nodes/aperture/AM.py +++ b/src/pygerber/gerber/ast/nodes/aperture/AM.py @@ -4,14 +4,14 @@ from typing import TYPE_CHECKING, Callable, List -from pygerber.gerberx3.ast.nodes.aperture.AM_close import AMclose -from pygerber.gerberx3.ast.nodes.aperture.AM_open import AMopen -from pygerber.gerberx3.ast.nodes.base import Node +from pygerber.gerber.ast.nodes.aperture.AM_close import AMclose +from pygerber.gerber.ast.nodes.aperture.AM_open import AMopen +from pygerber.gerber.ast.nodes.base import Node if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class AM(Node): diff --git a/src/pygerber/gerberx3/ast/nodes/aperture/AM_close.py b/src/pygerber/gerber/ast/nodes/aperture/AM_close.py similarity index 84% rename from src/pygerber/gerberx3/ast/nodes/aperture/AM_close.py rename to src/pygerber/gerber/ast/nodes/aperture/AM_close.py index bb4e6397..c7d8c081 100644 --- a/src/pygerber/gerberx3/ast/nodes/aperture/AM_close.py +++ b/src/pygerber/gerber/ast/nodes/aperture/AM_close.py @@ -4,12 +4,12 @@ from typing import TYPE_CHECKING, Callable -from pygerber.gerberx3.ast.nodes.base import Node +from pygerber.gerber.ast.nodes.base import Node if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class AMclose(Node): diff --git a/src/pygerber/gerberx3/ast/nodes/aperture/AM_open.py b/src/pygerber/gerber/ast/nodes/aperture/AM_open.py similarity index 85% rename from src/pygerber/gerberx3/ast/nodes/aperture/AM_open.py rename to src/pygerber/gerber/ast/nodes/aperture/AM_open.py index 716d9282..f9bf2b53 100644 --- a/src/pygerber/gerberx3/ast/nodes/aperture/AM_open.py +++ b/src/pygerber/gerber/ast/nodes/aperture/AM_open.py @@ -4,12 +4,12 @@ from typing import TYPE_CHECKING, Callable -from pygerber.gerberx3.ast.nodes.base import Node +from pygerber.gerber.ast.nodes.base import Node if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class AMopen(Node): diff --git a/src/pygerber/gerberx3/ast/nodes/aperture/SR.py b/src/pygerber/gerber/ast/nodes/aperture/SR.py similarity index 73% rename from src/pygerber/gerberx3/ast/nodes/aperture/SR.py rename to src/pygerber/gerber/ast/nodes/aperture/SR.py index bdd6a6a5..b19e15a8 100644 --- a/src/pygerber/gerberx3/ast/nodes/aperture/SR.py +++ b/src/pygerber/gerber/ast/nodes/aperture/SR.py @@ -4,14 +4,14 @@ from typing import TYPE_CHECKING, Callable, List -from pygerber.gerberx3.ast.nodes.aperture.SR_close import SRclose -from pygerber.gerberx3.ast.nodes.aperture.SR_open import SRopen -from pygerber.gerberx3.ast.nodes.base import Node +from pygerber.gerber.ast.nodes.aperture.SR_close import SRclose +from pygerber.gerber.ast.nodes.aperture.SR_open import SRopen +from pygerber.gerber.ast.nodes.base import Node if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class SR(Node): diff --git a/src/pygerber/gerberx3/ast/nodes/aperture/SR_close.py b/src/pygerber/gerber/ast/nodes/aperture/SR_close.py similarity index 84% rename from src/pygerber/gerberx3/ast/nodes/aperture/SR_close.py rename to src/pygerber/gerber/ast/nodes/aperture/SR_close.py index c10b071c..e42d1ea4 100644 --- a/src/pygerber/gerberx3/ast/nodes/aperture/SR_close.py +++ b/src/pygerber/gerber/ast/nodes/aperture/SR_close.py @@ -6,12 +6,12 @@ from typing import TYPE_CHECKING, Callable -from pygerber.gerberx3.ast.nodes.base import Node +from pygerber.gerber.ast.nodes.base import Node if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class SRclose(Node): diff --git a/src/pygerber/gerberx3/ast/nodes/aperture/SR_open.py b/src/pygerber/gerber/ast/nodes/aperture/SR_open.py similarity index 93% rename from src/pygerber/gerberx3/ast/nodes/aperture/SR_open.py rename to src/pygerber/gerber/ast/nodes/aperture/SR_open.py index 37af1ab4..85882fec 100644 --- a/src/pygerber/gerberx3/ast/nodes/aperture/SR_open.py +++ b/src/pygerber/gerber/ast/nodes/aperture/SR_open.py @@ -6,12 +6,12 @@ from pydantic import Field -from pygerber.gerberx3.ast.nodes.base import Node +from pygerber.gerber.ast.nodes.base import Node if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class SRopen(Node): diff --git a/src/pygerber/gerber/ast/nodes/aperture/__init__.py b/src/pygerber/gerber/ast/nodes/aperture/__init__.py new file mode 100644 index 00000000..10c27d19 --- /dev/null +++ b/src/pygerber/gerber/ast/nodes/aperture/__init__.py @@ -0,0 +1,3 @@ +"""`nodes.aperture` package contains all the aperture definition +related nodes. +""" diff --git a/src/pygerber/gerberx3/ast/nodes/attribute/TA.py b/src/pygerber/gerber/ast/nodes/attribute/TA.py similarity index 95% rename from src/pygerber/gerberx3/ast/nodes/attribute/TA.py rename to src/pygerber/gerber/ast/nodes/attribute/TA.py index 614ac2a2..68c01d51 100644 --- a/src/pygerber/gerberx3/ast/nodes/attribute/TA.py +++ b/src/pygerber/gerber/ast/nodes/attribute/TA.py @@ -6,13 +6,13 @@ from pydantic import Field -from pygerber.gerberx3.ast.nodes.base import Node -from pygerber.gerberx3.ast.nodes.enums import AperFunction +from pygerber.gerber.ast.nodes.base import Node +from pygerber.gerber.ast.nodes.enums import AperFunction if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class TA(Node): diff --git a/src/pygerber/gerberx3/ast/nodes/attribute/TD.py b/src/pygerber/gerber/ast/nodes/attribute/TD.py similarity index 85% rename from src/pygerber/gerberx3/ast/nodes/attribute/TD.py rename to src/pygerber/gerber/ast/nodes/attribute/TD.py index bf00932c..78270116 100644 --- a/src/pygerber/gerberx3/ast/nodes/attribute/TD.py +++ b/src/pygerber/gerber/ast/nodes/attribute/TD.py @@ -6,12 +6,12 @@ from pydantic import Field -from pygerber.gerberx3.ast.nodes.base import Node +from pygerber.gerber.ast.nodes.base import Node if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class TD(Node): diff --git a/src/pygerber/gerberx3/ast/nodes/attribute/TF.py b/src/pygerber/gerber/ast/nodes/attribute/TF.py similarity index 96% rename from src/pygerber/gerberx3/ast/nodes/attribute/TF.py rename to src/pygerber/gerber/ast/nodes/attribute/TF.py index 7ff94c68..58dfad68 100644 --- a/src/pygerber/gerberx3/ast/nodes/attribute/TF.py +++ b/src/pygerber/gerber/ast/nodes/attribute/TF.py @@ -8,14 +8,14 @@ from pydantic import Field -from pygerber.gerberx3.ast.errors import SourceNotAvailableError -from pygerber.gerberx3.ast.nodes.base import Node -from pygerber.gerberx3.ast.nodes.enums import FileFunction, Part +from pygerber.gerber.ast.errors import SourceNotAvailableError +from pygerber.gerber.ast.nodes.base import Node +from pygerber.gerber.ast.nodes.enums import FileFunction, Part if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class TF(Node): diff --git a/src/pygerber/gerberx3/ast/nodes/attribute/TO.py b/src/pygerber/gerber/ast/nodes/attribute/TO.py similarity index 97% rename from src/pygerber/gerberx3/ast/nodes/attribute/TO.py rename to src/pygerber/gerber/ast/nodes/attribute/TO.py index 4cadd769..c7b25fc9 100644 --- a/src/pygerber/gerberx3/ast/nodes/attribute/TO.py +++ b/src/pygerber/gerber/ast/nodes/attribute/TO.py @@ -6,14 +6,14 @@ from pydantic import Field -from pygerber.gerberx3.ast.nodes.base import Node -from pygerber.gerberx3.ast.nodes.enums import Mount -from pygerber.gerberx3.ast.nodes.types import Double +from pygerber.gerber.ast.nodes.base import Node +from pygerber.gerber.ast.nodes.enums import Mount +from pygerber.gerber.ast.nodes.types import Double if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class TO(Node): diff --git a/src/pygerber/gerber/ast/nodes/attribute/__init__.py b/src/pygerber/gerber/ast/nodes/attribute/__init__.py new file mode 100644 index 00000000..32959198 --- /dev/null +++ b/src/pygerber/gerber/ast/nodes/attribute/__init__.py @@ -0,0 +1,3 @@ +"""`nodes.attribute` package contains all the attribute related +nodes. +""" diff --git a/src/pygerber/gerberx3/ast/nodes/base.py b/src/pygerber/gerber/ast/nodes/base.py similarity index 91% rename from src/pygerber/gerberx3/ast/nodes/base.py rename to src/pygerber/gerber/ast/nodes/base.py index 5f3c1305..431ed032 100644 --- a/src/pygerber/gerberx3/ast/nodes/base.py +++ b/src/pygerber/gerber/ast/nodes/base.py @@ -1,4 +1,4 @@ -"""`pygerber.gerberx3.ast.nodes.base` contains definition of `node` class.""" +"""`nodes.base` contains definition of `node` class.""" from __future__ import annotations @@ -8,12 +8,12 @@ import pyparsing as pp from pydantic import Field -from pygerber.gerberx3.ast.nodes.model import ModelType +from pygerber.gerber.ast.nodes.model import ModelType if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class SourceInfo(ModelType): diff --git a/src/pygerber/gerberx3/ast/nodes/d_codes/D.py b/src/pygerber/gerber/ast/nodes/d_codes/D.py similarity index 94% rename from src/pygerber/gerberx3/ast/nodes/d_codes/D.py rename to src/pygerber/gerber/ast/nodes/d_codes/D.py index 753fed70..a5f6bb5b 100644 --- a/src/pygerber/gerberx3/ast/nodes/d_codes/D.py +++ b/src/pygerber/gerber/ast/nodes/d_codes/D.py @@ -4,7 +4,7 @@ from pydantic import Field -from pygerber.gerberx3.ast.nodes.base import Node +from pygerber.gerber.ast.nodes.base import Node class D(Node): diff --git a/src/pygerber/gerberx3/ast/nodes/d_codes/D01.py b/src/pygerber/gerber/ast/nodes/d_codes/D01.py similarity index 83% rename from src/pygerber/gerberx3/ast/nodes/d_codes/D01.py rename to src/pygerber/gerber/ast/nodes/d_codes/D01.py index c67d4ac0..d42d28e6 100644 --- a/src/pygerber/gerberx3/ast/nodes/d_codes/D01.py +++ b/src/pygerber/gerber/ast/nodes/d_codes/D01.py @@ -6,8 +6,8 @@ from pydantic import Field -from pygerber.gerberx3.ast.nodes.d_codes.D import D -from pygerber.gerberx3.ast.nodes.other.coordinate import ( +from pygerber.gerber.ast.nodes.d_codes.D import D +from pygerber.gerber.ast.nodes.other.coordinate import ( CoordinateI, CoordinateJ, CoordinateX, @@ -17,7 +17,7 @@ if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class D01(D): diff --git a/src/pygerber/gerberx3/ast/nodes/d_codes/D02.py b/src/pygerber/gerber/ast/nodes/d_codes/D02.py similarity index 81% rename from src/pygerber/gerberx3/ast/nodes/d_codes/D02.py rename to src/pygerber/gerber/ast/nodes/d_codes/D02.py index 218fcee5..26aa5571 100644 --- a/src/pygerber/gerberx3/ast/nodes/d_codes/D02.py +++ b/src/pygerber/gerber/ast/nodes/d_codes/D02.py @@ -6,8 +6,8 @@ from pydantic import Field -from pygerber.gerberx3.ast.nodes.d_codes.D import D -from pygerber.gerberx3.ast.nodes.other.coordinate import ( +from pygerber.gerber.ast.nodes.d_codes.D import D +from pygerber.gerber.ast.nodes.other.coordinate import ( CoordinateX, CoordinateY, ) @@ -15,7 +15,7 @@ if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class D02(D): diff --git a/src/pygerber/gerberx3/ast/nodes/d_codes/D03.py b/src/pygerber/gerber/ast/nodes/d_codes/D03.py similarity index 81% rename from src/pygerber/gerberx3/ast/nodes/d_codes/D03.py rename to src/pygerber/gerber/ast/nodes/d_codes/D03.py index 7487406c..b2043b7b 100644 --- a/src/pygerber/gerberx3/ast/nodes/d_codes/D03.py +++ b/src/pygerber/gerber/ast/nodes/d_codes/D03.py @@ -6,8 +6,8 @@ from pydantic import Field -from pygerber.gerberx3.ast.nodes.d_codes.D import D -from pygerber.gerberx3.ast.nodes.other.coordinate import ( +from pygerber.gerber.ast.nodes.d_codes.D import D +from pygerber.gerber.ast.nodes.other.coordinate import ( CoordinateX, CoordinateY, ) @@ -15,7 +15,7 @@ if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class D03(D): diff --git a/src/pygerber/gerberx3/ast/nodes/d_codes/Dnn.py b/src/pygerber/gerber/ast/nodes/d_codes/Dnn.py similarity index 78% rename from src/pygerber/gerberx3/ast/nodes/d_codes/Dnn.py rename to src/pygerber/gerber/ast/nodes/d_codes/Dnn.py index e2e71dc9..6f21ca8e 100644 --- a/src/pygerber/gerberx3/ast/nodes/d_codes/Dnn.py +++ b/src/pygerber/gerber/ast/nodes/d_codes/Dnn.py @@ -4,13 +4,13 @@ from typing import TYPE_CHECKING, Callable -from pygerber.gerberx3.ast.nodes.d_codes.D import D -from pygerber.gerberx3.ast.nodes.types import ApertureIdStr +from pygerber.gerber.ast.nodes.d_codes.D import D +from pygerber.gerber.ast.nodes.types import ApertureIdStr if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class Dnn(D): diff --git a/src/pygerber/gerber/ast/nodes/d_codes/__init__.py b/src/pygerber/gerber/ast/nodes/d_codes/__init__.py new file mode 100644 index 00000000..02fb5e72 --- /dev/null +++ b/src/pygerber/gerber/ast/nodes/d_codes/__init__.py @@ -0,0 +1 @@ +"""`nodes.d_codes` package contains all the D-code nodes.""" diff --git a/src/pygerber/gerberx3/ast/nodes/enums.py b/src/pygerber/gerber/ast/nodes/enums.py similarity index 97% rename from src/pygerber/gerberx3/ast/nodes/enums.py rename to src/pygerber/gerber/ast/nodes/enums.py index fca556c5..e798ebda 100644 --- a/src/pygerber/gerberx3/ast/nodes/enums.py +++ b/src/pygerber/gerber/ast/nodes/enums.py @@ -1,6 +1,4 @@ -"""`pygerber.gerberx3.ast.nodes.enums` module contains definition of enums used in -GerberX3 AST nodes. -""" +"""The `enums` module contains definition of enums used in Gerber X3 AST nodes.""" from __future__ import annotations diff --git a/src/pygerber/gerberx3/ast/nodes/file.py b/src/pygerber/gerber/ast/nodes/file.py similarity index 76% rename from src/pygerber/gerberx3/ast/nodes/file.py rename to src/pygerber/gerber/ast/nodes/file.py index 7ccdd227..552ef78b 100644 --- a/src/pygerber/gerberx3/ast/nodes/file.py +++ b/src/pygerber/gerber/ast/nodes/file.py @@ -1,15 +1,15 @@ -"""`pygerber.gerberx3.ast.nodes.file` module contains definition of `File` class.""" +"""`nodes.file` module contains definition of `File` class.""" from __future__ import annotations from typing import TYPE_CHECKING, Callable, List -from pygerber.gerberx3.ast.nodes.base import Node +from pygerber.gerber.ast.nodes.base import Node if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class File(Node): diff --git a/src/pygerber/gerberx3/ast/nodes/g_codes/G.py b/src/pygerber/gerber/ast/nodes/g_codes/G.py similarity index 94% rename from src/pygerber/gerberx3/ast/nodes/g_codes/G.py rename to src/pygerber/gerber/ast/nodes/g_codes/G.py index 2c802fa7..86cb3841 100644 --- a/src/pygerber/gerberx3/ast/nodes/g_codes/G.py +++ b/src/pygerber/gerber/ast/nodes/g_codes/G.py @@ -4,7 +4,7 @@ from pydantic import Field -from pygerber.gerberx3.ast.nodes.base import Node +from pygerber.gerber.ast.nodes.base import Node class G(Node): diff --git a/src/pygerber/gerberx3/ast/nodes/g_codes/G01.py b/src/pygerber/gerber/ast/nodes/g_codes/G01.py similarity index 83% rename from src/pygerber/gerberx3/ast/nodes/g_codes/G01.py rename to src/pygerber/gerber/ast/nodes/g_codes/G01.py index 1f729c0f..fe2a4770 100644 --- a/src/pygerber/gerberx3/ast/nodes/g_codes/G01.py +++ b/src/pygerber/gerber/ast/nodes/g_codes/G01.py @@ -4,12 +4,12 @@ from typing import TYPE_CHECKING, Callable -from pygerber.gerberx3.ast.nodes.g_codes.G import G +from pygerber.gerber.ast.nodes.g_codes.G import G if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class G01(G): diff --git a/src/pygerber/gerberx3/ast/nodes/g_codes/G02.py b/src/pygerber/gerber/ast/nodes/g_codes/G02.py similarity index 83% rename from src/pygerber/gerberx3/ast/nodes/g_codes/G02.py rename to src/pygerber/gerber/ast/nodes/g_codes/G02.py index c2216b90..702743da 100644 --- a/src/pygerber/gerberx3/ast/nodes/g_codes/G02.py +++ b/src/pygerber/gerber/ast/nodes/g_codes/G02.py @@ -4,12 +4,12 @@ from typing import TYPE_CHECKING, Callable -from pygerber.gerberx3.ast.nodes.g_codes.G import G +from pygerber.gerber.ast.nodes.g_codes.G import G if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class G02(G): diff --git a/src/pygerber/gerberx3/ast/nodes/g_codes/G03.py b/src/pygerber/gerber/ast/nodes/g_codes/G03.py similarity index 83% rename from src/pygerber/gerberx3/ast/nodes/g_codes/G03.py rename to src/pygerber/gerber/ast/nodes/g_codes/G03.py index f8a8e30c..55963eb9 100644 --- a/src/pygerber/gerberx3/ast/nodes/g_codes/G03.py +++ b/src/pygerber/gerber/ast/nodes/g_codes/G03.py @@ -4,12 +4,12 @@ from typing import TYPE_CHECKING, Callable -from pygerber.gerberx3.ast.nodes.g_codes.G import G +from pygerber.gerber.ast.nodes.g_codes.G import G if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class G03(G): diff --git a/src/pygerber/gerberx3/ast/nodes/g_codes/G04.py b/src/pygerber/gerber/ast/nodes/g_codes/G04.py similarity index 85% rename from src/pygerber/gerberx3/ast/nodes/g_codes/G04.py rename to src/pygerber/gerber/ast/nodes/g_codes/G04.py index 87f752b3..83e39973 100644 --- a/src/pygerber/gerberx3/ast/nodes/g_codes/G04.py +++ b/src/pygerber/gerber/ast/nodes/g_codes/G04.py @@ -6,12 +6,12 @@ from pydantic import Field -from pygerber.gerberx3.ast.nodes.g_codes.G import G +from pygerber.gerber.ast.nodes.g_codes.G import G if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class G04(G): diff --git a/src/pygerber/gerberx3/ast/nodes/g_codes/G36.py b/src/pygerber/gerber/ast/nodes/g_codes/G36.py similarity index 83% rename from src/pygerber/gerberx3/ast/nodes/g_codes/G36.py rename to src/pygerber/gerber/ast/nodes/g_codes/G36.py index d0396a0e..5252de3e 100644 --- a/src/pygerber/gerberx3/ast/nodes/g_codes/G36.py +++ b/src/pygerber/gerber/ast/nodes/g_codes/G36.py @@ -4,12 +4,12 @@ from typing import TYPE_CHECKING, Callable -from pygerber.gerberx3.ast.nodes.g_codes.G import G +from pygerber.gerber.ast.nodes.g_codes.G import G if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class G36(G): diff --git a/src/pygerber/gerberx3/ast/nodes/g_codes/G37.py b/src/pygerber/gerber/ast/nodes/g_codes/G37.py similarity index 83% rename from src/pygerber/gerberx3/ast/nodes/g_codes/G37.py rename to src/pygerber/gerber/ast/nodes/g_codes/G37.py index bcbacc49..e2c037ab 100644 --- a/src/pygerber/gerberx3/ast/nodes/g_codes/G37.py +++ b/src/pygerber/gerber/ast/nodes/g_codes/G37.py @@ -4,12 +4,12 @@ from typing import TYPE_CHECKING, Callable -from pygerber.gerberx3.ast.nodes.g_codes.G import G +from pygerber.gerber.ast.nodes.g_codes.G import G if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class G37(G): diff --git a/src/pygerber/gerberx3/ast/nodes/g_codes/G54.py b/src/pygerber/gerber/ast/nodes/g_codes/G54.py similarity index 83% rename from src/pygerber/gerberx3/ast/nodes/g_codes/G54.py rename to src/pygerber/gerber/ast/nodes/g_codes/G54.py index f76c7268..8912ac82 100644 --- a/src/pygerber/gerberx3/ast/nodes/g_codes/G54.py +++ b/src/pygerber/gerber/ast/nodes/g_codes/G54.py @@ -4,12 +4,12 @@ from typing import TYPE_CHECKING, Callable -from pygerber.gerberx3.ast.nodes.g_codes.G import G +from pygerber.gerber.ast.nodes.g_codes.G import G if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class G54(G): diff --git a/src/pygerber/gerberx3/ast/nodes/g_codes/G55.py b/src/pygerber/gerber/ast/nodes/g_codes/G55.py similarity index 83% rename from src/pygerber/gerberx3/ast/nodes/g_codes/G55.py rename to src/pygerber/gerber/ast/nodes/g_codes/G55.py index b5d7a219..b11a1886 100644 --- a/src/pygerber/gerberx3/ast/nodes/g_codes/G55.py +++ b/src/pygerber/gerber/ast/nodes/g_codes/G55.py @@ -4,12 +4,12 @@ from typing import TYPE_CHECKING, Callable -from pygerber.gerberx3.ast.nodes.g_codes.G import G +from pygerber.gerber.ast.nodes.g_codes.G import G if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class G55(G): diff --git a/src/pygerber/gerberx3/ast/nodes/g_codes/G70.py b/src/pygerber/gerber/ast/nodes/g_codes/G70.py similarity index 83% rename from src/pygerber/gerberx3/ast/nodes/g_codes/G70.py rename to src/pygerber/gerber/ast/nodes/g_codes/G70.py index 3bea1908..eae70672 100644 --- a/src/pygerber/gerberx3/ast/nodes/g_codes/G70.py +++ b/src/pygerber/gerber/ast/nodes/g_codes/G70.py @@ -4,12 +4,12 @@ from typing import TYPE_CHECKING, Callable -from pygerber.gerberx3.ast.nodes.g_codes.G import G +from pygerber.gerber.ast.nodes.g_codes.G import G if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class G70(G): diff --git a/src/pygerber/gerberx3/ast/nodes/g_codes/G71.py b/src/pygerber/gerber/ast/nodes/g_codes/G71.py similarity index 83% rename from src/pygerber/gerberx3/ast/nodes/g_codes/G71.py rename to src/pygerber/gerber/ast/nodes/g_codes/G71.py index a04e1333..aa7ea8c8 100644 --- a/src/pygerber/gerberx3/ast/nodes/g_codes/G71.py +++ b/src/pygerber/gerber/ast/nodes/g_codes/G71.py @@ -4,12 +4,12 @@ from typing import TYPE_CHECKING, Callable -from pygerber.gerberx3.ast.nodes.g_codes.G import G +from pygerber.gerber.ast.nodes.g_codes.G import G if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class G71(G): diff --git a/src/pygerber/gerberx3/ast/nodes/g_codes/G74.py b/src/pygerber/gerber/ast/nodes/g_codes/G74.py similarity index 83% rename from src/pygerber/gerberx3/ast/nodes/g_codes/G74.py rename to src/pygerber/gerber/ast/nodes/g_codes/G74.py index b2fd3340..aaf80d0d 100644 --- a/src/pygerber/gerberx3/ast/nodes/g_codes/G74.py +++ b/src/pygerber/gerber/ast/nodes/g_codes/G74.py @@ -4,12 +4,12 @@ from typing import TYPE_CHECKING, Callable -from pygerber.gerberx3.ast.nodes.g_codes.G import G +from pygerber.gerber.ast.nodes.g_codes.G import G if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class G74(G): diff --git a/src/pygerber/gerberx3/ast/nodes/g_codes/G75.py b/src/pygerber/gerber/ast/nodes/g_codes/G75.py similarity index 83% rename from src/pygerber/gerberx3/ast/nodes/g_codes/G75.py rename to src/pygerber/gerber/ast/nodes/g_codes/G75.py index d6f2c4ed..e1e0d40c 100644 --- a/src/pygerber/gerberx3/ast/nodes/g_codes/G75.py +++ b/src/pygerber/gerber/ast/nodes/g_codes/G75.py @@ -4,12 +4,12 @@ from typing import TYPE_CHECKING, Callable -from pygerber.gerberx3.ast.nodes.g_codes.G import G +from pygerber.gerber.ast.nodes.g_codes.G import G if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class G75(G): diff --git a/src/pygerber/gerberx3/ast/nodes/g_codes/G90.py b/src/pygerber/gerber/ast/nodes/g_codes/G90.py similarity index 83% rename from src/pygerber/gerberx3/ast/nodes/g_codes/G90.py rename to src/pygerber/gerber/ast/nodes/g_codes/G90.py index 0d174273..696cff51 100644 --- a/src/pygerber/gerberx3/ast/nodes/g_codes/G90.py +++ b/src/pygerber/gerber/ast/nodes/g_codes/G90.py @@ -4,12 +4,12 @@ from typing import TYPE_CHECKING, Callable -from pygerber.gerberx3.ast.nodes.g_codes.G import G +from pygerber.gerber.ast.nodes.g_codes.G import G if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class G90(G): diff --git a/src/pygerber/gerberx3/ast/nodes/g_codes/G91.py b/src/pygerber/gerber/ast/nodes/g_codes/G91.py similarity index 83% rename from src/pygerber/gerberx3/ast/nodes/g_codes/G91.py rename to src/pygerber/gerber/ast/nodes/g_codes/G91.py index 17732c4d..8475c7e5 100644 --- a/src/pygerber/gerberx3/ast/nodes/g_codes/G91.py +++ b/src/pygerber/gerber/ast/nodes/g_codes/G91.py @@ -4,12 +4,12 @@ from typing import TYPE_CHECKING, Callable -from pygerber.gerberx3.ast.nodes.g_codes.G import G +from pygerber.gerber.ast.nodes.g_codes.G import G if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class G91(G): diff --git a/src/pygerber/gerber/ast/nodes/g_codes/__init__.py b/src/pygerber/gerber/ast/nodes/g_codes/__init__.py new file mode 100644 index 00000000..b9fe2132 --- /dev/null +++ b/src/pygerber/gerber/ast/nodes/g_codes/__init__.py @@ -0,0 +1 @@ +"""`nodes.g_codes` package contains all the G-code nodes.""" diff --git a/src/pygerber/gerberx3/ast/nodes/invalid.py b/src/pygerber/gerber/ast/nodes/invalid.py similarity index 84% rename from src/pygerber/gerberx3/ast/nodes/invalid.py rename to src/pygerber/gerber/ast/nodes/invalid.py index 24424860..0e433e39 100644 --- a/src/pygerber/gerberx3/ast/nodes/invalid.py +++ b/src/pygerber/gerber/ast/nodes/invalid.py @@ -4,12 +4,12 @@ from typing import TYPE_CHECKING, Callable -from pygerber.gerberx3.ast.nodes.base import Node +from pygerber.gerber.ast.nodes.base import Node if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class Invalid(Node): diff --git a/src/pygerber/gerberx3/ast/nodes/load/LM.py b/src/pygerber/gerber/ast/nodes/load/LM.py similarity index 78% rename from src/pygerber/gerberx3/ast/nodes/load/LM.py rename to src/pygerber/gerber/ast/nodes/load/LM.py index 95550be7..848e72ad 100644 --- a/src/pygerber/gerberx3/ast/nodes/load/LM.py +++ b/src/pygerber/gerber/ast/nodes/load/LM.py @@ -4,13 +4,13 @@ from typing import TYPE_CHECKING, Callable -from pygerber.gerberx3.ast.nodes.base import Node -from pygerber.gerberx3.ast.nodes.enums import Mirroring +from pygerber.gerber.ast.nodes.base import Node +from pygerber.gerber.ast.nodes.enums import Mirroring if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class LM(Node): diff --git a/src/pygerber/gerberx3/ast/nodes/load/LN.py b/src/pygerber/gerber/ast/nodes/load/LN.py similarity index 84% rename from src/pygerber/gerberx3/ast/nodes/load/LN.py rename to src/pygerber/gerber/ast/nodes/load/LN.py index ffa642ab..1ec7cae7 100644 --- a/src/pygerber/gerberx3/ast/nodes/load/LN.py +++ b/src/pygerber/gerber/ast/nodes/load/LN.py @@ -4,12 +4,12 @@ from typing import TYPE_CHECKING, Callable -from pygerber.gerberx3.ast.nodes.base import Node +from pygerber.gerber.ast.nodes.base import Node if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class LN(Node): diff --git a/src/pygerber/gerberx3/ast/nodes/load/LP.py b/src/pygerber/gerber/ast/nodes/load/LP.py similarity index 78% rename from src/pygerber/gerberx3/ast/nodes/load/LP.py rename to src/pygerber/gerber/ast/nodes/load/LP.py index e581a23b..dd2eca40 100644 --- a/src/pygerber/gerberx3/ast/nodes/load/LP.py +++ b/src/pygerber/gerber/ast/nodes/load/LP.py @@ -4,13 +4,13 @@ from typing import TYPE_CHECKING, Callable -from pygerber.gerberx3.ast.nodes.base import Node -from pygerber.gerberx3.ast.nodes.enums import Polarity +from pygerber.gerber.ast.nodes.base import Node +from pygerber.gerber.ast.nodes.enums import Polarity if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class LP(Node): diff --git a/src/pygerber/gerberx3/ast/nodes/load/LR.py b/src/pygerber/gerber/ast/nodes/load/LR.py similarity index 78% rename from src/pygerber/gerberx3/ast/nodes/load/LR.py rename to src/pygerber/gerber/ast/nodes/load/LR.py index 9ad2dd90..f65736de 100644 --- a/src/pygerber/gerberx3/ast/nodes/load/LR.py +++ b/src/pygerber/gerber/ast/nodes/load/LR.py @@ -4,13 +4,13 @@ from typing import TYPE_CHECKING, Callable -from pygerber.gerberx3.ast.nodes.base import Node -from pygerber.gerberx3.ast.nodes.types import Double +from pygerber.gerber.ast.nodes.base import Node +from pygerber.gerber.ast.nodes.types import Double if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class LR(Node): diff --git a/src/pygerber/gerberx3/ast/nodes/load/LS.py b/src/pygerber/gerber/ast/nodes/load/LS.py similarity index 78% rename from src/pygerber/gerberx3/ast/nodes/load/LS.py rename to src/pygerber/gerber/ast/nodes/load/LS.py index ba601fd6..2b36b1cd 100644 --- a/src/pygerber/gerberx3/ast/nodes/load/LS.py +++ b/src/pygerber/gerber/ast/nodes/load/LS.py @@ -4,13 +4,13 @@ from typing import TYPE_CHECKING, Callable -from pygerber.gerberx3.ast.nodes.base import Node -from pygerber.gerberx3.ast.nodes.types import Double +from pygerber.gerber.ast.nodes.base import Node +from pygerber.gerber.ast.nodes.types import Double if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class LS(Node): diff --git a/src/pygerber/gerber/ast/nodes/load/__init__.py b/src/pygerber/gerber/ast/nodes/load/__init__.py new file mode 100644 index 00000000..d2572c07 --- /dev/null +++ b/src/pygerber/gerber/ast/nodes/load/__init__.py @@ -0,0 +1 @@ +"""`nodes.load` package contains all the load_ nodes.""" diff --git a/src/pygerber/gerberx3/ast/nodes/m_codes/M00.py b/src/pygerber/gerber/ast/nodes/m_codes/M00.py similarity index 84% rename from src/pygerber/gerberx3/ast/nodes/m_codes/M00.py rename to src/pygerber/gerber/ast/nodes/m_codes/M00.py index fb9821fd..d83076d8 100644 --- a/src/pygerber/gerberx3/ast/nodes/m_codes/M00.py +++ b/src/pygerber/gerber/ast/nodes/m_codes/M00.py @@ -4,12 +4,12 @@ from typing import TYPE_CHECKING, Callable -from pygerber.gerberx3.ast.nodes.base import Node +from pygerber.gerber.ast.nodes.base import Node if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class M00(Node): diff --git a/src/pygerber/gerberx3/ast/nodes/m_codes/M01.py b/src/pygerber/gerber/ast/nodes/m_codes/M01.py similarity index 84% rename from src/pygerber/gerberx3/ast/nodes/m_codes/M01.py rename to src/pygerber/gerber/ast/nodes/m_codes/M01.py index 8e93964d..c91037d6 100644 --- a/src/pygerber/gerberx3/ast/nodes/m_codes/M01.py +++ b/src/pygerber/gerber/ast/nodes/m_codes/M01.py @@ -4,12 +4,12 @@ from typing import TYPE_CHECKING, Callable -from pygerber.gerberx3.ast.nodes.base import Node +from pygerber.gerber.ast.nodes.base import Node if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class M01(Node): diff --git a/src/pygerber/gerberx3/ast/nodes/m_codes/M02.py b/src/pygerber/gerber/ast/nodes/m_codes/M02.py similarity index 84% rename from src/pygerber/gerberx3/ast/nodes/m_codes/M02.py rename to src/pygerber/gerber/ast/nodes/m_codes/M02.py index 0eda5819..85f5f219 100644 --- a/src/pygerber/gerberx3/ast/nodes/m_codes/M02.py +++ b/src/pygerber/gerber/ast/nodes/m_codes/M02.py @@ -4,12 +4,12 @@ from typing import TYPE_CHECKING, Callable -from pygerber.gerberx3.ast.nodes.base import Node +from pygerber.gerber.ast.nodes.base import Node if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class M02(Node): diff --git a/src/pygerber/gerber/ast/nodes/m_codes/__init__.py b/src/pygerber/gerber/ast/nodes/m_codes/__init__.py new file mode 100644 index 00000000..5703ec7a --- /dev/null +++ b/src/pygerber/gerber/ast/nodes/m_codes/__init__.py @@ -0,0 +1 @@ +"""`nodes.m_codes` package contains all the M-code nodes.""" diff --git a/src/pygerber/gerber/ast/nodes/math/__init__.py b/src/pygerber/gerber/ast/nodes/math/__init__.py new file mode 100644 index 00000000..31fdb4a9 --- /dev/null +++ b/src/pygerber/gerber/ast/nodes/math/__init__.py @@ -0,0 +1,3 @@ +"""`nodes.math` package contains all the macro math expression +nodes. +""" diff --git a/src/pygerber/gerberx3/ast/nodes/math/assignment.py b/src/pygerber/gerber/ast/nodes/math/assignment.py similarity index 74% rename from src/pygerber/gerberx3/ast/nodes/math/assignment.py rename to src/pygerber/gerber/ast/nodes/math/assignment.py index 604bad64..eb617ec0 100644 --- a/src/pygerber/gerberx3/ast/nodes/math/assignment.py +++ b/src/pygerber/gerber/ast/nodes/math/assignment.py @@ -4,14 +4,14 @@ from typing import TYPE_CHECKING, Callable -from pygerber.gerberx3.ast.nodes.base import Node -from pygerber.gerberx3.ast.nodes.math.expression import Expression -from pygerber.gerberx3.ast.nodes.math.variable import Variable +from pygerber.gerber.ast.nodes.base import Node +from pygerber.gerber.ast.nodes.math.expression import Expression +from pygerber.gerber.ast.nodes.math.variable import Variable if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class Assignment(Node): diff --git a/src/pygerber/gerberx3/ast/nodes/math/constant.py b/src/pygerber/gerber/ast/nodes/math/constant.py similarity index 83% rename from src/pygerber/gerberx3/ast/nodes/math/constant.py rename to src/pygerber/gerber/ast/nodes/math/constant.py index 2762f82d..62a52eca 100644 --- a/src/pygerber/gerberx3/ast/nodes/math/constant.py +++ b/src/pygerber/gerber/ast/nodes/math/constant.py @@ -4,13 +4,13 @@ from typing import TYPE_CHECKING, Callable -from pygerber.gerberx3.ast.nodes.math.expression import Expression -from pygerber.gerberx3.ast.nodes.types import Double +from pygerber.gerber.ast.nodes.math.expression import Expression +from pygerber.gerber.ast.nodes.types import Double if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class Constant(Expression): diff --git a/src/pygerber/gerberx3/ast/nodes/math/expression.py b/src/pygerber/gerber/ast/nodes/math/expression.py similarity index 80% rename from src/pygerber/gerberx3/ast/nodes/math/expression.py rename to src/pygerber/gerber/ast/nodes/math/expression.py index d9339d7a..0af1b54c 100644 --- a/src/pygerber/gerberx3/ast/nodes/math/expression.py +++ b/src/pygerber/gerber/ast/nodes/math/expression.py @@ -4,7 +4,7 @@ from __future__ import annotations -from pygerber.gerberx3.ast.nodes.base import Node +from pygerber.gerber.ast.nodes.base import Node class Expression(Node): diff --git a/src/pygerber/gerber/ast/nodes/math/operators/__init__.py b/src/pygerber/gerber/ast/nodes/math/operators/__init__.py new file mode 100644 index 00000000..f1c7f910 --- /dev/null +++ b/src/pygerber/gerber/ast/nodes/math/operators/__init__.py @@ -0,0 +1,3 @@ +"""`nodes.math.operators` package contains all the macro math +expression operators. +""" diff --git a/src/pygerber/gerber/ast/nodes/math/operators/binary/__init__.py b/src/pygerber/gerber/ast/nodes/math/operators/binary/__init__.py new file mode 100644 index 00000000..b11f7002 --- /dev/null +++ b/src/pygerber/gerber/ast/nodes/math/operators/binary/__init__.py @@ -0,0 +1,3 @@ +"""`nodes.math.operators.binary` package contains all the macro +math expression binary operators. +""" diff --git a/src/pygerber/gerberx3/ast/nodes/math/operators/binary/add.py b/src/pygerber/gerber/ast/nodes/math/operators/binary/add.py similarity index 85% rename from src/pygerber/gerberx3/ast/nodes/math/operators/binary/add.py rename to src/pygerber/gerber/ast/nodes/math/operators/binary/add.py index 90645ce2..b48e33b4 100644 --- a/src/pygerber/gerberx3/ast/nodes/math/operators/binary/add.py +++ b/src/pygerber/gerber/ast/nodes/math/operators/binary/add.py @@ -8,12 +8,12 @@ from pydantic import Field -from pygerber.gerberx3.ast.nodes.math.expression import Expression +from pygerber.gerber.ast.nodes.math.expression import Expression if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class Add(Expression): diff --git a/src/pygerber/gerberx3/ast/nodes/math/operators/binary/div.py b/src/pygerber/gerber/ast/nodes/math/operators/binary/div.py similarity index 85% rename from src/pygerber/gerberx3/ast/nodes/math/operators/binary/div.py rename to src/pygerber/gerber/ast/nodes/math/operators/binary/div.py index aed413d3..7c1a11a0 100644 --- a/src/pygerber/gerberx3/ast/nodes/math/operators/binary/div.py +++ b/src/pygerber/gerber/ast/nodes/math/operators/binary/div.py @@ -8,12 +8,12 @@ from pydantic import Field -from pygerber.gerberx3.ast.nodes.math.expression import Expression +from pygerber.gerber.ast.nodes.math.expression import Expression if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class Div(Expression): diff --git a/src/pygerber/gerberx3/ast/nodes/math/operators/binary/mul.py b/src/pygerber/gerber/ast/nodes/math/operators/binary/mul.py similarity index 85% rename from src/pygerber/gerberx3/ast/nodes/math/operators/binary/mul.py rename to src/pygerber/gerber/ast/nodes/math/operators/binary/mul.py index 92f859ca..5af40ecb 100644 --- a/src/pygerber/gerberx3/ast/nodes/math/operators/binary/mul.py +++ b/src/pygerber/gerber/ast/nodes/math/operators/binary/mul.py @@ -8,12 +8,12 @@ from pydantic import Field -from pygerber.gerberx3.ast.nodes.math.expression import Expression +from pygerber.gerber.ast.nodes.math.expression import Expression if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class Mul(Expression): diff --git a/src/pygerber/gerberx3/ast/nodes/math/operators/binary/sub.py b/src/pygerber/gerber/ast/nodes/math/operators/binary/sub.py similarity index 85% rename from src/pygerber/gerberx3/ast/nodes/math/operators/binary/sub.py rename to src/pygerber/gerber/ast/nodes/math/operators/binary/sub.py index 85666e98..dc007ba3 100644 --- a/src/pygerber/gerberx3/ast/nodes/math/operators/binary/sub.py +++ b/src/pygerber/gerber/ast/nodes/math/operators/binary/sub.py @@ -8,12 +8,12 @@ from pydantic import Field -from pygerber.gerberx3.ast.nodes.math.expression import Expression +from pygerber.gerber.ast.nodes.math.expression import Expression if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class Sub(Expression): diff --git a/src/pygerber/gerber/ast/nodes/math/operators/unary/__init__.py b/src/pygerber/gerber/ast/nodes/math/operators/unary/__init__.py new file mode 100644 index 00000000..a9bc6e9c --- /dev/null +++ b/src/pygerber/gerber/ast/nodes/math/operators/unary/__init__.py @@ -0,0 +1,3 @@ +"""`nodes.math.operators.unary` package contains all the macro +math expression unary operators. +""" diff --git a/src/pygerber/gerberx3/ast/nodes/math/operators/unary/neg.py b/src/pygerber/gerber/ast/nodes/math/operators/unary/neg.py similarity index 83% rename from src/pygerber/gerberx3/ast/nodes/math/operators/unary/neg.py rename to src/pygerber/gerber/ast/nodes/math/operators/unary/neg.py index 223c0aa8..ccc1f66a 100644 --- a/src/pygerber/gerberx3/ast/nodes/math/operators/unary/neg.py +++ b/src/pygerber/gerber/ast/nodes/math/operators/unary/neg.py @@ -6,12 +6,12 @@ from typing import TYPE_CHECKING, Callable -from pygerber.gerberx3.ast.nodes.math.expression import Expression +from pygerber.gerber.ast.nodes.math.expression import Expression if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class Neg(Expression): diff --git a/src/pygerber/gerberx3/ast/nodes/math/operators/unary/pos.py b/src/pygerber/gerber/ast/nodes/math/operators/unary/pos.py similarity index 83% rename from src/pygerber/gerberx3/ast/nodes/math/operators/unary/pos.py rename to src/pygerber/gerber/ast/nodes/math/operators/unary/pos.py index 07b6c57f..06c101f6 100644 --- a/src/pygerber/gerberx3/ast/nodes/math/operators/unary/pos.py +++ b/src/pygerber/gerber/ast/nodes/math/operators/unary/pos.py @@ -6,12 +6,12 @@ from typing import TYPE_CHECKING, Callable -from pygerber.gerberx3.ast.nodes.math.expression import Expression +from pygerber.gerber.ast.nodes.math.expression import Expression if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class Pos(Expression): diff --git a/src/pygerber/gerberx3/ast/nodes/math/parenthesis.py b/src/pygerber/gerber/ast/nodes/math/parenthesis.py similarity index 84% rename from src/pygerber/gerberx3/ast/nodes/math/parenthesis.py rename to src/pygerber/gerber/ast/nodes/math/parenthesis.py index 8765760a..1868b004 100644 --- a/src/pygerber/gerberx3/ast/nodes/math/parenthesis.py +++ b/src/pygerber/gerber/ast/nodes/math/parenthesis.py @@ -6,12 +6,12 @@ from typing import TYPE_CHECKING, Callable -from pygerber.gerberx3.ast.nodes.math.expression import Expression +from pygerber.gerber.ast.nodes.math.expression import Expression if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class Parenthesis(Expression): diff --git a/src/pygerber/gerberx3/ast/nodes/math/point.py b/src/pygerber/gerber/ast/nodes/math/point.py similarity index 78% rename from src/pygerber/gerberx3/ast/nodes/math/point.py rename to src/pygerber/gerber/ast/nodes/math/point.py index 614b6f74..f6ebb404 100644 --- a/src/pygerber/gerberx3/ast/nodes/math/point.py +++ b/src/pygerber/gerber/ast/nodes/math/point.py @@ -6,13 +6,13 @@ from typing import TYPE_CHECKING, Callable -from pygerber.gerberx3.ast.nodes.base import Node -from pygerber.gerberx3.ast.nodes.math.expression import Expression +from pygerber.gerber.ast.nodes.base import Node +from pygerber.gerber.ast.nodes.math.expression import Expression if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class Point(Node): diff --git a/src/pygerber/gerberx3/ast/nodes/math/variable.py b/src/pygerber/gerber/ast/nodes/math/variable.py similarity index 83% rename from src/pygerber/gerberx3/ast/nodes/math/variable.py rename to src/pygerber/gerber/ast/nodes/math/variable.py index 5e938c1a..4829571d 100644 --- a/src/pygerber/gerberx3/ast/nodes/math/variable.py +++ b/src/pygerber/gerber/ast/nodes/math/variable.py @@ -4,12 +4,12 @@ from typing import TYPE_CHECKING, Callable -from pygerber.gerberx3.ast.nodes.math.expression import Expression +from pygerber.gerber.ast.nodes.math.expression import Expression if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class Variable(Expression): diff --git a/src/pygerber/gerberx3/ast/nodes/model.py b/src/pygerber/gerber/ast/nodes/model.py similarity index 100% rename from src/pygerber/gerberx3/ast/nodes/model.py rename to src/pygerber/gerber/ast/nodes/model.py diff --git a/src/pygerber/gerber/ast/nodes/other/__init__.py b/src/pygerber/gerber/ast/nodes/other/__init__.py new file mode 100644 index 00000000..c0fb3dca --- /dev/null +++ b/src/pygerber/gerber/ast/nodes/other/__init__.py @@ -0,0 +1,3 @@ +"""`nodes.other` package contains all the nodes that don't fit in +other categories. +""" diff --git a/src/pygerber/gerberx3/ast/nodes/other/coordinate.py b/src/pygerber/gerber/ast/nodes/other/coordinate.py similarity index 91% rename from src/pygerber/gerberx3/ast/nodes/other/coordinate.py rename to src/pygerber/gerber/ast/nodes/other/coordinate.py index 2ada5736..a9000911 100644 --- a/src/pygerber/gerberx3/ast/nodes/other/coordinate.py +++ b/src/pygerber/gerber/ast/nodes/other/coordinate.py @@ -6,13 +6,13 @@ from typing import TYPE_CHECKING, Callable -from pygerber.gerberx3.ast.nodes.base import Node -from pygerber.gerberx3.ast.nodes.types import PackedCoordinateStr +from pygerber.gerber.ast.nodes.base import Node +from pygerber.gerber.ast.nodes.types import PackedCoordinateStr if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class Coordinate(Node): diff --git a/src/pygerber/gerber/ast/nodes/primitives/__init__.py b/src/pygerber/gerber/ast/nodes/primitives/__init__.py new file mode 100644 index 00000000..40567b4e --- /dev/null +++ b/src/pygerber/gerber/ast/nodes/primitives/__init__.py @@ -0,0 +1 @@ +"""The `primitives` package contains all the macro primitives nodes.""" diff --git a/src/pygerber/gerberx3/ast/nodes/primitives/code_0.py b/src/pygerber/gerber/ast/nodes/primitives/code_0.py similarity index 84% rename from src/pygerber/gerberx3/ast/nodes/primitives/code_0.py rename to src/pygerber/gerber/ast/nodes/primitives/code_0.py index 78c67ab7..7aa55133 100644 --- a/src/pygerber/gerberx3/ast/nodes/primitives/code_0.py +++ b/src/pygerber/gerber/ast/nodes/primitives/code_0.py @@ -4,12 +4,12 @@ from typing import TYPE_CHECKING, Callable -from pygerber.gerberx3.ast.nodes.base import Node +from pygerber.gerber.ast.nodes.base import Node if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class Code0(Node): diff --git a/src/pygerber/gerberx3/ast/nodes/primitives/code_1.py b/src/pygerber/gerber/ast/nodes/primitives/code_1.py similarity index 81% rename from src/pygerber/gerberx3/ast/nodes/primitives/code_1.py rename to src/pygerber/gerber/ast/nodes/primitives/code_1.py index 7102df33..65b6b1a4 100644 --- a/src/pygerber/gerberx3/ast/nodes/primitives/code_1.py +++ b/src/pygerber/gerber/ast/nodes/primitives/code_1.py @@ -6,13 +6,13 @@ from pydantic import Field -from pygerber.gerberx3.ast.nodes.base import Node -from pygerber.gerberx3.ast.nodes.math.expression import Expression +from pygerber.gerber.ast.nodes.base import Node +from pygerber.gerber.ast.nodes.math.expression import Expression if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class Code1(Node): diff --git a/src/pygerber/gerberx3/ast/nodes/primitives/code_2.py b/src/pygerber/gerber/ast/nodes/primitives/code_2.py similarity index 81% rename from src/pygerber/gerberx3/ast/nodes/primitives/code_2.py rename to src/pygerber/gerber/ast/nodes/primitives/code_2.py index 258df115..9fa02b49 100644 --- a/src/pygerber/gerberx3/ast/nodes/primitives/code_2.py +++ b/src/pygerber/gerber/ast/nodes/primitives/code_2.py @@ -4,13 +4,13 @@ from typing import TYPE_CHECKING, Callable -from pygerber.gerberx3.ast.nodes.base import Node -from pygerber.gerberx3.ast.nodes.math.expression import Expression +from pygerber.gerber.ast.nodes.base import Node +from pygerber.gerber.ast.nodes.math.expression import Expression if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class Code2(Node): diff --git a/src/pygerber/gerberx3/ast/nodes/primitives/code_20.py b/src/pygerber/gerber/ast/nodes/primitives/code_20.py similarity index 81% rename from src/pygerber/gerberx3/ast/nodes/primitives/code_20.py rename to src/pygerber/gerber/ast/nodes/primitives/code_20.py index 630ed230..0d1c827f 100644 --- a/src/pygerber/gerberx3/ast/nodes/primitives/code_20.py +++ b/src/pygerber/gerber/ast/nodes/primitives/code_20.py @@ -4,13 +4,13 @@ from typing import TYPE_CHECKING, Callable -from pygerber.gerberx3.ast.nodes.base import Node -from pygerber.gerberx3.ast.nodes.math.expression import Expression +from pygerber.gerber.ast.nodes.base import Node +from pygerber.gerber.ast.nodes.math.expression import Expression if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class Code20(Node): diff --git a/src/pygerber/gerberx3/ast/nodes/primitives/code_21.py b/src/pygerber/gerber/ast/nodes/primitives/code_21.py similarity index 81% rename from src/pygerber/gerberx3/ast/nodes/primitives/code_21.py rename to src/pygerber/gerber/ast/nodes/primitives/code_21.py index 818c0e4e..92aed514 100644 --- a/src/pygerber/gerberx3/ast/nodes/primitives/code_21.py +++ b/src/pygerber/gerber/ast/nodes/primitives/code_21.py @@ -4,13 +4,13 @@ from typing import TYPE_CHECKING, Callable -from pygerber.gerberx3.ast.nodes.base import Node -from pygerber.gerberx3.ast.nodes.math.expression import Expression +from pygerber.gerber.ast.nodes.base import Node +from pygerber.gerber.ast.nodes.math.expression import Expression if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class Code21(Node): diff --git a/src/pygerber/gerberx3/ast/nodes/primitives/code_22.py b/src/pygerber/gerber/ast/nodes/primitives/code_22.py similarity index 81% rename from src/pygerber/gerberx3/ast/nodes/primitives/code_22.py rename to src/pygerber/gerber/ast/nodes/primitives/code_22.py index ed0cf553..d7e5f3da 100644 --- a/src/pygerber/gerberx3/ast/nodes/primitives/code_22.py +++ b/src/pygerber/gerber/ast/nodes/primitives/code_22.py @@ -4,13 +4,13 @@ from typing import TYPE_CHECKING, Callable -from pygerber.gerberx3.ast.nodes.base import Node -from pygerber.gerberx3.ast.nodes.math.expression import Expression +from pygerber.gerber.ast.nodes.base import Node +from pygerber.gerber.ast.nodes.math.expression import Expression if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class Code22(Node): diff --git a/src/pygerber/gerberx3/ast/nodes/primitives/code_4.py b/src/pygerber/gerber/ast/nodes/primitives/code_4.py similarity index 76% rename from src/pygerber/gerberx3/ast/nodes/primitives/code_4.py rename to src/pygerber/gerber/ast/nodes/primitives/code_4.py index b24d2412..54ea63ad 100644 --- a/src/pygerber/gerberx3/ast/nodes/primitives/code_4.py +++ b/src/pygerber/gerber/ast/nodes/primitives/code_4.py @@ -4,14 +4,14 @@ from typing import TYPE_CHECKING, Callable, List -from pygerber.gerberx3.ast.nodes.base import Node -from pygerber.gerberx3.ast.nodes.math.expression import Expression -from pygerber.gerberx3.ast.nodes.math.point import Point +from pygerber.gerber.ast.nodes.base import Node +from pygerber.gerber.ast.nodes.math.expression import Expression +from pygerber.gerber.ast.nodes.math.point import Point if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class Code4(Node): diff --git a/src/pygerber/gerberx3/ast/nodes/primitives/code_5.py b/src/pygerber/gerber/ast/nodes/primitives/code_5.py similarity index 81% rename from src/pygerber/gerberx3/ast/nodes/primitives/code_5.py rename to src/pygerber/gerber/ast/nodes/primitives/code_5.py index 0cfd78ed..196ec61a 100644 --- a/src/pygerber/gerberx3/ast/nodes/primitives/code_5.py +++ b/src/pygerber/gerber/ast/nodes/primitives/code_5.py @@ -4,13 +4,13 @@ from typing import TYPE_CHECKING, Callable -from pygerber.gerberx3.ast.nodes.base import Node -from pygerber.gerberx3.ast.nodes.math.expression import Expression +from pygerber.gerber.ast.nodes.base import Node +from pygerber.gerber.ast.nodes.math.expression import Expression if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class Code5(Node): diff --git a/src/pygerber/gerberx3/ast/nodes/primitives/code_6.py b/src/pygerber/gerber/ast/nodes/primitives/code_6.py similarity index 83% rename from src/pygerber/gerberx3/ast/nodes/primitives/code_6.py rename to src/pygerber/gerber/ast/nodes/primitives/code_6.py index a0eab9ee..b60f4aad 100644 --- a/src/pygerber/gerberx3/ast/nodes/primitives/code_6.py +++ b/src/pygerber/gerber/ast/nodes/primitives/code_6.py @@ -4,13 +4,13 @@ from typing import TYPE_CHECKING, Callable -from pygerber.gerberx3.ast.nodes.base import Node -from pygerber.gerberx3.ast.nodes.math.expression import Expression +from pygerber.gerber.ast.nodes.base import Node +from pygerber.gerber.ast.nodes.math.expression import Expression if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class Code6(Node): diff --git a/src/pygerber/gerberx3/ast/nodes/primitives/code_7.py b/src/pygerber/gerber/ast/nodes/primitives/code_7.py similarity index 81% rename from src/pygerber/gerberx3/ast/nodes/primitives/code_7.py rename to src/pygerber/gerber/ast/nodes/primitives/code_7.py index 4517034b..95ea9e2f 100644 --- a/src/pygerber/gerberx3/ast/nodes/primitives/code_7.py +++ b/src/pygerber/gerber/ast/nodes/primitives/code_7.py @@ -4,13 +4,13 @@ from typing import TYPE_CHECKING, Callable -from pygerber.gerberx3.ast.nodes.base import Node -from pygerber.gerberx3.ast.nodes.math.expression import Expression +from pygerber.gerber.ast.nodes.base import Node +from pygerber.gerber.ast.nodes.math.expression import Expression if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class Code7(Node): diff --git a/src/pygerber/gerberx3/ast/nodes/properties/AS.py b/src/pygerber/gerber/ast/nodes/properties/AS.py similarity index 78% rename from src/pygerber/gerberx3/ast/nodes/properties/AS.py rename to src/pygerber/gerber/ast/nodes/properties/AS.py index 276eb7bd..fbaa76b3 100644 --- a/src/pygerber/gerberx3/ast/nodes/properties/AS.py +++ b/src/pygerber/gerber/ast/nodes/properties/AS.py @@ -4,13 +4,13 @@ from typing import TYPE_CHECKING, Callable -from pygerber.gerberx3.ast.nodes.base import Node -from pygerber.gerberx3.ast.nodes.enums import AxisCorrespondence +from pygerber.gerber.ast.nodes.base import Node +from pygerber.gerber.ast.nodes.enums import AxisCorrespondence if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class AS(Node): diff --git a/src/pygerber/gerberx3/ast/nodes/properties/FS.py b/src/pygerber/gerber/ast/nodes/properties/FS.py similarity index 79% rename from src/pygerber/gerberx3/ast/nodes/properties/FS.py rename to src/pygerber/gerber/ast/nodes/properties/FS.py index cc257795..390b0514 100644 --- a/src/pygerber/gerberx3/ast/nodes/properties/FS.py +++ b/src/pygerber/gerber/ast/nodes/properties/FS.py @@ -4,13 +4,13 @@ from typing import TYPE_CHECKING, Callable -from pygerber.gerberx3.ast.nodes.base import Node -from pygerber.gerberx3.ast.nodes.enums import CoordinateNotation, Zeros +from pygerber.gerber.ast.nodes.base import Node +from pygerber.gerber.ast.nodes.enums import CoordinateNotation, Zeros if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class FS(Node): diff --git a/src/pygerber/gerberx3/ast/nodes/properties/IN.py b/src/pygerber/gerber/ast/nodes/properties/IN.py similarity index 85% rename from src/pygerber/gerberx3/ast/nodes/properties/IN.py rename to src/pygerber/gerber/ast/nodes/properties/IN.py index e5e806c2..43da3df4 100644 --- a/src/pygerber/gerberx3/ast/nodes/properties/IN.py +++ b/src/pygerber/gerber/ast/nodes/properties/IN.py @@ -6,12 +6,12 @@ from pydantic import Field -from pygerber.gerberx3.ast.nodes.base import Node +from pygerber.gerber.ast.nodes.base import Node if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class IN(Node): diff --git a/src/pygerber/gerberx3/ast/nodes/properties/IP.py b/src/pygerber/gerber/ast/nodes/properties/IP.py similarity index 78% rename from src/pygerber/gerberx3/ast/nodes/properties/IP.py rename to src/pygerber/gerber/ast/nodes/properties/IP.py index 39c85b1b..1719bf4e 100644 --- a/src/pygerber/gerberx3/ast/nodes/properties/IP.py +++ b/src/pygerber/gerber/ast/nodes/properties/IP.py @@ -4,13 +4,13 @@ from typing import TYPE_CHECKING, Callable -from pygerber.gerberx3.ast.nodes.base import Node -from pygerber.gerberx3.ast.nodes.enums import ImagePolarity +from pygerber.gerber.ast.nodes.base import Node +from pygerber.gerber.ast.nodes.enums import ImagePolarity if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class IP(Node): diff --git a/src/pygerber/gerberx3/ast/nodes/properties/IR.py b/src/pygerber/gerber/ast/nodes/properties/IR.py similarity index 84% rename from src/pygerber/gerberx3/ast/nodes/properties/IR.py rename to src/pygerber/gerber/ast/nodes/properties/IR.py index 3293462a..04b028bb 100644 --- a/src/pygerber/gerberx3/ast/nodes/properties/IR.py +++ b/src/pygerber/gerber/ast/nodes/properties/IR.py @@ -4,12 +4,12 @@ from typing import TYPE_CHECKING, Callable -from pygerber.gerberx3.ast.nodes.base import Node +from pygerber.gerber.ast.nodes.base import Node if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class IR(Node): diff --git a/src/pygerber/gerberx3/ast/nodes/properties/MI.py b/src/pygerber/gerber/ast/nodes/properties/MI.py similarity index 86% rename from src/pygerber/gerberx3/ast/nodes/properties/MI.py rename to src/pygerber/gerber/ast/nodes/properties/MI.py index 7c5f21d7..491dd421 100644 --- a/src/pygerber/gerberx3/ast/nodes/properties/MI.py +++ b/src/pygerber/gerber/ast/nodes/properties/MI.py @@ -6,12 +6,12 @@ from pydantic import Field -from pygerber.gerberx3.ast.nodes.base import Node +from pygerber.gerber.ast.nodes.base import Node if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class MI(Node): diff --git a/src/pygerber/gerberx3/ast/nodes/properties/MO.py b/src/pygerber/gerber/ast/nodes/properties/MO.py similarity index 78% rename from src/pygerber/gerberx3/ast/nodes/properties/MO.py rename to src/pygerber/gerber/ast/nodes/properties/MO.py index 5a94728e..aca84e23 100644 --- a/src/pygerber/gerberx3/ast/nodes/properties/MO.py +++ b/src/pygerber/gerber/ast/nodes/properties/MO.py @@ -4,13 +4,13 @@ from typing import TYPE_CHECKING, Callable -from pygerber.gerberx3.ast.nodes.base import Node -from pygerber.gerberx3.ast.nodes.enums import UnitMode +from pygerber.gerber.ast.nodes.base import Node +from pygerber.gerber.ast.nodes.enums import UnitMode if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class MO(Node): diff --git a/src/pygerber/gerberx3/ast/nodes/properties/OF.py b/src/pygerber/gerber/ast/nodes/properties/OF.py similarity index 86% rename from src/pygerber/gerberx3/ast/nodes/properties/OF.py rename to src/pygerber/gerber/ast/nodes/properties/OF.py index 7068ca4e..1c0e5576 100644 --- a/src/pygerber/gerberx3/ast/nodes/properties/OF.py +++ b/src/pygerber/gerber/ast/nodes/properties/OF.py @@ -6,12 +6,12 @@ from pydantic import Field -from pygerber.gerberx3.ast.nodes.base import Node +from pygerber.gerber.ast.nodes.base import Node if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class OF(Node): diff --git a/src/pygerber/gerberx3/ast/nodes/properties/SF.py b/src/pygerber/gerber/ast/nodes/properties/SF.py similarity index 81% rename from src/pygerber/gerberx3/ast/nodes/properties/SF.py rename to src/pygerber/gerber/ast/nodes/properties/SF.py index 38858d64..650891ee 100644 --- a/src/pygerber/gerberx3/ast/nodes/properties/SF.py +++ b/src/pygerber/gerber/ast/nodes/properties/SF.py @@ -6,13 +6,13 @@ from pydantic import Field -from pygerber.gerberx3.ast.nodes.base import Node -from pygerber.gerberx3.ast.nodes.types import Double +from pygerber.gerber.ast.nodes.base import Node +from pygerber.gerber.ast.nodes.types import Double if TYPE_CHECKING: from typing_extensions import Self - from pygerber.gerberx3.ast.ast_visitor import AstVisitor + from pygerber.gerber.ast.ast_visitor import AstVisitor class SF(Node): diff --git a/src/pygerber/gerber/ast/nodes/properties/__init__.py b/src/pygerber/gerber/ast/nodes/properties/__init__.py new file mode 100644 index 00000000..6be82f3c --- /dev/null +++ b/src/pygerber/gerber/ast/nodes/properties/__init__.py @@ -0,0 +1,3 @@ +"""`nodes.properties` package contains all the nodes that set +image properties. +""" diff --git a/src/pygerber/gerberx3/ast/nodes/ruff.toml b/src/pygerber/gerber/ast/nodes/ruff.toml similarity index 100% rename from src/pygerber/gerberx3/ast/nodes/ruff.toml rename to src/pygerber/gerber/ast/nodes/ruff.toml diff --git a/src/pygerber/gerberx3/ast/nodes/types.py b/src/pygerber/gerber/ast/nodes/types.py similarity index 100% rename from src/pygerber/gerberx3/ast/nodes/types.py rename to src/pygerber/gerber/ast/nodes/types.py diff --git a/src/pygerber/gerberx3/ast/state_tracking_visitor.py b/src/pygerber/gerber/ast/state_tracking_visitor.py similarity index 98% rename from src/pygerber/gerberx3/ast/state_tracking_visitor.py rename to src/pygerber/gerber/ast/state_tracking_visitor.py index fd240823..95d7d0ac 100644 --- a/src/pygerber/gerberx3/ast/state_tracking_visitor.py +++ b/src/pygerber/gerber/ast/state_tracking_visitor.py @@ -1,4 +1,4 @@ -"""`pygerber.gerberx3.state_tracking_visitor` contains definition of +"""The `state_tracking_visitor` module contains definition of `StateTrackingVisitor` class. """ @@ -12,8 +12,8 @@ from pydantic import BaseModel, ConfigDict, Field from pygerber.common.error import throw -from pygerber.gerberx3.ast.ast_visitor import AstVisitor -from pygerber.gerberx3.ast.errors import ( +from pygerber.gerber.ast.ast_visitor import AstVisitor +from pygerber.gerber.ast.errors import ( ApertureNotFoundError, ApertureNotSelectedError, CoordinateFormatNotSetError, @@ -21,7 +21,7 @@ PackedCoordinateTooLongError, PackedCoordinateTooShortError, ) -from pygerber.gerberx3.ast.nodes import ( +from pygerber.gerber.ast.nodes import ( AB, AD, ADC, @@ -74,8 +74,8 @@ File, PackedCoordinateStr, ) -from pygerber.gerberx3.ast.nodes.base import Node -from pygerber.gerberx3.ast.nodes.enums import ( +from pygerber.gerber.ast.nodes.base import Node +from pygerber.gerber.ast.nodes.enums import ( AxisCorrespondence, CoordinateNotation, ImagePolarity, @@ -445,7 +445,7 @@ def __init__(self, node: M00 | M02) -> None: class StateTrackingVisitor(AstVisitor): """`StateTrackingVisitor` is a visitor class that tracks the internal state - defined in the GerberX3 specification and modifies it according to Gerber + defined in the Gerber X3 specification and modifies it according to Gerber commands. Additionally, it defines a set of higher level callback methods that extend diff --git a/src/pygerber/gerberx3/compiler/__init__.py b/src/pygerber/gerber/compiler/__init__.py similarity index 67% rename from src/pygerber/gerberx3/compiler/__init__.py rename to src/pygerber/gerber/compiler/__init__.py index 4f156131..83afc9c6 100644 --- a/src/pygerber/gerberx3/compiler/__init__.py +++ b/src/pygerber/gerber/compiler/__init__.py @@ -1,12 +1,12 @@ -"""`compiler` module contains internals of Gerber X3 to RVMC compiler.""" +"""The `compiler` module contains internals of Gerber X3 to RVMC compiler.""" from __future__ import annotations from typing import TYPE_CHECKING -from pygerber.gerberx3.ast.nodes import File -from pygerber.gerberx3.compiler.compiler import Compiler -from pygerber.gerberx3.compiler.errors import CompilerError, CyclicBufferDependencyError +from pygerber.gerber.ast.nodes import File +from pygerber.gerber.compiler.compiler import Compiler +from pygerber.gerber.compiler.errors import CompilerError, CyclicBufferDependencyError if TYPE_CHECKING: from pygerber.vm.rvmc import RVMC @@ -15,7 +15,7 @@ def compile(ast: File, *, ignore_program_stop: bool = False) -> RVMC: # noqa: A001 - """Compile GerberX3 AST to RVMC code. + """Compile Gerber X3 AST to RVMC code. Parameters ---------- diff --git a/src/pygerber/gerberx3/compiler/compiler.py b/src/pygerber/gerber/compiler/compiler.py similarity index 98% rename from src/pygerber/gerberx3/compiler/compiler.py rename to src/pygerber/gerber/compiler/compiler.py index 6a9c5598..1436c44a 100644 --- a/src/pygerber/gerberx3/compiler/compiler.py +++ b/src/pygerber/gerber/compiler/compiler.py @@ -1,5 +1,5 @@ -"""`pygerber.gerberx3.compiler.compiler` module contains implementation of compiler for -transforming Gerber (AST) to PyGerber rendering VM commands (RVMC). +"""The `compiler` module contains implementation of compiler for transforming Gerber +abstract syntax tree to PyGerber rendering VM commands (RVMC). """ from __future__ import annotations @@ -9,9 +9,9 @@ from math import cos, radians, sin from typing import TYPE_CHECKING, ClassVar, Optional -from pygerber.gerberx3.ast.ast_visitor import AstVisitor -from pygerber.gerberx3.ast.expression_eval_visitor import ExpressionEvalVisitor -from pygerber.gerberx3.ast.nodes import ( +from pygerber.gerber.ast.ast_visitor import AstVisitor +from pygerber.gerber.ast.expression_eval_visitor import ExpressionEvalVisitor +from pygerber.gerber.ast.nodes import ( AB, ADC, ADO, @@ -33,12 +33,12 @@ Expression, File, ) -from pygerber.gerberx3.ast.nodes.aperture.SR import SR -from pygerber.gerberx3.ast.nodes.types import ApertureIdStr, Double -from pygerber.gerberx3.ast.state_tracking_visitor import ( +from pygerber.gerber.ast.nodes.aperture.SR import SR +from pygerber.gerber.ast.nodes.types import ApertureIdStr, Double +from pygerber.gerber.ast.state_tracking_visitor import ( StateTrackingVisitor, ) -from pygerber.gerberx3.compiler.errors import ( +from pygerber.gerber.compiler.errors import ( ContourBufferNotSetError, CyclicBufferDependencyError, MacroNotDefinedError, diff --git a/src/pygerber/gerberx3/compiler/errors.py b/src/pygerber/gerber/compiler/errors.py similarity index 95% rename from src/pygerber/gerberx3/compiler/errors.py rename to src/pygerber/gerber/compiler/errors.py index 9f336ff3..30aca983 100644 --- a/src/pygerber/gerberx3/compiler/errors.py +++ b/src/pygerber/gerber/compiler/errors.py @@ -5,7 +5,7 @@ from typing import TYPE_CHECKING if TYPE_CHECKING: - from pygerber.gerberx3.compiler.compiler import CommandBuffer + from pygerber.gerber.compiler.compiler import CommandBuffer class CompilerError(Exception): diff --git a/src/pygerber/gerberx3/formatter/__init__.py b/src/pygerber/gerber/formatter/__init__.py similarity index 55% rename from src/pygerber/gerberx3/formatter/__init__.py rename to src/pygerber/gerber/formatter/__init__.py index 5e96264b..fd6b4b46 100644 --- a/src/pygerber/gerberx3/formatter/__init__.py +++ b/src/pygerber/gerber/formatter/__init__.py @@ -2,7 +2,7 @@ from __future__ import annotations -from pygerber.gerberx3.formatter.formatter import Formatter -from pygerber.gerberx3.formatter.presets import balanced, extra_indent, small_indent +from pygerber.gerber.formatter.formatter import Formatter +from pygerber.gerber.formatter.presets import balanced, extra_indent, small_indent __all__ = ["Formatter", "extra_indent", "balanced", "small_indent"] diff --git a/src/pygerber/gerberx3/formatter/formatter.py b/src/pygerber/gerber/formatter/formatter.py similarity index 99% rename from src/pygerber/gerberx3/formatter/formatter.py rename to src/pygerber/gerber/formatter/formatter.py index ac1b000e..5c67d9e9 100644 --- a/src/pygerber/gerberx3/formatter/formatter.py +++ b/src/pygerber/gerber/formatter/formatter.py @@ -1,5 +1,5 @@ -"""`pygerber.gerberx3.formatter` module contains implementation `Formatter` class -which implements configurable Gerber code formatting. +"""The `formatter` module contains implementation `Formatter` class which implements +configurable Gerber code formatting. """ from __future__ import annotations @@ -20,8 +20,8 @@ from pyparsing import cached_property from typing_extensions import ParamSpec -from pygerber.gerberx3.ast.ast_visitor import AstVisitor -from pygerber.gerberx3.ast.nodes import ( +from pygerber.gerber.ast.ast_visitor import AstVisitor +from pygerber.gerber.ast.nodes import ( ADC, ADO, ADP, diff --git a/src/pygerber/gerberx3/formatter/presets.py b/src/pygerber/gerber/formatter/presets.py similarity index 92% rename from src/pygerber/gerberx3/formatter/presets.py rename to src/pygerber/gerber/formatter/presets.py index a8fe8555..eadaacd4 100644 --- a/src/pygerber/gerberx3/formatter/presets.py +++ b/src/pygerber/gerber/formatter/presets.py @@ -1,10 +1,8 @@ -"""`pygerber.gerberx3.formatter_presets` module contains named predefined formatter -configurations. -""" +"""The `presets` module contains named predefined formatter configurations.""" from __future__ import annotations -from pygerber.gerberx3.formatter import Formatter +from pygerber.gerber.formatter import Formatter extra_indent = { "indent_character": " ", diff --git a/src/pygerber/gerberx3/language_server/__init__.py b/src/pygerber/gerber/language_server/__init__.py similarity index 100% rename from src/pygerber/gerberx3/language_server/__init__.py rename to src/pygerber/gerber/language_server/__init__.py diff --git a/src/pygerber/gerberx3/language_server/__main__.py b/src/pygerber/gerber/language_server/__main__.py similarity index 91% rename from src/pygerber/gerberx3/language_server/__main__.py rename to src/pygerber/gerber/language_server/__main__.py index 31463199..9f87c879 100644 --- a/src/pygerber/gerberx3/language_server/__main__.py +++ b/src/pygerber/gerber/language_server/__main__.py @@ -8,7 +8,7 @@ import click -from pygerber.gerberx3.language_server.status import is_language_server_available +from pygerber.gerber.language_server.status import is_language_server_available BUNDLE_DIR = pathlib.Path(__file__).parent.parent logger = logging.getLogger(__name__) @@ -64,7 +64,7 @@ def main(ctx: click.Context, *, quiet: bool, check_only: bool) -> None: # noqa: }, }, ) - from pygerber.gerberx3.language_server._server.server import get_server + from pygerber.gerber.language_server._server.server import get_server server = get_server() server.start_io() diff --git a/src/pygerber/gerberx3/language_server/_server/__init__.py b/src/pygerber/gerber/language_server/_server/__init__.py similarity index 100% rename from src/pygerber/gerberx3/language_server/_server/__init__.py rename to src/pygerber/gerber/language_server/_server/__init__.py diff --git a/src/pygerber/gerberx3/language_server/_server/document_cache.py b/src/pygerber/gerber/language_server/_server/document_cache.py similarity index 94% rename from src/pygerber/gerberx3/language_server/_server/document_cache.py rename to src/pygerber/gerber/language_server/_server/document_cache.py index afa3877f..8eb56d1b 100644 --- a/src/pygerber/gerberx3/language_server/_server/document_cache.py +++ b/src/pygerber/gerber/language_server/_server/document_cache.py @@ -5,7 +5,7 @@ from contextlib import suppress from typing import TYPE_CHECKING, Optional -from pygerber.gerberx3.language_server._server.documents.document import Document +from pygerber.gerber.language_server._server.documents.document import Document if TYPE_CHECKING: from types import TracebackType diff --git a/src/pygerber/gerberx3/language_server/_server/documents/__init__.py b/src/pygerber/gerber/language_server/_server/documents/__init__.py similarity index 100% rename from src/pygerber/gerberx3/language_server/_server/documents/__init__.py rename to src/pygerber/gerber/language_server/_server/documents/__init__.py diff --git a/src/pygerber/gerberx3/language_server/_server/documents/document.py b/src/pygerber/gerber/language_server/_server/documents/document.py similarity index 96% rename from src/pygerber/gerberx3/language_server/_server/documents/document.py rename to src/pygerber/gerber/language_server/_server/documents/document.py index 42f5084c..9a09fa4b 100644 --- a/src/pygerber/gerberx3/language_server/_server/documents/document.py +++ b/src/pygerber/gerber/language_server/_server/documents/document.py @@ -5,7 +5,7 @@ from contextlib import suppress from typing import TYPE_CHECKING, Any, Optional -from pygerber.gerberx3.language_server.status import is_language_server_available +from pygerber.gerber.language_server.status import is_language_server_available if TYPE_CHECKING: from types import TracebackType diff --git a/src/pygerber/gerberx3/language_server/_server/documents/gerber.py b/src/pygerber/gerber/language_server/_server/documents/gerber.py similarity index 95% rename from src/pygerber/gerberx3/language_server/_server/documents/gerber.py rename to src/pygerber/gerber/language_server/_server/documents/gerber.py index 2360ff23..8cc9436a 100644 --- a/src/pygerber/gerberx3/language_server/_server/documents/gerber.py +++ b/src/pygerber/gerber/language_server/_server/documents/gerber.py @@ -5,14 +5,14 @@ from io import StringIO from typing import Optional -from pygerber.gerberx3.ast.node_finder import NodeFinder, ZeroBasedPosition -from pygerber.gerberx3.ast.nodes import File, Invalid -from pygerber.gerberx3.ast.state_tracking_visitor import State, StateTrackingVisitor -from pygerber.gerberx3.formatter import Formatter -from pygerber.gerberx3.language_server._server.documents.document import Document -from pygerber.gerberx3.language_server._server.hover.gerber import GerberHoverCreator -from pygerber.gerberx3.language_server.status import is_language_server_available -from pygerber.gerberx3.parser.pyparsing.parser import Parser +from pygerber.gerber.ast.node_finder import NodeFinder, ZeroBasedPosition +from pygerber.gerber.ast.nodes import File, Invalid +from pygerber.gerber.ast.state_tracking_visitor import State, StateTrackingVisitor +from pygerber.gerber.formatter import Formatter +from pygerber.gerber.language_server._server.documents.document import Document +from pygerber.gerber.language_server._server.hover.gerber import GerberHoverCreator +from pygerber.gerber.language_server.status import is_language_server_available +from pygerber.gerber.parser.pyparsing.parser import Parser if is_language_server_available(): import lsprotocol.types as lspt diff --git a/src/pygerber/gerberx3/language_server/_server/hover/__init__.py b/src/pygerber/gerber/language_server/_server/hover/__init__.py similarity index 100% rename from src/pygerber/gerberx3/language_server/_server/hover/__init__.py rename to src/pygerber/gerber/language_server/_server/hover/__init__.py diff --git a/src/pygerber/gerberx3/language_server/_server/hover/gerber.py b/src/pygerber/gerber/language_server/_server/hover/gerber.py similarity index 98% rename from src/pygerber/gerberx3/language_server/_server/hover/gerber.py rename to src/pygerber/gerber/language_server/_server/hover/gerber.py index f05d775f..6eed4ee7 100644 --- a/src/pygerber/gerberx3/language_server/_server/hover/gerber.py +++ b/src/pygerber/gerber/language_server/_server/hover/gerber.py @@ -6,8 +6,8 @@ from io import StringIO from typing import TYPE_CHECKING, Generator, Optional, cast -from pygerber.gerberx3.ast.ast_visitor import AstVisitor -from pygerber.gerberx3.ast.nodes import ( +from pygerber.gerber.ast.ast_visitor import AstVisitor +from pygerber.gerber.ast.nodes import ( AD, ADC, ADO, @@ -61,16 +61,16 @@ TO_CVal, TO_UserName, ) -from pygerber.gerberx3.ast.nodes.enums import Mirroring, Polarity -from pygerber.gerberx3.ast.state_tracking_visitor import ( +from pygerber.gerber.ast.nodes.enums import Mirroring, Polarity +from pygerber.gerber.ast.state_tracking_visitor import ( ArcInterpolation, PlotMode, ProgramStop, StateTrackingVisitor, ) -from pygerber.gerberx3.compiler import compile -from pygerber.gerberx3.formatter import Formatter -from pygerber.gerberx3.spec import rev_2024_05 as spec +from pygerber.gerber.compiler import compile +from pygerber.gerber.formatter import Formatter +from pygerber.gerber.spec import rev_2024_05 as spec from pygerber.vm import render from pygerber.vm.pillow import PillowResult diff --git a/src/pygerber/gerberx3/language_server/_server/ruff.toml b/src/pygerber/gerber/language_server/_server/ruff.toml similarity index 100% rename from src/pygerber/gerberx3/language_server/_server/ruff.toml rename to src/pygerber/gerber/language_server/_server/ruff.toml diff --git a/src/pygerber/gerberx3/language_server/_server/server.py b/src/pygerber/gerber/language_server/_server/server.py similarity index 90% rename from src/pygerber/gerberx3/language_server/_server/server.py rename to src/pygerber/gerber/language_server/_server/server.py index e5820f0e..0a207e6e 100644 --- a/src/pygerber/gerberx3/language_server/_server/server.py +++ b/src/pygerber/gerber/language_server/_server/server.py @@ -5,10 +5,10 @@ from unittest.mock import MagicMock import pygerber -from pygerber.gerberx3.language_server._server.document_cache import DocumentCache -from pygerber.gerberx3.language_server._server.documents.document import Document -from pygerber.gerberx3.language_server._server.documents.gerber import GerberDocument -from pygerber.gerberx3.language_server.status import throw_if_server_not_available +from pygerber.gerber.language_server._server.document_cache import DocumentCache +from pygerber.gerber.language_server._server.documents.document import Document +from pygerber.gerber.language_server._server.documents.gerber import GerberDocument +from pygerber.gerber.language_server.status import throw_if_server_not_available if TYPE_CHECKING: from pygls.server import LanguageServer @@ -22,7 +22,7 @@ def get_server() -> LanguageServer: # noqa: PLR0915, C901 import pygls.server as pygls_server gls = pygls_server.LanguageServer( - "pygerber.gerberx3.language_server", + "pygerber.gerber.language_server", pygerber.__version__, max_workers=4, ) diff --git a/src/pygerber/gerberx3/language_server/errors.py b/src/pygerber/gerber/language_server/errors.py similarity index 100% rename from src/pygerber/gerberx3/language_server/errors.py rename to src/pygerber/gerber/language_server/errors.py diff --git a/src/pygerber/gerberx3/language_server/status.py b/src/pygerber/gerber/language_server/status.py similarity index 92% rename from src/pygerber/gerberx3/language_server/status.py rename to src/pygerber/gerber/language_server/status.py index 18be5e68..ff9b568f 100644 --- a/src/pygerber/gerberx3/language_server/status.py +++ b/src/pygerber/gerber/language_server/status.py @@ -7,7 +7,7 @@ import importlib.util from typing import Optional -from pygerber.gerberx3.language_server.errors import LanguageServerNotAvailableError +from pygerber.gerber.language_server.errors import LanguageServerNotAvailableError _IS_LANGUAGE_SERVER_AVAILABLE: Optional[bool] = None diff --git a/src/pygerber/gerberx3/linter/__init__.py b/src/pygerber/gerber/linter/__init__.py similarity index 100% rename from src/pygerber/gerberx3/linter/__init__.py rename to src/pygerber/gerber/linter/__init__.py diff --git a/src/pygerber/gerberx3/linter/diagnostic.py b/src/pygerber/gerber/linter/diagnostic.py similarity index 99% rename from src/pygerber/gerberx3/linter/diagnostic.py rename to src/pygerber/gerber/linter/diagnostic.py index d2810fd0..847eb65e 100644 --- a/src/pygerber/gerberx3/linter/diagnostic.py +++ b/src/pygerber/gerber/linter/diagnostic.py @@ -9,7 +9,7 @@ from pygerber.common.frozen_general_model import FrozenGeneralModel from pygerber.common.position import Position -from pygerber.gerberx3.language_server.status import ( +from pygerber.gerber.language_server.status import ( is_language_server_available, ) diff --git a/src/pygerber/gerberx3/optimizer/__init__.py b/src/pygerber/gerber/optimizer/__init__.py similarity index 100% rename from src/pygerber/gerberx3/optimizer/__init__.py rename to src/pygerber/gerber/optimizer/__init__.py diff --git a/src/pygerber/gerberx3/optimizer/optimizer_pass/__init__.py b/src/pygerber/gerber/optimizer/optimizer_pass/__init__.py similarity index 100% rename from src/pygerber/gerberx3/optimizer/optimizer_pass/__init__.py rename to src/pygerber/gerber/optimizer/optimizer_pass/__init__.py diff --git a/src/pygerber/gerberx3/parser/__init__.py b/src/pygerber/gerber/parser/__init__.py similarity index 85% rename from src/pygerber/gerberx3/parser/__init__.py rename to src/pygerber/gerber/parser/__init__.py index 2a6da7f5..84d48dd0 100644 --- a/src/pygerber/gerberx3/parser/__init__.py +++ b/src/pygerber/gerber/parser/__init__.py @@ -1,4 +1,9 @@ -"""`pygerber.gerberx3.parser` package contains Gerber X3 parser implementations.""" +"""The `parser` package contains existing and future Gerber X3 parser +implementations. + +Additionally, it exposes a high level `parse` function that allows to parse Gerber +source. +""" from __future__ import annotations @@ -7,7 +12,7 @@ from typing_extensions import Protocol if TYPE_CHECKING: - from pygerber.gerberx3.ast.nodes import File, Node + from pygerber.gerber.ast.nodes import File, Node class ParserProtocol(Protocol): @@ -25,7 +30,7 @@ def parse( resilient: bool = False, ast_node_class_overrides: Optional[dict[str, Type[Node]]] = None, ) -> File: - """Parse GerberX3 file source code and construct AST from it. + """Parse Gerber X3 file source code and construct AST from it. Parameters ---------- @@ -60,7 +65,7 @@ def parse( """ if parser == "pyparsing": - from pygerber.gerberx3.parser.pyparsing.parser import Parser + from pygerber.gerber.parser.pyparsing.parser import Parser return Parser( resilient=resilient, ast_node_class_overrides=ast_node_class_overrides diff --git a/src/pygerber/gerber/parser/native/__init__.py b/src/pygerber/gerber/parser/native/__init__.py new file mode 100644 index 00000000..29222762 --- /dev/null +++ b/src/pygerber/gerber/parser/native/__init__.py @@ -0,0 +1 @@ +"""The `native` package will contain C++/Rust implementation of Gerber X3 parser.""" diff --git a/src/pygerber/gerber/parser/pyparsing/__init__.py b/src/pygerber/gerber/parser/pyparsing/__init__.py new file mode 100644 index 00000000..8a67f8fd --- /dev/null +++ b/src/pygerber/gerber/parser/pyparsing/__init__.py @@ -0,0 +1,3 @@ +"""The `pyparsing` package contains Gerber X3 parser implementation +based on pyparsing library. +""" diff --git a/src/pygerber/gerberx3/parser/pyparsing/grammar.py b/src/pygerber/gerber/parser/pyparsing/grammar.py similarity index 99% rename from src/pygerber/gerberx3/parser/pyparsing/grammar.py rename to src/pygerber/gerber/parser/pyparsing/grammar.py index 9660ebc9..4abfe801 100644 --- a/src/pygerber/gerberx3/parser/pyparsing/grammar.py +++ b/src/pygerber/gerber/parser/pyparsing/grammar.py @@ -1,5 +1,5 @@ -"""`pygerber.gerberx3.parser.pyparsing.grammar` module contains the Gerber X3 grammar -implemented using the pyparsing library. +"""The `grammar` module contains the Gerber X3 grammar implemented using the pyparsing +library. """ from __future__ import annotations @@ -10,7 +10,7 @@ import pyparsing as pp from pydantic import BaseModel -from pygerber.gerberx3.ast.nodes import ( +from pygerber.gerber.ast.nodes import ( AB, ADC, ADO, @@ -120,7 +120,7 @@ TO_UserName, Variable, ) -from pygerber.gerberx3.ast.nodes.enums import AperFunction +from pygerber.gerber.ast.nodes.enums import AperFunction T = TypeVar("T", bound=Node) diff --git a/src/pygerber/gerberx3/parser/pyparsing/parser.py b/src/pygerber/gerber/parser/pyparsing/parser.py similarity index 72% rename from src/pygerber/gerberx3/parser/pyparsing/parser.py rename to src/pygerber/gerber/parser/pyparsing/parser.py index e6489a58..f7be68f4 100644 --- a/src/pygerber/gerberx3/parser/pyparsing/parser.py +++ b/src/pygerber/gerber/parser/pyparsing/parser.py @@ -1,14 +1,14 @@ -"""`pygerber.gerberx3.parser.pyparsing.parser` module contains Gerber X3 parser -implementation based on pyparsing library. +"""The `parser` module contains Gerber X3 parser implementation based on pyparsing +library. """ from __future__ import annotations from typing import Optional, Type -from pygerber.gerberx3.ast.nodes.base import Node -from pygerber.gerberx3.ast.nodes.file import File -from pygerber.gerberx3.parser.pyparsing.grammar import Grammar +from pygerber.gerber.ast.nodes.base import Node +from pygerber.gerber.ast.nodes.file import File +from pygerber.gerber.parser.pyparsing.grammar import Grammar class Parser: diff --git a/src/pygerber/gerberx3/spec/__init__.py b/src/pygerber/gerber/spec/__init__.py similarity index 100% rename from src/pygerber/gerberx3/spec/__init__.py rename to src/pygerber/gerber/spec/__init__.py diff --git a/src/pygerber/gerberx3/spec/rev_2024_05.py b/src/pygerber/gerber/spec/rev_2024_05.py similarity index 100% rename from src/pygerber/gerberx3/spec/rev_2024_05.py rename to src/pygerber/gerber/spec/rev_2024_05.py diff --git a/src/pygerber/gerberx3/spec/ruff.toml b/src/pygerber/gerber/spec/ruff.toml similarity index 100% rename from src/pygerber/gerberx3/spec/ruff.toml rename to src/pygerber/gerber/spec/ruff.toml diff --git a/src/pygerber/gerberx3/ast/nodes/__init__.py b/src/pygerber/gerberx3/ast/nodes/__init__.py deleted file mode 100644 index b15b10b3..00000000 --- a/src/pygerber/gerberx3/ast/nodes/__init__.py +++ /dev/null @@ -1,256 +0,0 @@ -"""`pygerber.gerberx3.ast.nodes` package contains all the node container classes -generated by the Gerber X3 parser. -""" - -from __future__ import annotations - -from pygerber.gerberx3.ast.nodes.aperture.AB import AB -from pygerber.gerberx3.ast.nodes.aperture.AB_close import ABclose -from pygerber.gerberx3.ast.nodes.aperture.AB_open import ABopen -from pygerber.gerberx3.ast.nodes.aperture.AD import AD -from pygerber.gerberx3.ast.nodes.aperture.ADC import ADC -from pygerber.gerberx3.ast.nodes.aperture.ADmacro import ADmacro -from pygerber.gerberx3.ast.nodes.aperture.ADO import ADO -from pygerber.gerberx3.ast.nodes.aperture.ADP import ADP -from pygerber.gerberx3.ast.nodes.aperture.ADR import ADR -from pygerber.gerberx3.ast.nodes.aperture.AM import AM -from pygerber.gerberx3.ast.nodes.aperture.AM_close import AMclose -from pygerber.gerberx3.ast.nodes.aperture.AM_open import AMopen -from pygerber.gerberx3.ast.nodes.aperture.SR import SR -from pygerber.gerberx3.ast.nodes.aperture.SR_close import SRclose -from pygerber.gerberx3.ast.nodes.aperture.SR_open import SRopen -from pygerber.gerberx3.ast.nodes.attribute.TA import ( - TA, - TA_AperFunction, - TA_DrillTolerance, - TA_FlashText, - TA_UserName, -) -from pygerber.gerberx3.ast.nodes.attribute.TD import TD -from pygerber.gerberx3.ast.nodes.attribute.TF import ( - TF, - TF_MD5, - TF_CreationDate, - TF_FileFunction, - TF_FilePolarity, - TF_GenerationSoftware, - TF_Part, - TF_ProjectId, - TF_SameCoordinates, - TF_UserName, -) -from pygerber.gerberx3.ast.nodes.attribute.TO import ( - TO, - TO_C, - TO_CMNP, - TO_N, - TO_P, - TO_CFtp, - TO_CHgt, - TO_CLbD, - TO_CLbN, - TO_CMfr, - TO_CMnt, - TO_CPgD, - TO_CPgN, - TO_CRot, - TO_CSup, - TO_CVal, - TO_UserName, -) -from pygerber.gerberx3.ast.nodes.base import Node, SourceInfo -from pygerber.gerberx3.ast.nodes.d_codes.D01 import D01 -from pygerber.gerberx3.ast.nodes.d_codes.D02 import D02 -from pygerber.gerberx3.ast.nodes.d_codes.D03 import D03 -from pygerber.gerberx3.ast.nodes.d_codes.Dnn import Dnn -from pygerber.gerberx3.ast.nodes.file import File -from pygerber.gerberx3.ast.nodes.g_codes.G import G -from pygerber.gerberx3.ast.nodes.g_codes.G01 import G01 -from pygerber.gerberx3.ast.nodes.g_codes.G02 import G02 -from pygerber.gerberx3.ast.nodes.g_codes.G03 import G03 -from pygerber.gerberx3.ast.nodes.g_codes.G04 import G04 -from pygerber.gerberx3.ast.nodes.g_codes.G36 import G36 -from pygerber.gerberx3.ast.nodes.g_codes.G37 import G37 -from pygerber.gerberx3.ast.nodes.g_codes.G54 import G54 -from pygerber.gerberx3.ast.nodes.g_codes.G55 import G55 -from pygerber.gerberx3.ast.nodes.g_codes.G70 import G70 -from pygerber.gerberx3.ast.nodes.g_codes.G71 import G71 -from pygerber.gerberx3.ast.nodes.g_codes.G74 import G74 -from pygerber.gerberx3.ast.nodes.g_codes.G75 import G75 -from pygerber.gerberx3.ast.nodes.g_codes.G90 import G90 -from pygerber.gerberx3.ast.nodes.g_codes.G91 import G91 -from pygerber.gerberx3.ast.nodes.invalid import Invalid -from pygerber.gerberx3.ast.nodes.load.LM import LM -from pygerber.gerberx3.ast.nodes.load.LN import LN -from pygerber.gerberx3.ast.nodes.load.LP import LP -from pygerber.gerberx3.ast.nodes.load.LR import LR -from pygerber.gerberx3.ast.nodes.load.LS import LS -from pygerber.gerberx3.ast.nodes.m_codes.M00 import M00 -from pygerber.gerberx3.ast.nodes.m_codes.M01 import M01 -from pygerber.gerberx3.ast.nodes.m_codes.M02 import M02 -from pygerber.gerberx3.ast.nodes.math.assignment import Assignment -from pygerber.gerberx3.ast.nodes.math.constant import Constant -from pygerber.gerberx3.ast.nodes.math.expression import Expression -from pygerber.gerberx3.ast.nodes.math.operators.binary.add import Add -from pygerber.gerberx3.ast.nodes.math.operators.binary.div import Div -from pygerber.gerberx3.ast.nodes.math.operators.binary.mul import Mul -from pygerber.gerberx3.ast.nodes.math.operators.binary.sub import Sub -from pygerber.gerberx3.ast.nodes.math.operators.unary.neg import Neg -from pygerber.gerberx3.ast.nodes.math.operators.unary.pos import Pos -from pygerber.gerberx3.ast.nodes.math.parenthesis import Parenthesis -from pygerber.gerberx3.ast.nodes.math.point import Point -from pygerber.gerberx3.ast.nodes.math.variable import Variable -from pygerber.gerberx3.ast.nodes.other.coordinate import ( - Coordinate, - CoordinateI, - CoordinateJ, - CoordinateX, - CoordinateY, -) -from pygerber.gerberx3.ast.nodes.primitives.code_0 import Code0 -from pygerber.gerberx3.ast.nodes.primitives.code_1 import Code1 -from pygerber.gerberx3.ast.nodes.primitives.code_2 import Code2 -from pygerber.gerberx3.ast.nodes.primitives.code_4 import Code4 -from pygerber.gerberx3.ast.nodes.primitives.code_5 import Code5 -from pygerber.gerberx3.ast.nodes.primitives.code_6 import Code6 -from pygerber.gerberx3.ast.nodes.primitives.code_7 import Code7 -from pygerber.gerberx3.ast.nodes.primitives.code_20 import Code20 -from pygerber.gerberx3.ast.nodes.primitives.code_21 import Code21 -from pygerber.gerberx3.ast.nodes.primitives.code_22 import Code22 -from pygerber.gerberx3.ast.nodes.properties.AS import AS -from pygerber.gerberx3.ast.nodes.properties.FS import FS -from pygerber.gerberx3.ast.nodes.properties.IN import IN -from pygerber.gerberx3.ast.nodes.properties.IP import IP -from pygerber.gerberx3.ast.nodes.properties.IR import IR -from pygerber.gerberx3.ast.nodes.properties.MI import MI -from pygerber.gerberx3.ast.nodes.properties.MO import MO -from pygerber.gerberx3.ast.nodes.properties.OF import OF -from pygerber.gerberx3.ast.nodes.properties.SF import SF -from pygerber.gerberx3.ast.nodes.types import ( - ApertureIdStr, - Double, - Integer, - PackedCoordinateStr, -) - -__all__ = [ - "ABclose", - "ABopen", - "ADC", - "ADmacro", - "AD", - "ADO", - "ADP", - "ADR", - "AMclose", - "AMopen", - "SR", - "SRclose", - "SRopen", - "TA_AperFunction", - "TA_DrillTolerance", - "TA_FlashText", - "TA_UserName", - "TD", - "TF_MD5", - "TF_CreationDate", - "TF_FileFunction", - "TF_FilePolarity", - "TF_GenerationSoftware", - "TF_Part", - "TF_ProjectId", - "TF_SameCoordinates", - "TF_UserName", - "TO_C", - "TO_CMNP", - "TO_N", - "TO_P", - "TO_CFtp", - "TO_CHgt", - "TO_CLbD", - "TO_CLbN", - "TO_CMfr", - "TO_CMnt", - "TO_CPgD", - "TO_CPgN", - "TO_CRot", - "TO_CSup", - "TO_CVal", - "TO_UserName", - "D01", - "D02", - "D03", - "Dnn", - "File", - "G", - "G01", - "G02", - "G03", - "G04", - "G36", - "G37", - "G54", - "G55", - "G70", - "G71", - "G74", - "G75", - "G90", - "G91", - "LM", - "LN", - "LP", - "LR", - "LS", - "M00", - "M01", - "M02", - "Assignment", - "Constant", - "Add", - "Div", - "Mul", - "Sub", - "Neg", - "Pos", - "Point", - "Variable", - "CoordinateI", - "CoordinateJ", - "CoordinateX", - "CoordinateY", - "Code0", - "Code1", - "Code2", - "Code4", - "Code5", - "Code6", - "Code7", - "Code20", - "Code21", - "Code22", - "AS", - "FS", - "IN", - "IP", - "IR", - "MI", - "MO", - "OF", - "SF", - "Double", - "Integer", - "ApertureIdStr", - "PackedCoordinateStr", - "Node", - "TA", - "TO", - "TF", - "Coordinate", - "Expression", - "Parenthesis", - "AB", - "AM", - "SourceInfo", - "Invalid", -] diff --git a/src/pygerber/gerberx3/ast/nodes/aperture/__init__.py b/src/pygerber/gerberx3/ast/nodes/aperture/__init__.py deleted file mode 100644 index 09e9b414..00000000 --- a/src/pygerber/gerberx3/ast/nodes/aperture/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -"""`pygerber.gerberx3.ast.nodes.aperture` package contains all the aperture definition -related nodes. -""" diff --git a/src/pygerber/gerberx3/ast/nodes/attribute/__init__.py b/src/pygerber/gerberx3/ast/nodes/attribute/__init__.py deleted file mode 100644 index 94469f59..00000000 --- a/src/pygerber/gerberx3/ast/nodes/attribute/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -"""`pygerber.gerberx3.ast.nodes.attribute` package contains all the attribute related -nodes. -""" diff --git a/src/pygerber/gerberx3/ast/nodes/d_codes/__init__.py b/src/pygerber/gerberx3/ast/nodes/d_codes/__init__.py deleted file mode 100644 index 96e220e5..00000000 --- a/src/pygerber/gerberx3/ast/nodes/d_codes/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""`pygerber.gerberx3.ast.nodes.d_codes` package contains all the D-code nodes.""" diff --git a/src/pygerber/gerberx3/ast/nodes/g_codes/__init__.py b/src/pygerber/gerberx3/ast/nodes/g_codes/__init__.py deleted file mode 100644 index 1c1541ef..00000000 --- a/src/pygerber/gerberx3/ast/nodes/g_codes/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""`pygerber.gerberx3.ast.nodes.g_codes` package contains all the G-code nodes.""" diff --git a/src/pygerber/gerberx3/ast/nodes/load/__init__.py b/src/pygerber/gerberx3/ast/nodes/load/__init__.py deleted file mode 100644 index 93f819d0..00000000 --- a/src/pygerber/gerberx3/ast/nodes/load/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""`pygerber.gerberx3.ast.nodes.load` package contains all the load_ nodes.""" diff --git a/src/pygerber/gerberx3/ast/nodes/m_codes/__init__.py b/src/pygerber/gerberx3/ast/nodes/m_codes/__init__.py deleted file mode 100644 index e6129fdf..00000000 --- a/src/pygerber/gerberx3/ast/nodes/m_codes/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""`pygerber.gerberx3.ast.nodes.m_codes` package contains all the M-code nodes.""" diff --git a/src/pygerber/gerberx3/ast/nodes/math/__init__.py b/src/pygerber/gerberx3/ast/nodes/math/__init__.py deleted file mode 100644 index 184d7f1e..00000000 --- a/src/pygerber/gerberx3/ast/nodes/math/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -"""`pygerber.gerberx3.ast.nodes.math` package contains all the macro math expression -nodes. -""" diff --git a/src/pygerber/gerberx3/ast/nodes/math/operators/__init__.py b/src/pygerber/gerberx3/ast/nodes/math/operators/__init__.py deleted file mode 100644 index 6f6ca380..00000000 --- a/src/pygerber/gerberx3/ast/nodes/math/operators/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -"""`pygerber.gerberx3.ast.nodes.math.operators` package contains all the macro math -expression operators. -""" diff --git a/src/pygerber/gerberx3/ast/nodes/math/operators/binary/__init__.py b/src/pygerber/gerberx3/ast/nodes/math/operators/binary/__init__.py deleted file mode 100644 index 6abca187..00000000 --- a/src/pygerber/gerberx3/ast/nodes/math/operators/binary/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -"""`pygerber.gerberx3.ast.nodes.math.operators.binary` package contains all the macro -math expression binary operators. -""" diff --git a/src/pygerber/gerberx3/ast/nodes/math/operators/unary/__init__.py b/src/pygerber/gerberx3/ast/nodes/math/operators/unary/__init__.py deleted file mode 100644 index cef2861e..00000000 --- a/src/pygerber/gerberx3/ast/nodes/math/operators/unary/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -"""`pygerber.gerberx3.ast.nodes.math.operators.unary` package contains all the macro -math expression unary operators. -""" diff --git a/src/pygerber/gerberx3/ast/nodes/other/__init__.py b/src/pygerber/gerberx3/ast/nodes/other/__init__.py deleted file mode 100644 index 559d6c5b..00000000 --- a/src/pygerber/gerberx3/ast/nodes/other/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -"""`pygerber.gerberx3.ast.nodes.other` package contains all the nodes that don't fit in -other categories. -""" diff --git a/src/pygerber/gerberx3/ast/nodes/primitives/__init__.py b/src/pygerber/gerberx3/ast/nodes/primitives/__init__.py deleted file mode 100644 index c5a59fb0..00000000 --- a/src/pygerber/gerberx3/ast/nodes/primitives/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -"""`pygerber.gerberx3.ast.nodes.primitives` package contains all the macro primitives -nodes. -""" diff --git a/src/pygerber/gerberx3/ast/nodes/properties/__init__.py b/src/pygerber/gerberx3/ast/nodes/properties/__init__.py deleted file mode 100644 index 8954a113..00000000 --- a/src/pygerber/gerberx3/ast/nodes/properties/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -"""`pygerber.gerberx3.ast.nodes.properties` package contains all the nodes that set -image properties. -""" diff --git a/src/pygerber/gerberx3/math/__init__.py b/src/pygerber/gerberx3/math/__init__.py deleted file mode 100644 index bae7157e..00000000 --- a/src/pygerber/gerberx3/math/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Module containing math utilities.""" diff --git a/src/pygerber/gerberx3/math/bounding_box.py b/src/pygerber/gerberx3/math/bounding_box.py deleted file mode 100644 index 0d260cbb..00000000 --- a/src/pygerber/gerberx3/math/bounding_box.py +++ /dev/null @@ -1,197 +0,0 @@ -"""Utility class for calculating bounding boxes of drawing elements.""" - -from __future__ import annotations - -import operator -from decimal import Decimal -from typing import Callable, ClassVar, Tuple - -from pydantic import Field - -from pygerber.common.frozen_general_model import FrozenGeneralModel -from pygerber.gerberx3.math.offset import Offset -from pygerber.gerberx3.math.vector_2d import Vector2D - - -class BoundingBox(FrozenGeneralModel): - """Class for calculating bounding boxes.""" - - NULL: ClassVar[BoundingBox] - - max_x: Offset = Field(default=Offset.NULL) - max_y: Offset = Field(default=Offset.NULL) - - min_x: Offset = Field(default=Offset.NULL) - min_y: Offset = Field(default=Offset.NULL) - - @classmethod - def from_diameter(cls, diameter: Offset) -> BoundingBox: - """Create a bounding box from a given diameter.""" - half_diameter = diameter / 2 - return cls( - max_x=half_diameter, - max_y=half_diameter, - min_x=-half_diameter, - min_y=-half_diameter, - ) - - @classmethod - def from_rectangle(cls, x_size: Offset, y_size: Offset) -> BoundingBox: - """Create a bounding box from a given diameter.""" - half_x = x_size / 2 - half_y = y_size / 2 - return cls( - max_x=half_x, - max_y=half_y, - min_x=-half_x, - min_y=-half_y, - ) - - @property - def width(self) -> Offset: - """Return width of the bounding box.""" - return self.max_x - self.min_x - - @property - def height(self) -> Offset: - """Return height of the bounding box.""" - return self.max_y - self.min_y - - def get_size(self) -> Vector2D: - """Get bounding box size.""" - return Vector2D(x=self.width, y=self.height) - - @property - def center(self) -> Vector2D: - """Return current center of the bounding box.""" - center_x = (self.max_x + self.min_x) / Offset(value=Decimal(2)) - center_y = (self.max_y + self.min_y) / Offset(value=Decimal(2)) - return Vector2D(x=center_x, y=center_y) - - def get_min_vector(self) -> Vector2D: - """Return Vector2D of min_x and min_y.""" - return Vector2D(x=self.min_x, y=self.min_y) - - def get_max_vector(self) -> Vector2D: - """Return Vector2D of min_x and min_y.""" - return Vector2D(x=self.max_x, y=self.max_y) - - def as_pixel_box( - self, - dpi: int, - *, - dx_max: int = 0, - dy_max: int = 0, - dx_min: int = 0, - dy_min: int = 0, - ) -> PixelBox: - """Return box as tuple of ints with order. - - [x0, y0, x1, y1], where x1 >= x0 and y1 >= y0 - """ - return PixelBox( - ( - self.min_x.as_pixels(dpi) + dx_min, - self.min_y.as_pixels(dpi) + dy_min, - self.max_x.as_pixels(dpi) + dx_max, - self.max_y.as_pixels(dpi) + dy_max, - ), - ) - - def _operator( - self, - other: object, - op: Callable, - ) -> BoundingBox: - if isinstance(other, Vector2D): - return BoundingBox( - max_x=op(self.max_x, other.x), - max_y=op(self.max_y, other.y), - min_x=op(self.min_x, other.x), - min_y=op(self.min_y, other.y), - ) - if isinstance(other, (Offset, Decimal, int, float)): - return BoundingBox( - max_x=op(self.max_x, other), - max_y=op(self.max_y, other), - min_x=op(self.min_x, -other), - min_y=op(self.min_y, -other), - ) - return NotImplemented - - def __add__(self, other: object) -> BoundingBox: - if isinstance(other, BoundingBox): - return BoundingBox( - max_x=max(self.max_x, other.max_x), - max_y=max(self.max_y, other.max_y), - min_x=min(self.min_x, other.min_x), - min_y=min(self.min_y, other.min_y), - ) - return self._operator(other, operator.add) - - def __sub__(self, other: object) -> BoundingBox: - return self._operator(other, operator.sub) - - def __mul__(self, other: object) -> BoundingBox: - return self._operator(other, operator.mul) - - def __truediv__(self, other: object) -> BoundingBox: - return self._operator(other, operator.truediv) - - def __str__(self) -> str: - return ( - f"{self.__class__.__qualname__}(max_x={self.max_x}, max_y={self.max_y}, " - f"min_x={self.min_x}, min_y={self.min_y})" - ) - - def include_point(self, point: Vector2D) -> BoundingBox: - """Include point in bounding box by extending bounding box overt the point.""" - # Check for the x-coordinate - new_max_x = max(self.max_x, point.x) - new_min_x = min(self.min_x, point.x) - - # Check for the y-coordinate - new_max_y = max(self.max_y, point.y) - new_min_y = min(self.min_y, point.y) - - return BoundingBox( - max_x=new_max_x, - max_y=new_max_y, - min_x=new_min_x, - min_y=new_min_y, - ) - - def get_rotated(self, angle: Decimal) -> BoundingBox: - """Return bounding box rotated around (0, 0).""" - v_x_max = Vector2D(x=self.max_x, y=Offset.new(0)).get_rotated(-angle) - v_y_max = Vector2D(x=Offset.new(0), y=self.max_y).get_rotated(-angle) - - v_new_max_0 = v_x_max + v_y_max - v_new_max_1 = v_x_max - v_y_max - - v_x_min = Vector2D(x=self.min_x, y=Offset.new(0)).get_rotated(-angle) - v_y_min = Vector2D(x=Offset.new(0), y=self.min_y).get_rotated(-angle) - - v_new_min_0 = v_x_min + v_y_min - v_new_min_1 = v_x_min - v_y_min - - return BoundingBox( - max_x=max(v_new_max_0.x, v_new_max_1.x, v_new_min_0.x, v_new_min_1.x), - max_y=max(v_new_max_0.y, v_new_max_1.y, v_new_min_0.y, v_new_min_1.y), - min_x=min(v_new_max_0.x, v_new_max_1.x, v_new_min_0.x, v_new_min_1.x), - min_y=min(v_new_max_0.y, v_new_max_1.y, v_new_min_0.y, v_new_min_1.y), - ) - - -BoundingBox.NULL = BoundingBox( - max_x=Offset.NULL, - max_y=Offset.NULL, - min_x=Offset.NULL, - min_y=Offset.NULL, -) - - -class PixelBox(Tuple[int, int, int, int]): - """Custom class for representing pixel boxes.""" - - __slots__ = () diff --git a/src/pygerber/gerberx3/math/offset.py b/src/pygerber/gerberx3/math/offset.py deleted file mode 100644 index 9453c5de..00000000 --- a/src/pygerber/gerberx3/math/offset.py +++ /dev/null @@ -1,168 +0,0 @@ -"""Offset representation used by drawing backend.""" - -from __future__ import annotations - -import operator -from decimal import Decimal, getcontext -from typing import TYPE_CHECKING, Callable, ClassVar, Sequence - -from pygerber.common.frozen_general_model import FrozenGeneralModel -from pygerber.gerberx3.state_enums import Unit - -if TYPE_CHECKING: - from typing_extensions import Self - -getcontext().prec = 60 -INCH_TO_MM_MULTIPLIER = Decimal("25.4") -MM_TO_INCH_MULTIPLIER = Decimal("1") / INCH_TO_MM_MULTIPLIER - - -class Offset(FrozenGeneralModel): - """Class representing offset in 2D space.""" - - NULL: ClassVar[Offset] - - value: Decimal - - @classmethod - def new( - cls, - value: Decimal | float | str | tuple[int, Sequence[int], int], - unit: Unit = Unit.Millimeters, - ) -> Self: - """Initialize offset with value.""" - # Gerber spec recommends using millimeters as unit, so they are used here too. - if unit == Unit.Millimeters: - value = Decimal(value) - else: - value = Decimal(value) * INCH_TO_MM_MULTIPLIER - - return cls(value=value) - - @classmethod - def from_pixels( - cls, - value: Decimal | float | str | tuple[int, Sequence[int], int], - dpi: int, - ) -> Self: - """Initialize offset with value.""" - # Gerber spec recommends using millimeters as unit, so they are used here too. - value = (Decimal(value) / dpi) * INCH_TO_MM_MULTIPLIER - return cls(value=value) - - def as_millimeters(self) -> Decimal: - """Offset in millimeters.""" - return self.value - - def as_inches(self) -> Decimal: - """Offset in millimeters.""" - return self.value * MM_TO_INCH_MULTIPLIER - - def as_unit(self, unit: Unit) -> Decimal: - """Offset in specified unit.""" - if unit == Unit.Inches: - return self.as_inches() - - return self.as_millimeters() - - def as_pixels(self, dpi: int | Decimal) -> int: - """Offset in pixels with respect to drawing DPI.""" - return int(self.as_inches() * dpi) - - def sqrt(self) -> Offset: - """Return square root of the offset.""" - return Offset(value=self.value.sqrt()) - - def _compare( - self, - other: object, - op: Callable, - ) -> bool: - if isinstance(other, Offset): - return op(self.value, other.value) # type: ignore[no-any-return] - if isinstance(other, (Decimal, int, float, str)): - return op(self.value, Decimal(other)) # type: ignore[no-any-return] - return NotImplemented - - def __eq__(self, other: object) -> bool: - return self._compare(other, operator.eq) - - def __lt__(self, other: object) -> bool: - return self._compare(other, operator.lt) - - def __le__(self, other: object) -> bool: - return self._compare(other, operator.le) - - def __gt__(self, other: object) -> bool: - return self._compare(other, operator.gt) - - def __ge__(self, other: object) -> bool: - return self._compare(other, operator.ge) - - def _operator( - self, - other: object, - op: Callable, - ) -> Offset: - if isinstance(other, Offset): - return Offset(value=op(self.value, other.value)) - if isinstance(other, (Decimal, int, float, str)): - return Offset(value=op(self.value, Decimal(other))) - return NotImplemented - - def __add__(self, other: object) -> Offset: - return self._operator(other, operator.add) - - def __sub__(self, other: object) -> Offset: - return self._operator(other, operator.sub) - - def __mul__(self, other: object) -> Offset: - return self._operator(other, operator.mul) - - def __truediv__(self, other: object) -> Offset: - return self._operator(other, operator.truediv) - - def __neg__(self) -> Offset: - return Offset(value=-self.value) - - def __pow__(self, other: object) -> Offset: - return self._operator(other, operator.pow) - - def _i_operator( - self, - other: object, - op: Callable, - ) -> Self: - if isinstance(other, Offset): - return self.model_copy( - update={ - "value": op(self.value, other.value), - }, - ) - if isinstance(other, (Decimal, int, float, str)): - return self.model_copy( - update={ - "value": op(self.value, Decimal(other)), - }, - ) - return NotImplemented - - def __iadd__(self, other: object) -> Self: - return self._i_operator(other, operator.add) - - def __isub__(self, other: object) -> Self: - return self._i_operator(other, operator.sub) - - def __imul__(self, other: object) -> Self: - return self._i_operator(other, operator.mul) - - def __itruediv__(self, other: object) -> Self: - return self._i_operator(other, operator.truediv) - - def __str__(self) -> str: - return f"Offset({float(self.value)})" - - __repr__ = __str__ - - -Offset.NULL = Offset(value=Decimal(0)) diff --git a/src/pygerber/gerberx3/math/rotate_point.py b/src/pygerber/gerberx3/math/rotate_point.py deleted file mode 100644 index ff8b1c86..00000000 --- a/src/pygerber/gerberx3/math/rotate_point.py +++ /dev/null @@ -1,32 +0,0 @@ -"""Tool for rotating point around center.""" - -from __future__ import annotations - -from math import cos, sin -from typing import TYPE_CHECKING - -from pygerber.gerberx3.math.vector_2d import Vector2D - -if TYPE_CHECKING: - from decimal import Decimal - - -def rotate_point(center: Vector2D, angle: Decimal | float, point: Vector2D) -> Vector2D: - """Rotate point around center by given angle.""" - s = sin(angle) - c = cos(angle) - - # Translate point back to origin - x, y = point.x, point.y - x -= center.x - y -= center.y - - # Rotate point - x_new = x * c - y * s - y_new = x * s + y * c - - # Translate point back - x = x_new + center.x - y = y_new + center.y - - return Vector2D(x=x, y=y) diff --git a/src/pygerber/gerberx3/math/vector_2d.py b/src/pygerber/gerberx3/math/vector_2d.py deleted file mode 100644 index cc4cdb75..00000000 --- a/src/pygerber/gerberx3/math/vector_2d.py +++ /dev/null @@ -1,253 +0,0 @@ -"""Simple of 2D vector container class.""" - -from __future__ import annotations - -import math -import operator -from decimal import Decimal -from typing import TYPE_CHECKING, Callable, ClassVar - -from pygerber.common.frozen_general_model import FrozenGeneralModel -from pygerber.gerberx3.math.offset import Offset -from pygerber.gerberx3.state_enums import Mirroring, Unit - -if TYPE_CHECKING: - from typing_extensions import Self - - -class Vector2D(FrozenGeneralModel): - """Tuple wrapper for representing size with custom accessors.""" - - NULL: ClassVar[Vector2D] - UNIT_X: ClassVar[Vector2D] - UNIT_Y: ClassVar[Vector2D] - - x: Offset - y: Offset - - @classmethod - def new( - cls, - x: float | str | Decimal, - y: float | str | Decimal, - unit: Unit = Unit.Millimeters, - ) -> Self: - """Create new vector with default Offset constructor.""" - return cls( - x=Offset.new(Decimal(x), unit=unit), - y=Offset.new(Decimal(y), unit=unit), - ) - - def get_mirrored(self, mirror: Mirroring) -> Self: - """Get mirrored vector.""" - return self._GET_MIRRORED_DISPATCH_TABLE[mirror](self) - - def _get_mirrored_x(self) -> Self: - return self.model_copy( - update={ - "x": -self.x, - }, - ) - - def _get_mirrored_y(self) -> Self: - return self.model_copy( - update={ - "y": -self.y, - }, - ) - - def _get_mirrored_xy(self) -> Self: - return self.model_copy( - update={ - "x": -self.x, - "y": -self.y, - }, - ) - - _GET_MIRRORED_DISPATCH_TABLE: ClassVar[dict[Mirroring, Callable[[Self], Self]]] = { - Mirroring.NoMirroring: lambda s: s, - Mirroring.X: _get_mirrored_x, - Mirroring.Y: _get_mirrored_y, - Mirroring.XY: _get_mirrored_xy, - } - - def get_rotated(self, angle: float | Decimal) -> Self: - """Get copy of this vector rotated around (0, 0). - - Angle is in degrees. - """ - if angle == Decimal("0.0"): - return self - return self.rotate_around_origin(angle) - - def get_scaled(self, scale: Decimal) -> Vector2D: - """Get copy of this vector scaled by factor.""" - if scale == Decimal("1.0"): - return self - return self * scale - - def as_pixels(self, dpi: int) -> tuple[int, int]: - """Return size as pixels using given DPI for conversion.""" - return (self.x.as_pixels(dpi), self.y.as_pixels(dpi)) - - def __eq__(self, other: object) -> bool: - if isinstance(other, Vector2D): - return self.x == other.x and self.y == other.y - return NotImplemented - - def _operator( - self, - other: object, - op: Callable, - ) -> Vector2D: - if isinstance(other, Offset): - return Vector2D( - x=op(self.x, other), - y=op(self.y, other), - ) - if isinstance(other, Vector2D): - return Vector2D( - x=op(self.x, other.x), - y=op(self.y, other.y), - ) - - if isinstance(other, (Decimal, int, float, str)): - return Vector2D( - x=op(self.x, Decimal(other)), - y=op(self.y, Decimal(other)), - ) - return NotImplemented - - def __add__(self, other: object) -> Vector2D: - return self._operator(other, operator.add) - - def __sub__(self, other: object) -> Vector2D: - return self._operator(other, operator.sub) - - def __mul__(self, other: object) -> Vector2D: - return self._operator(other, operator.mul) - - def __truediv__(self, other: object) -> Vector2D: - return self._operator(other, operator.truediv) - - def __neg__(self) -> Vector2D: - return Vector2D(x=-self.x, y=-self.y) - - def _i_operator( - self, - other: object, - op: Callable, - ) -> Self: - if isinstance(other, Vector2D): - return self.model_copy( - update={ - "x": op(self.x, other.x), - "y": op(self.y, other.y), - }, - ) - if isinstance(other, Offset): - return self.model_copy( - update={ - "x": op(self.x, other), - "y": op(self.y, other), - }, - ) - if isinstance(other, (Decimal, int, float, str)): - return self.model_copy( - update={ - "x": op(self.x, Decimal(other)), - "y": op(self.y, Decimal(other)), - }, - ) - return NotImplemented - - def __iadd__(self, other: object) -> Self: - return self._i_operator(other, operator.add) - - def __isub__(self, other: object) -> Self: - return self._i_operator(other, operator.sub) - - def __imul__(self, other: object) -> Self: - return self._i_operator(other, operator.mul) - - def __itruediv__(self, other: object) -> Self: - return self._i_operator(other, operator.truediv) - - def __str__(self) -> str: - return f"{self.__class__.__qualname__}(x={self.x}, y={self.y})" - - def length(self) -> Offset: - """Return length of vector.""" - return Offset(value=((self.x * self.x).value + (self.y * self.y).value).sqrt()) - - def angle_between_clockwise(self, other: Vector2D) -> float: - """Calculate angle between two vectors in degrees clockwise. - - (Bugged?) - """ - self_norm = self / self.length() - other_norm = other / other.length() - - dot = other_norm.dot(self_norm) - determinant = self_norm.determinant(other_norm) - - theta = math.atan2(float(dot.value), float(determinant.value)) - - return math.degrees(theta) - - def angle_between(self, other: Vector2D) -> float: - """Calculate clockwise angle between two vectors in degrees clockwise. - - Value returned is always between 0 and 360 (can be 0, never 360). - """ - return 360 - self.angle_between_cc(other) - - def angle_between_cc(self, other: Vector2D) -> float: - """Calculate counter clockwise angle between two vectors in degrees. - - Value returned is always between 0 and 360 (can be 0, never 360). - """ - v0 = self.normalize() - v1 = other.normalize() - angle_radians = math.atan2( - ((v0.x * v1.y) - (v1.x * v0.y)).value, # determinant - ((v0.x * v1.x) + (v0.y * v1.y)).value, # dot product - ) - angle_degrees = math.degrees(angle_radians) - return angle_degrees + (360 * (angle_degrees < 0)) - - def dot(self, other: Vector2D) -> Offset: - """Calculate dot product of two vectors.""" - return self.x * other.x + self.y * other.y - - def determinant(self, other: Vector2D) -> Offset: - """Calculate determinant of matrix constructed from self and other.""" - return self.x * other.y - self.y * other.x - - def perpendicular(self) -> Vector2D: - """Return perpendicular vector to self.""" - return Vector2D(x=self.y, y=-self.x) - - def normalize(self) -> Vector2D: - """Return normalized (unit length) vector.""" - if self == Vector2D.NULL: - return Vector2D.UNIT_X - - return self / self.length() - - def as_float_tuple(self) -> tuple[float, float]: - """Return x, y Offset as tuple.""" - return (float(self.x.value), float(self.y.value)) - - def rotate_around_origin(self, angle_degrees: float | Decimal) -> Self: - """Return vector rotated x degrees around origin.""" - angle_radians = math.radians(angle_degrees) - return self.__class__( - x=self.x * math.cos(angle_radians) - self.y * math.sin(angle_radians), - y=self.x * math.sin(angle_radians) + self.y * math.cos(angle_radians), - ) - - -Vector2D.NULL = Vector2D(x=Offset.NULL, y=Offset.NULL) -Vector2D.UNIT_X = Vector2D(x=Offset(value=Decimal(1)), y=Offset.NULL) -Vector2D.UNIT_Y = Vector2D(x=Offset.NULL, y=Offset(value=Decimal(1))) diff --git a/src/pygerber/gerberx3/parser/native/__init__.py b/src/pygerber/gerberx3/parser/native/__init__.py deleted file mode 100644 index 25350d49..00000000 --- a/src/pygerber/gerberx3/parser/native/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -"""`pygerber.gerberx3.parser.native` package will contain C++/Rust implementation -of Gerber X3 parser. -""" diff --git a/src/pygerber/gerberx3/parser/pyparsing/__init__.py b/src/pygerber/gerberx3/parser/pyparsing/__init__.py deleted file mode 100644 index 53756678..00000000 --- a/src/pygerber/gerberx3/parser/pyparsing/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -"""`pygerber.gerberx3.parser.pyparsing` package contains Gerber X3 parser implementation -based on pyparsing library. -""" diff --git a/src/pygerber/gerberx3/parser2/__init__.py b/src/pygerber/gerberx3/parser2/__init__.py deleted file mode 100644 index bad67c19..00000000 --- a/src/pygerber/gerberx3/parser2/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Gerber AST parser, version 2.""" diff --git a/src/pygerber/gerberx3/parser2/apertures2/__init__.py b/src/pygerber/gerberx3/parser2/apertures2/__init__.py deleted file mode 100644 index 8ac8934a..00000000 --- a/src/pygerber/gerberx3/parser2/apertures2/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Parser level abstraction of aperture info for Gerber AST parser, version 2.""" diff --git a/src/pygerber/gerberx3/parser2/apertures2/aperture2.py b/src/pygerber/gerberx3/parser2/apertures2/aperture2.py deleted file mode 100644 index fdfae1b2..00000000 --- a/src/pygerber/gerberx3/parser2/apertures2/aperture2.py +++ /dev/null @@ -1,54 +0,0 @@ -"""Parser level abstraction of aperture info for Gerber AST parser, version 2.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from pydantic import Field - -from pygerber.common.frozen_general_model import FrozenGeneralModel -from pygerber.common.immutable_map_model import ImmutableMapping -from pygerber.gerberx3.math.bounding_box import BoundingBox -from pygerber.gerberx3.math.offset import Offset -from pygerber.gerberx3.parser2.attributes2 import ApertureAttributes -from pygerber.gerberx3.state_enums import Mirroring -from pygerber.gerberx3.tokenizer.aperture_id import ApertureID - -if TYPE_CHECKING: - from decimal import Decimal - - from typing_extensions import Self - - from pygerber.gerberx3.parser2.commands2.flash2 import Flash2 - from pygerber.gerberx3.renderer2.abstract import Renderer2 - - -class Aperture2(FrozenGeneralModel): - """Parser level abstraction of aperture info.""" - - identifier: ApertureID - attributes: ApertureAttributes = Field(default_factory=ImmutableMapping) - - def render_flash(self, renderer: Renderer2, command: Flash2) -> None: - """Render draw operation.""" - raise NotImplementedError - - def get_bounding_box(self) -> BoundingBox: - """Return bounding box of aperture.""" - raise NotImplementedError - - def get_stroke_width(self) -> Offset: - """Get stroke width of command.""" - raise NotImplementedError - - def get_mirrored(self, mirror: Mirroring) -> Self: # noqa: ARG002 - """Get mirrored aperture.""" - return self - - def get_rotated(self, angle: Decimal) -> Self: # noqa: ARG002 - """Get copy of this aperture rotated around (0, 0).""" - return self - - def get_scaled(self, scale: Decimal) -> Self: # noqa: ARG002 - """Get copy of this aperture scaled by factor.""" - return self diff --git a/src/pygerber/gerberx3/parser2/apertures2/block2.py b/src/pygerber/gerberx3/parser2/apertures2/block2.py deleted file mode 100644 index f08f5c23..00000000 --- a/src/pygerber/gerberx3/parser2/apertures2/block2.py +++ /dev/null @@ -1,64 +0,0 @@ -"""Parser level abstraction of block aperture info for Gerber AST parser, version 2.""" - -from __future__ import annotations - -from decimal import Decimal -from typing import TYPE_CHECKING - -from pygerber.gerberx3.math.bounding_box import BoundingBox -from pygerber.gerberx3.parser2.apertures2.aperture2 import Aperture2 -from pygerber.gerberx3.parser2.command_buffer2 import ( - ReadonlyCommandBuffer2, -) -from pygerber.gerberx3.state_enums import Mirroring - -if TYPE_CHECKING: - from typing_extensions import Self - - from pygerber.gerberx3.parser2.commands2.flash2 import Flash2 - from pygerber.gerberx3.renderer2.abstract import Renderer2 - - -class Block2(Aperture2): - """Parser level abstraction of aperture info for block aperture.""" - - command_buffer: ReadonlyCommandBuffer2 - - def render_flash(self, renderer: Renderer2, command: Flash2) -> None: - """Render draw operation.""" - # Block apertures are resolved into series of commands at parser level. - raise NotImplementedError - - def get_bounding_box(self) -> BoundingBox: - """Return bounding box of aperture.""" - return self.command_buffer.get_bounding_box() - - def get_rotated(self, angle: Decimal) -> Self: - """Get copy of this aperture rotated around (0, 0).""" - if angle == Decimal("0.0"): - return self - return self.model_copy( - update={ - "command_buffer": self.command_buffer.get_rotated(angle), - }, - ) - - def get_mirrored(self, mirror: Mirroring) -> Self: - """Get mirrored aperture.""" - if mirror == Mirroring.NoMirroring: - return self - return self.model_copy( - update={ - "command_buffer": self.command_buffer.get_mirrored(mirror), - }, - ) - - def get_scaled(self, scale: Decimal) -> Self: - """Get copy of this aperture scaled by factor.""" - if scale == Decimal("1.0"): - return self - return self.model_copy( - update={ - "command_buffer": self.command_buffer.get_scaled(scale), - }, - ) diff --git a/src/pygerber/gerberx3/parser2/apertures2/circle2.py b/src/pygerber/gerberx3/parser2/apertures2/circle2.py deleted file mode 100644 index 49fbf3d8..00000000 --- a/src/pygerber/gerberx3/parser2/apertures2/circle2.py +++ /dev/null @@ -1,58 +0,0 @@ -"""Parser level abstraction of circle aperture info for Gerber AST parser, version 2.""" - -from __future__ import annotations - -from decimal import Decimal -from typing import TYPE_CHECKING, Optional - -from pygerber.gerberx3.math.bounding_box import BoundingBox -from pygerber.gerberx3.math.offset import Offset -from pygerber.gerberx3.parser2.apertures2.aperture2 import Aperture2 - -if TYPE_CHECKING: - from typing_extensions import Self - - from pygerber.gerberx3.parser2.commands2.flash2 import Flash2 - from pygerber.gerberx3.renderer2.abstract import Renderer2 - - -class Circle2(Aperture2): - """Parser level abstraction of aperture info for circle aperture.""" - - diameter: Offset - hole_diameter: Optional[Offset] - - def render_flash(self, renderer: Renderer2, command: Flash2) -> None: - """Render draw operation.""" - renderer.hooks.render_flash_circle(command, self) - - def get_bounding_box(self) -> BoundingBox: - """Get bounding box of draw operation.""" - return BoundingBox.from_diameter(self.diameter) - - def get_stroke_width(self) -> Offset: - """Get stroke width of command.""" - return self.diameter - - def get_scaled(self, scale: Decimal) -> Self: - """Get copy of this aperture scaled by factor.""" - if scale == Decimal("1.0"): - return self - return self.model_copy( - update={ - "diameter": (self.diameter * scale), - "hole_diameter": ( - None if self.hole_diameter is None else self.hole_diameter * scale - ), - }, - ) - - -class NoCircle2(Circle2): - """Dummy aperture representing case when aperture is not needed but has to be - given to denote width of draw line command. - """ - - def render_flash(self, renderer: Renderer2, command: Flash2) -> None: - """Render draw operation.""" - renderer.hooks.render_flash_no_circle(command, self) diff --git a/src/pygerber/gerberx3/parser2/apertures2/macro2.py b/src/pygerber/gerberx3/parser2/apertures2/macro2.py deleted file mode 100644 index c0beb6c2..00000000 --- a/src/pygerber/gerberx3/parser2/apertures2/macro2.py +++ /dev/null @@ -1,61 +0,0 @@ -"""Parser level abstraction of macro aperture info for Gerber AST parser, version 2.""" - -from __future__ import annotations - -from decimal import Decimal -from typing import TYPE_CHECKING - -from pygerber.gerberx3.math.bounding_box import BoundingBox -from pygerber.gerberx3.parser2.apertures2.aperture2 import Aperture2 -from pygerber.gerberx3.parser2.command_buffer2 import ReadonlyCommandBuffer2 -from pygerber.gerberx3.state_enums import Mirroring - -if TYPE_CHECKING: - from typing_extensions import Self - - from pygerber.gerberx3.parser2.commands2.flash2 import Flash2 - from pygerber.gerberx3.renderer2.abstract import Renderer2 - - -class Macro2(Aperture2): - """Parser level abstraction of aperture info for macro aperture.""" - - command_buffer: ReadonlyCommandBuffer2 - - def render_flash(self, renderer: Renderer2, command: Flash2) -> None: - """Render draw operation.""" - renderer.hooks.render_flash_macro(command, self) - - def get_bounding_box(self) -> BoundingBox: - """Return bounding box of aperture.""" - return self.command_buffer.get_bounding_box() - - def get_mirrored(self, mirror: Mirroring) -> Self: - """Get mirrored aperture.""" - if mirror == Mirroring.NoMirroring: - return self - return self.model_copy( - update={ - "command_buffer": self.command_buffer.get_mirrored(mirror), - }, - ) - - def get_rotated(self, angle: Decimal) -> Self: - """Get copy of this aperture rotated around (0, 0).""" - if angle == Decimal("0.0"): - return self - return self.model_copy( - update={ - "command_buffer": self.command_buffer.get_rotated(angle), - }, - ) - - def get_scaled(self, scale: Decimal) -> Self: - """Get copy of this aperture scaled by factor.""" - if scale == Decimal("1.0"): - return self - return self.model_copy( - update={ - "command_buffer": self.command_buffer.get_scaled(scale), - }, - ) diff --git a/src/pygerber/gerberx3/parser2/apertures2/obround2.py b/src/pygerber/gerberx3/parser2/apertures2/obround2.py deleted file mode 100644 index 32cdf72c..00000000 --- a/src/pygerber/gerberx3/parser2/apertures2/obround2.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Parser level abstraction of obround aperture info for Gerber AST parser, -version 2. -""" - -from __future__ import annotations - -from typing import TYPE_CHECKING, Optional - -from pygerber.gerberx3.math.offset import Offset -from pygerber.gerberx3.parser2.apertures2.rectangle2 import Rectangle2 - -if TYPE_CHECKING: - from pygerber.gerberx3.parser2.commands2.flash2 import Flash2 - from pygerber.gerberx3.renderer2.abstract import Renderer2 - - -class Obround2(Rectangle2): - """Parser level abstraction of aperture info for obround aperture.""" - - x_size: Offset - y_size: Offset - hole_diameter: Optional[Offset] - - def render_flash(self, renderer: Renderer2, command: Flash2) -> None: - """Render draw operation.""" - renderer.hooks.render_flash_obround(command, self) diff --git a/src/pygerber/gerberx3/parser2/apertures2/polygon2.py b/src/pygerber/gerberx3/parser2/apertures2/polygon2.py deleted file mode 100644 index ad867205..00000000 --- a/src/pygerber/gerberx3/parser2/apertures2/polygon2.py +++ /dev/null @@ -1,50 +0,0 @@ -"""Parser level abstraction of polygon aperture info for Gerber AST parser, -version 2. -""" - -from __future__ import annotations - -from decimal import Decimal # noqa: TCH003 -from typing import TYPE_CHECKING, Optional - -from pygerber.gerberx3.math.bounding_box import BoundingBox -from pygerber.gerberx3.math.offset import Offset -from pygerber.gerberx3.parser2.apertures2.aperture2 import Aperture2 - -if TYPE_CHECKING: - from typing_extensions import Self - - from pygerber.gerberx3.parser2.commands2.flash2 import Flash2 - from pygerber.gerberx3.renderer2.abstract import Renderer2 - - -class Polygon2(Aperture2): - """Parser level abstraction of aperture info for polygon aperture.""" - - outer_diameter: Offset - number_vertices: int - rotation: Decimal - hole_diameter: Optional[Offset] - - def render_flash(self, renderer: Renderer2, command: Flash2) -> None: - """Render draw operation.""" - renderer.hooks.render_flash_polygon(command, self) - - def get_bounding_box(self) -> BoundingBox: - """Return bounding box of aperture.""" - return BoundingBox.from_diameter(self.outer_diameter) - - def get_stroke_width(self) -> Offset: - """Get stroke width of command.""" - return self.outer_diameter - - def get_scaled(self, scale: Decimal) -> Self: - """Get copy of this aperture scaled by factor.""" - return self.model_copy( - update={ - "outer_diameter": self.outer_diameter * scale, - "hole_diameter": ( - None if self.hole_diameter is None else self.hole_diameter * scale - ), - }, - ) diff --git a/src/pygerber/gerberx3/parser2/apertures2/rectangle2.py b/src/pygerber/gerberx3/parser2/apertures2/rectangle2.py deleted file mode 100644 index 104378ae..00000000 --- a/src/pygerber/gerberx3/parser2/apertures2/rectangle2.py +++ /dev/null @@ -1,61 +0,0 @@ -"""Parser level abstraction of rectangle aperture info for Gerber AST parser, -version 2. -""" - -from __future__ import annotations - -from decimal import Decimal -from typing import TYPE_CHECKING, Optional - -from pydantic import Field - -from pygerber.gerberx3.math.bounding_box import BoundingBox -from pygerber.gerberx3.math.offset import Offset -from pygerber.gerberx3.parser2.apertures2.aperture2 import Aperture2 - -if TYPE_CHECKING: - from typing_extensions import Self - - from pygerber.gerberx3.parser2.commands2.flash2 import Flash2 - from pygerber.gerberx3.renderer2.abstract import Renderer2 - - -class Rectangle2(Aperture2): - """Parser level abstraction of aperture info for rectangle aperture.""" - - x_size: Offset - y_size: Offset - hole_diameter: Optional[Offset] - rotation: Decimal = Field(default=Decimal("0.0")) - - def render_flash(self, renderer: Renderer2, command: Flash2) -> None: - """Render draw operation.""" - renderer.hooks.render_flash_rectangle(command, self) - - def get_stroke_width(self) -> Offset: - """Return stroke width of aperture.""" - return (self.x_size + self.y_size) / 2 - - def get_bounding_box(self) -> BoundingBox: - """Return bounding box of aperture.""" - return BoundingBox.from_rectangle(self.x_size, self.y_size).get_rotated( - self.rotation, - ) - - def get_scaled(self, scale: Decimal) -> Self: - """Get copy of this aperture scaled by factor.""" - return self.model_copy( - update={ - "x_size": self.x_size * scale, - "y_size": self.y_size * scale, - "hole_diameter": ( - None if self.hole_diameter is None else self.hole_diameter * scale - ), - }, - ) - - def get_rotated(self, angle: Decimal) -> Self: - """Get copy of this aperture rotated around (0, 0).""" - return self.model_copy( - update={"rotation": (self.rotation + angle) % Decimal(360)}, - ) diff --git a/src/pygerber/gerberx3/parser2/attributes2.py b/src/pygerber/gerberx3/parser2/attributes2.py deleted file mode 100644 index beae6f2f..00000000 --- a/src/pygerber/gerberx3/parser2/attributes2.py +++ /dev/null @@ -1,295 +0,0 @@ -"""Attribute dictionaries for Gerber X3 parser.""" - -from __future__ import annotations - -import datetime -from enum import Enum -from typing import TYPE_CHECKING, Optional - -from pydantic import Field - -from pygerber.common.frozen_general_model import FrozenGeneralModel -from pygerber.common.immutable_map_model import ImmutableMapping -from pygerber.gerberx3.parser2.errors2 import ( - MissingGuidFieldError, -) - -if TYPE_CHECKING: - from typing_extensions import Self - - -class AttributesDictionary(ImmutableMapping[str, Optional[str]]): - """Base class for container holding attributes.""" - - def __str__(self) -> str: - return f"{self.__class__.__qualname__}({self.mapping})" - - -class StandardAttributeBase(FrozenGeneralModel): - """Class for wrapping standard attribute content.""" - - @classmethod - def parse(cls, content: str) -> Self: - """Parse attribute content.""" - raise NotImplementedError - - -class PartAttribute(StandardAttributeBase): - """The value of the .Part file attribute identifies which part is described. The - attribute - if present - must be defined in the header. - """ - - class Part(Enum): - """Enumerate supported part types.""" - - Single = "Single" - Array = "Array" - FabricationPanel = "FabricationPanel" - Coupon = "Coupon" - Other = "Other" - - part: PartAttribute.Part - field: str = Field(default="") - - @classmethod - def parse(cls, content: str) -> Self: - """Return original content.""" - parts = dict(enumerate(content.split(",", 1))) - return cls( - part=cls.Part(parts[0]), - field=parts.get(1, ""), - ) - - -class GenerationSoftwareAttribute(StandardAttributeBase): - """Usually a Gerber file is part of a PCB project with a sequence of revisions. - The purpose of the .ProjectId file attribute is to uniquely identify project and - revision.This is especially important to check whether all files belong to the same - revision. By its nature, these values can only be defined by the creator of the - project and revision. The attribute - if present - must be defined in the header. - - The syntax is as follows: - - ``` - %TF.ProjectId,,,*% - ``` - """ - - name: str - guid: str - revision: str - - @classmethod - def parse(cls, content: str) -> Self: - """Return original content.""" - items = dict(enumerate(content.split(","))) - - if (name := items.get(0)) is None: - msg = "Missing name field for .GenerationSoftware attribute." - raise MissingGuidFieldError(msg) - - if (guid := items.get(1)) is None: - msg = "Missing guid field for .GenerationSoftware attribute." - raise MissingGuidFieldError(msg) - - if (revision := items.get(2)) is None: - msg = "Missing revision field for .GenerationSoftware attribute." - raise MissingGuidFieldError(msg) - - return cls( - name=name, - guid=guid, - revision=revision, - ) - - -class FileAttributes(AttributesDictionary): - """File attributes.""" - - @property - def Part(self) -> Optional[str]: # noqa: N802 - """Identifies the part the file represents, e.g. a single PCB. - - Standard file attribute. - """ - return self.get(".Part") - - @property - def FileFunction(self) -> Optional[str]: # noqa: N802 - """Identifies the file's function in the PCB, e.g. top copper layer. - - Standard file attribute. - """ - return self.get(".FileFunction") - - @property - def FilePolarity(self) -> Optional[str]: # noqa: N802 - """Positive or Negative. This defines whether the image represents the presence - or absence of material. - - Standard file attribute. - """ - return self.get(".FilePolarity") - - @property - def SameCoordinates(self) -> Optional[str]: # noqa: N802 - """All files in a fabrication data set with this attribute use the same - coordinates. In other words, they align. - - Standard file attribute. - """ - return self.get(".SameCoordinates") - - @property - def CreationDate(self) -> Optional[datetime.datetime]: # noqa: N802 - """Defines the creation date and time of the file. - - Standard file attribute. - """ - if (val := self.get(".CreationDate")) is not None: - return datetime.datetime.fromisoformat(val) - return None - - @property - def GenerationSoftware(self) -> Optional[GenerationSoftwareAttribute]: # noqa: N802 - """Identifies the software creating the file. - - Standard file attribute. - """ - if (val := self.get(".GenerationSoftware")) is not None: - return GenerationSoftwareAttribute.parse(val) - return None - - @property - def ProjectId(self) -> Optional[str]: # noqa: N802 - """Defines project and revisions. - - Standard file attribute. - """ - return self.get(".ProjectId") - - @property - def MD5(self) -> Optional[str]: # noqa: N802 - """Sets the MD5 file signature or checksum. - - Standard file attribute. - """ - return self.get(".MD5") - - -class AperFunctionAttribute(StandardAttributeBase): - """Function of objects created with the apertures, e.g. SMD pad.""" - - class Function(Enum): - """Type of drilling.""" - - ViaDrill = "ViaDrill" - BackDrill = "BackDrill" - ComponentDrill = "ComponentDrill" - MechanicalDrill = "MechanicalDrill" - CastellatedDrill = "CastellatedDrill" - OtherDrill = "OtherDrill" - ComponentPad = "ComponentPad" - SMDPad = "SMDPad" - BGAPad = "BGAPad" - ConnectorPad = "ConnectorPad" - HeatsinkPad = "HeatsinkPad" - ViaPad = "ViaPad" - TestPad = "TestPad" - CastellatedPad = "CastellatedPad" - FiducialPad = "FiducialPad" - ThermalReliefPad = "ThermalReliefPad" - WasherPad = "WasherPad" - AntiPad = "AntiPad" - OtherPad = "OtherPad" - Conductor = "Conductor" - EtchedComponent = "EtchedComponent" - NonConductor = "NonConductor" - CopperBalancing = "CopperBalancing" - Border = "Border" - OtherCopper = "OtherCopper" - ComponentMain = "ComponentMain" - ComponentOutline = "ComponentOutline" - ComponentPin = "ComponentPin" - Profile = "Profile" - Material = "Material" - NonMaterial = "NonMaterial" - Other = "Other" - - function: Optional[AperFunctionAttribute.Function] - field: str = Field(default="") - - @classmethod - def parse(cls, content: str) -> Self: - """Return original content.""" - parts = dict(enumerate(content.split(",", 1))) - return cls( - function=cls.Function(parts[0]), - field=parts.get(1, ""), - ) - - -class ApertureAttributes(AttributesDictionary): - """Aperture attributes.""" - - @property - def AperFunction(self) -> Optional[AperFunctionAttribute]: # noqa: N802 - """Function of objects created with the apertures, e.g. SMD pad.""" - if (val := self.get(".AperFunction")) is not None: - return AperFunctionAttribute.parse(val) - return None - - @property - def DrillTolerance(self) -> Optional[str]: # noqa: N802 - """Tolerance of drill holes.""" - return self.get(".DrillTolerance") - - @property - def FlashText(self) -> Optional[str]: # noqa: N802 - """Provides the source text and font for flashes representing text.""" - return self.get(".FlashText") - - -class PAttribute(StandardAttributeBase): - """The .P object attribute attaches the reference descriptor and pin number of a - component pin to a pad on an outer copper layer or a ComponentPin in a component - layer. - - The syntax is: - ``` - <.P Attribute> = .P,,[,] - ``` - """ - - refdes: str - number: str - function: Optional[str] - - @classmethod - def parse(cls, content: str) -> Self: - """Return original content.""" - parts = dict(enumerate(content.split(","))) - return cls(refdes=parts[0], number=parts[1], function=parts.get(2)) - - -class ObjectAttributes(AttributesDictionary): - """Object attributes.""" - - @property - def N(self) -> Optional[str]: # noqa: N802 - """The CAD net name of a conducting object, e.g. Clk13.""" - return self.get(".N") - - @property - def P(self) -> Optional[PAttribute]: # noqa: N802 - """The pin number (or name) and reference descriptor of a component pad on an - outer layer, e.g. IC3,7. - """ - if (val := self.get(".P")) is not None: - return PAttribute.parse(val) - return None - - @property - def C(self) -> Optional[str]: # noqa: N802 - """The component reference designator linked to an object, e.g. C2.""" - return self.get(".C") diff --git a/src/pygerber/gerberx3/parser2/command_buffer2.py b/src/pygerber/gerberx3/parser2/command_buffer2.py deleted file mode 100644 index 0c7fc5fb..00000000 --- a/src/pygerber/gerberx3/parser2/command_buffer2.py +++ /dev/null @@ -1,112 +0,0 @@ -"""Module contains definition of class for buffering draw commands.""" - -from __future__ import annotations - -import textwrap -from typing import TYPE_CHECKING, Iterator, List, Optional - -from pydantic import Field - -from pygerber.common.frozen_general_model import FrozenGeneralModel -from pygerber.gerberx3.math.bounding_box import BoundingBox -from pygerber.gerberx3.math.vector_2d import Vector2D -from pygerber.gerberx3.parser2.commands2.command2 import Command2 -from pygerber.gerberx3.state_enums import Mirroring - -if TYPE_CHECKING: - from decimal import Decimal - - from typing_extensions import Self - - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class CommandBuffer2: - """Container for buffering draw commands.""" - - def __init__(self, commands: Optional[list[Command2]] = None) -> None: - self.commands: list[Command2] = [] if commands is None else commands - - @classmethod - def factory(cls, context: Parser2Context) -> Self: # noqa: ARG003 - """CommandBuffer2 factory.""" - return cls(commands=[]) - - def add_command(self, __command: Command2) -> None: - """Add draw command to command buffer.""" - self.commands.append(__command) - - def get_readonly(self) -> ReadonlyCommandBuffer2: - """Make buffer read-only.""" - return ReadonlyCommandBuffer2(commands=self.commands) - - def copy(self) -> CommandBuffer2: - """Create copy of command buffer.""" - return CommandBuffer2(commands=self.commands.copy()) - - def __iter__(self) -> Iterator[Command2]: - """Iterate over buffered draw commands.""" - yield from self.commands - - def __getitem__(self, index: int) -> Command2: - """Get item by index from commands.""" - return self.commands[index] - - -class ReadonlyCommandBuffer2(FrozenGeneralModel): - """Read only command buffer proxy.""" - - commands: List[Command2] = Field(default_factory=list) - - def __len__(self) -> int: - """Return length of buffered commands.""" - return len(self.commands) - - def __iter__(self) -> Iterator[Command2]: # type: ignore[override] - """Iterate over buffered draw commands.""" - yield from self.commands - - def __getitem__(self, index: int) -> Command2: - """Get item by index from commands.""" - return self.commands[index] - - def debug_buffer_to_json(self, indent: int = 4) -> str: - """Convert buffered draw commands to JSON.""" - command_chain = ",\n".join(c.command_to_json() for c in self) - return f"[\n{textwrap.indent(command_chain, ' ' * indent)}\n]" - - def get_mirrored(self, mirror: Mirroring) -> Self: - """Get new command buffer with all commands mirrored.""" - return self.model_copy( - update={"commands": [c.get_mirrored(mirror) for c in self.commands]}, - ) - - def get_transposed(self, vector: Vector2D) -> Self: - """Get new command buffer with all commands transposed.""" - return self.model_copy( - update={"commands": [c.get_transposed(vector) for c in self.commands]}, - ) - - def get_rotated(self, angle: Decimal) -> Self: - """Get copy of this command rotated around (0, 0).""" - return self.model_copy( - update={"commands": [c.get_rotated(angle) for c in self.commands]}, - ) - - def get_scaled(self, scale: Decimal) -> Self: - """Get copy of this aperture scaled by factor.""" - return self.model_copy( - update={"commands": [c.get_scaled(scale) for c in self.commands]}, - ) - - def get_bounding_box(self) -> BoundingBox: - """Get bounding box of command buffer.""" - bbox: Optional[BoundingBox] = None - - for command in self: - if bbox is None: - bbox = command.get_bounding_box() - else: - bbox += command.get_bounding_box() - - return BoundingBox.NULL if bbox is None else bbox diff --git a/src/pygerber/gerberx3/parser2/commands2/__init__.py b/src/pygerber/gerberx3/parser2/commands2/__init__.py deleted file mode 100644 index 6db2845d..00000000 --- a/src/pygerber/gerberx3/parser2/commands2/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Parser level abstraction of draw operation info for Gerber AST parser, version 2.""" diff --git a/src/pygerber/gerberx3/parser2/commands2/aperture_draw_command2.py b/src/pygerber/gerberx3/parser2/commands2/aperture_draw_command2.py deleted file mode 100644 index b0e1cfd0..00000000 --- a/src/pygerber/gerberx3/parser2/commands2/aperture_draw_command2.py +++ /dev/null @@ -1,31 +0,0 @@ -"""Parser level abstraction of draw operation which utilizes apertures for Gerber AST -parser, version 2. -""" - -from __future__ import annotations - -from typing import TYPE_CHECKING, Generator - -from pydantic import Field - -from pygerber.gerberx3.parser2.apertures2.aperture2 import Aperture2 -from pygerber.gerberx3.parser2.attributes2 import ObjectAttributes -from pygerber.gerberx3.parser2.commands2.command2 import Command2 - -if TYPE_CHECKING: - from pygerber.gerberx3.renderer2.abstract import Renderer2 - - -class ApertureDrawCommand2(Command2): - """Parser level abstraction of draw operation for Gerber AST parser, version 2.""" - - attributes: ObjectAttributes = Field(default_factory=ObjectAttributes) - aperture: Aperture2 - - def render_iter(self, hooks: Renderer2) -> Generator[Command2, None, None]: - """Render draw operation.""" - self.render(hooks) - yield self - - def __str__(self) -> str: - return f"{self.__class__.__qualname__}()" diff --git a/src/pygerber/gerberx3/parser2/commands2/arc2.py b/src/pygerber/gerberx3/parser2/commands2/arc2.py deleted file mode 100644 index 4e1abc59..00000000 --- a/src/pygerber/gerberx3/parser2/commands2/arc2.py +++ /dev/null @@ -1,131 +0,0 @@ -"""Parser level abstraction of draw arc operation for Gerber AST parser, version 2.""" - -from __future__ import annotations - -from decimal import Decimal -from typing import TYPE_CHECKING - -from pygerber.gerberx3.math.bounding_box import BoundingBox -from pygerber.gerberx3.math.offset import Offset -from pygerber.gerberx3.math.vector_2d import Vector2D -from pygerber.gerberx3.parser2.commands2.aperture_draw_command2 import ( - ApertureDrawCommand2, -) -from pygerber.gerberx3.state_enums import Mirroring - -if TYPE_CHECKING: - from typing_extensions import Self - - from pygerber.gerberx3.renderer2.abstract import Renderer2 - - -class Arc2(ApertureDrawCommand2): - """Parser level abstraction of draw arc operation for Gerber AST parser, - version 2. - """ - - start_point: Vector2D - end_point: Vector2D - center_point: Vector2D - - def get_relative_start_point(self) -> Vector2D: - """Get starting point relative to arc center.""" - return self.start_point - self.center_point - - def get_relative_end_point(self) -> Vector2D: - """Get ending point relative to arc center.""" - return self.end_point - self.center_point - - def get_relative_center_point(self) -> Vector2D: - """Get center point relative to arc center.""" - return self.center_point - self.center_point - - def get_radius(self) -> Offset: - """Get radius of circle arc.""" - return self.get_relative_start_point().length() - - def get_bounding_box(self) -> BoundingBox: - """Return bounding box of arc.""" - return ( - BoundingBox.from_diameter( - (self.get_radius() * 2) + (self.aperture.get_stroke_width() * 2), - ) - + self.center_point - ) - - def get_mirrored(self, mirror: Mirroring) -> Self: - """Get mirrored command. - - Mirroring is a NOOP if mirror is `Mirroring.NoMirroring`. - """ - if mirror == Mirroring.NoMirroring: - return self - - new_end_point = self.end_point.get_mirrored(mirror) - new_start_point = self.start_point.get_mirrored(mirror) - new_center_point = self.center_point.get_mirrored(mirror) - - if mirror == Mirroring.XY: - return self.model_copy( - update={ - "end_point": new_end_point, - "start_point": new_start_point, - "center_point": new_center_point, - }, - ) - - return self.model_copy( - update={ - "end_point": new_start_point, - "start_point": new_end_point, - "center_point": new_center_point, - }, - ) - - def get_rotated(self, angle: Decimal) -> Self: - """Get copy of this command rotated around (0, 0).""" - return self.model_copy( - update={ - "start_point": self.start_point.get_rotated(angle), - "end_point": self.end_point.get_rotated(angle), - "center_point": self.center_point.get_rotated(angle), - }, - ) - - def get_transposed(self, vector: Vector2D) -> Self: - """Get transposed command.""" - return self.model_copy( - update={ - "start_point": self.start_point + vector, - "end_point": self.end_point + vector, - "center_point": self.center_point + vector, - }, - ) - - def get_scaled(self, scale: Decimal) -> Self: - """Get copy of this aperture scaled by factor.""" - if scale == Decimal("1.0"): - return self - return self.model_copy( - update={ - "start_point": self.start_point.get_scaled(scale), - "end_point": self.end_point.get_scaled(scale), - "center_point": self.center_point.get_scaled(scale), - "aperture": self.aperture.get_scaled(scale), - "transform": self.transform.get_scaled(scale), - }, - ) - - def render(self, renderer: Renderer2) -> None: - """Render draw operation.""" - renderer.hooks.render_arc(self) - - -class CCArc2(Arc2): - """Parser level abstraction of draw counterclockwise arc operation for Gerber AST - parser, version 2. - """ - - def render(self, renderer: Renderer2) -> None: - """Render draw operation.""" - renderer.hooks.render_cc_arc(self) diff --git a/src/pygerber/gerberx3/parser2/commands2/buffer_command2.py b/src/pygerber/gerberx3/parser2/commands2/buffer_command2.py deleted file mode 100644 index f4cc0cc2..00000000 --- a/src/pygerber/gerberx3/parser2/commands2/buffer_command2.py +++ /dev/null @@ -1,90 +0,0 @@ -"""Parser level abstraction of command that consists of multiple commands for Gerber AST -parser, version 2. -""" - -from __future__ import annotations - -from typing import TYPE_CHECKING, Generator, Iterator - -from pygerber.gerberx3.math.bounding_box import BoundingBox -from pygerber.gerberx3.parser2.command_buffer2 import ReadonlyCommandBuffer2 -from pygerber.gerberx3.parser2.commands2.command2 import Command2 -from pygerber.gerberx3.state_enums import Mirroring - -if TYPE_CHECKING: - from decimal import Decimal - - from typing_extensions import Self - - from pygerber.gerberx3.math.vector_2d import Vector2D - from pygerber.gerberx3.renderer2.abstract import Renderer2 - - -class BufferCommand2(Command2): - """Parser level abstraction of command that consists of multiple commands for Gerber - AST parser, version 2. - """ - - command_buffer: ReadonlyCommandBuffer2 - - def get_mirrored(self, mirror: Mirroring) -> Self: - """Get mirrored command. - - Mirroring is a NOOP if mirror is `Mirroring.NoMirroring`. - """ - if mirror == Mirroring.NoMirroring: - return self - return self.model_copy( - update={ - "command_buffer": self.command_buffer.get_mirrored(mirror), - }, - ) - - def get_transposed(self, vector: Vector2D) -> Self: - """Get transposed command.""" - return self.model_copy( - update={ - "command_buffer": self.command_buffer.get_transposed(vector), - }, - ) - - def get_rotated(self, angle: Decimal) -> Self: - """Get copy of this command rotated around (0, 0).""" - return self.model_copy( - update={ - "command_buffer": self.command_buffer.get_rotated(angle), - }, - ) - - def get_scaled(self, scale: Decimal) -> Self: - """Get copy of this aperture scaled by factor.""" - return self.model_copy( - update={ - "command_buffer": self.command_buffer.get_scaled(scale), - }, - ) - - def get_bounding_box(self) -> BoundingBox: - """Get bounding box of draw operation.""" - return self.command_buffer.get_bounding_box() - - def render(self, renderer: Renderer2) -> None: - """Render draw operation.""" - for _ in renderer.hooks.render_buffer(self): - pass - - def render_iter(self, renderer: Renderer2) -> Generator[Command2, None, None]: - """Render draw operation.""" - yield from renderer.hooks.render_buffer(self) - - def __len__(self) -> int: - """Return length of buffered commands.""" - return len(self.command_buffer) - - def __iter__(self) -> Iterator[Command2]: # type: ignore[override] - """Iterate over buffered draw commands.""" - yield from self.command_buffer - - def __getitem__(self, index: int) -> Command2: - """Get item by index from commands.""" - return self.command_buffer[index] diff --git a/src/pygerber/gerberx3/parser2/commands2/command2.py b/src/pygerber/gerberx3/parser2/commands2/command2.py deleted file mode 100644 index 80cbb72e..00000000 --- a/src/pygerber/gerberx3/parser2/commands2/command2.py +++ /dev/null @@ -1,72 +0,0 @@ -"""Parser level abstraction of draw operation for Gerber AST parser, version 2.""" - -from __future__ import annotations - -import json -from typing import TYPE_CHECKING, Generator - -from pygerber.common.frozen_general_model import FrozenGeneralModel -from pygerber.gerberx3.math.bounding_box import BoundingBox -from pygerber.gerberx3.math.vector_2d import Vector2D -from pygerber.gerberx3.parser2.state2 import ApertureTransform -from pygerber.gerberx3.state_enums import Mirroring - -if TYPE_CHECKING: - from decimal import Decimal - - from typing_extensions import Self - - from pygerber.gerberx3.renderer2.abstract import Renderer2 - - -class Command2(FrozenGeneralModel): - """Parser level abstraction of draw operation for Gerber AST parser, version 2.""" - - transform: ApertureTransform - - def get_bounding_box(self) -> BoundingBox: - """Get bounding box of draw operation.""" - raise NotImplementedError - - def get_mirrored(self, mirror: Mirroring) -> Self: - """Get mirrored command. - - Mirroring is a NOOP if mirror is `Mirroring.NoMirroring`. - """ - raise NotImplementedError - - def get_transposed(self, vector: Vector2D) -> Self: - """Get transposed command.""" - raise NotImplementedError - - def get_rotated(self, angle: Decimal) -> Self: - """Get copy of this command rotated around (0, 0).""" - raise NotImplementedError - - def get_scaled(self, scale: Decimal) -> Self: - """Get copy of this aperture scaled by factor.""" - raise NotImplementedError - - def render(self, renderer: Renderer2) -> None: - """Render draw operation.""" - raise NotImplementedError - - def render_iter( - self, - renderer: Renderer2, # noqa: ARG002 - ) -> Generator[Command2, None, None]: - """Render draw operation.""" - raise NotImplementedError - yield # type: ignore[unreachable] - - def command_to_json(self) -> str: - """Dump draw operation.""" - return json.dumps( - { - "cls": f"{self.__module__}.{self.__class__.__qualname__}", - "dict": json.loads(self.model_dump_json()), - }, - ) - - def __str__(self) -> str: - return f"{self.__class__.__qualname__}()" diff --git a/src/pygerber/gerberx3/parser2/commands2/flash2.py b/src/pygerber/gerberx3/parser2/commands2/flash2.py deleted file mode 100644 index 0d8e9d15..00000000 --- a/src/pygerber/gerberx3/parser2/commands2/flash2.py +++ /dev/null @@ -1,77 +0,0 @@ -"""Parser level abstraction of flash operation for Gerber AST parser, version 2.""" - -from __future__ import annotations - -from decimal import Decimal -from typing import TYPE_CHECKING - -from pygerber.gerberx3.math.bounding_box import BoundingBox -from pygerber.gerberx3.math.vector_2d import Vector2D -from pygerber.gerberx3.parser2.commands2.aperture_draw_command2 import ( - ApertureDrawCommand2, -) -from pygerber.gerberx3.state_enums import Mirroring - -if TYPE_CHECKING: - from typing_extensions import Self - - from pygerber.gerberx3.renderer2.abstract import Renderer2 - - -class Flash2(ApertureDrawCommand2): - """Parser level abstraction of flash operation for Gerber AST parser, - version 2. - """ - - flash_point: Vector2D - - def get_mirrored(self, mirror: Mirroring) -> Self: - """Get mirrored command. - - Mirroring is a NOOP if mirror is `Mirroring.NoMirroring`. - """ - if mirror == Mirroring.NoMirroring: - return self - return self.model_copy( - update={ - "flash_point": self.flash_point.get_mirrored(mirror), - "aperture": self.aperture.get_mirrored(mirror), - }, - ) - - def get_transposed(self, vector: Vector2D) -> Self: - """Get transposed command.""" - return self.model_copy( - update={ - "flash_point": self.flash_point + vector, - }, - ) - - def get_rotated(self, angle: Decimal) -> Self: - """Get copy of this command rotated around (0, 0).""" - return self.model_copy( - update={ - "flash_point": self.flash_point.get_rotated(angle), - "aperture": self.aperture.get_rotated(angle), - }, - ) - - def get_scaled(self, scale: Decimal) -> Self: - """Get copy of this aperture scaled by factor.""" - if scale == Decimal("1.0"): - return self - return self.model_copy( - update={ - "flash_point": self.flash_point.get_scaled(scale), - "aperture": self.aperture.get_scaled(scale), - "transform": self.transform.get_scaled(scale), - }, - ) - - def render(self, renderer: Renderer2) -> None: - """Render draw operation.""" - self.aperture.render_flash(renderer, self) - - def get_bounding_box(self) -> BoundingBox: - """Get bounding box of draw operation.""" - return self.aperture.get_bounding_box() + self.flash_point diff --git a/src/pygerber/gerberx3/parser2/commands2/line2.py b/src/pygerber/gerberx3/parser2/commands2/line2.py deleted file mode 100644 index d3fd6b22..00000000 --- a/src/pygerber/gerberx3/parser2/commands2/line2.py +++ /dev/null @@ -1,81 +0,0 @@ -"""Parser level abstraction of draw line operation for Gerber AST parser, version 2.""" - -from __future__ import annotations - -from decimal import Decimal -from typing import TYPE_CHECKING - -from pygerber.gerberx3.math.bounding_box import BoundingBox -from pygerber.gerberx3.math.vector_2d import Vector2D -from pygerber.gerberx3.parser2.commands2.aperture_draw_command2 import ( - ApertureDrawCommand2, -) -from pygerber.gerberx3.state_enums import Mirroring - -if TYPE_CHECKING: - from typing_extensions import Self - - from pygerber.gerberx3.renderer2.abstract import Renderer2 - - -class Line2(ApertureDrawCommand2): - """Parser level abstraction of draw line operation for Gerber AST parser, - version 2. - """ - - start_point: Vector2D - end_point: Vector2D - - def get_bounding_box(self) -> BoundingBox: - """Return bounding box of draw operation.""" - vertex_box = self.aperture.get_bounding_box() - return (vertex_box + self.start_point) + (vertex_box + self.end_point) - - def get_mirrored(self, mirror: Mirroring) -> Self: - """Get mirrored command. - - Mirroring is a NOOP if mirror is `Mirroring.NoMirroring`. - """ - if mirror == Mirroring.NoMirroring: - return self - return self.model_copy( - update={ - "start_point": self.start_point.get_mirrored(mirror), - "end_point": self.end_point.get_mirrored(mirror), - }, - ) - - def get_transposed(self, vector: Vector2D) -> Self: - """Get transposed command.""" - return self.model_copy( - update={ - "start_point": self.start_point + vector, - "end_point": self.end_point + vector, - }, - ) - - def get_rotated(self, angle: Decimal) -> Self: - """Get copy of this command rotated around (0, 0).""" - return self.model_copy( - update={ - "start_point": self.start_point.get_rotated(angle), - "end_point": self.end_point.get_rotated(angle), - }, - ) - - def get_scaled(self, scale: Decimal) -> Self: - """Get copy of this aperture scaled by factor.""" - if scale == Decimal("1.0"): - return self - return self.model_copy( - update={ - "start_point": self.start_point.get_scaled(scale), - "end_point": self.end_point.get_scaled(scale), - "aperture": self.aperture.get_scaled(scale), - "transform": self.transform.get_scaled(scale), - }, - ) - - def render(self, renderer: Renderer2) -> None: - """Render draw operation.""" - renderer.hooks.render_line(self) diff --git a/src/pygerber/gerberx3/parser2/commands2/region2.py b/src/pygerber/gerberx3/parser2/commands2/region2.py deleted file mode 100644 index 37d4447c..00000000 --- a/src/pygerber/gerberx3/parser2/commands2/region2.py +++ /dev/null @@ -1,47 +0,0 @@ -"""Parser level abstraction of draw region operation for Gerber AST parser, -version 2. -""" - -from __future__ import annotations - -from typing import TYPE_CHECKING, Generator - -from pydantic import Field - -from pygerber.gerberx3.parser2.attributes2 import ApertureAttributes, ObjectAttributes -from pygerber.gerberx3.parser2.command_buffer2 import ReadonlyCommandBuffer2 -from pygerber.gerberx3.parser2.commands2.buffer_command2 import BufferCommand2 -from pygerber.gerberx3.parser2.commands2.command2 import Command2 - -if TYPE_CHECKING: - from pygerber.gerberx3.renderer2.abstract import Renderer2 - - -class Region2(BufferCommand2): - """Parser level abstraction of draw region operation for Gerber AST parser, - version 2. - """ - - aperture_attributes: ApertureAttributes = Field(default_factory=ApertureAttributes) - object_attributes: ObjectAttributes = Field(default_factory=ObjectAttributes) - command_buffer: ReadonlyCommandBuffer2 - - def command_to_json(self) -> str: - """Dump draw operation.""" - return f"""{{ "cls": "{self.__module__}.{self.__class__.__qualname__}", "dict": {{ - "polarity": "{self.transform.polarity.value}", - "aperture_attributes": {self.aperture_attributes.model_dump_json()}, - "command_buffer": {self.command_buffer.model_dump_json()}, - "command_buffer": { - self.command_buffer.debug_buffer_to_json(8)} - }} -}}""" # noqa: E501 - - def render(self, renderer: Renderer2) -> None: - """Render draw operation.""" - renderer.hooks.render_region(self) - - def render_iter(self, renderer: Renderer2) -> Generator[Command2, None, None]: - """Render draw operation.""" - renderer.hooks.render_region(self) - yield self diff --git a/src/pygerber/gerberx3/parser2/context2.py b/src/pygerber/gerberx3/parser2/context2.py deleted file mode 100644 index 6c210fb9..00000000 --- a/src/pygerber/gerberx3/parser2/context2.py +++ /dev/null @@ -1,612 +0,0 @@ -"""Gerber AST parser, version 2, parsing context.""" - -from __future__ import annotations - -from dataclasses import dataclass -from typing import TYPE_CHECKING, NoReturn, Optional, Type - -from pydantic import Field - -from pygerber.common.frozen_general_model import FrozenGeneralModel -from pygerber.gerberx3.math.offset import Offset -from pygerber.gerberx3.parser2.apertures2.circle2 import NoCircle2 -from pygerber.gerberx3.parser2.attributes2 import ( - ApertureAttributes, - FileAttributes, - ObjectAttributes, -) -from pygerber.gerberx3.parser2.command_buffer2 import CommandBuffer2 -from pygerber.gerberx3.parser2.errors2 import ( - ApertureNotDefined2Error, - ExitParsingProcess2Interrupt, - MacroNotDefinedError, - MacroNotInitializedError, - ReferencedNotInitializedBlockBufferError, - RegionNotInitializedError, - SkipTokenInterrupt, - StepAndRepeatNotInitializedError, -) -from pygerber.gerberx3.parser2.macro2.expressions2.binary2 import ( - Addition2, - Division2, - Multiplication2, - Subtraction2, -) -from pygerber.gerberx3.parser2.macro2.expressions2.constant2 import Constant2 -from pygerber.gerberx3.parser2.macro2.expressions2.unary2 import Negation2, Positive2 -from pygerber.gerberx3.parser2.macro2.expressions2.variable_name import VariableName2 -from pygerber.gerberx3.parser2.macro2.macro2 import ApertureMacro2 -from pygerber.gerberx3.parser2.macro2.statement_buffer2 import StatementBuffer2 -from pygerber.gerberx3.parser2.parser2hooks import Parser2Hooks -from pygerber.gerberx3.parser2.parser2hooks_base import Parser2HooksBase -from pygerber.gerberx3.parser2.state2 import ApertureTransform, State2 -from pygerber.gerberx3.state_enums import AxisCorrespondence -from pygerber.gerberx3.tokenizer.aperture_id import ApertureID - -if TYPE_CHECKING: - from decimal import Decimal - - from pygerber.gerberx3.math.vector_2d import Vector2D - from pygerber.gerberx3.parser2.apertures2.aperture2 import Aperture2 - from pygerber.gerberx3.parser2.commands2.command2 import Command2 - from pygerber.gerberx3.state_enums import DrawMode, Mirroring, Polarity, Unit - from pygerber.gerberx3.tokenizer.tokens.bases.token import Token - from pygerber.gerberx3.tokenizer.tokens.fs_coordinate_format import CoordinateParser - - -REGION_OUTLINE_DEFAULT_APERTURE_ID = ApertureID("%*__REGION_OUTLINE_APERTURE__*%") - - -class Parser2Context: - """Context used by Gerber AST parser, version 2.""" - - def __init__(self, options: Parser2ContextOptions | None = None) -> None: - self.options = Parser2ContextOptions() if options is None else options - self.state: State2 = ( - State2() - if self.options.initial_state is None - else self.options.initial_state - ) - self.main_command_buffer: CommandBuffer2 = ( - CommandBuffer2() - if self.options.initial_main_command_buffer is None - else self.options.initial_main_command_buffer - ) - self.region_command_buffer: Optional[CommandBuffer2] = None - self.block_command_buffer_stack: list[CommandBuffer2] = [] - self.block_state_stack: list[State2] = [] - self.step_and_repeat_command_buffer: Optional[CommandBuffer2] = None - self.state_before_step_and_repeat: Optional[State2] = None - self.macro_statement_buffer: Optional[StatementBuffer2] = None - self.macro_eval_buffer: Optional[CommandBuffer2] = None - self.macro_variable_buffer: dict[str, Decimal] = {} - self.hooks: Parser2HooksBase = ( - Parser2Hooks() if self.options.hooks is None else self.options.hooks - ) - self.current_token: Optional[Token] = None - self.reached_program_stop: bool = False - self.reached_optional_stop: bool = False - self.reached_end_of_file: bool = False - - self.file_attributes = FileAttributes() - self.aperture_attributes = ApertureAttributes() - self.object_attributes = ObjectAttributes() - - self.macro_expressions = ( - Parser2ContextMacroExpressionFactories() - if self.options.custom_macro_expression_factories is None - else self.options.custom_macro_expression_factories - ) - self.apertures: dict[ApertureID, Aperture2] = { - REGION_OUTLINE_DEFAULT_APERTURE_ID: NoCircle2( - identifier=REGION_OUTLINE_DEFAULT_APERTURE_ID, - diameter=Offset.NULL, - hole_diameter=None, - ), - } - - def push_block_command_buffer(self) -> None: - """Add new command buffer for block aperture draw commands.""" - self.block_command_buffer_stack.append( - CommandBuffer2() - if self.options.initial_block_command_buffer is None - else self.options.initial_block_command_buffer.copy(), - ) - - def pop_block_command_buffer(self) -> CommandBuffer2: - """Return latest block aperture command buffer and delete it from the stack.""" - if len(self.block_command_buffer_stack) == 0: - raise ReferencedNotInitializedBlockBufferError(self.current_token) - return self.block_command_buffer_stack.pop() - - def first_block_command_buffer(self) -> CommandBuffer2: - """Return first (topmost) block aperture command buffer.""" - if len(self.block_command_buffer_stack) == 0: - raise ReferencedNotInitializedBlockBufferError(self.current_token) - return self.block_command_buffer_stack[-1] - - def push_block_state(self) -> None: - """Add new command buffer for block aperture draw commands.""" - self.block_state_stack.append(self.state) - - def pop_block_state(self) -> State2: - """Return latest block aperture command buffer and delete it from the stack.""" - if len(self.block_state_stack) == 0: - raise ReferencedNotInitializedBlockBufferError(self.current_token) - return self.block_state_stack.pop() - - def set_region_command_buffer(self) -> None: - """Add new command buffer for block aperture draw commands.""" - self.region_command_buffer = ( - CommandBuffer2() - if self.options.initial_region_command_buffer is None - else self.options.initial_region_command_buffer.copy() - ) - - def unset_region_command_buffer(self) -> None: - """Add new command buffer for block aperture draw commands.""" - self.region_command_buffer = None - - def get_region_command_buffer(self) -> CommandBuffer2: - """Return latest block aperture command buffer and delete it from the stack.""" - if self.region_command_buffer is None: - raise RegionNotInitializedError(self.current_token) - return self.region_command_buffer - - def set_step_and_repeat_command_buffer(self) -> None: - """Add new command buffer for block aperture draw commands.""" - self.step_and_repeat_command_buffer = ( - CommandBuffer2() - if self.options.initial_region_command_buffer is None - else self.options.initial_region_command_buffer.copy() - ) - - def unset_step_and_repeat_command_buffer(self) -> None: - """Unset step and repeat command buffer.""" - self.step_and_repeat_command_buffer = None - - def get_step_and_repeat_command_buffer(self) -> CommandBuffer2: - """Return step and repeat command buffer.""" - if self.step_and_repeat_command_buffer is None: - raise StepAndRepeatNotInitializedError(self.current_token) - return self.step_and_repeat_command_buffer - - def get_state_before_step_and_repeat(self) -> State2: - """Return step and repeat command buffer.""" - if self.state_before_step_and_repeat is None: - raise StepAndRepeatNotInitializedError(self.current_token) - return self.state_before_step_and_repeat - - def unset_state_before_step_and_repeat(self) -> None: - """Unset step and repeat command buffer.""" - self.state_before_step_and_repeat = None - - def set_state_before_step_and_repeat(self) -> None: - """Add new command buffer for block aperture draw commands.""" - self.state_before_step_and_repeat = self.state - - def reset_state_to_pre_step_and_repeat(self) -> None: - """Set state to state before step and repeat.""" - self.set_state(self.get_state_before_step_and_repeat()) - - def get_macro_statement_buffer(self) -> StatementBuffer2: - """Return macro statement buffer.""" - if self.macro_statement_buffer is None: - raise MacroNotInitializedError(self.current_token) - return self.macro_statement_buffer - - def set_macro_statement_buffer(self) -> None: - """Add new command buffer for block aperture draw commands.""" - self.macro_statement_buffer = ( - StatementBuffer2() - if self.options.initial_macro_statement_buffer is None - else self.options.initial_macro_statement_buffer - ) - - def unset_macro_statement_buffer(self) -> None: - """Unset step and repeat command buffer.""" - self.macro_statement_buffer = None - - def get_macro_eval_buffer(self) -> CommandBuffer2: - """Return macro evaluation buffer.""" - if self.macro_eval_buffer is None: - raise MacroNotInitializedError(self.current_token) - return self.macro_eval_buffer - - def set_macro_eval_buffer(self) -> None: - """Add new command buffer for block aperture draw commands.""" - self.macro_eval_buffer = ( - CommandBuffer2() - if self.options.initial_macro_eval_buffer is None - else self.options.initial_macro_eval_buffer - ) - - def unset_macro_eval_buffer(self) -> None: - """Unset step and repeat command buffer.""" - self.macro_eval_buffer = None - - def skip_token(self) -> NoReturn: - """Skip this token.""" - raise SkipTokenInterrupt - - def halt_parser(self) -> NoReturn: - """Halt parsing process.""" - raise ExitParsingProcess2Interrupt - - def get_hooks(self) -> Parser2HooksBase: - """Get hooks object.""" - return self.hooks - - def get_current_token(self) -> Optional[Token]: - """Get current token object.""" - return self.current_token - - def set_current_token(self, token: Token) -> None: - """Get current token object.""" - self.current_token = token - - def set_state(self, state: State2) -> None: - """Set parser state.""" - self.state = state - - def add_command(self, __command: Command2) -> None: - """Add draw command to command buffer.""" - if self.get_is_region(): - self.get_region_command_buffer().add_command(__command) - return - - if self.get_is_aperture_block(): - self.first_block_command_buffer().add_command(__command) - return - - if self.get_is_step_and_repeat(): - self.get_step_and_repeat_command_buffer().add_command(__command) - return - - self.main_command_buffer.add_command(__command) - - def get_state(self) -> State2: - """Get parser state.""" - return self.state - - def get_draw_units(self) -> Unit: - """Get draw_units property value.""" - return self.get_state().get_draw_units() - - def set_draw_units(self, draw_units: Unit) -> None: - """Set the draw_units property value.""" - return self.set_state(self.get_state().set_draw_units(draw_units)) - - def get_coordinate_parser(self) -> CoordinateParser: - """Get coordinate_parser property value.""" - return self.get_state().get_coordinate_parser() - - def set_coordinate_parser(self, coordinate_parser: CoordinateParser) -> None: - """Set the coordinate_parser property value.""" - return self.set_state( - self.get_state().set_coordinate_parser(coordinate_parser), - ) - - def get_polarity(self) -> Polarity: - """Get polarity property value.""" - return self.get_state().get_polarity() - - def set_polarity(self, polarity: Polarity) -> None: - """Set the polarity property value.""" - return self.set_state(self.get_state().set_polarity(polarity)) - - def get_mirroring(self) -> Mirroring: - """Get mirroring property value.""" - return self.get_state().get_mirroring() - - def set_mirroring(self, mirroring: Mirroring) -> None: - """Set the mirroring property value.""" - return self.set_state(self.get_state().set_mirroring(mirroring)) - - def get_rotation(self) -> Decimal: - """Get rotation property value.""" - return self.get_state().get_rotation() - - def set_rotation(self, rotation: Decimal) -> None: - """Set the rotation property value.""" - return self.set_state(self.get_state().set_rotation(rotation)) - - def get_scaling(self) -> Decimal: - """Get scaling property value.""" - return self.get_state().get_scaling() - - def set_scaling(self, scaling: Decimal) -> None: - """Set the scaling property value.""" - return self.set_state(self.get_state().set_scaling(scaling)) - - def get_is_output_image_negation_required(self) -> bool: - """Get is_output_image_negation_required property value.""" - return self.get_state().get_is_output_image_negation_required() - - def set_is_output_image_negation_required(self, *, value: bool) -> None: - """Set the is_output_image_negation_required property value.""" - return self.set_state( - self.get_state().set_is_output_image_negation_required(value), - ) - - def get_image_name(self) -> Optional[str]: - """Get image_name property value.""" - return self.get_state().get_image_name() - - def set_image_name(self, image_name: Optional[str]) -> None: - """Set the image_name property value.""" - return self.set_state(self.get_state().set_image_name(image_name)) - - def get_file_name(self) -> Optional[str]: - """Get file_name property value.""" - return self.get_state().get_file_name() - - def set_file_name(self, file_name: Optional[str]) -> None: - """Set the file_name property value.""" - return self.set_state(self.get_state().set_file_name(file_name)) - - def get_axis_correspondence(self) -> AxisCorrespondence: - """Get axis_correspondence property value.""" - return self.get_state().get_axis_correspondence() - - def set_axis_correspondence(self, axis_correspondence: AxisCorrespondence) -> None: - """Set the axis_correspondence property value.""" - return self.set_state( - self.get_state().set_axis_correspondence(axis_correspondence), - ) - - def get_draw_mode(self) -> DrawMode: - """Get draw_mode property value.""" - return self.get_state().get_draw_mode() - - def set_draw_mode(self, draw_mode: DrawMode) -> None: - """Set the draw_mode property value.""" - return self.set_state(self.get_state().set_draw_mode(draw_mode)) - - def get_is_region(self) -> bool: - """Get is_region property value.""" - return self.get_state().get_is_region() - - def set_is_region(self, is_region: bool) -> None: # noqa: FBT001 - """Set the is_region property value.""" - return self.set_state(self.get_state().set_is_region(is_region)) - - def get_is_aperture_block(self) -> bool: - """Get is_aperture_block property value.""" - return self.get_state().get_is_aperture_block() - - def set_is_aperture_block(self, is_aperture_block: bool) -> None: # noqa: FBT001 - """Set the is_aperture_block property value.""" - return self.set_state( - self.get_state().set_is_aperture_block(is_aperture_block), - ) - - def get_aperture_block_id(self) -> Optional[ApertureID]: - """Get is_aperture_block property value.""" - return self.get_state().get_aperture_block_id() - - def set_aperture_block_id(self, aperture_block_id: Optional[ApertureID]) -> None: - """Set the is_aperture_block property value.""" - return self.set_state( - self.get_state().set_aperture_block_id(aperture_block_id), - ) - - def get_is_multi_quadrant(self) -> bool: - """Get is_aperture_block property value.""" - return self.get_state().get_is_multi_quadrant() - - def set_is_multi_quadrant(self, is_multi_quadrant: bool) -> None: # noqa: FBT001 - """Set the is_aperture_block property value.""" - return self.set_state( - self.get_state().set_is_multi_quadrant(is_multi_quadrant), - ) - - def get_is_step_and_repeat(self) -> bool: - """Get is_step_and_repeat property value.""" - return self.get_state().get_is_step_and_repeat() - - def set_is_step_and_repeat(self, is_step_and_repeat: bool) -> None: # noqa: FBT001 - """Set the is_step_and_repeat property value.""" - return self.set_state( - self.get_state().set_is_step_and_repeat(is_step_and_repeat), - ) - - def get_x_repeat(self) -> int: - """Get x_step property value.""" - return self.get_state().get_x_repeat() - - def set_x_repeat(self, x_repeat: int) -> None: - """Set the x_repeat property value.""" - return self.set_state(self.get_state().set_x_repeat(x_repeat)) - - def get_y_repeat(self) -> int: - """Get y_step property value.""" - return self.get_state().get_y_repeat() - - def set_y_repeat(self, y_repeat: int) -> None: - """Set the y_repeat property value.""" - return self.set_state(self.get_state().set_y_repeat(y_repeat)) - - def get_x_step(self) -> Offset: - """Get x_step property value.""" - return self.get_state().get_x_step() - - def set_x_step(self, x_step: Offset) -> None: - """Set the x_step property value.""" - return self.set_state(self.get_state().set_x_step(x_step)) - - def get_y_step(self) -> Offset: - """Get y_step property value.""" - return self.get_state().get_y_step() - - def set_y_step(self, y_step: Offset) -> None: - """Set the y_step property value.""" - return self.set_state(self.get_state().set_y_step(y_step)) - - def get_current_position(self) -> Vector2D: - """Get current_position property value.""" - return self.get_state().get_current_position() - - def set_current_position(self, current_position: Vector2D) -> None: - """Set the current_position property value.""" - return self.set_state( - self.get_state().set_current_position(current_position), - ) - - def get_current_aperture_id(self) -> Optional[ApertureID]: - """Get current_aperture property value.""" - current_aperture_id = self.get_state().get_current_aperture_id() - if current_aperture_id is None and self.get_is_region(): - return REGION_OUTLINE_DEFAULT_APERTURE_ID - - return current_aperture_id - - def set_current_aperture_id(self, current_aperture: Optional[ApertureID]) -> None: - """Set the current_aperture property value.""" - return self.set_state( - self.get_state().set_current_aperture_id(current_aperture), - ) - - def get_aperture( - self, - __key: ApertureID, - transform: ApertureTransform, - ) -> Aperture2: - """Get apertures property value.""" - key_with_transform = ApertureID( - f"{__key}+{transform.get_transform_key()}", - ) - transformed_aperture = self.apertures.get(key_with_transform) - if transformed_aperture is None: - # Retrieve aperture with no transform and create a transformed copy. - # If transform is all default, no copy is made. - aperture = self._get_aperture(__key) - transformed_aperture = ( - aperture.get_mirrored(transform.mirroring) - .get_rotated(transform.rotation) - .get_scaled(transform.scaling) - ) - self.set_aperture(key_with_transform, transformed_aperture) - - return transformed_aperture - - def _get_aperture(self, __key: ApertureID) -> Aperture2: - try: - aperture = self.apertures[__key] - except KeyError as e: - raise ApertureNotDefined2Error(self.current_token) from e - return aperture - - def set_aperture(self, __key: ApertureID, __value: Aperture2) -> None: - """Set the apertures property value.""" - self.apertures[__key] = __value - - def get_macro(self, __key: str) -> ApertureMacro2: - """Get macro property value.""" - try: - return self.get_state().get_macro(__key) - except KeyError as e: - raise MacroNotDefinedError(self.current_token) from e - - def set_macro(self, __key: str, __value: ApertureMacro2) -> None: - """Set the macro property value.""" - return self.set_state(self.get_state().set_macro(__key, __value)) - - def set_reached_program_stop(self) -> None: - """Set flag indicating that M00 token was reached.""" - self.reached_program_stop = True - - def get_reached_program_stop(self) -> bool: - """Get flag indicating that M00 token was reached.""" - return self.reached_program_stop - - def set_reached_optional_stop(self) -> None: - """Set flag indicating that M01 token was reached.""" - self.reached_optional_stop = True - - def get_reached_optional_stop(self) -> bool: - """Get flag indicating that M01 token was reached.""" - return self.reached_optional_stop - - def set_reached_end_of_file(self) -> None: - """Set flag indicating that M02 end of file was reached.""" - self.reached_end_of_file = True - - def get_reached_end_of_file(self) -> bool: - """Get flag indicating that M02 end of file was reached.""" - return self.reached_end_of_file - - def get_file_attribute(self, key: str) -> Optional[str]: - """Get file attributes property.""" - return self.file_attributes.get(key) - - def delete_file_attribute(self, key: str) -> None: - """Get file attributes property.""" - self.file_attributes = self.file_attributes.delete(key) - - def set_file_attribute(self, key: str, value: Optional[str]) -> None: - """Set file attributes property.""" - self.file_attributes = self.file_attributes.update(key, value) - - def get_aperture_attribute(self, key: str) -> Optional[str]: - """Get aperture attributes property.""" - return self.aperture_attributes.get(key) - - def delete_aperture_attribute(self, key: str) -> None: - """Delete aperture attributes property.""" - self.aperture_attributes = self.aperture_attributes.delete(key) - - def clear_aperture_attributes(self) -> None: - """Clear aperture attributes property.""" - self.aperture_attributes = ApertureAttributes() - - def set_aperture_attribute(self, key: str, value: Optional[str]) -> None: - """Set aperture attributes property.""" - self.aperture_attributes = self.aperture_attributes.update(key, value) - - def get_object_attribute(self, key: str) -> Optional[str]: - """Get object attributes property.""" - return self.object_attributes.get(key) - - def delete_object_attribute(self, key: str) -> None: - """Delete object attributes property.""" - self.object_attributes = self.object_attributes.delete(key) - - def set_object_attribute(self, key: str, value: Optional[str]) -> None: - """Set object attributes property.""" - self.object_attributes = self.object_attributes.update(key, value) - - def clear_object_attributes(self) -> None: - """Clear object attributes property.""" - self.object_attributes = ObjectAttributes() - - -@dataclass -class Parser2ContextMacroExpressionFactories: - """Collection of factories for all macro expressions.""" - - constant: Type[Constant2] = Constant2 - variable_name: Type[VariableName2] = VariableName2 - addition: Type[Addition2] = Addition2 - subtraction: Type[Subtraction2] = Subtraction2 - multiplication: Type[Multiplication2] = Multiplication2 - division: Type[Division2] = Division2 - negation: Type[Negation2] = Negation2 - positive: Type[Positive2] = Positive2 - - -class Parser2ContextOptions(FrozenGeneralModel): - """Options for Parser2Context.""" - - initial_state: Optional[State2] = Field(default=None) - initial_main_command_buffer: Optional[CommandBuffer2] = Field(default=None) - initial_region_command_buffer: Optional[CommandBuffer2] = Field(default=None) - initial_block_command_buffer: Optional[CommandBuffer2] = Field(default=None) - initial_macro_statement_buffer: Optional[StatementBuffer2] = Field(default=None) - initial_macro_eval_buffer: Optional[CommandBuffer2] = Field(default=None) - custom_macro_expression_factories: Optional[ - Parser2ContextMacroExpressionFactories - ] = Field( - default=None, - ) - hooks: Optional[Parser2HooksBase] = Field(default=None) diff --git a/src/pygerber/gerberx3/parser2/errors2.py b/src/pygerber/gerberx3/parser2/errors2.py deleted file mode 100644 index 09083077..00000000 --- a/src/pygerber/gerberx3/parser2/errors2.py +++ /dev/null @@ -1,134 +0,0 @@ -"""Base error classes used in this module.""" - -from __future__ import annotations - -from pygerber.gerberx3.tokenizer.tokens.bases.token import Token - - -class Parser2Error(ValueError): - """Base class for parser errors. - - Exceptions derived from this exception are exclusively raised in PyGerber's Gerber - X3 Parser. This exception can be used in - `#!python try: ... except Parser2Error: ...` block to catch all exceptions - raised by Parser while allowing other exceptions to interrupt execution. - """ - - def get_message(self) -> str: - """Get parser error help message.""" - return f"{self.__class__.__qualname__}: {self.__doc__}" - - -class ZeroOmissionNotSupported2Error(Parser2Error): - """Raised when incremental coordinates are selected. (Spec. 8.2.1.2).""" - - -class IncrementalCoordinatesNotSupported2Error(Parser2Error): - """Raised when incremental coordinates are selected. (Spec. 8.2.1.2).""" - - -class UnsupportedCoordinateType2Error(Parser2Error): - """Raised for unsupported coordinate types.""" - - -class InvalidCoordinateLength2Error(Parser2Error): - """Raised when coordinate string is too long.""" - - -class ParserFatal2Error(Parser2Error): - """Raised when parser encounters fatal failure from non-parser specific - exception. - """ - - -class OnUpdateDrawingState2Error(Parser2Error): - """Raised when parser encounters fatal failure from non-parser specific - exception during call to .update_drawing_state() call. - """ - - def __init__(self, token: Token, *args: object) -> None: - super().__init__(*args) - self.token = token - - def __str__(self) -> str: - return f"{self.token} {self.token.get_token_position()}" - - -class UnitNotSet2Error(Parser2Error): - """Raised when operation which requires units to be set is executed before units - are set. - """ - - -class ReferencedNotInitializedBlockBufferError(Parser2Error): - """Raised when Gerber file references block buffer which has not been - initialized, ie. when block aperture was not correctly started. - """ - - -class UnnamedBlockApertureNotAllowedError(Parser2Error): - """Raised when aperture block with no ID is encountered.""" - - -class RegionNotInitializedError(Parser2Error): - """Raised when region is modified without being accessed without initialization.""" - - -class ApertureNotDefined2Error(Parser2Error): - """Raised when undefined aperture is selected.""" - - -class MacroNotDefinedError(Parser2Error): - """Raised when undefined macro is referenced.""" - - -class NoValidArcCenterFoundError(Parser2Error): - """Raised when no valid arc center point can not be deduced from IJ offset in - single quadrant mode (G74). - """ - - -class CoordinateFormatNotSet2Error(Parser2Error): - """Raised when coordinate parser is requested before coordinate format was set.""" - - -class ApertureNotSelected2Error(Parser2Error): - """Raised when attempting to use aperture without selecting it first.""" - - -class StepAndRepeatNotInitializedError(Parser2Error): - """Raised when step and repeat block is closed without being correctly opened.""" - - -class MacroNotInitializedError(Parser2Error): - """Raised when macro statement buffer is requested without being correctly - initialized. - """ - - -class StandardAttributeError(Parser2Error): - """Raised when parser encounters an error while processing a standard attribute.""" - - -class MissingNameFieldError(StandardAttributeError): - """Raised when a missing name field is detected.""" - - -class MissingGuidFieldError(StandardAttributeError): - """Raised when a missing name field is detected.""" - - -class MissingRevisionFieldError(StandardAttributeError): - """Raised when a missing name field is detected.""" - - -class Parser2Interrupt(Exception): # noqa: N818 - """Base class for implementing interrupts.""" - - -class ExitParsingProcess2Interrupt(Exception): # noqa: N818 - """Raised to stop parsing.""" - - -class SkipTokenInterrupt(Exception): # noqa: N818 - """Raised to skip all other actions that would be performed on current token.""" diff --git a/src/pygerber/gerberx3/parser2/macro2/__init__.py b/src/pygerber/gerberx3/parser2/macro2/__init__.py deleted file mode 100644 index 0fe0b118..00000000 --- a/src/pygerber/gerberx3/parser2/macro2/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Macro elements used to construct a macro aperture template.""" diff --git a/src/pygerber/gerberx3/parser2/macro2/assignment2.py b/src/pygerber/gerberx3/parser2/macro2/assignment2.py deleted file mode 100644 index e5530a8a..00000000 --- a/src/pygerber/gerberx3/parser2/macro2/assignment2.py +++ /dev/null @@ -1,24 +0,0 @@ -"""`assignment2` module contains a `Assignment2` class wrapping variable assignment -within macro definition. -""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from pygerber.gerberx3.parser2.macro2.expressions2.expression2 import Expression2 -from pygerber.gerberx3.parser2.macro2.statement2 import Statement2 - -if TYPE_CHECKING: - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class Assignment2(Statement2): - """Variable assignment.""" - - variable_name: str - value: Expression2 - - def on_parser2_eval_statement(self, context: Parser2Context) -> None: - """Evaluate macro to create concrete macro aperture.""" - context.hooks.macro_eval.on_assignment(context, self) diff --git a/src/pygerber/gerberx3/parser2/macro2/element2.py b/src/pygerber/gerberx3/parser2/macro2/element2.py deleted file mode 100644 index 3f8bc6de..00000000 --- a/src/pygerber/gerberx3/parser2/macro2/element2.py +++ /dev/null @@ -1,9 +0,0 @@ -"""Base class for creating macro elements.""" - -from __future__ import annotations - -from pygerber.common.frozen_general_model import FrozenGeneralModel - - -class Element2(FrozenGeneralModel): - """Base class for creating macro elements.""" diff --git a/src/pygerber/gerberx3/parser2/macro2/enums.py b/src/pygerber/gerberx3/parser2/macro2/enums.py deleted file mode 100644 index 152d2858..00000000 --- a/src/pygerber/gerberx3/parser2/macro2/enums.py +++ /dev/null @@ -1,14 +0,0 @@ -"""`enums` module contains Exposure2 enumeration containing possible macro primitive -exposures. -""" - -from __future__ import annotations - -from enum import Enum - - -class Exposure(Enum): - """Macro primitive exposure.""" - - ON = 1 - OFF = 0 diff --git a/src/pygerber/gerberx3/parser2/macro2/expressions2/__init__.py b/src/pygerber/gerberx3/parser2/macro2/expressions2/__init__.py deleted file mode 100644 index e1ce7667..00000000 --- a/src/pygerber/gerberx3/parser2/macro2/expressions2/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""`expressions2` package contains all macro expressions.""" diff --git a/src/pygerber/gerberx3/parser2/macro2/expressions2/binary2.py b/src/pygerber/gerberx3/parser2/macro2/expressions2/binary2.py deleted file mode 100644 index 92b93ae8..00000000 --- a/src/pygerber/gerberx3/parser2/macro2/expressions2/binary2.py +++ /dev/null @@ -1,59 +0,0 @@ -"""`binary2` module contain classes wrapping binary operations within macro.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from pygerber.gerberx3.parser2.macro2.expressions2.expression2 import Expression2 - -if TYPE_CHECKING: - from decimal import Decimal - - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class BinaryOperator2(Expression2): - """Single binary operation.""" - - lhs: Expression2 - rhs: Expression2 - - -class Addition2(BinaryOperator2): - """Addition expression.""" - - def on_parser2_eval_expression(self, context: Parser2Context) -> Decimal: - """Reduce expression to numerical value.""" - return self.lhs.on_parser2_eval_expression( - context, - ) + self.rhs.on_parser2_eval_expression(context) - - -class Subtraction2(BinaryOperator2): - """Subtract expression.""" - - def on_parser2_eval_expression(self, context: Parser2Context) -> Decimal: - """Reduce expression to numerical value.""" - return self.lhs.on_parser2_eval_expression( - context, - ) - self.rhs.on_parser2_eval_expression(context) - - -class Multiplication2(BinaryOperator2): - """Multiply expression.""" - - def on_parser2_eval_expression(self, context: Parser2Context) -> Decimal: - """Reduce expression to numerical value.""" - return self.lhs.on_parser2_eval_expression( - context, - ) * self.rhs.on_parser2_eval_expression(context) - - -class Division2(BinaryOperator2): - """Divide expression.""" - - def on_parser2_eval_expression(self, context: Parser2Context) -> Decimal: - """Reduce expression to numerical value.""" - return self.lhs.on_parser2_eval_expression( - context, - ) / self.rhs.on_parser2_eval_expression(context) diff --git a/src/pygerber/gerberx3/parser2/macro2/expressions2/constant2.py b/src/pygerber/gerberx3/parser2/macro2/expressions2/constant2.py deleted file mode 100644 index 6625a0f7..00000000 --- a/src/pygerber/gerberx3/parser2/macro2/expressions2/constant2.py +++ /dev/null @@ -1,24 +0,0 @@ -"""`constant2` module contain class wrapping constant value in macro definition.""" - -from __future__ import annotations - -from decimal import Decimal # noqa: TCH003 -from typing import TYPE_CHECKING - -from pygerber.gerberx3.parser2.macro2.expressions2.expression2 import Expression2 - -if TYPE_CHECKING: - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class Constant2(Expression2): - """Class wrapping constant value in macro definition.""" - - value: Decimal - - def on_parser2_eval_expression( - self, - context: Parser2Context, # noqa: ARG002 - ) -> Decimal: - """Reduce expression to numerical value.""" - return self.value diff --git a/src/pygerber/gerberx3/parser2/macro2/expressions2/expression2.py b/src/pygerber/gerberx3/parser2/macro2/expressions2/expression2.py deleted file mode 100644 index 583e0072..00000000 --- a/src/pygerber/gerberx3/parser2/macro2/expressions2/expression2.py +++ /dev/null @@ -1,34 +0,0 @@ -"""Macro expression base class, a part which can be used to build more complicated -expressions. - -Example of macro expression would be a variable reference, a constant or addition, -everything what can be composed into more complicated structures, but doesn't appear -alone as a macro content. Expressions can be reduced to numerical value during macro -evaluation. -""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from pygerber.gerberx3.parser2.macro2.element2 import Element2 - -if TYPE_CHECKING: - from decimal import Decimal - - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class Expression2(Element2): - """Macro expression base class, a part which can be used to build more complicated - expressions. - - Example of macro expression would be a variable reference, a constant or addition, - everything what can be composed into more complicated structures, but doesn't appear - alone as a macro content. Expressions can be reduced to numerical value during macro - evaluation. - """ - - def on_parser2_eval_expression(self, context: Parser2Context) -> Decimal: - """Reduce expression to numerical value.""" - raise NotImplementedError diff --git a/src/pygerber/gerberx3/parser2/macro2/expressions2/unary2.py b/src/pygerber/gerberx3/parser2/macro2/expressions2/unary2.py deleted file mode 100644 index 72825c8d..00000000 --- a/src/pygerber/gerberx3/parser2/macro2/expressions2/unary2.py +++ /dev/null @@ -1,34 +0,0 @@ -"""`unary2` module contain classes wrapping unary operations within macro.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from pygerber.gerberx3.parser2.macro2.expressions2.expression2 import Expression2 - -if TYPE_CHECKING: - from decimal import Decimal - - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class UnaryOperator2(Expression2): - """Single binary operation.""" - - op: Expression2 - - -class Negation2(UnaryOperator2): - """Unary minus operation.""" - - def on_parser2_eval_expression(self, context: Parser2Context) -> Decimal: - """Reduce expression to numerical value.""" - return -self.op.on_parser2_eval_expression(context) - - -class Positive2(UnaryOperator2): - """Unary plus operation.""" - - def on_parser2_eval_expression(self, context: Parser2Context) -> Decimal: - """Reduce expression to numerical value.""" - return self.op.on_parser2_eval_expression(context) diff --git a/src/pygerber/gerberx3/parser2/macro2/expressions2/variable_name.py b/src/pygerber/gerberx3/parser2/macro2/expressions2/variable_name.py deleted file mode 100644 index b5101994..00000000 --- a/src/pygerber/gerberx3/parser2/macro2/expressions2/variable_name.py +++ /dev/null @@ -1,24 +0,0 @@ -"""Module `variable_name.py` contains a class `VariableName` used to wrap variable -name. -""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from pygerber.gerberx3.parser2.macro2.expressions2.expression2 import Expression2 - -if TYPE_CHECKING: - from decimal import Decimal - - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class VariableName2(Expression2): - """Class wrapping variable name in macro definition.""" - - name: str - - def on_parser2_eval_expression(self, context: Parser2Context) -> Decimal: - """Reduce expression to numerical value.""" - return context.macro_variable_buffer[self.name] diff --git a/src/pygerber/gerberx3/parser2/macro2/macro2.py b/src/pygerber/gerberx3/parser2/macro2/macro2.py deleted file mode 100644 index da36df47..00000000 --- a/src/pygerber/gerberx3/parser2/macro2/macro2.py +++ /dev/null @@ -1,23 +0,0 @@ -"""Container for aperture macro elements.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from pygerber.common.frozen_general_model import FrozenGeneralModel -from pygerber.gerberx3.parser2.macro2.statement_buffer2 import ReadonlyStatementBuffer2 - -if TYPE_CHECKING: - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class ApertureMacro2(FrozenGeneralModel): - """Container for the elements contained within an aperture macro.""" - - name: str - statements: ReadonlyStatementBuffer2 - - def on_parser2_eval_statement(self, context: Parser2Context) -> None: - """Evaluate macro to create concrete macro aperture.""" - for stmt in self.statements: - stmt.on_parser2_eval_statement(context) diff --git a/src/pygerber/gerberx3/parser2/macro2/point2.py b/src/pygerber/gerberx3/parser2/macro2/point2.py deleted file mode 100644 index 30f1e158..00000000 --- a/src/pygerber/gerberx3/parser2/macro2/point2.py +++ /dev/null @@ -1,13 +0,0 @@ -"""Container for two macro expressions tied together as point in 2D space.""" - -from __future__ import annotations - -from pygerber.gerberx3.parser2.macro2.element2 import Element2 -from pygerber.gerberx3.parser2.macro2.expressions2.expression2 import Expression2 - - -class Point2(Element2): - """Pair of two expressions representing a point in 2D space.""" - - x: Expression2 - y: Expression2 diff --git a/src/pygerber/gerberx3/parser2/macro2/primitives2/__init__.py b/src/pygerber/gerberx3/parser2/macro2/primitives2/__init__.py deleted file mode 100644 index f2f5c72c..00000000 --- a/src/pygerber/gerberx3/parser2/macro2/primitives2/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Package primitives2 contain all macro primitives.""" diff --git a/src/pygerber/gerberx3/parser2/macro2/primitives2/code_1_circle2.py b/src/pygerber/gerberx3/parser2/macro2/primitives2/code_1_circle2.py deleted file mode 100644 index 2af74239..00000000 --- a/src/pygerber/gerberx3/parser2/macro2/primitives2/code_1_circle2.py +++ /dev/null @@ -1,25 +0,0 @@ -"""Macro primitive circle.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from pygerber.gerberx3.parser2.macro2.expressions2.expression2 import Expression2 -from pygerber.gerberx3.parser2.macro2.primitives2.primitive2 import Primitive2 - -if TYPE_CHECKING: - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class Code1Circle2(Primitive2): - """Circle macro primitive.""" - - exposure: Expression2 - diameter: Expression2 - center_x: Expression2 - center_y: Expression2 - rotation: Expression2 - - def on_parser2_eval_statement(self, context: Parser2Context) -> None: - """Evaluate macro to create concrete macro aperture.""" - context.hooks.macro_eval.on_code_1_circle(context, self) diff --git a/src/pygerber/gerberx3/parser2/macro2/primitives2/code_20_vector_line2.py b/src/pygerber/gerberx3/parser2/macro2/primitives2/code_20_vector_line2.py deleted file mode 100644 index b75ddff8..00000000 --- a/src/pygerber/gerberx3/parser2/macro2/primitives2/code_20_vector_line2.py +++ /dev/null @@ -1,27 +0,0 @@ -"""Macro primitive vector line.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from pygerber.gerberx3.parser2.macro2.expressions2.expression2 import Expression2 -from pygerber.gerberx3.parser2.macro2.primitives2.primitive2 import Primitive2 - -if TYPE_CHECKING: - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class Code20VectorLine2(Primitive2): - """Vector line macro primitive.""" - - exposure: Expression2 - width: Expression2 - start_x: Expression2 - start_y: Expression2 - end_x: Expression2 - end_y: Expression2 - rotation: Expression2 - - def on_parser2_eval_statement(self, context: Parser2Context) -> None: - """Evaluate macro to create concrete macro aperture.""" - context.hooks.macro_eval.on_code_20_vector_line(context, self) diff --git a/src/pygerber/gerberx3/parser2/macro2/primitives2/code_21_center_line2.py b/src/pygerber/gerberx3/parser2/macro2/primitives2/code_21_center_line2.py deleted file mode 100644 index cfad8ad6..00000000 --- a/src/pygerber/gerberx3/parser2/macro2/primitives2/code_21_center_line2.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Macro primitive center line.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from pygerber.gerberx3.parser2.macro2.expressions2.expression2 import Expression2 -from pygerber.gerberx3.parser2.macro2.primitives2.primitive2 import Primitive2 - -if TYPE_CHECKING: - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class Code21CenterLine2(Primitive2): - """Center line macro primitive.""" - - exposure: Expression2 - width: Expression2 - height: Expression2 - center_x: Expression2 - center_y: Expression2 - rotation: Expression2 - - def on_parser2_eval_statement(self, context: Parser2Context) -> None: - """Evaluate macro to create concrete macro aperture.""" - context.hooks.macro_eval.on_code_21_center_line(context, self) diff --git a/src/pygerber/gerberx3/parser2/macro2/primitives2/code_22_lower_left_line2.py b/src/pygerber/gerberx3/parser2/macro2/primitives2/code_22_lower_left_line2.py deleted file mode 100644 index b874802f..00000000 --- a/src/pygerber/gerberx3/parser2/macro2/primitives2/code_22_lower_left_line2.py +++ /dev/null @@ -1,18 +0,0 @@ -"""Code 22 lower left line macro primitive.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from pygerber.gerberx3.parser2.macro2.primitives2.primitive2 import Primitive2 - -if TYPE_CHECKING: - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class Code22LowerLeftLine2(Primitive2): - """Code 22 lower left line macro primitive.""" - - def on_parser2_eval_statement(self, context: Parser2Context) -> None: - """Evaluate macro to create concrete macro aperture.""" - context.hooks.macro_eval.on_code_22_lower_left_line(context, self) diff --git a/src/pygerber/gerberx3/parser2/macro2/primitives2/code_2_vector_line2.py b/src/pygerber/gerberx3/parser2/macro2/primitives2/code_2_vector_line2.py deleted file mode 100644 index 77d96c17..00000000 --- a/src/pygerber/gerberx3/parser2/macro2/primitives2/code_2_vector_line2.py +++ /dev/null @@ -1,18 +0,0 @@ -"""Vector line macro primitive.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from pygerber.gerberx3.parser2.macro2.primitives2.primitive2 import Primitive2 - -if TYPE_CHECKING: - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class Code2VectorLine2(Primitive2): - """Vector line macro primitive.""" - - def on_parser2_eval_statement(self, context: Parser2Context) -> None: - """Evaluate macro to create concrete macro aperture.""" - context.hooks.macro_eval.on_code_2_vector_line(context, self) diff --git a/src/pygerber/gerberx3/parser2/macro2/primitives2/code_4_outline2.py b/src/pygerber/gerberx3/parser2/macro2/primitives2/code_4_outline2.py deleted file mode 100644 index c5dcb277..00000000 --- a/src/pygerber/gerberx3/parser2/macro2/primitives2/code_4_outline2.py +++ /dev/null @@ -1,27 +0,0 @@ -"""Outline macro primitive.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING, List - -from pygerber.gerberx3.parser2.macro2.expressions2.expression2 import Expression2 -from pygerber.gerberx3.parser2.macro2.point2 import Point2 -from pygerber.gerberx3.parser2.macro2.primitives2.primitive2 import Primitive2 - -if TYPE_CHECKING: - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class Code4Outline2(Primitive2): - """Vector line macro primitive.""" - - exposure: Expression2 - vertex_count: Expression2 - start_x: Expression2 - start_y: Expression2 - points: List[Point2] - rotation: Expression2 - - def on_parser2_eval_statement(self, context: Parser2Context) -> None: - """Evaluate macro to create concrete macro aperture.""" - context.hooks.macro_eval.on_code_4_outline(context, self) diff --git a/src/pygerber/gerberx3/parser2/macro2/primitives2/code_5_polygon2.py b/src/pygerber/gerberx3/parser2/macro2/primitives2/code_5_polygon2.py deleted file mode 100644 index b6ef8c1c..00000000 --- a/src/pygerber/gerberx3/parser2/macro2/primitives2/code_5_polygon2.py +++ /dev/null @@ -1,28 +0,0 @@ -"""Macro primitive polygon.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from pygerber.gerberx3.parser2.macro2.expressions2.expression2 import Expression2 -from pygerber.gerberx3.parser2.macro2.primitives2.primitive2 import Primitive2 - -if TYPE_CHECKING: - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class Code5Polygon2(Primitive2): - """Polygon macro primitive.""" - - # 5 Polygon Exposure, # vertices, Center X, Center Y, Diameter, Rotation 4.5.1.7 - - exposure: Expression2 - number_of_vertices: Expression2 - center_x: Expression2 - center_y: Expression2 - diameter: Expression2 - rotation: Expression2 - - def on_parser2_eval_statement(self, context: Parser2Context) -> None: - """Evaluate macro to create concrete macro aperture.""" - context.hooks.macro_eval.on_code_5_polygon(context, self) diff --git a/src/pygerber/gerberx3/parser2/macro2/primitives2/code_6_moire2.py b/src/pygerber/gerberx3/parser2/macro2/primitives2/code_6_moire2.py deleted file mode 100644 index 953b32b0..00000000 --- a/src/pygerber/gerberx3/parser2/macro2/primitives2/code_6_moire2.py +++ /dev/null @@ -1,18 +0,0 @@ -"""Macro primitive Moire.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from pygerber.gerberx3.parser2.macro2.primitives2.primitive2 import Primitive2 - -if TYPE_CHECKING: - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class Code6Moire2(Primitive2): - """Moire macro primitive.""" - - def on_parser2_eval_statement(self, context: Parser2Context) -> None: - """Evaluate macro to create concrete macro aperture.""" - context.hooks.macro_eval.on_code_6_moire(context, self) diff --git a/src/pygerber/gerberx3/parser2/macro2/primitives2/code_7_thermal2.py b/src/pygerber/gerberx3/parser2/macro2/primitives2/code_7_thermal2.py deleted file mode 100644 index e742734d..00000000 --- a/src/pygerber/gerberx3/parser2/macro2/primitives2/code_7_thermal2.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Thermal macro primitive.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from pygerber.gerberx3.parser2.macro2.expressions2.expression2 import Expression2 -from pygerber.gerberx3.parser2.macro2.primitives2.primitive2 import Primitive2 - -if TYPE_CHECKING: - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class Code7Thermal2(Primitive2): - """Thermal macro primitive.""" - - center_x: Expression2 - center_y: Expression2 - outer_diameter: Expression2 - inner_diameter: Expression2 - gap: Expression2 - rotation: Expression2 - - def on_parser2_eval_statement(self, context: Parser2Context) -> None: - """Evaluate macro to create concrete macro aperture.""" - context.hooks.macro_eval.on_code_7_thermal(context, self) diff --git a/src/pygerber/gerberx3/parser2/macro2/primitives2/primitive2.py b/src/pygerber/gerberx3/parser2/macro2/primitives2/primitive2.py deleted file mode 100644 index 500087e6..00000000 --- a/src/pygerber/gerberx3/parser2/macro2/primitives2/primitive2.py +++ /dev/null @@ -1,9 +0,0 @@ -"""Macro primitive base class.""" - -from __future__ import annotations - -from pygerber.gerberx3.parser2.macro2.statement2 import Statement2 - - -class Primitive2(Statement2): - """Macro primitive base class.""" diff --git a/src/pygerber/gerberx3/parser2/macro2/statement2.py b/src/pygerber/gerberx3/parser2/macro2/statement2.py deleted file mode 100644 index 5527955c..00000000 --- a/src/pygerber/gerberx3/parser2/macro2/statement2.py +++ /dev/null @@ -1,28 +0,0 @@ -"""Macro statement base class. - -A statement is everything what have to appear alone, for example a primitive or a -variable assignment. Statements by themselves can't be reduced to numerical value during -macro evaluation. -""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from pygerber.gerberx3.parser2.macro2.element2 import Element2 - -if TYPE_CHECKING: - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class Statement2(Element2): - """Macro statement base class. - - A statement is everything what have to appear alone, for example a primitive or a - variable assignment. Statements by themselves can't be reduced to numerical value - during macro evaluation. - """ - - def on_parser2_eval_statement(self, context: Parser2Context) -> None: - """Evaluate macro to create concrete macro aperture.""" - raise NotImplementedError diff --git a/src/pygerber/gerberx3/parser2/macro2/statement_buffer2.py b/src/pygerber/gerberx3/parser2/macro2/statement_buffer2.py deleted file mode 100644 index 7a5bc968..00000000 --- a/src/pygerber/gerberx3/parser2/macro2/statement_buffer2.py +++ /dev/null @@ -1,41 +0,0 @@ -"""Module contains class `StatementBuffer2 for macro statements.""" - -from __future__ import annotations - -from typing import Iterator, List - -from pygerber.common.frozen_general_model import FrozenGeneralModel -from pygerber.gerberx3.parser2.macro2.statement2 import Statement2 - - -class StatementBuffer2: - """Buffer for macro statements.""" - - def __init__(self) -> None: - self.statements: list[Statement2] = [] - - def add_statement(self, statement: Statement2) -> None: - """Append a complete statement to the buffer.""" - self.statements.append(statement) - - def get_readonly(self) -> ReadonlyStatementBuffer2: - """Return readonly buffer.""" - return ReadonlyStatementBuffer2(statements=self.statements) - - -class ReadonlyStatementBuffer2(FrozenGeneralModel): - """Read-only macro statement buffer.""" - - statements: List[Statement2] - - def __len__(self) -> int: - """Return length of buffered commands.""" - return len(self.statements) - - def __iter__(self) -> Iterator[Statement2]: # type: ignore[override] - """Iterate over buffered draw commands.""" - yield from self.statements - - def __getitem__(self, position: int) -> Statement2: - """Get draw command at position.""" - return self.statements[position] diff --git a/src/pygerber/gerberx3/parser2/parser2.py b/src/pygerber/gerberx3/parser2/parser2.py deleted file mode 100644 index 703e862a..00000000 --- a/src/pygerber/gerberx3/parser2/parser2.py +++ /dev/null @@ -1,154 +0,0 @@ -"""Implementation of Gerber AST parser, version 2.""" - -from __future__ import annotations - -import logging -from enum import Enum -from typing import Generator, Optional - -from pydantic import Field - -from pygerber.common.frozen_general_model import FrozenGeneralModel -from pygerber.gerberx3.parser2.command_buffer2 import ( - ReadonlyCommandBuffer2, -) -from pygerber.gerberx3.parser2.context2 import Parser2Context, Parser2ContextOptions -from pygerber.gerberx3.parser2.errors2 import ( - ExitParsingProcess2Interrupt, - OnUpdateDrawingState2Error, - Parser2Error, - SkipTokenInterrupt, -) -from pygerber.gerberx3.parser2.parser2hooks_base import Parser2HooksBase -from pygerber.gerberx3.tokenizer.tokens.bases.token import Token -from pygerber.gerberx3.tokenizer.tokens.groups.ast import AST - - -class Parser2: - """Gerber AST parser, version 2.""" - - def __init__( - self, - options: Optional[Parser2Options] = None, - ) -> None: - """Initialize parser. - - Parameters - ---------- - options : ParserOptions | None - Additional options for modifying parser behavior. - - """ - self.options = Parser2Options() if options is None else options - self.is_used = False - self.context = ( - Parser2Context(self.options.context_options) - if self.options.initial_context is None - else self.options.initial_context - ) - self.get_hooks().on_parser_init(self) - - def parse(self, ast: AST) -> ReadonlyCommandBuffer2: - """Parse token stack.""" - for _ in self.parse_iter(ast): - pass - - return self.context.main_command_buffer.get_readonly() - - def parse_iter( - self, - ast: AST, - ) -> Generator[tuple[Token, Parser2Context], None, None]: - """Iterate over tokens in stack and parse them.""" - self.get_hooks().pre_parse(self.context) - self.is_used = True - - try: - for token in ast: - self.context.set_current_token(token) - self._token_try_visit_except(token) - - yield token, self.context - - except ExitParsingProcess2Interrupt: - pass - - self.get_hooks().post_parse(self.context) - - def _token_try_visit_except(self, token: Token) -> None: - try: - self.get_hooks().pre_parser_visit_any_token(self.context) - token.parser2_visit_token(self.context) - self.get_hooks().post_parser_visit_any_token(self.context) - - except SkipTokenInterrupt: - return - - except ExitParsingProcess2Interrupt: - raise - - except Exception as e: - if ( - self.options.on_update_drawing_state_error - == Parser2OnErrorAction.Ignore - ): - pass - - elif ( - self.options.on_update_drawing_state_error == Parser2OnErrorAction.Raise - ): - if not isinstance(e, Parser2Error): - raise OnUpdateDrawingState2Error(token) from e - - raise - - elif ( - self.options.on_update_drawing_state_error == Parser2OnErrorAction.Warn - ): - logging.warning( - "Encountered fatal error during call to update_drawing_state() " - "of '%s' token %s. Parser will skip this token and continue.", - token, - token.get_token_position(), - ) - - elif ( - self.options.on_update_drawing_state_error - == Parser2OnErrorAction.UseHook - ): - if isinstance(e, Parser2Error): - self.get_hooks().on_parser_error(self.context, e) - else: - self.get_hooks().on_other_error(self.context, e) - - def get_hooks(self) -> Parser2HooksBase: - """Get hooks object.""" - return self.context.get_hooks() - - -class Parser2OnErrorAction(Enum): - """Possible error actions.""" - - Ignore = "ignore" - """Ignore parser errors. Errors which occurred will not be signaled. May yield - unexpected results for broken files, with missing draw commands or even more - significant errors.""" - - Warn = "warn" - """Warn on parser error. Parser will log warning message about what went wrong. - Best for supporting wide range of files without silently ignoring errors in code.""" - - Raise = "raise" - """Raise exception whenever parser encounters error. Will completely break out of - parsing process, making it impossible to render slightly malformed files.""" - - UseHook = "use_hook" - """Use appropriate hooks to dispatch exception.""" - - -class Parser2Options(FrozenGeneralModel): - """Container class for Gerber parser options.""" - - initial_context: Optional[Parser2Context] = Field(default=None) - context_options: Optional[Parser2ContextOptions] = Field(default=None) - on_update_drawing_state_error: Parser2OnErrorAction = Parser2OnErrorAction.Raise diff --git a/src/pygerber/gerberx3/parser2/parser2hooks.py b/src/pygerber/gerberx3/parser2/parser2hooks.py deleted file mode 100644 index 617699a3..00000000 --- a/src/pygerber/gerberx3/parser2/parser2hooks.py +++ /dev/null @@ -1,2236 +0,0 @@ -"""Implementation of hooks for Gerber AST Parser, version 2.""" - -# ruff: noqa: D401 -from __future__ import annotations - -import math -from decimal import Decimal -from types import MappingProxyType -from typing import TYPE_CHECKING - -from pygerber.common.error import throw -from pygerber.gerberx3.math.offset import Offset -from pygerber.gerberx3.math.vector_2d import Vector2D -from pygerber.gerberx3.parser2.apertures2.block2 import Block2 -from pygerber.gerberx3.parser2.apertures2.circle2 import Circle2, NoCircle2 -from pygerber.gerberx3.parser2.apertures2.macro2 import Macro2 -from pygerber.gerberx3.parser2.apertures2.obround2 import Obround2 -from pygerber.gerberx3.parser2.apertures2.polygon2 import Polygon2 -from pygerber.gerberx3.parser2.apertures2.rectangle2 import Rectangle2 -from pygerber.gerberx3.parser2.command_buffer2 import CommandBuffer2 -from pygerber.gerberx3.parser2.commands2.arc2 import Arc2, CCArc2 -from pygerber.gerberx3.parser2.commands2.buffer_command2 import BufferCommand2 -from pygerber.gerberx3.parser2.commands2.command2 import Command2 -from pygerber.gerberx3.parser2.commands2.flash2 import Flash2 -from pygerber.gerberx3.parser2.commands2.line2 import Line2 -from pygerber.gerberx3.parser2.commands2.region2 import Region2 -from pygerber.gerberx3.parser2.errors2 import ( - ApertureNotSelected2Error, - IncrementalCoordinatesNotSupported2Error, - NoValidArcCenterFoundError, - StepAndRepeatNotInitializedError, - UnnamedBlockApertureNotAllowedError, -) -from pygerber.gerberx3.parser2.macro2.assignment2 import Assignment2 -from pygerber.gerberx3.parser2.macro2.macro2 import ApertureMacro2 -from pygerber.gerberx3.parser2.macro2.point2 import Point2 -from pygerber.gerberx3.parser2.macro2.primitives2.code_1_circle2 import Code1Circle2 -from pygerber.gerberx3.parser2.macro2.primitives2.code_2_vector_line2 import ( - Code2VectorLine2, -) -from pygerber.gerberx3.parser2.macro2.primitives2.code_4_outline2 import Code4Outline2 -from pygerber.gerberx3.parser2.macro2.primitives2.code_5_polygon2 import Code5Polygon2 -from pygerber.gerberx3.parser2.macro2.primitives2.code_6_moire2 import Code6Moire2 -from pygerber.gerberx3.parser2.macro2.primitives2.code_7_thermal2 import Code7Thermal2 -from pygerber.gerberx3.parser2.macro2.primitives2.code_20_vector_line2 import ( - Code20VectorLine2, -) -from pygerber.gerberx3.parser2.macro2.primitives2.code_21_center_line2 import ( - Code21CenterLine2, -) -from pygerber.gerberx3.parser2.macro2.primitives2.code_22_lower_left_line2 import ( - Code22LowerLeftLine2, -) -from pygerber.gerberx3.parser2.parser2hooks_base import Parser2HooksBase -from pygerber.gerberx3.parser2.state2 import ApertureTransform -from pygerber.gerberx3.state_enums import ( - DrawMode, - ImagePolarityEnum, - Mirroring, - Polarity, - Unit, -) -from pygerber.gerberx3.tokenizer.aperture_id import ApertureID -from pygerber.gerberx3.tokenizer.tokens.fs_coordinate_format import ( - CoordinateParser, -) - -if TYPE_CHECKING: - from pygerber.gerberx3.parser2.context2 import Parser2Context - from pygerber.gerberx3.tokenizer.tokens.ab_block_aperture import ( - BlockApertureBegin, - BlockApertureEnd, - ) - from pygerber.gerberx3.tokenizer.tokens.ad_define_aperture import ( - DefineCircle, - DefineMacro, - DefineObround, - DefinePolygon, - DefineRectangle, - ) - from pygerber.gerberx3.tokenizer.tokens.as_axis_select import AxisSelect - from pygerber.gerberx3.tokenizer.tokens.d01_draw import D01Draw - from pygerber.gerberx3.tokenizer.tokens.d02_move import D02Move - from pygerber.gerberx3.tokenizer.tokens.d03_flash import D03Flash - from pygerber.gerberx3.tokenizer.tokens.dnn_select_aperture import DNNSelectAperture - from pygerber.gerberx3.tokenizer.tokens.fs_coordinate_format import ( - CoordinateFormat, - ) - from pygerber.gerberx3.tokenizer.tokens.g01_set_linear import SetLinear - from pygerber.gerberx3.tokenizer.tokens.g02_set_clockwise_circular import ( - SetClockwiseCircular, - ) - from pygerber.gerberx3.tokenizer.tokens.g03_set_counterclockwise_circular import ( - SetCounterclockwiseCircular, - ) - from pygerber.gerberx3.tokenizer.tokens.g36_begin_region import BeginRegion - from pygerber.gerberx3.tokenizer.tokens.g37_end_region import EndRegion - from pygerber.gerberx3.tokenizer.tokens.g54_select_aperture import G54SelectAperture - from pygerber.gerberx3.tokenizer.tokens.g70_set_unit_inch import SetUnitInch - from pygerber.gerberx3.tokenizer.tokens.g71_set_unit_mm import SetUnitMillimeters - from pygerber.gerberx3.tokenizer.tokens.g74_single_quadrant import ( - SetSingleQuadrantMode, - ) - from pygerber.gerberx3.tokenizer.tokens.g75_multi_quadrant import ( - SetMultiQuadrantMode, - ) - from pygerber.gerberx3.tokenizer.tokens.g90_set_coordinate_absolute import ( - SetAbsoluteNotation, - ) - from pygerber.gerberx3.tokenizer.tokens.g91_set_coordinate_incremental import ( - SetIncrementalNotation, - ) - from pygerber.gerberx3.tokenizer.tokens.in_image_name import ImageName - from pygerber.gerberx3.tokenizer.tokens.ip_image_polarity import ImagePolarity - from pygerber.gerberx3.tokenizer.tokens.lm_load_mirroring import LoadMirroring - from pygerber.gerberx3.tokenizer.tokens.ln_load_name import LoadName - from pygerber.gerberx3.tokenizer.tokens.lp_load_polarity import LoadPolarity - from pygerber.gerberx3.tokenizer.tokens.lr_load_rotation import LoadRotation - from pygerber.gerberx3.tokenizer.tokens.ls_load_scaling import LoadScaling - from pygerber.gerberx3.tokenizer.tokens.m00_program_stop import M00ProgramStop - from pygerber.gerberx3.tokenizer.tokens.m01_optional_stop import M01OptionalStop - from pygerber.gerberx3.tokenizer.tokens.m02_end_of_file import M02EndOfFile - from pygerber.gerberx3.tokenizer.tokens.macro.am_macro import MacroDefinition - from pygerber.gerberx3.tokenizer.tokens.macro.macro_begin import MacroBegin - from pygerber.gerberx3.tokenizer.tokens.macro.statements.code_1_circle import ( - Code1CircleToken, - ) - from pygerber.gerberx3.tokenizer.tokens.macro.statements.code_2_vector_line import ( - Code2VectorLineToken, - ) - from pygerber.gerberx3.tokenizer.tokens.macro.statements.code_4_outline import ( - Code4OutlineToken, - ) - from pygerber.gerberx3.tokenizer.tokens.macro.statements.code_5_polygon import ( - Code5PolygonToken, - ) - from pygerber.gerberx3.tokenizer.tokens.macro.statements.code_6_moire import ( - Code6MoireToken, - ) - from pygerber.gerberx3.tokenizer.tokens.macro.statements.code_7_thermal import ( - Code7ThermalToken, - ) - from pygerber.gerberx3.tokenizer.tokens.macro.statements.code_20_vector_line import ( # noqa: E501 - Code20VectorLineToken, - ) - from pygerber.gerberx3.tokenizer.tokens.macro.statements.code_21_center_line import ( # noqa: E501 - Code21CenterLineToken, - ) - from pygerber.gerberx3.tokenizer.tokens.macro.statements.code_22_lower_left_line import ( # noqa: E501 - Code22LowerLeftLineToken, - ) - from pygerber.gerberx3.tokenizer.tokens.macro.statements.variable_assignment import ( # noqa: E501 - MacroVariableAssignment, - ) - from pygerber.gerberx3.tokenizer.tokens.mo_unit_mode import UnitMode - from pygerber.gerberx3.tokenizer.tokens.sr_step_repeat import ( - StepRepeatBegin, - StepRepeatEnd, - ) - from pygerber.gerberx3.tokenizer.tokens.ta_aperture_attribute import ( - ApertureAttribute, - ) - from pygerber.gerberx3.tokenizer.tokens.td_delete_attribute import DeleteAttribute - from pygerber.gerberx3.tokenizer.tokens.tf_file_attribute import FileAttribute - from pygerber.gerberx3.tokenizer.tokens.to_object_attribute import ObjectAttribute - -MAX_SINGLE_QUADRANT_ANGLE = 91.0 - - -class Parser2Hooks(Parser2HooksBase): - """Implementation of hooks for Gerber AST Parser, version 2.""" - - class MacroBeginTokenHooks(Parser2HooksBase.MacroBeginTokenHooks): - """Hooks for visiting macro begin token (AM).""" - - def on_parser_visit_token( - self, - token: MacroBegin, - context: Parser2Context, - ) -> None: - """Called when parser visits a token. - - This hook should perform all changes on context implicated by token type. - - Parameters - ---------- - token: TokenT - The token that is being visited. - context : Parser2Context - The context object containing information about the parser state. - - """ - context.set_macro_statement_buffer() - return super().on_parser_visit_token(token, context) - - class MacroCode1CircleTokenHooks(Parser2HooksBase.MacroCode1CircleTokenHooks): - """Hooks for visiting macro primitive code 0 circle.""" - - def on_parser_visit_token( - self, - token: Code1CircleToken, - context: Parser2Context, - ) -> None: - """Adds the primitive to the statement buffer. - - Parameters - ---------- - token: TokenT - The token that is being visited. - context : Parser2Context - The context object containing information about the parser state. - - """ - context.get_macro_statement_buffer().add_statement( - Code1Circle2( - exposure=token.exposure.to_parser2_expression(context), - diameter=token.diameter.to_parser2_expression(context), - center_x=token.center_x.to_parser2_expression(context), - center_y=token.center_y.to_parser2_expression(context), - rotation=token.rotation.to_parser2_expression(context), - ), - ) - return super().on_parser_visit_token(token, context) - - class MacroCode2VectorLineTokenHooks( - Parser2HooksBase.MacroCode2VectorLineTokenHooks, - ): - """Hooks for visiting macro primitive code 2 vector line.""" - - def on_parser_visit_token( - self, - token: Code2VectorLineToken, - context: Parser2Context, - ) -> None: - """Adds the primitive to the statement buffer. - - Parameters - ---------- - token: Code2VectorLine - The token that is being visited. - context : Parser2Context - The context object containing information about the parser state. - - """ - context.get_macro_statement_buffer().add_statement( - Code2VectorLine2(), - ) - return super().on_parser_visit_token(token, context) - - class MacroCode4OutlineTokenHooks(Parser2HooksBase.MacroCode4OutlineTokenHooks): - """Hooks for visiting macro primitive code 4 outline.""" - - def on_parser_visit_token( - self, - token: Code4OutlineToken, - context: Parser2Context, - ) -> None: - """Adds the primitive to the statement buffer. - - Parameters - ---------- - token: TokenT - The token that is being visited. - context : Parser2Context - The context object containing information about the parser state. - - """ - context.get_macro_statement_buffer().add_statement( - Code4Outline2( - exposure=token.exposure.to_parser2_expression(context), - vertex_count=token.number_of_vertices.to_parser2_expression( - context, - ), - start_x=token.start_x.to_parser2_expression(context), - start_y=token.start_y.to_parser2_expression(context), - points=[point.to_parser2_point2(context) for point in token.point], - rotation=token.rotation.to_parser2_expression(context), - ), - ) - return super().on_parser_visit_token(token, context) - - class MacroCode5PolygonTokenHooks(Parser2HooksBase.MacroCode5PolygonTokenHooks): - """Hooks for visiting macro primitive code 5 polygon.""" - - def on_parser_visit_token( - self, - token: Code5PolygonToken, - context: Parser2Context, - ) -> None: - """Adds the primitive to the statement buffer. - - Parameters - ---------- - token: TokenT - The token that is being visited. - context : Parser2Context - The context object containing information about the parser state. - - """ - context.get_macro_statement_buffer().add_statement( - Code5Polygon2( - exposure=token.exposure.to_parser2_expression(context), - number_of_vertices=token.number_of_vertices.to_parser2_expression( - context, - ), - center_x=token.center_x.to_parser2_expression(context), - center_y=token.center_y.to_parser2_expression(context), - diameter=token.diameter.to_parser2_expression(context), - rotation=token.rotation.to_parser2_expression(context), - ), - ) - return super().on_parser_visit_token(token, context) - - class MacroCode6MoireTokenHooks(Parser2HooksBase.MacroCode6MoireTokenHooks): - """Hooks for visiting macro primitive code 6 moire.""" - - def on_parser_visit_token( - self, - token: Code6MoireToken, - context: Parser2Context, - ) -> None: - """Adds the primitive to the statement buffer. - - Parameters - ---------- - token: TokenT - The token that is being visited. - context : Parser2Context - The context object containing information about the parser state. - - """ - context.get_macro_statement_buffer().add_statement( - Code6Moire2(), - ) - return super().on_parser_visit_token(token, context) - - class MacroCode7ThermalTokenHooks(Parser2HooksBase.MacroCode7ThermalTokenHooks): - """Hooks for visiting macro primitive code 7 thermal.""" - - def on_parser_visit_token( - self, - token: Code7ThermalToken, - context: Parser2Context, - ) -> None: - """Adds the primitive to the statement buffer. - - Parameters - ---------- - token: TokenT - The token that is being visited. - context : Parser2Context - The context object containing information about the parser state. - - """ - context.get_macro_statement_buffer().add_statement( - Code7Thermal2( - center_x=token.center_x.to_parser2_expression(context), - center_y=token.center_y.to_parser2_expression(context), - outer_diameter=token.outer_diameter.to_parser2_expression(context), - inner_diameter=token.inner_diameter.to_parser2_expression(context), - gap=token.gap.to_parser2_expression(context), - rotation=token.rotation.to_parser2_expression(context), - ), - ) - return super().on_parser_visit_token(token, context) - - class MacroCode20VectorLineTokenHooks( - Parser2HooksBase.MacroCode20VectorLineTokenHooks, - ): - """Hooks for visiting macro primitive code 20 vector line.""" - - def on_parser_visit_token( - self, - token: Code20VectorLineToken, - context: Parser2Context, - ) -> None: - """Adds the primitive to the statement buffer. - - Parameters - ---------- - token: TokenT - The token that is being visited. - context : Parser2Context - The context object containing information about the parser state. - - """ - context.get_macro_statement_buffer().add_statement( - Code20VectorLine2( - exposure=token.exposure.to_parser2_expression(context), - width=token.width.to_parser2_expression(context), - start_x=token.start_x.to_parser2_expression(context), - start_y=token.start_y.to_parser2_expression(context), - end_x=token.end_x.to_parser2_expression(context), - end_y=token.end_y.to_parser2_expression(context), - rotation=token.rotation.to_parser2_expression(context), - ), - ) - return super().on_parser_visit_token(token, context) - - class MacroCode21CenterLineTokenHooks( - Parser2HooksBase.MacroCode21CenterLineTokenHooks, - ): - """Hooks for visiting macro primitive code 21 center line.""" - - def on_parser_visit_token( - self, - token: Code21CenterLineToken, - context: Parser2Context, - ) -> None: - """Adds the primitive to the statement buffer. - - Parameters - ---------- - token: TokenT - The token that is being visited. - context : Parser2Context - The context object containing information about the parser state. - - """ - context.get_macro_statement_buffer().add_statement( - Code21CenterLine2( - exposure=token.exposure.to_parser2_expression(context), - width=token.width.to_parser2_expression(context), - height=token.height.to_parser2_expression(context), - center_x=token.center_x.to_parser2_expression(context), - center_y=token.center_y.to_parser2_expression(context), - rotation=token.rotation.to_parser2_expression(context), - ), - ) - return super().on_parser_visit_token(token, context) - - class MacroCode22LowerLeftLineTokenHooks( - Parser2HooksBase.MacroCode22LowerLeftLineTokenHooks, - ): - """Hooks for visiting macro primitive code 22 lower left line.""" - - def on_parser_visit_token( - self, - token: Code22LowerLeftLineToken, - context: Parser2Context, - ) -> None: - """Adds the primitive to the statement buffer. - - Parameters - ---------- - token: TokenT - The token that is being visited. - context : Parser2Context - The context object containing information about the parser state. - - """ - context.get_macro_statement_buffer().add_statement(Code22LowerLeftLine2()) - return super().on_parser_visit_token(token, context) - - class MacroVariableAssignment(Parser2HooksBase.MacroVariableAssignment): - """Hooks for visiting macro variable assignment token.""" - - def on_parser_visit_token( - self, - token: MacroVariableAssignment, - context: Parser2Context, - ) -> None: - """Adds the primitive to the statement buffer. - - Parameters - ---------- - token: TokenT - The token that is being visited. - context : Parser2Context - The context object containing information about the parser state. - - """ - context.get_macro_statement_buffer().add_statement( - Assignment2( - variable_name=token.variable.name, - value=token.value.to_parser2_expression(context), - ), - ) - return super().on_parser_visit_token(token, context) - - class MacroDefinitionTokenHooks(Parser2HooksBase.MacroDefinitionTokenHooks): - """Hooks for visiting macro definition token (AM).""" - - def on_parser_visit_token( - self, - token: MacroDefinition, - context: Parser2Context, - ) -> None: - """Called when parser visits a token. - - This hook should perform all changes on context implicated by token type. - - Parameters - ---------- - token: TokenT - The token that is being visited. - context : Parser2Context - The context object containing information about the parser state. - - """ - stmt_buff = context.get_macro_statement_buffer() - macro_name = token.macro_name - - context.set_macro( - macro_name, - ApertureMacro2(name=macro_name, statements=stmt_buff.get_readonly()), - ) - context.unset_macro_statement_buffer() - return super().on_parser_visit_token(token, context) - - class BeginBlockApertureTokenHooks(Parser2HooksBase.BeginBlockApertureTokenHooks): - """Hooks for visiting begin block aperture token (AB).""" - - def on_parser_visit_token( - self, - token: BlockApertureBegin, - context: Parser2Context, - ) -> None: - """Called when parser visits a token. - - This hook should perform all changes on context implicated by token type. - - Parameters - ---------- - token: TokenT - The token that is being visited. - context : Parser2Context - The context object containing information about the parser state. - - """ - context.push_block_command_buffer() - # Save state from before block definition started. - context.push_block_state() - - context.set_current_position(Vector2D.NULL) - context.set_is_aperture_block(is_aperture_block=True) - context.set_aperture_block_id(token.identifier) - - return super().on_parser_visit_token(token, context) - - class EndBlockApertureTokenHooks(Parser2HooksBase.EndBlockApertureTokenHooks): - """Hooks for visiting end block aperture token (AB).""" - - def on_parser_visit_token( - self, - token: BlockApertureEnd, - context: Parser2Context, - ) -> None: - """Called when parser visits a token. - - This hook should perform all changes on context implicated by token type. - - Parameters - ---------- - token: TokenT - The token that is being visited. - context : Parser2Context - The context object containing information about the parser state. - - """ - command_buffer = context.pop_block_command_buffer() - identifier = context.get_aperture_block_id() - if identifier is None: - raise UnnamedBlockApertureNotAllowedError(token) - - context.set_aperture( - identifier, - Block2( - identifier=identifier, - attributes=context.aperture_attributes, - command_buffer=command_buffer.get_readonly(), - ), - ) - # Restore context state from before the block definition. - context.set_state(context.pop_block_state()) - return super().on_parser_visit_token(token, context) - - class DefineApertureCircleTokenHooks( - Parser2HooksBase.DefineApertureCircleTokenHooks, - ): - """Hooks for visiting circle aperture definition token (ADD).""" - - def on_parser_visit_token( - self, - token: DefineCircle, - context: Parser2Context, - ) -> None: - """Called when parser visits a token. - - This hook should perform all changes on context implicated by token type. - - Parameters - ---------- - token: TokenT - The token that is being visited. - context : Parser2Context - The context object containing information about the parser state. - - """ - hole_diameter = ( - None - if token.hole_diameter is None - else Offset.new(token.hole_diameter, context.get_draw_units()) - ) - - context.set_aperture( - token.aperture_id, - Circle2( - identifier=token.aperture_id, - attributes=context.aperture_attributes, - diameter=Offset.new(token.diameter, context.get_draw_units()), - hole_diameter=hole_diameter, - ), - ) - return super().on_parser_visit_token(token, context) - - class DefineApertureRectangleTokenHooks( - Parser2HooksBase.DefineApertureRectangleTokenHooks, - ): - """Hooks for visiting rectangle aperture definition token (ADD).""" - - def on_parser_visit_token( - self, - token: DefineRectangle, - context: Parser2Context, - ) -> None: - """Called when parser visits a token. - - This hook should perform all changes on context implicated by token type. - - Parameters - ---------- - token: TokenT - The token that is being visited. - context : Parser2Context - The context object containing information about the parser state. - - """ - hole_diameter = ( - None - if token.hole_diameter is None - else Offset.new(token.hole_diameter, context.get_draw_units()) - ) - - context.set_aperture( - token.aperture_id, - Rectangle2( - identifier=token.aperture_id, - attributes=context.aperture_attributes, - x_size=Offset.new(token.x_size, context.get_draw_units()), - y_size=Offset.new(token.y_size, context.get_draw_units()), - hole_diameter=hole_diameter, - ), - ) - return super().on_parser_visit_token(token, context) - - class DefineApertureObroundTokenHooks( - Parser2HooksBase.DefineApertureObroundTokenHooks, - ): - """Hooks for visiting obround aperture definition token (ADD).""" - - def on_parser_visit_token( - self, - token: DefineObround, - context: Parser2Context, - ) -> None: - """Called when parser visits a token. - - This hook should perform all changes on context implicated by token type. - - Parameters - ---------- - token: TokenT - The token that is being visited. - context : Parser2Context - The context object containing information about the parser state. - - """ - hole_diameter = ( - None - if token.hole_diameter is None - else Offset.new(token.hole_diameter, context.get_draw_units()) - ) - - context.set_aperture( - token.aperture_id, - Obround2( - identifier=token.aperture_id, - attributes=context.aperture_attributes, - x_size=Offset.new(token.x_size, context.get_draw_units()), - y_size=Offset.new(token.y_size, context.get_draw_units()), - hole_diameter=hole_diameter, - ), - ) - return super().on_parser_visit_token(token, context) - - class DefineAperturePolygonTokenHooks( - Parser2HooksBase.DefineAperturePolygonTokenHooks, - ): - """Hooks for visiting polygon aperture definition token (ADD).""" - - def on_parser_visit_token( - self, - token: DefinePolygon, - context: Parser2Context, - ) -> None: - """Called when parser visits a token. - - This hook should perform all changes on context implicated by token type. - - Parameters - ---------- - token: TokenT - The token that is being visited. - context : Parser2Context - The context object containing information about the parser state. - - """ - hole_diameter = ( - None - if token.hole_diameter is None - else Offset.new(token.hole_diameter, context.get_draw_units()) - ) - rotation = Decimal("0.0") if token.rotation is None else token.rotation - - context.set_aperture( - token.aperture_id, - Polygon2( - identifier=token.aperture_id, - attributes=context.aperture_attributes, - outer_diameter=Offset.new( - token.outer_diameter, - context.get_draw_units(), - ), - number_vertices=token.number_of_vertices, - rotation=rotation, - hole_diameter=hole_diameter, - ), - ) - return super().on_parser_visit_token(token, context) - - class DefineApertureMacroTokenHooks(Parser2HooksBase.DefineApertureMacroTokenHooks): - """Hooks for visiting macro aperture definition token (ADD).""" - - def on_parser_visit_token( - self, - token: DefineMacro, - context: Parser2Context, - ) -> None: - """Called when parser visits a token. - - This hook should perform all changes on context implicated by token type. - - Parameters - ---------- - token: TokenT - The token that is being visited. - context : Parser2Context - The context object containing information about the parser state. - - """ - macro = context.get_macro(token.aperture_type) - context.set_macro_eval_buffer() - context.macro_variable_buffer = { - f"${i}": Decimal(param) for i, param in enumerate(token.am_param, 1) - } - macro.on_parser2_eval_statement(context) - - context.set_aperture( - token.aperture_id, - Macro2( - identifier=token.aperture_id, - attributes=context.aperture_attributes, - command_buffer=context.get_macro_eval_buffer().get_readonly(), - ), - ) - - context.unset_macro_eval_buffer() - context.macro_variable_buffer = {} - - return super().on_parser_visit_token(token, context) - - class MacroEvalHooks: - """Hooks called when evaluating macro aperture.""" - - def __init__(self) -> None: - self.macro_id_counter = 0 - - def get_next_id(self) -> ApertureID: - """Get next aperture id for macro.""" - next_id = ApertureID(self.macro_id_counter) - self.macro_id_counter += 1 - return next_id - - def on_code_1_circle( - self, - context: Parser2Context, - primitive: Code1Circle2, - ) -> None: - """Evaluate code 1 circle primitive.""" - exposure = primitive.exposure.on_parser2_eval_expression(context) - polarity = ( - Polarity.Clear - if math.isclose(exposure, Decimal("0.0")) - else Polarity.Dark - ) - rotation_degrees = primitive.rotation.on_parser2_eval_expression(context) - context.get_macro_eval_buffer().add_command( - Flash2( - transform=ApertureTransform( - polarity=polarity, - mirroring=Mirroring.NoMirroring, - rotation=Decimal("0.0"), - scaling=Decimal("1.0"), - ), - aperture=Circle2( - identifier=ApertureID(self.get_next_id()), - diameter=Offset.new( - primitive.diameter.on_parser2_eval_expression(context), - context.get_draw_units(), - ), - hole_diameter=None, - ), - flash_point=Vector2D( - x=Offset.new( - primitive.center_x.on_parser2_eval_expression(context), - context.get_draw_units(), - ), - y=Offset.new( - primitive.center_y.on_parser2_eval_expression(context), - context.get_draw_units(), - ), - ), - ).get_rotated(rotation_degrees), - ) - - def on_code_2_vector_line( - self, - context: Parser2Context, - primitive: Code2VectorLine2, - ) -> None: - """Evaluate code 2 vector line primitive.""" - - def on_code_4_outline( - self, - context: Parser2Context, - primitive: Code4Outline2, - ) -> None: - """Evaluate code 4 outline primitive.""" - exposure = primitive.exposure.on_parser2_eval_expression(context) - polarity = ( - Polarity.Clear - if math.isclose(exposure, Decimal("0.0")) - else Polarity.Dark - ) - transform = ApertureTransform( - polarity=polarity, - mirroring=Mirroring.NoMirroring, - rotation=Decimal("0.0"), - scaling=Decimal("1.0"), - ) - aperture = Circle2( - identifier=ApertureID(self.get_next_id()), - diameter=Offset.NULL, - hole_diameter=None, - ) - context.get_macro_eval_buffer().add_command( - Region2( - transform=ApertureTransform( - polarity=polarity, - mirroring=Mirroring.NoMirroring, - rotation=Decimal("0.0"), - scaling=Decimal("1.0"), - ), - command_buffer=CommandBuffer2( - [ - Line2( - transform=transform, - aperture=aperture, - start_point=Vector2D( - x=Offset.new( - start_point.x.on_parser2_eval_expression( - context, - ), - context.get_draw_units(), - ), - y=Offset.new( - start_point.y.on_parser2_eval_expression( - context, - ), - context.get_draw_units(), - ), - ), - end_point=Vector2D( - x=Offset.new( - end_point.x.on_parser2_eval_expression( - context, - ), - context.get_draw_units(), - ), - y=Offset.new( - end_point.y.on_parser2_eval_expression( - context, - ), - context.get_draw_units(), - ), - ), - ) - for start_point, end_point in zip( - [ - Point2(x=primitive.start_x, y=primitive.start_y), - *primitive.points, - ], - [ - *primitive.points, - Point2(x=primitive.start_x, y=primitive.start_y), - ], - ) - ], - ).get_readonly(), - ).get_rotated(primitive.rotation.on_parser2_eval_expression(context)), - ) - - def on_code_5_polygon( - self, - context: Parser2Context, - primitive: Code5Polygon2, - ) -> None: - """Evaluate code 5 polygon primitive.""" - exposure = primitive.exposure.on_parser2_eval_expression(context) - polarity = ( - Polarity.Clear - if math.isclose(exposure, Decimal("0.0")) - else Polarity.Dark - ) - context.get_macro_eval_buffer().add_command( - Flash2( - transform=ApertureTransform( - polarity=polarity, - mirroring=Mirroring.NoMirroring, - rotation=Decimal("0.0"), - scaling=Decimal("1.0"), - ), - aperture=Polygon2( - identifier=ApertureID(self.get_next_id()), - outer_diameter=Offset.new( - primitive.diameter.on_parser2_eval_expression( - context, - ), - ), - number_vertices=round( - primitive.number_of_vertices.on_parser2_eval_expression( - context, - ), - ), - rotation=Decimal("0.0"), - hole_diameter=None, - ), - flash_point=Vector2D( - x=Offset.new( - primitive.center_x.on_parser2_eval_expression(context), - context.get_draw_units(), - ), - y=Offset.new( - primitive.center_y.on_parser2_eval_expression(context), - context.get_draw_units(), - ), - ), - ).get_rotated(primitive.rotation.on_parser2_eval_expression(context)), - ) - - def on_code_6_moire( - self, - context: Parser2Context, - primitive: Code6Moire2, - ) -> None: - """Evaluate code 6 moire primitive.""" - - def on_code_7_thermal( - self, - context: Parser2Context, - primitive: Code7Thermal2, - ) -> None: - """Evaluate code 7 thermal primitive.""" - - def on_code_20_vector_line( - self, - context: Parser2Context, - primitive: Code20VectorLine2, - ) -> None: - """Evaluate code 20 vector line primitive.""" - exposure = primitive.exposure.on_parser2_eval_expression(context) - polarity = ( - Polarity.Clear - if math.isclose(exposure, Decimal("0.0")) - else Polarity.Dark - ) - context.get_macro_eval_buffer().add_command( - Line2( - transform=ApertureTransform( - polarity=polarity, - mirroring=Mirroring.NoMirroring, - rotation=Decimal("0.0"), - scaling=Decimal("1.0"), - ), - aperture=NoCircle2( - identifier=ApertureID(self.get_next_id()), - diameter=Offset.new( - primitive.width.on_parser2_eval_expression(context), - context.get_draw_units(), - ), - hole_diameter=None, - ), - start_point=Vector2D( - x=Offset.new( - primitive.start_x.on_parser2_eval_expression(context), - context.get_draw_units(), - ), - y=Offset.new( - primitive.start_y.on_parser2_eval_expression(context), - context.get_draw_units(), - ), - ), - end_point=Vector2D( - x=Offset.new( - primitive.end_x.on_parser2_eval_expression(context), - context.get_draw_units(), - ), - y=Offset.new( - primitive.end_y.on_parser2_eval_expression(context), - context.get_draw_units(), - ), - ), - ).get_rotated(primitive.rotation.on_parser2_eval_expression(context)), - ) - - def on_code_21_center_line( - self, - context: Parser2Context, - primitive: Code21CenterLine2, - ) -> None: - """Evaluate code 21 center line primitive.""" - exposure = primitive.exposure.on_parser2_eval_expression(context) - polarity = ( - Polarity.Clear - if math.isclose(exposure, Decimal("0.0")) - else Polarity.Dark - ) - context.get_macro_eval_buffer().add_command( - Flash2( - transform=ApertureTransform( - polarity=polarity, - mirroring=Mirroring.NoMirroring, - rotation=Decimal("0.0"), - scaling=Decimal("1.0"), - ), - aperture=Rectangle2( - identifier=ApertureID(self.get_next_id()), - x_size=Offset.new( - primitive.width.on_parser2_eval_expression(context), - context.get_draw_units(), - ), - y_size=Offset.new( - primitive.height.on_parser2_eval_expression(context), - context.get_draw_units(), - ), - hole_diameter=None, - ), - flash_point=Vector2D( - x=Offset.new( - primitive.center_x.on_parser2_eval_expression(context), - context.get_draw_units(), - ), - y=Offset.new( - primitive.center_y.on_parser2_eval_expression(context), - context.get_draw_units(), - ), - ), - ).get_rotated(primitive.rotation.on_parser2_eval_expression(context)), - ) - - def on_code_22_lower_left_line( - self, - context: Parser2Context, - primitive: Code22LowerLeftLine2, - ) -> None: - """Evaluate code 22 lower left line primitive.""" - - def on_assignment( - self, - context: Parser2Context, - assignment: Assignment2, - ) -> None: - """Evaluate macro variable assignment statement.""" - context.macro_variable_buffer[assignment.variable_name] = ( - assignment.value.on_parser2_eval_expression(context) - ) - - class AxisSelectTokenHooksTokenHooks( - Parser2HooksBase.AxisSelectTokenHooksTokenHooks, - ): - """Hooks for visiting axis select token (AS).""" - - def on_parser_visit_token( - self, - token: AxisSelect, - context: Parser2Context, - ) -> None: - """Perform actions on the context implicated by this token. - - Parameters - ---------- - token: TokenT - The token that is being visited. - context : Parser2Context - The context on which to perform the actions. - - """ - context.set_axis_correspondence(token.correspondence) - return super().on_parser_visit_token(token, context) - - class CommandDrawTokenHooks(Parser2HooksBase.CommandDrawTokenHooks): - """Hooks for visiting draw token (D01).""" - - def on_parser_visit_token( - self, - token: D01Draw, - context: Parser2Context, - ) -> None: - """Called when parser visits a token. - - This hook should perform all changes on context implicated by token type. - - Parameters - ---------- - token: TokenT - The token that is being visited. - context : Parser2Context - The context object containing information about the parser state. - - """ - self.DRAW_MODE_DISPATCH_TABLE[context.get_draw_mode()](self, token, context) - return super().on_parser_visit_token(token, context) - - def on_parser_visit_token_line( - self, - token: D01Draw, - context: Parser2Context, - ) -> None: - """Called when parser visits a token. - - This hook should perform all changes on context implicated by token type. - - Parameters - ---------- - token: TokenT - The token that is being visited. - context : Parser2Context - The context object containing information about the parser state. - - """ - state = context.get_state() - - x = state.parse_coordinate(token.x) - y = state.parse_coordinate(token.y) - - start_point = context.get_current_position() - end_point = Vector2D(x=x, y=y) - - aperture_id = context.get_current_aperture_id() or throw( - ApertureNotSelected2Error(token), - ) - transform = context.get_state().get_aperture_transform() - aperture = context.get_aperture(aperture_id, transform) - - command = Line2( - attributes=context.object_attributes, - aperture=aperture, - start_point=start_point, - end_point=end_point, - transform=transform, - ).get_mirrored(transform.get_mirroring()) - - context.add_command(command) - context.set_current_position(end_point) - - def on_parser_visit_token_arc( - self, - token: D01Draw, - context: Parser2Context, - ) -> None: - """Called when parser visits a token. - - This hook should perform all changes on context implicated by token type. - - Parameters - ---------- - token: TokenT - The token that is being visited. - context : Parser2Context - The context object containing information about the parser state. - - """ - state = context.get_state() - - x = state.parse_coordinate(token.x) - y = state.parse_coordinate(token.y) - i = state.parse_coordinate(token.i) - j = state.parse_coordinate(token.j) - - start_point = context.get_current_position() - end_point = Vector2D(x=x, y=y) - final_center_point = Vector2D.NULL - - if context.get_is_multi_quadrant() is False: - # In single quadrant mode I and J offsets are unsigned, therefore we - # need to check all 4 possible center points. We will choose first - # valid, if anyone needs behavior strictly matching this from spec, - # they can always create issue. - for center_offset in ( - Vector2D(x=i, y=j), - Vector2D(x=-i, y=j), - Vector2D(x=i, y=-j), - Vector2D(x=-i, y=-j), - ): - center_point = start_point + center_offset - relative_start_point = start_point - center_point - relative_end_point = end_point - center_point - # Calculate radius of arc from center to start point and end point, - # If they aren't equal, this center candidate is not valid and we - # can skip it. - if not math.isclose( - relative_start_point.length().value, - relative_end_point.length().value, - rel_tol=1e-3, - ): - continue - - # Calculate angle between vector pointing from center of arc to - # start, and vector pointing from center of arc to end point. If - # this angle is above 90 degrees, we exceeded allowed angle size in - # single quadrant mode and need to try other possible center points. - clockwise_angle = relative_start_point.angle_between( - relative_end_point, - ) - if clockwise_angle > MAX_SINGLE_QUADRANT_ANGLE: - continue - - final_center_point = center_point - break - else: - raise NoValidArcCenterFoundError(token) - - else: - # In multi quadrant mode I and J offsets are signed, so we can simply - # use them to calculate center point relative to start point. - center_offset = Vector2D(x=i, y=j) - final_center_point = start_point + center_offset - - aperture_id = context.get_current_aperture_id() or throw( - ApertureNotSelected2Error(token), - ) - transform = context.get_state().get_aperture_transform() - aperture = context.get_aperture(aperture_id, transform) - command = Arc2( - attributes=context.object_attributes, - aperture=aperture, - start_point=start_point, - end_point=end_point, - center_point=final_center_point, - transform=context.get_state().get_aperture_transform(), - ).get_mirrored(transform.get_mirroring()) - - context.add_command(command) - context.set_current_position(end_point) - - def on_parser_visit_token_cc_arc( - self, - token: D01Draw, - context: Parser2Context, - ) -> None: - """Called when parser visits a token. - - This hook should perform all changes on context implicated by token type. - - Parameters - ---------- - token: TokenT - The token that is being visited. - context : Parser2Context - The context object containing information about the parser state. - - """ - state = context.get_state() - - x = state.parse_coordinate(token.x) - y = state.parse_coordinate(token.y) - i = state.parse_coordinate(token.i) - j = state.parse_coordinate(token.j) - - start_point = context.get_current_position() - end_point = Vector2D(x=x, y=y) - final_center_point = Vector2D.NULL - - if context.get_is_multi_quadrant() is False: - for center_offset in ( - Vector2D(x=i, y=j), - Vector2D(x=-i, y=j), - Vector2D(x=i, y=-j), - Vector2D(x=-i, y=-j), - ): - center_point = start_point + center_offset - relative_start_point = start_point - center_point - relative_end_point = end_point - center_point - - if not math.isclose( - relative_start_point.length().value, - relative_end_point.length().value, - rel_tol=1e-6, - ): - continue - - # Calculate angle between vector pointing from center of arc to - # start, and vector pointing from center of arc to end point. If - # this angle is above 90 degrees, we exceeded allowed angle size in - # single quadrant mode and need to try other possible center points. - clockwise_angle = relative_start_point.angle_between_cc( - relative_end_point, - ) - if clockwise_angle > MAX_SINGLE_QUADRANT_ANGLE: - continue - - final_center_point = center_point - break - else: - raise NoValidArcCenterFoundError(token) - - else: - center_offset = Vector2D(x=i, y=j) - final_center_point = start_point + center_offset - - aperture_id = context.get_current_aperture_id() or throw( - ApertureNotSelected2Error(token), - ) - transform = context.get_state().get_aperture_transform() - aperture = context.get_aperture(aperture_id, transform) - command = CCArc2( - attributes=context.object_attributes, - aperture=aperture, - start_point=start_point, - end_point=end_point, - center_point=final_center_point, - transform=context.get_state().get_aperture_transform(), - ).get_mirrored(transform.get_mirroring()) - - context.add_command(command) - context.set_current_position(end_point) - - DRAW_MODE_DISPATCH_TABLE = MappingProxyType( - { - DrawMode.Linear: on_parser_visit_token_line, - DrawMode.ClockwiseCircular: on_parser_visit_token_arc, - DrawMode.CounterclockwiseCircular: on_parser_visit_token_cc_arc, - }, - ) - - class CommandMoveTokenHooks(Parser2HooksBase.CommandMoveTokenHooks): - """Hooks for visiting move token (D02).""" - - def on_parser_visit_token( - self, - token: D02Move, - context: Parser2Context, - ) -> None: - """Called when parser visits a token. - - This hook should perform all changes on context implicated by token type. - - Parameters - ---------- - token: TokenT - The token that is being visited. - context : Parser2Context - The context object containing information about the parser state. - - """ - state = context.get_state() - - x = state.parse_coordinate(token.x) - y = state.parse_coordinate(token.y) - - destination_point = Vector2D(x=x, y=y) - - context.set_current_position(destination_point) - return super().on_parser_visit_token(token, context) - - class CommandFlashTokenHooks(Parser2HooksBase.CommandFlashTokenHooks): - """Hooks for visiting flash token (D03).""" - - def on_parser_visit_token( - self, - token: D03Flash, - context: Parser2Context, - ) -> None: - """Called when parser visits a token. - - This hook should perform all changes on context implicated by token type. - - Parameters - ---------- - token: TokenT - The token that is being visited. - context : Parser2Context - The context object containing information about the parser state. - - """ - state = context.get_state() - - x = state.parse_coordinate(token.x) - y = state.parse_coordinate(token.y) - - flash_point = Vector2D(x=x, y=y) - - aperture_id = context.get_current_aperture_id() or throw( - ApertureNotSelected2Error(token), - ) - transform = context.get_state().get_aperture_transform() - aperture = context.get_aperture(aperture_id, transform) - - if isinstance(aperture, Block2): - cmd_buffer = aperture.command_buffer.get_transposed(flash_point) - context.add_command( - BufferCommand2( - transform=transform, - command_buffer=cmd_buffer, - ), - ) - - else: - context.add_command( - Flash2( - attributes=context.object_attributes, - aperture=aperture, - flash_point=flash_point, - transform=transform, - ), - ) - - context.set_current_position(flash_point) - return super().on_parser_visit_token(token, context) - - class SelectApertureTokenHooks(Parser2HooksBase.SelectApertureTokenHooks): - """Hooks for visiting select aperture token (DNN).""" - - def on_parser_visit_token( - self, - token: DNNSelectAperture, - context: Parser2Context, - ) -> None: - """Called when parser visits a token. - - This hook should perform all changes on context implicated by token type. - - Parameters - ---------- - token: TokenT - The token that is being visited. - context : Parser2Context - The context object containing information about the parser state. - - """ - context.set_current_aperture_id(token.aperture_id) - return super().on_parser_visit_token(token, context) - - class CoordinateFormatTokenHooks(Parser2HooksBase.CoordinateFormatTokenHooks): - """Hooks for visiting coordinate format token (FS).""" - - def on_parser_visit_token( - self, - token: CoordinateFormat, - context: Parser2Context, - ) -> None: - """Called when parser visits a token. - - This hook should perform all changes on context implicated by token type. - - Parameters - ---------- - token: TokenT - The token that is being visited. - context : Parser2Context - The context object containing information about the parser state. - - """ - context.set_coordinate_parser( - CoordinateParser.new( - x_format=token.x_format, - y_format=token.y_format, - coordinate_mode=token.coordinate_mode, - zeros_mode=token.zeros_mode, - ), - ) - - class SetLinearTokenHooks(Parser2HooksBase.SetLinearTokenHooks): - """Hooks for visiting set linear token (G01).""" - - def on_parser_visit_token( - self, - token: SetLinear, - context: Parser2Context, - ) -> None: - """Called when parser visits a token. - - This hook should perform all changes on context implicated by token type. - - Parameters - ---------- - token: TokenT - The token that is being visited. - context : Parser2Context - The context object containing information about the parser state. - - """ - context.set_draw_mode(DrawMode.Linear) - return super().on_parser_visit_token(token, context) - - class SetClockwiseCircularTokenHooks( - Parser2HooksBase.SetClockwiseCircularTokenHooks, - ): - """Hooks for visiting set clockwise circular token (G02).""" - - def on_parser_visit_token( - self, - token: SetClockwiseCircular, - context: Parser2Context, - ) -> None: - """Called when parser visits a token. - - This hook should perform all changes on context implicated by token type. - - Parameters - ---------- - token: TokenT - The token that is being visited. - context : Parser2Context - The context object containing information about the parser state. - - """ - context.set_draw_mode(DrawMode.ClockwiseCircular) - return super().on_parser_visit_token(token, context) - - class SetCounterClockwiseCircularTokenHooks( - Parser2HooksBase.SetCounterClockwiseCircularTokenHooks, - ): - """Hooks for visiting set counter clockwise circular token (G03).""" - - def on_parser_visit_token( - self, - token: SetCounterclockwiseCircular, - context: Parser2Context, - ) -> None: - """Called when parser visits a token. - - This hook should perform all changes on context implicated by token type. - - Parameters - ---------- - token: TokenT - The token that is being visited. - context : Parser2Context - The context object containing information about the parser state. - - """ - context.set_draw_mode(DrawMode.CounterclockwiseCircular) - return super().on_parser_visit_token(token, context) - - class CommentTokenHooks(Parser2HooksBase.CommentTokenHooks): - """Hooks for visiting comment token (G04).""" - - class BeginRegionTokenHooks(Parser2HooksBase.BeginRegionTokenHooks): - """Hooks for visiting begin region token (G36).""" - - def on_parser_visit_token( - self, - token: BeginRegion, - context: Parser2Context, - ) -> None: - """Called when parser visits a token. - - This hook should perform all changes on context implicated by token type. - - Parameters - ---------- - token: TokenT - The token that is being visited. - context : Parser2Context - The context object containing information about the parser state. - - """ - context.set_is_region(is_region=True) - context.set_region_command_buffer() - - return super().on_parser_visit_token(token, context) - - class EndRegionTokenHooks(Parser2HooksBase.EndRegionTokenHooks): - """Hooks for visiting end region token (G37).""" - - def on_parser_visit_token( - self, - token: EndRegion, - context: Parser2Context, - ) -> None: - """Called when parser visits a token. - - This hook should perform all changes on context implicated by token type. - - Parameters - ---------- - token: TokenT - The token that is being visited. - context : Parser2Context - The context object containing information about the parser state. - - """ - context.set_is_region(is_region=False) - command_buffer = context.get_region_command_buffer() - - context.add_command( - Region2( - aperture_attributes=context.aperture_attributes, - object_attributes=context.object_attributes, - command_buffer=command_buffer.get_readonly(), - transform=context.get_state().get_aperture_transform(), - ), - ) - - context.unset_region_command_buffer() - return super().on_parser_visit_token(token, context) - - class PrepareSelectApertureTokenHooks( - Parser2HooksBase.PrepareSelectApertureTokenHooks, - ): - """Hooks for visiting prepare select aperture token (G54).""" - - def on_parser_visit_token( - self, - token: G54SelectAperture, - context: Parser2Context, - ) -> None: - """Called when parser visits a token. - - This hook should perform all changes on context implicated by token type. - - Parameters - ---------- - token: TokenT - The token that is being visited. - context : Parser2Context - The context object containing information about the parser state. - - """ - self.hooks.select_aperture.on_parser_visit_token(token, context) - return super().on_parser_visit_token(token, context) - - class SetUnitInchTokenHooks(Parser2HooksBase.SetUnitInchTokenHooks): - """Hooks for visiting set unit inch token (G70).""" - - def on_parser_visit_token( - self, - token: SetUnitInch, - context: Parser2Context, - ) -> None: - """Called when parser visits a token. - - This hook should perform all changes on context implicated by token type. - - Parameters - ---------- - token: TokenT - The token that is being visited. - context : Parser2Context - The context object containing information about the parser state. - - """ - context.set_draw_units(Unit.Inches) - return super().on_parser_visit_token(token, context) - - class SetUnitMillimetersTokenHooks(Parser2HooksBase.SetUnitMillimetersTokenHooks): - """Hooks for visiting set unit millimeters token (G71).""" - - def on_parser_visit_token( - self, - token: SetUnitMillimeters, - context: Parser2Context, - ) -> None: - """Called when parser visits a token. - - This hook should perform all changes on context implicated by token type. - - Parameters - ---------- - token: TokenT - The token that is being visited. - context : Parser2Context - The context object containing information about the parser state. - - """ - context.set_draw_units(Unit.Millimeters) - return super().on_parser_visit_token(token, context) - - class SetSingleQuadrantModeTokenHooks( - Parser2HooksBase.SetSingleQuadrantModeTokenHooks, - ): - """Hooks for visiting set single-quadrant mode token (G74).""" - - def on_parser_visit_token( - self, - token: SetSingleQuadrantMode, - context: Parser2Context, - ) -> None: - """Called when parser visits a token. - - This hook should perform all changes on context implicated by token type. - - Parameters - ---------- - token: TokenT - The token that is being visited. - context : Parser2Context - The context object containing information about the parser state. - - """ - context.set_is_multi_quadrant(is_multi_quadrant=False) - return super().on_parser_visit_token(token, context) - - class SetMultiQuadrantModeTokenHooks( - Parser2HooksBase.SetMultiQuadrantModeTokenHooks, - ): - """Hooks for visiting set multi-quadrant mode token (G75).""" - - def on_parser_visit_token( - self, - token: SetMultiQuadrantMode, - context: Parser2Context, - ) -> None: - """Called when parser visits a token. - - This hook should perform all changes on context implicated by token type. - - Parameters - ---------- - token: TokenT - The token that is being visited. - context : Parser2Context - The context object containing information about the parser state. - - """ - context.set_is_multi_quadrant(is_multi_quadrant=True) - return super().on_parser_visit_token(token, context) - - class SetCoordinateAbsoluteTokenHooks( - Parser2HooksBase.SetCoordinateAbsoluteTokenHooks, - ): - """Hooks for visiting set coordinate absolute token (G90).""" - - def on_parser_visit_token( - self, - token: SetAbsoluteNotation, - context: Parser2Context, - ) -> None: - """Called when parser visits a token. - - This hook should perform all changes on context implicated by token type. - - Parameters - ---------- - token: TokenT - The token that is being visited. - context : Parser2Context - The context object containing information about the parser state. - - """ - # NOOP - only absolute format supported. - return super().on_parser_visit_token(token, context) - - class SetCoordinateIncrementalTokenHooks( - Parser2HooksBase.SetCoordinateIncrementalTokenHooks, - ): - """Hooks for visiting set coordinate incremental token (G91).""" - - def on_parser_visit_token( - self, - token: SetIncrementalNotation, # noqa: ARG002 - context: Parser2Context, # noqa: ARG002 - ) -> None: - """Called when parser visits a token. - - This hook should perform all changes on context implicated by token type. - - Parameters - ---------- - token: TokenT - The token that is being visited. - context : Parser2Context - The context object containing information about the parser state. - - """ - raise IncrementalCoordinatesNotSupported2Error - - class ImageNameTokenHooks(Parser2HooksBase.ImageNameTokenHooks): - """Hooks for visiting image name token (IN).""" - - def on_parser_visit_token( - self, - token: ImageName, - context: Parser2Context, - ) -> None: - """Called when parser visits a token. - - This hook should perform all changes on context implicated by token type. - - Parameters - ---------- - token: TokenT - The token that is being visited. - context : Parser2Context - The context object containing information about the parser state. - - """ - context.set_image_name(token.content) - return super().on_parser_visit_token(token, context) - - class InvalidTokenHooks(Parser2HooksBase.InvalidTokenHooks): - """Hooks for visiting invalid token.""" - - class ImagePolarityTokenHooks(Parser2HooksBase.ImagePolarityTokenHooks): - """Hooks for visiting image polarity token (IP).""" - - def on_parser_visit_token( - self, - token: ImagePolarity, - context: Parser2Context, - ) -> None: - """Called when parser visits a token. - - This hook should perform all changes on context implicated by token type. - - Parameters - ---------- - token: TokenT - The token that is being visited. - context : Parser2Context - The context object containing information about the parser state. - - """ - context.set_is_output_image_negation_required( - value=(token.image_polarity == ImagePolarityEnum.NEGATIVE), - ) - return super().on_parser_visit_token(token, context) - - class LoadMirroringTokenHooks(Parser2HooksBase.LoadMirroringTokenHooks): - """Hooks for visiting load mirroring token (LM).""" - - def on_parser_visit_token( - self, - token: LoadMirroring, - context: Parser2Context, - ) -> None: - """Called when parser visits a token. - - This hook should perform all changes on context implicated by token type. - - Parameters - ---------- - token: TokenT - The token that is being visited. - context : Parser2Context - The context object containing information about the parser state. - - """ - context.set_mirroring(token.mirroring) - return super().on_parser_visit_token(token, context) - - class LoadNameTokenHooks(Parser2HooksBase.LoadNameTokenHooks): - """Hooks for visiting load name token (LN).""" - - def on_parser_visit_token( - self, - token: LoadName, - context: Parser2Context, - ) -> None: - """Called when parser visits a token. - - This hook should perform all changes on context implicated by token type. - - Parameters - ---------- - token: TokenT - The token that is being visited. - context : Parser2Context - The context object containing information about the parser state. - - """ - context.set_file_name(token.content) - return super().on_parser_visit_token(token, context) - - class LoadPolarityTokenHooks(Parser2HooksBase.LoadPolarityTokenHooks): - """Hooks for visiting load polarity token (LP).""" - - def on_parser_visit_token( - self, - token: LoadPolarity, - context: Parser2Context, - ) -> None: - """Called when parser visits a token. - - This hook should perform all changes on context implicated by token type. - - Parameters - ---------- - token: TokenT - The token that is being visited. - context : Parser2Context - The context object containing information about the parser state. - - """ - context.set_polarity(token.polarity) - return super().on_parser_visit_token(token, context) - - class LoadRotationTokenHooks(Parser2HooksBase.LoadRotationTokenHooks): - """Hooks for visiting load rotation token (LR).""" - - def on_parser_visit_token( - self, - token: LoadRotation, - context: Parser2Context, - ) -> None: - """Called when parser visits a token. - - This hook should perform all changes on context implicated by token type. - - Parameters - ---------- - token: TokenT - The token that is being visited. - context : Parser2Context - The context object containing information about the parser state. - - """ - context.set_rotation(token.rotation) - return super().on_parser_visit_token(token, context) - - class LoadScalingTokenHooks(Parser2HooksBase.LoadScalingTokenHooks): - """Hooks for visiting load scaling token (LS).""" - - def on_parser_visit_token( - self, - token: LoadScaling, - context: Parser2Context, - ) -> None: - """Called when parser visits a token. - - This hook should perform all changes on context implicated by token type. - - Parameters - ---------- - token: TokenT - The token that is being visited. - context : Parser2Context - The context object containing information about the parser state. - - """ - context.set_scaling(token.scaling) - return super().on_parser_visit_token(token, context) - - class ProgramStopTokenHooks(Parser2HooksBase.ProgramStopTokenHooks): - """Hooks for visiting program stop token (M00).""" - - def on_parser_visit_token( - self, - token: M00ProgramStop, # noqa: ARG002 - context: Parser2Context, - ) -> None: - """Called when parser visits a token. - - This hook should perform all changes on context implicated by token type. - - Parameters - ---------- - token: TokenT - The token that is being visited. - context : Parser2Context - The context object containing information about the parser state. - - """ - context.set_reached_program_stop() - context.halt_parser() - - class OptionalStopTokenHooks(Parser2HooksBase.OptionalStopTokenHooks): - """Hooks for visiting optional stop token (M01).""" - - def on_parser_visit_token( - self, - token: M01OptionalStop, # noqa: ARG002 - context: Parser2Context, - ) -> None: - """Handle child parsing being completed.""" - context.set_reached_optional_stop() - - class EndOfFileTokenHooks(Parser2HooksBase.EndOfFileTokenHooks): - """Hooks for visiting end of file token (M02).""" - - def on_parser_visit_token( - self, - token: M02EndOfFile, # noqa: ARG002 - context: Parser2Context, - ) -> None: - """Called when parser visits a token. - - This hook should perform all changes on context implicated by token type. - - Parameters - ---------- - token: TokenT - The token that is being visited. - context : Parser2Context - The context object containing information about the parser state. - - """ - context.set_reached_end_of_file() - context.halt_parser() - - class UnitModeTokenHooks(Parser2HooksBase.UnitModeTokenHooks): - """Hooks for visiting unit mode token (MO).""" - - def on_parser_visit_token( - self, - token: UnitMode, - context: Parser2Context, - ) -> None: - """Called when parser visits a token. - - This hook should perform all changes on context implicated by token type. - - Parameters - ---------- - token: TokenT - The token that is being visited. - context : Parser2Context - The context object containing information about the parser state. - - """ - context.set_draw_units(token.unit) - return super().on_parser_visit_token(token, context) - - class ImageOffsetTokenHooks(Parser2HooksBase.ImageOffsetTokenHooks): - """Hooks for visiting image offset token (OF).""" - - class StepRepeatBeginTokenHooks(Parser2HooksBase.StepRepeatBeginTokenHooks): - """Hooks for visiting step and repeat begin token (SR).""" - - def on_parser_visit_token( - self, - token: StepRepeatBegin, - context: Parser2Context, - ) -> None: - """Called when parser visits a token. - - This hook should perform all changes on context implicated by token type. - - Parameters - ---------- - token: TokenT - The token that is being visited. - context : Parser2Context - The context object containing information about the parser state. - - """ - context.set_state_before_step_and_repeat() - - context.set_is_step_and_repeat(is_step_and_repeat=True) - context.set_x_repeat(token.x_repeat) - context.set_y_repeat(token.y_repeat) - context.set_x_step(Offset.new(token.x_step, unit=context.get_draw_units())) - context.set_y_step(Offset.new(token.y_step, unit=context.get_draw_units())) - context.set_step_and_repeat_command_buffer() - - return super().on_parser_visit_token(token, context) - - class StepRepeatEndTokenHooks(Parser2HooksBase.StepRepeatEndTokenHooks): - """Hooks for visiting step and repeat end token (SR).""" - - def on_parser_visit_token( - self, - token: StepRepeatEnd, - context: Parser2Context, - ) -> None: - """Called when parser visits a token. - - This hook should perform all changes on context implicated by token type. - - Parameters - ---------- - token: TokenT - The token that is being visited. - context : Parser2Context - The context object containing information about the parser state. - - """ - if context.get_is_step_and_repeat() is False: - raise StepAndRepeatNotInitializedError(token) - - command_buffer = context.get_step_and_repeat_command_buffer().get_readonly() - commands: list[Command2] = [] - - for x_index in range(context.get_x_repeat()): - for y_index in range(context.get_y_repeat()): - buffer_command = BufferCommand2( - transform=context.get_state().get_aperture_transform(), - command_buffer=command_buffer, - ).get_transposed( - Vector2D( - x=(context.get_x_step() * x_index), - y=(context.get_y_step() * y_index), - ), - ) - commands.append(buffer_command) - - # Resets all variables, including is_step_and_repeat and possibly other - # set during recording of SR command block. Must be done before - # add_command() to push SR command buffers to main command buffers. - context.reset_state_to_pre_step_and_repeat() - context.unset_state_before_step_and_repeat() - context.unset_step_and_repeat_command_buffer() - - for command in commands: - context.add_command(command) - - return super().on_parser_visit_token(token, context) - - class ApertureAttributeHooks(Parser2HooksBase.ApertureAttributeHooks): - """Hooks for visiting aperture attribute token (TA).""" - - def on_parser_visit_token( - self, - token: ApertureAttribute, - context: Parser2Context, - ) -> None: - """Called when parser visits a token. - - This hook should perform all changes on context implicated by token type. - - Parameters - ---------- - token: TokenT - The token that is being visited. - context : Parser2Context - The context object containing information about the parser state. - - """ - context.set_aperture_attribute(token.name, token.value) - return super().on_parser_visit_token(token, context) - - class DeleteAttributeHooks(Parser2HooksBase.DeleteAttributeHooks): - """Hooks for visiting delete attribute token (TD).""" - - def on_parser_visit_token( - self, - token: DeleteAttribute, - context: Parser2Context, - ) -> None: - """Called when parser visits a token. - - This hook should perform all changes on context implicated by token type. - - Parameters - ---------- - token: TokenT - The token that is being visited. - context : Parser2Context - The context object containing information about the parser state. - - """ - if token.name is not None: - context.delete_aperture_attribute(token.name) - context.delete_object_attribute(token.name) - else: - context.clear_aperture_attributes() - context.clear_object_attributes() - return super().on_parser_visit_token(token, context) - - class FileAttributeHooks(Parser2HooksBase.FileAttributeHooks): - """Hooks for visiting file attribute token (TF).""" - - def on_parser_visit_token( - self, - token: FileAttribute, - context: Parser2Context, - ) -> None: - """Called when parser visits a token. - - This hook should perform all changes on context implicated by token type. - - Parameters - ---------- - token: TokenT - The token that is being visited. - context : Parser2Context - The context object containing information about the parser state. - - """ - context.set_file_attribute(token.name, token.value) - return super().on_parser_visit_token(token, context) - - class ObjectAttributeHooks(Parser2HooksBase.ObjectAttributeHooks): - """Hooks for visiting object attribute token (TO).""" - - def on_parser_visit_token( - self, - token: ObjectAttribute, - context: Parser2Context, - ) -> None: - """Called when parser visits a token. - - This hook should perform all changes on context implicated by token type. - - Parameters - ---------- - token: TokenT - The token that is being visited. - context : Parser2Context - The context object containing information about the parser state. - - """ - context.set_object_attribute(token.name, token.value) - return super().on_parser_visit_token(token, context) diff --git a/src/pygerber/gerberx3/parser2/parser2hooks_base.py b/src/pygerber/gerberx3/parser2/parser2hooks_base.py deleted file mode 100644 index 67244dd8..00000000 --- a/src/pygerber/gerberx3/parser2/parser2hooks_base.py +++ /dev/null @@ -1,697 +0,0 @@ -"""Parser hooks interface class, for Gerber AST parser, version 2.""" - -# ruff: noqa: D401 -from __future__ import annotations - -from typing import TYPE_CHECKING, Generic, TypeVar, Union - -if TYPE_CHECKING: - from typing_extensions import TypeAlias - - from pygerber.gerberx3.parser2.context2 import Parser2Context - from pygerber.gerberx3.parser2.errors2 import Parser2Error - from pygerber.gerberx3.parser2.macro2.assignment2 import Assignment2 - from pygerber.gerberx3.parser2.macro2.primitives2.code_1_circle2 import Code1Circle2 - from pygerber.gerberx3.parser2.macro2.primitives2.code_2_vector_line2 import ( - Code2VectorLine2, - ) - from pygerber.gerberx3.parser2.macro2.primitives2.code_4_outline2 import ( - Code4Outline2, - ) - from pygerber.gerberx3.parser2.macro2.primitives2.code_5_polygon2 import ( - Code5Polygon2, - ) - from pygerber.gerberx3.parser2.macro2.primitives2.code_6_moire2 import Code6Moire2 - from pygerber.gerberx3.parser2.macro2.primitives2.code_7_thermal2 import ( - Code7Thermal2, - ) - from pygerber.gerberx3.parser2.macro2.primitives2.code_20_vector_line2 import ( - Code20VectorLine2, - ) - from pygerber.gerberx3.parser2.macro2.primitives2.code_21_center_line2 import ( - Code21CenterLine2, - ) - from pygerber.gerberx3.parser2.macro2.primitives2.code_22_lower_left_line2 import ( - Code22LowerLeftLine2, - ) - from pygerber.gerberx3.parser2.parser2 import Parser2 - from pygerber.gerberx3.tokenizer.tokens.ab_block_aperture import ( - BlockApertureBegin, - BlockApertureEnd, - ) - from pygerber.gerberx3.tokenizer.tokens.ad_define_aperture import ( - DefineCircle, - DefineMacro, - DefineObround, - DefinePolygon, - DefineRectangle, - ) - from pygerber.gerberx3.tokenizer.tokens.as_axis_select import AxisSelect - from pygerber.gerberx3.tokenizer.tokens.d01_draw import D01Draw - from pygerber.gerberx3.tokenizer.tokens.d02_move import D02Move - from pygerber.gerberx3.tokenizer.tokens.d03_flash import D03Flash - from pygerber.gerberx3.tokenizer.tokens.dnn_select_aperture import DNNSelectAperture - from pygerber.gerberx3.tokenizer.tokens.fs_coordinate_format import CoordinateFormat - from pygerber.gerberx3.tokenizer.tokens.g01_set_linear import SetLinear - from pygerber.gerberx3.tokenizer.tokens.g02_set_clockwise_circular import ( - SetClockwiseCircular, - ) - from pygerber.gerberx3.tokenizer.tokens.g03_set_counterclockwise_circular import ( - SetCounterclockwiseCircular, - ) - from pygerber.gerberx3.tokenizer.tokens.g04_comment import Comment - from pygerber.gerberx3.tokenizer.tokens.g36_begin_region import BeginRegion - from pygerber.gerberx3.tokenizer.tokens.g37_end_region import EndRegion - from pygerber.gerberx3.tokenizer.tokens.g54_select_aperture import G54SelectAperture - from pygerber.gerberx3.tokenizer.tokens.g70_set_unit_inch import SetUnitInch - from pygerber.gerberx3.tokenizer.tokens.g71_set_unit_mm import SetUnitMillimeters - from pygerber.gerberx3.tokenizer.tokens.g74_single_quadrant import ( - SetSingleQuadrantMode, - ) - from pygerber.gerberx3.tokenizer.tokens.g75_multi_quadrant import ( - SetMultiQuadrantMode, - ) - from pygerber.gerberx3.tokenizer.tokens.g90_set_coordinate_absolute import ( - SetAbsoluteNotation, - ) - from pygerber.gerberx3.tokenizer.tokens.g91_set_coordinate_incremental import ( - SetIncrementalNotation, - ) - from pygerber.gerberx3.tokenizer.tokens.in_image_name import ImageName - from pygerber.gerberx3.tokenizer.tokens.invalid_token import InvalidToken - from pygerber.gerberx3.tokenizer.tokens.ip_image_polarity import ImagePolarity - from pygerber.gerberx3.tokenizer.tokens.lm_load_mirroring import LoadMirroring - from pygerber.gerberx3.tokenizer.tokens.ln_load_name import LoadName - from pygerber.gerberx3.tokenizer.tokens.lp_load_polarity import LoadPolarity - from pygerber.gerberx3.tokenizer.tokens.lr_load_rotation import LoadRotation - from pygerber.gerberx3.tokenizer.tokens.ls_load_scaling import LoadScaling - from pygerber.gerberx3.tokenizer.tokens.m00_program_stop import M00ProgramStop - from pygerber.gerberx3.tokenizer.tokens.m01_optional_stop import M01OptionalStop - from pygerber.gerberx3.tokenizer.tokens.m02_end_of_file import M02EndOfFile - from pygerber.gerberx3.tokenizer.tokens.macro.am_macro import MacroDefinition - from pygerber.gerberx3.tokenizer.tokens.macro.macro_begin import MacroBegin - from pygerber.gerberx3.tokenizer.tokens.macro.statements.code_1_circle import ( - Code1CircleToken, - ) - from pygerber.gerberx3.tokenizer.tokens.macro.statements.code_2_vector_line import ( - Code2VectorLineToken, - ) - from pygerber.gerberx3.tokenizer.tokens.macro.statements.code_4_outline import ( - Code4OutlineToken, - ) - from pygerber.gerberx3.tokenizer.tokens.macro.statements.code_5_polygon import ( - Code5PolygonToken, - ) - from pygerber.gerberx3.tokenizer.tokens.macro.statements.code_6_moire import ( - Code6MoireToken, - ) - from pygerber.gerberx3.tokenizer.tokens.macro.statements.code_7_thermal import ( - Code7ThermalToken, - ) - from pygerber.gerberx3.tokenizer.tokens.macro.statements.code_20_vector_line import ( # noqa: E501 - Code20VectorLineToken, - ) - from pygerber.gerberx3.tokenizer.tokens.macro.statements.code_21_center_line import ( # noqa: E501 - Code21CenterLineToken, - ) - from pygerber.gerberx3.tokenizer.tokens.macro.statements.code_22_lower_left_line import ( # noqa: E501 - Code22LowerLeftLineToken, - ) - from pygerber.gerberx3.tokenizer.tokens.macro.statements.variable_assignment import ( # noqa: E501 - MacroVariableAssignment, - ) - from pygerber.gerberx3.tokenizer.tokens.mo_unit_mode import UnitMode - from pygerber.gerberx3.tokenizer.tokens.of_image_offset import ImageOffset - from pygerber.gerberx3.tokenizer.tokens.sr_step_repeat import ( - StepRepeatBegin, - StepRepeatEnd, - ) - from pygerber.gerberx3.tokenizer.tokens.ta_aperture_attribute import ( - ApertureAttribute, - ) - from pygerber.gerberx3.tokenizer.tokens.td_delete_attribute import DeleteAttribute - from pygerber.gerberx3.tokenizer.tokens.tf_file_attribute import FileAttribute - from pygerber.gerberx3.tokenizer.tokens.to_object_attribute import ObjectAttribute - - -__all__ = ["TokenHooksBase", "Parser2HooksBase"] - - -BlockApertureEndT: TypeAlias = "BlockApertureEnd" -BlockApertureBeginT: TypeAlias = "BlockApertureBegin" -DefineCircleT: TypeAlias = "DefineCircle" -DefineRectangleT: TypeAlias = "DefineRectangle" -DefineObroundT: TypeAlias = "DefineObround" -DefinePolygonT: TypeAlias = "DefinePolygon" -DefineMacroT: TypeAlias = "DefineMacro" -DefineAnyT = Union[ - DefineCircleT, - DefineRectangleT, - DefineObroundT, - DefinePolygonT, - DefineMacroT, -] -AxisSelectT: TypeAlias = "AxisSelect" -D01DrawT: TypeAlias = "D01Draw" -D02MoveT: TypeAlias = "D02Move" -D03FlashT: TypeAlias = "D03Flash" -DNNSelectApertureT: TypeAlias = "DNNSelectAperture" -CoordinateFormatT: TypeAlias = "CoordinateFormat" -SetLinearT: TypeAlias = "SetLinear" -SetClockwiseCircularT: TypeAlias = "SetClockwiseCircular" -SetCounterclockwiseCircularT: TypeAlias = "SetCounterclockwiseCircular" -CommentT: TypeAlias = "Comment" -BeginRegionT: TypeAlias = "BeginRegion" -EndRegionT: TypeAlias = "EndRegion" -G54SelectApertureT: TypeAlias = "G54SelectAperture" -SetUnitInchT: TypeAlias = "SetUnitInch" -SetUnitMillimetersT: TypeAlias = "SetUnitMillimeters" -SetSingleQuadrantModeT: TypeAlias = "SetSingleQuadrantMode" -SetMultiQuadrantModeT: TypeAlias = "SetMultiQuadrantMode" -SetAbsoluteNotationT: TypeAlias = "SetAbsoluteNotation" -SetIncrementalNotationT: TypeAlias = "SetIncrementalNotation" -ImageNameT: TypeAlias = "ImageName" -InvalidTokenT: TypeAlias = "InvalidToken" -ImagePolarityT: TypeAlias = "ImagePolarity" -LoadMirroringT: TypeAlias = "LoadMirroring" -LoadNameT: TypeAlias = "LoadName" -LoadPolarityT: TypeAlias = "LoadPolarity" -LoadRotationT: TypeAlias = "LoadRotation" -LoadScalingT: TypeAlias = "LoadScaling" -M00ProgramStopT: TypeAlias = "M00ProgramStop" -M01OptionalStopT: TypeAlias = "M01OptionalStop" -M02EndOfFileT: TypeAlias = "M02EndOfFile" -MacroBeginT: TypeAlias = "MacroBegin" -Code1CircleTokenT: TypeAlias = "Code1CircleToken" -Code2VectorLineTokenT: TypeAlias = "Code2VectorLineToken" -Code4OutlineTokenT: TypeAlias = "Code4OutlineToken" -Code5PolygonTokenT: TypeAlias = "Code5PolygonToken" -Code6MoireTokenT: TypeAlias = "Code6MoireToken" -Code7ThermalTokenT: TypeAlias = "Code7ThermalToken" -Code20VectorLineTokenT: TypeAlias = "Code20VectorLineToken" -Code21CenterLineTokenT: TypeAlias = "Code21CenterLineToken" -Code22LowerLeftLineTokenT: TypeAlias = "Code22LowerLeftLineToken" -MacroVariableAssignmentT: TypeAlias = "MacroVariableAssignment" -MacroDefinitionT: TypeAlias = "MacroDefinition" -UnitModeT: TypeAlias = "UnitMode" -ImageOffsetT: TypeAlias = "ImageOffset" -StepRepeatBeginT: TypeAlias = "StepRepeatBegin" -StepRepeatEndT: TypeAlias = "StepRepeatEnd" -ApertureAttributeT: TypeAlias = "ApertureAttribute" -DeleteAttributeT: TypeAlias = "DeleteAttribute" -FileAttributeT: TypeAlias = "FileAttribute" -ObjectAttributeT: TypeAlias = "ObjectAttribute" - - -TokenT = TypeVar("TokenT") - - -class TokenHooksBase(Generic[TokenT]): - """Class for creating token visit hooks.""" - - def __init__(self, hooks: Parser2HooksBase) -> None: - self.hooks = hooks - - def post_hooks_init(self) -> None: - """Called after all hooks are assigned.""" - - def pre_parser_visit_token( - self, - token: TokenT, - context: Parser2Context, - ) -> None: - """Called before parser visits a token. - - Parameters - ---------- - token: TokenT - The token that will be visited. - context : Parser2Context - The context object containing information about the parser state. - - """ - - def on_parser_visit_token( - self, - token: TokenT, - context: Parser2Context, - ) -> None: - """Called when parser visits a token. - - This hook should perform all changes on context implicated by token type. - - Parameters - ---------- - token: TokenT - The token that is being visited. - context : Parser2Context - The context object containing information about the parser state. - - """ - - def post_parser_visit_token( - self, - token: TokenT, - context: Parser2Context, - ) -> None: - """Called after parser visits a token. - - Parameters - ---------- - token: TokenT - The token that was visited. - context : Parser2Context - The context object containing information about the parser state. - - """ - - -class Parser2HooksBase: - """Collection of overridable hooks for Gerber AST parser, version 2.""" - - def __init__(self) -> None: # noqa: PLR0915 - super().__init__() - self.macro_begin = self.MacroBeginTokenHooks(self) - self.macro_code_1_circle = self.MacroCode1CircleTokenHooks(self) - self.macro_code_2_vector_line = self.MacroCode2VectorLineTokenHooks(self) - self.macro_code_4_outline = self.MacroCode4OutlineTokenHooks(self) - self.macro_code_5_polygon = self.MacroCode5PolygonTokenHooks(self) - self.macro_code_6_moire = self.MacroCode6MoireTokenHooks(self) - self.macro_code_7_thermal = self.MacroCode7ThermalTokenHooks(self) - self.macro_code_20_vector_line = self.MacroCode20VectorLineTokenHooks(self) - self.macro_code_21_center_line = self.MacroCode21CenterLineTokenHooks(self) - self.macro_code_22_lower_left_line = self.MacroCode22LowerLeftLineTokenHooks( - self, - ) - self.macro_variable_assignment = self.MacroVariableAssignment(self) - self.macro_definition = self.MacroDefinitionTokenHooks(self) - self.macro_eval = self.MacroEvalHooks() - - self.end_block_aperture = self.EndBlockApertureTokenHooks(self) - self.begin_block_aperture = self.BeginBlockApertureTokenHooks(self) - - self.define_circle_aperture = self.DefineApertureCircleTokenHooks(self) - self.define_rectangle_aperture = self.DefineApertureRectangleTokenHooks(self) - self.define_obround_aperture = self.DefineApertureObroundTokenHooks(self) - self.define_polygon_aperture = self.DefineAperturePolygonTokenHooks(self) - self.define_macro_aperture = self.DefineApertureMacroTokenHooks(self) - self.define_aperture = self.DefineApertureTokenHooks(self) - - self.axis_select = self.AxisSelectTokenHooksTokenHooks(self) - - self.command_draw = self.CommandDrawTokenHooks(self) - self.command_move = self.CommandMoveTokenHooks(self) - self.command_flash = self.CommandFlashTokenHooks(self) - - self.select_aperture = self.SelectApertureTokenHooks(self) - self.coordinate_format = self.CoordinateFormatTokenHooks(self) - - self.set_linear = self.SetLinearTokenHooks(self) - self.set_clockwise_circular = self.SetClockwiseCircularTokenHooks(self) - self.set_counter_clockwise_circular = ( - self.SetCounterClockwiseCircularTokenHooks(self) - ) - - self.comment = self.CommentTokenHooks(self) - self.begin_region = self.BeginRegionTokenHooks(self) - self.end_region = self.EndRegionTokenHooks(self) - self.prepare_select_aperture = self.PrepareSelectApertureTokenHooks(self) - self.set_unit_inch = self.SetUnitInchTokenHooks(self) - self.set_unit_millimeters = self.SetUnitMillimetersTokenHooks(self) - - self.set_single_quadrant_mode = self.SetSingleQuadrantModeTokenHooks(self) - self.set_multi_quadrant_mode = self.SetMultiQuadrantModeTokenHooks(self) - - self.set_coordinate_absolute = self.SetCoordinateAbsoluteTokenHooks(self) - self.set_coordinate_incremental = self.SetCoordinateIncrementalTokenHooks(self) - - self.image_name = self.ImageNameTokenHooks(self) - self.invalid_token = self.InvalidTokenHooks(self) - self.image_polarity = self.ImagePolarityTokenHooks(self) - self.load_name = self.LoadNameTokenHooks(self) - - self.load_mirroring = self.LoadMirroringTokenHooks(self) - self.load_polarity = self.LoadPolarityTokenHooks(self) - self.load_rotation = self.LoadRotationTokenHooks(self) - self.load_scaling = self.LoadScalingTokenHooks(self) - - self.program_stop = self.ProgramStopTokenHooks(self) - self.optional_stop = self.OptionalStopTokenHooks(self) - self.end_of_file = self.EndOfFileTokenHooks(self) - self.unit_mode = self.UnitModeTokenHooks(self) - self.image_offset = self.ImageOffsetTokenHooks(self) - - self.step_repeat_begin = self.StepRepeatBeginTokenHooks(self) - self.step_repeat_end = self.StepRepeatEndTokenHooks(self) - - self.aperture_attribute = self.ApertureAttributeHooks(self) - self.delete_attribute = self.DeleteAttributeHooks(self) - self.file_attribute = self.FileAttributeHooks(self) - self.object_attribute = self.ObjectAttributeHooks(self) - - self._call_post_hooks_init() - - def _call_post_hooks_init(self) -> None: # noqa: PLR0915 - self.macro_begin.post_hooks_init() - self.macro_code_1_circle.post_hooks_init() - self.macro_code_2_vector_line.post_hooks_init() - self.macro_code_4_outline.post_hooks_init() - self.macro_code_5_polygon.post_hooks_init() - self.macro_code_6_moire.post_hooks_init() - self.macro_code_7_thermal.post_hooks_init() - self.macro_code_20_vector_line.post_hooks_init() - self.macro_code_21_center_line.post_hooks_init() - self.macro_code_22_lower_left_line.post_hooks_init() - self.macro_variable_assignment.post_hooks_init() - self.macro_definition.post_hooks_init() - - self.end_block_aperture.post_hooks_init() - self.begin_block_aperture.post_hooks_init() - - self.define_circle_aperture.post_hooks_init() - self.define_rectangle_aperture.post_hooks_init() - self.define_obround_aperture.post_hooks_init() - self.define_polygon_aperture.post_hooks_init() - self.define_macro_aperture.post_hooks_init() - self.define_aperture.post_hooks_init() - - self.axis_select.post_hooks_init() - - self.command_draw.post_hooks_init() - self.command_move.post_hooks_init() - self.command_flash.post_hooks_init() - - self.select_aperture.post_hooks_init() - self.coordinate_format.post_hooks_init() - - self.set_linear.post_hooks_init() - self.set_clockwise_circular.post_hooks_init() - self.set_counter_clockwise_circular.post_hooks_init() - - self.comment.post_hooks_init() - self.begin_region.post_hooks_init() - self.end_region.post_hooks_init() - self.prepare_select_aperture.post_hooks_init() - - self.set_unit_inch.post_hooks_init() - self.set_unit_millimeters.post_hooks_init() - - self.set_single_quadrant_mode.post_hooks_init() - self.set_multi_quadrant_mode.post_hooks_init() - - self.set_coordinate_absolute.post_hooks_init() - self.set_coordinate_incremental.post_hooks_init() - - self.image_name.post_hooks_init() - self.invalid_token.post_hooks_init() - self.image_polarity.post_hooks_init() - self.load_name.post_hooks_init() - - self.load_mirroring.post_hooks_init() - self.load_polarity.post_hooks_init() - self.load_rotation.post_hooks_init() - self.load_scaling.post_hooks_init() - - self.program_stop.post_hooks_init() - self.optional_stop.post_hooks_init() - self.end_of_file.post_hooks_init() - self.unit_mode.post_hooks_init() - self.image_offset.post_hooks_init() - - self.step_repeat_begin.post_hooks_init() - self.step_repeat_end.post_hooks_init() - - self.aperture_attribute.post_hooks_init() - self.delete_attribute.post_hooks_init() - self.file_attribute.post_hooks_init() - self.object_attribute.post_hooks_init() - - def on_parser_init(self, parser: Parser2) -> None: - """Called after parser initialization.""" - - def pre_parse(self, context: Parser2Context) -> None: - """Called before parsing starts.""" - - def post_parse(self, context: Parser2Context) -> None: - """Called after parsing finishes.""" - - def on_parser_error(self, context: Parser2Context, error: Parser2Error) -> None: - """Called when parsing error is thrown.""" - - def on_other_error(self, context: Parser2Context, error: Exception) -> None: - """Called when other error is thrown.""" - - def pre_parser_visit_any_token(self, context: Parser2Context) -> None: - """Called before parser visits any token.""" - - def post_parser_visit_any_token(self, context: Parser2Context) -> None: - """Called after parser visits any token.""" - - class MacroBeginTokenHooks(TokenHooksBase[MacroBeginT]): - """Hooks for visiting macro definition begin token (AM).""" - - class MacroCode1CircleTokenHooks(TokenHooksBase[Code1CircleTokenT]): - """Hooks for visiting macro primitive code 0 circle.""" - - class MacroCode2VectorLineTokenHooks(TokenHooksBase[Code2VectorLineTokenT]): - """Hooks for visiting macro primitive code 2 vector line.""" - - class MacroCode4OutlineTokenHooks(TokenHooksBase[Code4OutlineTokenT]): - """Hooks for visiting macro primitive code 4 outline.""" - - class MacroCode5PolygonTokenHooks(TokenHooksBase[Code5PolygonTokenT]): - """Hooks for visiting macro primitive code 5 polygon.""" - - class MacroCode6MoireTokenHooks(TokenHooksBase[Code6MoireTokenT]): - """Hooks for visiting macro primitive code 6 moire.""" - - class MacroCode7ThermalTokenHooks(TokenHooksBase[Code7ThermalTokenT]): - """Hooks for visiting macro primitive code 7 thermal.""" - - class MacroCode20VectorLineTokenHooks(TokenHooksBase[Code20VectorLineTokenT]): - """Hooks for visiting macro primitive code 20 vector line.""" - - class MacroCode21CenterLineTokenHooks(TokenHooksBase[Code21CenterLineTokenT]): - """Hooks for visiting macro primitive code 21 center line.""" - - class MacroCode22LowerLeftLineTokenHooks(TokenHooksBase[Code22LowerLeftLineTokenT]): - """Hooks for visiting macro primitive code 22 lower left line.""" - - class MacroVariableAssignment(TokenHooksBase[MacroVariableAssignmentT]): - """Hooks for visiting macro variable assignment token.""" - - class MacroDefinitionTokenHooks(TokenHooksBase[MacroDefinitionT]): - """Hooks for visiting macro definition token (AM).""" - - class MacroEvalHooks: - """Hooks called when evaluating macro aperture.""" - - def on_code_1_circle( - self, - context: Parser2Context, - primitive: Code1Circle2, - ) -> None: - """Evaluate code 1 circle primitive.""" - - def on_code_2_vector_line( - self, - context: Parser2Context, - primitive: Code2VectorLine2, - ) -> None: - """Evaluate code 2 vector line primitive.""" - - def on_code_4_outline( - self, - context: Parser2Context, - primitive: Code4Outline2, - ) -> None: - """Evaluate code 4 outline primitive.""" - - def on_code_5_polygon( - self, - context: Parser2Context, - primitive: Code5Polygon2, - ) -> None: - """Evaluate code 5 polygon primitive.""" - - def on_code_6_moire( - self, - context: Parser2Context, - primitive: Code6Moire2, - ) -> None: - """Evaluate code 6 moire primitive.""" - - def on_code_7_thermal( - self, - context: Parser2Context, - primitive: Code7Thermal2, - ) -> None: - """Evaluate code 7 thermal primitive.""" - - def on_code_20_vector_line( - self, - context: Parser2Context, - primitive: Code20VectorLine2, - ) -> None: - """Evaluate code 20 vector line primitive.""" - - def on_code_21_center_line( - self, - context: Parser2Context, - primitive: Code21CenterLine2, - ) -> None: - """Evaluate code 21 center line primitive.""" - - def on_code_22_lower_left_line( - self, - context: Parser2Context, - primitive: Code22LowerLeftLine2, - ) -> None: - """Evaluate code 22 lower left line primitive.""" - - def on_assignment( - self, - context: Parser2Context, - assignment: Assignment2, - ) -> None: - """Evaluate macro variable assignment statement.""" - - class BeginBlockApertureTokenHooks(TokenHooksBase[BlockApertureBeginT]): - """Hooks for visiting begin block aperture token (AB).""" - - class EndBlockApertureTokenHooks(TokenHooksBase[BlockApertureEndT]): - """Hooks for visiting end block aperture token (AB).""" - - class DefineApertureCircleTokenHooks(TokenHooksBase[DefineCircleT]): - """Hooks for visiting circle aperture definition token (ADD).""" - - class DefineApertureRectangleTokenHooks(TokenHooksBase[DefineRectangleT]): - """Hooks for visiting rectangle aperture definition token (ADD).""" - - class DefineApertureObroundTokenHooks(TokenHooksBase[DefineObroundT]): - """Hooks for visiting obround aperture definition token (ADD).""" - - class DefineAperturePolygonTokenHooks(TokenHooksBase[DefinePolygonT]): - """Hooks for visiting polygon aperture definition token (ADD).""" - - class DefineApertureMacroTokenHooks(TokenHooksBase[DefineMacroT]): - """Hooks for visiting macro aperture definition token (ADD).""" - - class DefineApertureTokenHooks(TokenHooksBase[DefineAnyT]): - """Hooks for visiting any aperture definition token (ADD).""" - - class AxisSelectTokenHooksTokenHooks(TokenHooksBase[AxisSelectT]): - """Hooks for visiting axis select token (AS).""" - - class CommandDrawTokenHooks(TokenHooksBase[D01DrawT]): - """Hooks for visiting draw token (D01).""" - - class CommandMoveTokenHooks(TokenHooksBase[D02MoveT]): - """Hooks for visiting move token (D02).""" - - class CommandFlashTokenHooks(TokenHooksBase[D03FlashT]): - """Hooks for visiting flash token (D03).""" - - class SelectApertureTokenHooks(TokenHooksBase[DNNSelectApertureT]): - """Hooks for visiting select aperture token (DNN).""" - - class CoordinateFormatTokenHooks(TokenHooksBase[CoordinateFormatT]): - """Hooks for visiting coordinate format token (FS).""" - - class SetLinearTokenHooks(TokenHooksBase[SetLinearT]): - """Hooks for visiting set linear token (G01).""" - - class SetClockwiseCircularTokenHooks(TokenHooksBase[SetClockwiseCircularT]): - """Hooks for visiting set clockwise circular token (G02).""" - - class SetCounterClockwiseCircularTokenHooks( - TokenHooksBase[SetCounterclockwiseCircularT], - ): - """Hooks for visiting set counter clockwise circular token (G03).""" - - class CommentTokenHooks(TokenHooksBase[CommentT]): - """Hooks for visiting comment token (G04).""" - - class BeginRegionTokenHooks(TokenHooksBase[BeginRegionT]): - """Hooks for visiting begin region token (G36).""" - - class EndRegionTokenHooks(TokenHooksBase[EndRegionT]): - """Hooks for visiting end region token (G37).""" - - class PrepareSelectApertureTokenHooks(TokenHooksBase[G54SelectApertureT]): - """Hooks for visiting prepare select aperture token (G54).""" - - class SetUnitInchTokenHooks(TokenHooksBase[SetUnitInchT]): - """Hooks for visiting set unit inch token (G70).""" - - class SetUnitMillimetersTokenHooks(TokenHooksBase[SetUnitMillimetersT]): - """Hooks for visiting set unit millimeters token (G71).""" - - class SetSingleQuadrantModeTokenHooks(TokenHooksBase[SetSingleQuadrantModeT]): - """Hooks for visiting set single-quadrant mode token (G74).""" - - class SetMultiQuadrantModeTokenHooks(TokenHooksBase[SetMultiQuadrantModeT]): - """Hooks for visiting set multi-quadrant mode token (G75).""" - - class SetCoordinateAbsoluteTokenHooks(TokenHooksBase[SetAbsoluteNotationT]): - """Hooks for visiting set coordinate absolute token (G90).""" - - class SetCoordinateIncrementalTokenHooks(TokenHooksBase[SetIncrementalNotationT]): - """Hooks for visiting set coordinate incremental token (G91).""" - - class ImageNameTokenHooks(TokenHooksBase[ImageNameT]): - """Hooks for visiting image name token (IN).""" - - class InvalidTokenHooks(TokenHooksBase[InvalidTokenT]): - """Hooks for visiting invalid token.""" - - class ImagePolarityTokenHooks(TokenHooksBase[ImagePolarityT]): - """Hooks for visiting image polarity token (IP).""" - - class LoadMirroringTokenHooks(TokenHooksBase[LoadMirroringT]): - """Hooks for visiting load mirroring token (LM).""" - - class LoadNameTokenHooks(TokenHooksBase[LoadNameT]): - """Hooks for visiting load name token (LN).""" - - class LoadPolarityTokenHooks(TokenHooksBase[LoadPolarityT]): - """Hooks for visiting load polarity token (LP).""" - - class LoadRotationTokenHooks(TokenHooksBase[LoadRotationT]): - """Hooks for visiting load rotation token (LR).""" - - class LoadScalingTokenHooks(TokenHooksBase[LoadScalingT]): - """Hooks for visiting load scaling token (LS).""" - - class ProgramStopTokenHooks(TokenHooksBase[M00ProgramStopT]): - """Hooks for visiting program stop token (M00).""" - - class OptionalStopTokenHooks(TokenHooksBase[M01OptionalStopT]): - """Hooks for visiting optional stop token (M01).""" - - class EndOfFileTokenHooks(TokenHooksBase[M02EndOfFileT]): - """Hooks for visiting end of file token (M02).""" - - class UnitModeTokenHooks(TokenHooksBase[UnitModeT]): - """Hooks for visiting unit mode token (MO).""" - - class ImageOffsetTokenHooks(TokenHooksBase[ImageOffsetT]): - """Hooks for visiting image offset token (OF).""" - - class StepRepeatBeginTokenHooks(TokenHooksBase[StepRepeatBeginT]): - """Hooks for visiting step and repeat begin token (SR).""" - - class StepRepeatEndTokenHooks(TokenHooksBase[StepRepeatEndT]): - """Hooks for visiting step and repeat end token (SR).""" - - class ApertureAttributeHooks(TokenHooksBase[ApertureAttributeT]): - """Hooks for visiting aperture attribute token (TA).""" - - class DeleteAttributeHooks(TokenHooksBase[DeleteAttributeT]): - """Hooks for visiting delete attribute token (TD).""" - - class FileAttributeHooks(TokenHooksBase[FileAttributeT]): - """Hooks for visiting file attribute token (TF).""" - - class ObjectAttributeHooks(TokenHooksBase[ObjectAttributeT]): - """Hooks for visiting object attribute token (TO).""" diff --git a/src/pygerber/gerberx3/parser2/state2.py b/src/pygerber/gerberx3/parser2/state2.py deleted file mode 100644 index 58abef55..00000000 --- a/src/pygerber/gerberx3/parser2/state2.py +++ /dev/null @@ -1,890 +0,0 @@ -"""Alternative implementation of Gerber AST parser state, version 2. - -Parser state is immutable and composed out of multiple sub objects. This approach allows -for cheap storage and updates of parser state, as whenever parser state is updated, only -one value must be changed, while rest of the structures remain unchanged and only -references to them are copied. -""" - -from __future__ import annotations - -from decimal import Decimal -from typing import TYPE_CHECKING, Optional - -from pydantic import Field - -from pygerber.common.frozen_general_model import FrozenGeneralModel -from pygerber.common.immutable_map_model import ImmutableMapping -from pygerber.gerberx3.math.offset import Offset -from pygerber.gerberx3.math.vector_2d import Vector2D -from pygerber.gerberx3.parser2.errors2 import ( - CoordinateFormatNotSet2Error, - UnitNotSet2Error, -) -from pygerber.gerberx3.parser2.macro2.macro2 import ApertureMacro2 -from pygerber.gerberx3.state_enums import ( - AxisCorrespondence, - DrawMode, - Mirroring, - Polarity, - Unit, -) -from pygerber.gerberx3.tokenizer.tokens.coordinate import Coordinate, CoordinateType -from pygerber.gerberx3.tokenizer.tokens.dnn_select_aperture import ApertureID -from pygerber.gerberx3.tokenizer.tokens.fs_coordinate_format import ( - CoordinateParser, -) - -if TYPE_CHECKING: - from typing_extensions import Self - - -class State2Constants(FrozenGeneralModel): - """Collection of rarely changing (usually once per AST) parser constants. - - This class represents the state constants used in the Gerber parser. It contains - properties for various parser constants such as draw units, coordinate format, - polarity, mirroring, rotation, scaling, image polarity, and file name. These - constants are typically set once per AST (Abstract Syntax Tree) and are used - throughout the parsing process. - """ - - draw_units: Optional[Unit] = Field(default=None) - """The draw units used for the Gerber file. (Spec reference: 4.2.1)""" - - coordinate_parser: Optional[CoordinateParser] = Field(default=None) - """The coordinate format specification, including the number of decimals. - (Spec reference: 4.2.2)""" - - is_output_image_negation_required: bool = Field(default=False) - """Flag indicating whether image polarity flipping is required. - (Spec reference: 8.1.4)""" - - image_name: Optional[str] = Field(default=None) - """The name of the image. (Spec reference: 8.1.3)""" - - file_name: Optional[str] = Field(default=None) - """The name of the file. (Spec reference: 8.1.6)""" - - axis_correspondence: AxisCorrespondence = Field(default=AxisCorrespondence.AXBY) - """Correspondence between the X, Y data axes and the A, B output device axes. - It does not affect the image in computer to computer data exchange. It only - has an effect how the image is positioned on an output device.""" - - def get_draw_units(self) -> Unit: - """Get the draw units. - - This method returns the draw units used for the Gerber file. - - Returns - ------- - Unit - The draw units. - - Raises - ------ - UnitNotSet2Error - If the draw units are not set. - - """ - if self.draw_units is None: - raise UnitNotSet2Error - return self.draw_units - - def set_draw_units(self, draw_units: Unit) -> Self: - """Set the draw units for the state. - - This method updates the draw units of the state and returns a new instance of - the state with the updated draw units. - - Parameters - ---------- - draw_units : Unit - The draw units to be set. - - Returns - ------- - Self - A new instance of the state with the updated draw units. - - """ - return self.model_copy( - update={ - "draw_units": draw_units, - }, - ) - - def get_coordinate_parser(self) -> CoordinateParser: - """Get coordinate_parser property value.""" - if self.coordinate_parser is None: - raise CoordinateFormatNotSet2Error - return self.coordinate_parser - - def set_coordinate_parser(self, coordinate_parser: CoordinateParser) -> Self: - """Set the coordinate_parser property value.""" - return self.model_copy( - update={ - "coordinate_parser": coordinate_parser, - }, - ) - - def get_is_output_image_negation_required(self) -> bool: - """Get is_output_image_negation_required property value.""" - return self.is_output_image_negation_required - - def set_is_output_image_negation_required( - self, - value: bool, # noqa: FBT001 - ) -> Self: - """Set the is_output_image_negation_required property value.""" - return self.model_copy( - update={ - "is_output_image_negation_required": value, - }, - ) - - def get_image_name(self) -> Optional[str]: - """Get image_name property value.""" - return self.image_name - - def set_image_name(self, image_name: Optional[str]) -> Self: - """Set the image_name property value.""" - return self.model_copy( - update={ - "image_name": image_name, - }, - ) - - def get_file_name(self) -> Optional[str]: - """Get file_name property value.""" - return self.file_name - - def set_file_name(self, file_name: Optional[str]) -> Self: - """Set the file_name property value.""" - return self.model_copy( - update={ - "file_name": file_name, - }, - ) - - def get_axis_correspondence(self) -> AxisCorrespondence: - """Get token axis correspondence property value.""" - return self.axis_correspondence - - def set_axis_correspondence(self, axis_correspondence: AxisCorrespondence) -> Self: - """Set token axis correspondence property value.""" - return self.model_copy( - update={ - "axis_correspondence": axis_correspondence, - }, - ) - - -class ApertureTransform(FrozenGeneralModel): - """Proxy for accessing Parser2State from the moment of creation of command.""" - - polarity: Polarity = Field(default=Polarity.Dark) - """The polarity object transformation parameter. (Spec reference: 4.9.2)""" - - mirroring: Mirroring = Field(default=Mirroring.NoMirroring) - """The mirror object transformation parameter. (Spec reference: 4.9.3)""" - - rotation: Decimal = Field(default=Decimal("0.0")) - """The rotation object transformation parameter. (Spec reference: 4.9.4)""" - - scaling: Decimal = Field(default=Decimal("1.0")) - """The scale object transformation parameter. (Spec reference: 4.9.5)""" - - def get_polarity(self) -> Polarity: - """Get polarity property value.""" - return self.polarity - - def set_polarity(self, polarity: Polarity) -> Self: - """Set the polarity property value.""" - return self.model_copy( - update={ - "polarity": polarity, - }, - ) - - def get_mirroring(self) -> Mirroring: - """Get mirroring property value.""" - return self.mirroring - - def set_mirroring(self, mirroring: Mirroring) -> Self: - """Set the mirroring property value.""" - return self.model_copy( - update={ - "mirroring": mirroring, - }, - ) - - def get_rotation(self) -> Decimal: - """Get rotation property value.""" - return self.rotation - - def set_rotation(self, rotation: Decimal) -> Self: - """Set the rotation property value.""" - return self.model_copy( - update={ - "rotation": rotation, - }, - ) - - def get_scaling(self) -> Decimal: - """Get scaling property value.""" - return self.scaling - - def set_scaling(self, scaling: Decimal) -> Self: - """Set the scaling property value.""" - return self.model_copy( - update={ - "scaling": scaling, - }, - ) - - def get_scaled(self, scale: Decimal) -> Self: - """Get copy of object scaled by factor.""" - return self.model_copy( - update={ - "scaling": self.scaling * scale, - }, - ) - - def get_transform_key(self) -> str: - """Get key describing rotation and scaling.""" - return ( - f"*%{self.get_rotation():.3f}*%{self.get_scaling():.3f}" - f"*%{self.get_mirroring()}" - ) - - def has_mirroring_enabled(self) -> bool: - """Check if there is any mirroring set.""" - return self.get_mirroring() is not Mirroring.NoMirroring - - -class State2MacroIndex(ImmutableMapping[str, ApertureMacro2]): - """Index of all macros defined in Gerber AST until currently parsed token.""" - - def set_macro(self, __id: str, __macro: ApertureMacro2) -> Self: - """Add new macro to macros index.""" - # TODO(argmaster): Add warning handling. # noqa: TD003 - return self.update(__id, __macro) - - def get_macro(self, __id: str) -> ApertureMacro2: - """Get existing macro from index. When macro is missing KeyError is - raised. - """ - return self.mapping[__id] - - -class State2DrawFlags(FrozenGeneralModel): - """Collection of drawing flags of Gerber AST parser, version 2. - - This class represents the drawing flags used in the Gerber AST parser. - It contains properties to control various drawing modes and settings. - """ - - draw_mode: DrawMode = DrawMode.Linear - """The current draw mode (linear, clockwise circular, or counterclockwise circular). - """ - is_region: bool = False - """Indicates whether the current statement is a region statement.""" - is_aperture_block: bool = False - """Indicates whether the current statement is an aperture block statement.""" - aperture_block_id: Optional[ApertureID] = Field(default=None) - """The ID of the current aperture block, if any.""" - is_multi_quadrant: bool = False - """Indicates whether the multi-quadrant mode is enabled.""" - - def get_draw_mode(self) -> DrawMode: - """Get the current draw mode. - - Returns - ------- - DrawMode: The current draw mode. - - """ - return self.draw_mode - - def set_draw_mode(self, draw_mode: DrawMode) -> State2DrawFlags: - """Set the draw mode. - - Args: - ---- - draw_mode (DrawMode): The new draw mode. - - Returns: - ------- - State2DrawFlags: A new instance of State2DrawFlags with the updated draw - mode. - - """ - return self.model_copy( - update={ - "draw_mode": draw_mode, - }, - ) - - def get_is_region(self) -> bool: - """Check if the current statement is a region statement. - - Returns - ------- - bool: True if the current statement is a region statement, False otherwise. - - """ - return self.is_region - - def set_is_region(self, val: bool) -> State2DrawFlags: # noqa: FBT001 - """Set the flag indicating whether the current statement is a region statement. - - Args: - ---- - val (bool): True if the current statement is a region statement, False - otherwise. - - Returns: - ------- - State2DrawFlags: A new instance of State2DrawFlags with the updated flag. - - """ - return self.model_copy( - update={ - "is_region": val, - }, - ) - - def get_is_aperture_block(self) -> bool: - """Check if the current statement is an aperture block statement. - - Returns - ------- - bool: True if the current statement is an aperture block statement, False - otherwise. - - """ - return self.is_aperture_block - - def set_is_aperture_block(self, val: bool) -> State2DrawFlags: # noqa: FBT001 - """Set the flag indicating whether the current statement is an aperture block - statement. - - Args: - ---- - val (bool): True if the current statement is an aperture block statement, - False otherwise. - - Returns: - ------- - State2DrawFlags: A new instance of State2DrawFlags with the updated flag. - - """ - return self.model_copy( - update={ - "is_aperture_block": val, - }, - ) - - def get_aperture_block_id(self) -> Optional[ApertureID]: - """Get the ID of the current aperture block. - - This method returns the ID of the current aperture block. - - Returns - ------- - Optional[ApertureID] - The ID of the current aperture block, or None if no aperture block is set. - - """ - return self.aperture_block_id - - def set_aperture_block_id( - self, - aperture_block_id: Optional[ApertureID], - ) -> State2DrawFlags: - """Set the ID of the current aperture block. - - This method sets the ID of the current aperture block. - - Parameters - ---------- - aperture_block_id : Optional[ApertureID] - The ID of the current aperture block. - - Returns - ------- - State2DrawFlags - A new instance of the State2DrawFlags with the updated flag. - - """ - return self.model_copy( - update={ - "aperture_block_id": aperture_block_id, - }, - ) - - def get_is_multi_quadrant(self) -> bool: - """Check if the multi-quadrant mode is enabled. - - Returns - ------- - bool: True if the multi-quadrant mode is enabled, False otherwise. - - """ - return self.is_multi_quadrant - - def set_is_multi_quadrant(self, val: bool) -> Self: # noqa: FBT001 - """Set the flag indicating whether the multi-quadrant mode is enabled. - - Args: - ---- - val (bool): True to enable the multi-quadrant mode, False to disable it. - - Returns: - ------- - State2DrawFlags: A new instance of State2DrawFlags with the updated flag. - - """ - return self.model_copy( - update={ - "is_multi_quadrant": val, - }, - ) - - -class StepAndRepeatState2(FrozenGeneralModel): - """Step and Repeat state maintainer.""" - - is_step_and_repeat: bool = False - """Indicates whether the current statement is a step and repeat statement.""" - - x_repeat: int = 0 - """Number of repeats in the X direction.""" - - y_repeat: int = 0 - """Number of repeats in the Y direction.""" - - x_step: Offset = Offset.NULL - """Step repeat distance in X direction.""" - - y_step: Offset = Offset.NULL - """Step repeat distance in Y direction.""" - - def get_is_step_and_repeat(self) -> bool: - """Check if the current statement is a step and repeat statement. - - Returns - ------- - bool: True if the current statement is a step and repeat statement, False - otherwise. - - """ - return self.is_step_and_repeat - - def set_is_step_and_repeat(self, val: bool) -> StepAndRepeatState2: # noqa: FBT001 - """Set the flag indicating whether the current statement is a step and repeat - statement. - - Args: - ---- - val (bool): True if the current statement is a step and repeat statement, - False otherwise. - - Returns: - ------- - State2DrawFlags: A new instance of State2DrawFlags with the updated flag. - - """ - return self.model_copy( - update={ - "is_step_and_repeat": val, - }, - ) - - def get_x_repeat(self) -> int: - """Get x_repeat property value.""" - return self.x_repeat - - def set_x_repeat(self, val: int) -> StepAndRepeatState2: - """Set the x_repeat property value.""" - return self.model_copy( - update={ - "x_repeat": val, - }, - ) - - def get_y_repeat(self) -> int: - """Get y_repeat property value.""" - return self.y_repeat - - def set_y_repeat(self, val: int) -> StepAndRepeatState2: - """Set the y_repeat property value.""" - return self.model_copy( - update={ - "y_repeat": val, - }, - ) - - def get_x_step(self) -> Offset: - """Get x_step property value.""" - return self.x_step - - def set_x_step(self, val: Offset) -> StepAndRepeatState2: - """Set the x_step property value.""" - return self.model_copy( - update={ - "x_step": val, - }, - ) - - def get_y_step(self) -> Offset: - """Get y_step property value.""" - return self.y_step - - def set_y_step(self, val: Offset) -> Self: - """Set the y_step property value.""" - return self.model_copy( - update={ - "y_step": val, - }, - ) - - -class State2(FrozenGeneralModel): - """Gerber AST parser, version 2, parsing state. - - This object is immutable and intended way to update the state is through setters - which return updated copy of state. - """ - - constants: State2Constants = Field(default_factory=State2Constants) - """Collection of rarely changing Gerber state constants.""" - - def get_constants(self) -> State2Constants: - """Get constants property value.""" - return self.constants - - def set_constants(self, constants: State2Constants) -> Self: - """Set the constants property value.""" - return self.model_copy( - update={ - "constants": constants, - }, - ) - - def get_draw_units(self) -> Unit: - """Get draw_units property value.""" - return self.get_constants().get_draw_units() - - def set_draw_units(self, draw_units: Unit) -> Self: - """Set the draw_units property value.""" - return self.set_constants(self.get_constants().set_draw_units(draw_units)) - - def get_coordinate_parser(self) -> CoordinateParser: - """Get coordinate_parser property value.""" - return self.get_constants().get_coordinate_parser() - - def set_coordinate_parser(self, coordinate_parser: CoordinateParser) -> Self: - """Set the coordinate_parser property value.""" - return self.set_constants( - self.get_constants().set_coordinate_parser(coordinate_parser), - ) - - aperture_transform: ApertureTransform = Field(default_factory=ApertureTransform) - - def get_aperture_transform(self) -> ApertureTransform: - """Get aperture_transform property value.""" - return self.aperture_transform - - def set_aperture_transform(self, aperture_transform: ApertureTransform) -> Self: - """Set the aperture_transform property value.""" - return self.model_copy( - update={ - "aperture_transform": aperture_transform, - }, - ) - - def get_polarity(self) -> Polarity: - """Get polarity property value.""" - return self.get_aperture_transform().get_polarity() - - def set_polarity(self, polarity: Polarity) -> Self: - """Set the polarity property value.""" - return self.set_aperture_transform( - self.get_aperture_transform().set_polarity(polarity), - ) - - def get_mirroring(self) -> Mirroring: - """Get mirroring property value.""" - return self.get_aperture_transform().get_mirroring() - - def set_mirroring(self, mirroring: Mirroring) -> Self: - """Set the mirroring property value.""" - return self.set_aperture_transform( - self.get_aperture_transform().set_mirroring(mirroring), - ) - - def get_rotation(self) -> Decimal: - """Get rotation property value.""" - return self.get_aperture_transform().get_rotation() - - def set_rotation(self, rotation: Decimal) -> Self: - """Set the rotation property value.""" - return self.set_aperture_transform( - self.get_aperture_transform().set_rotation(rotation), - ) - - def get_scaling(self) -> Decimal: - """Get scaling property value.""" - return self.get_aperture_transform().get_scaling() - - def set_scaling(self, scaling: Decimal) -> Self: - """Set the scaling property value.""" - return self.set_aperture_transform( - self.get_aperture_transform().set_scaling(scaling), - ) - - def get_is_output_image_negation_required(self) -> bool: - """Get is_output_image_negation_required property value.""" - return self.get_constants().get_is_output_image_negation_required() - - def set_is_output_image_negation_required( - self, - value: bool, # noqa: FBT001 - ) -> Self: - """Set the is_output_image_negation_required property value.""" - return self.set_constants( - self.get_constants().set_is_output_image_negation_required(value), - ) - - def get_image_name(self) -> Optional[str]: - """Get image_name property value.""" - return self.get_constants().get_image_name() - - def set_image_name(self, image_name: Optional[str]) -> Self: - """Set the image_name property value.""" - return self.set_constants(self.get_constants().set_image_name(image_name)) - - def get_file_name(self) -> Optional[str]: - """Get file_name property value.""" - return self.get_constants().get_file_name() - - def set_file_name(self, file_name: Optional[str]) -> Self: - """Set the file_name property value.""" - return self.set_constants(self.get_constants().set_file_name(file_name)) - - def get_axis_correspondence(self) -> AxisCorrespondence: - """Get token axis correspondence property value.""" - return self.get_constants().get_axis_correspondence() - - def set_axis_correspondence(self, axis_correspondence: AxisCorrespondence) -> Self: - """Set token axis correspondence property value.""" - return self.set_constants( - self.get_constants().set_axis_correspondence(axis_correspondence), - ) - - flags: State2DrawFlags = Field(default_factory=State2DrawFlags) - """Collection of more often changing Gerber state flags.""" - - def get_flags(self) -> State2DrawFlags: - """Get flags property value.""" - return self.flags - - def set_flags(self, flags: State2DrawFlags) -> Self: - """Set the flags property value.""" - return self.model_copy( - update={ - "flags": flags, - }, - ) - - def get_draw_mode(self) -> DrawMode: - """Get draw_mode property value.""" - return self.get_flags().get_draw_mode() - - def set_draw_mode(self, draw_mode: DrawMode) -> Self: - """Set the draw_mode property value.""" - return self.set_flags(self.get_flags().set_draw_mode(draw_mode)) - - def get_is_region(self) -> bool: - """Get is_region property value.""" - return self.get_flags().get_is_region() - - def set_is_region(self, is_region: bool) -> Self: # noqa: FBT001 - """Set the is_region property value.""" - return self.set_flags(self.get_flags().set_is_region(is_region)) - - def get_is_aperture_block(self) -> bool: - """Get is_aperture_block property value.""" - return self.get_flags().get_is_aperture_block() - - def set_is_aperture_block(self, is_aperture_block: bool) -> Self: # noqa: FBT001 - """Set the is_aperture_block property value.""" - return self.set_flags( - self.get_flags().set_is_aperture_block(is_aperture_block), - ) - - def get_aperture_block_id(self) -> Optional[ApertureID]: - """Get aperture_block_id property value.""" - return self.get_flags().get_aperture_block_id() - - def set_aperture_block_id(self, aperture_block_id: Optional[ApertureID]) -> Self: - """Set the aperture_block_id property value.""" - return self.set_flags( - self.get_flags().set_aperture_block_id(aperture_block_id), - ) - - def get_is_multi_quadrant(self) -> bool: - """Get is_aperture_block property value.""" - return self.get_flags().get_is_multi_quadrant() - - def set_is_multi_quadrant(self, is_multi_quadrant: bool) -> Self: # noqa: FBT001 - """Set the is_aperture_block property value.""" - return self.set_flags( - self.get_flags().set_is_multi_quadrant(is_multi_quadrant), - ) - - step_repeat: StepAndRepeatState2 = Field(default_factory=StepAndRepeatState2) - - def get_step_and_repeat(self) -> StepAndRepeatState2: - """Get step_repeat property value.""" - return self.step_repeat - - def set_step_and_repeat(self, step_and_repeat: StepAndRepeatState2) -> Self: - """Set step_repeat property value.""" - return self.model_copy( - update={ - "step_repeat": step_and_repeat, - }, - ) - - def get_is_step_and_repeat(self) -> bool: - """Get is_step_and_repeat property value.""" - return self.get_step_and_repeat().get_is_step_and_repeat() - - def set_is_step_and_repeat(self, is_step_and_repeat: bool) -> Self: # noqa: FBT001 - """Set the is_step_and_repeat property value.""" - return self.set_step_and_repeat( - self.get_step_and_repeat().set_is_step_and_repeat(is_step_and_repeat), - ) - - def get_x_repeat(self) -> int: - """Get x_repeat property value.""" - return self.get_step_and_repeat().get_x_repeat() - - def set_x_repeat(self, value: int) -> Self: - """Set the x_repeat property value.""" - return self.set_step_and_repeat( - self.get_step_and_repeat().set_x_repeat(value), - ) - - def get_y_repeat(self) -> int: - """Get y_repeat property value.""" - return self.get_step_and_repeat().get_y_repeat() - - def set_y_repeat(self, value: int) -> Self: - """Set the y_repeat property value.""" - return self.set_step_and_repeat( - self.get_step_and_repeat().set_y_repeat(value), - ) - - def get_x_step(self) -> Offset: - """Get x_step property value.""" - return self.get_step_and_repeat().get_x_step() - - def set_x_step(self, value: Offset) -> Self: - """Set the x_repeat property value.""" - return self.set_step_and_repeat( - self.get_step_and_repeat().set_x_step(value), - ) - - def get_y_step(self) -> Offset: - """Get y_step property value.""" - return self.get_step_and_repeat().get_y_step() - - def set_y_step(self, value: Offset) -> Self: - """Set the y_repeat property value.""" - return self.set_step_and_repeat( - self.get_step_and_repeat().set_y_step(value), - ) - - current_position: Vector2D = Vector2D(x=Offset.NULL, y=Offset.NULL) - """Current position of drawing head.""" - - def get_current_position(self) -> Vector2D: - """Get current_position property value.""" - return self.current_position - - def set_current_position(self, current_position: Vector2D) -> Self: - """Set the current_position property value.""" - return self.model_copy( - update={ - "current_position": current_position, - }, - ) - - current_aperture_id: Optional[ApertureID] = None - """Reference to currently selected aperture.""" - - def get_current_aperture_id(self) -> Optional[ApertureID]: - """Get current_aperture property value.""" - return self.current_aperture_id - - def set_current_aperture_id(self, current_aperture: Optional[ApertureID]) -> Self: - """Set the current_aperture property value.""" - return self.model_copy( - update={ - "current_aperture_id": current_aperture, - }, - ) - - macros: State2MacroIndex = Field(default_factory=State2MacroIndex) - """Collection of all macros defined until given point in code.""" - - def get_macro(self, __key: str) -> ApertureMacro2: - """Get macros property value.""" - return self.macros.get_macro(__key) - - def set_macro(self, __key: str, __value: ApertureMacro2) -> Self: - """Set the macros property value.""" - return self.model_copy( - update={ - "macros": self.macros.set_macro(__key, __value), - }, - ) - - def parse_coordinate(self, coordinate: Coordinate) -> Offset: - """Parse a coordinate and convert it to an Offset. - - This method parses a given coordinate and converts it to an Offset object. - It handles missing X, Y by substituting them with the current - position and missing I, J by substituting them with NULL offset. - - Parameters - ---------- - coordinate : Coordinate - The coordinate to be parsed. - - Returns - ------- - Offset - The parsed coordinate converted to an Offset object. - - """ - if coordinate.coordinate_type == CoordinateType.MISSING_X: - return self.current_position.x - - if coordinate.coordinate_type == CoordinateType.MISSING_Y: - return self.current_position.y - - if coordinate.coordinate_type == CoordinateType.MISSING_I: - return Offset.NULL - - if coordinate.coordinate_type == CoordinateType.MISSING_J: - return Offset.NULL - - return Offset.new( - self.get_coordinate_parser().parse(coordinate), - unit=self.get_draw_units(), - ) diff --git a/src/pygerber/gerberx3/renderer2/__init__.py b/src/pygerber/gerberx3/renderer2/__init__.py deleted file mode 100644 index ab831879..00000000 --- a/src/pygerber/gerberx3/renderer2/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -"""Package `backend2` contains classes implementing Gerber rendering for command -buffers generated by Parser2 based Gerber source. -""" diff --git a/src/pygerber/gerberx3/renderer2/abstract.py b/src/pygerber/gerberx3/renderer2/abstract.py deleted file mode 100644 index a97c1cd3..00000000 --- a/src/pygerber/gerberx3/renderer2/abstract.py +++ /dev/null @@ -1,136 +0,0 @@ -"""Module contains base class Rendering backend for Parser2 based Gerber data -structures. -""" - -from __future__ import annotations - -from pathlib import Path -from typing import TYPE_CHECKING, BinaryIO, Generator, Optional - -from pygerber.gerberx3.parser2.apertures2.circle2 import Circle2, NoCircle2 -from pygerber.gerberx3.parser2.apertures2.macro2 import Macro2 -from pygerber.gerberx3.parser2.apertures2.obround2 import Obround2 -from pygerber.gerberx3.parser2.apertures2.polygon2 import Polygon2 -from pygerber.gerberx3.parser2.apertures2.rectangle2 import Rectangle2 -from pygerber.gerberx3.parser2.command_buffer2 import ( - ReadonlyCommandBuffer2, -) -from pygerber.gerberx3.parser2.commands2.arc2 import Arc2, CCArc2 -from pygerber.gerberx3.parser2.commands2.buffer_command2 import BufferCommand2 -from pygerber.gerberx3.parser2.commands2.command2 import Command2 -from pygerber.gerberx3.parser2.commands2.flash2 import Flash2 -from pygerber.gerberx3.parser2.commands2.line2 import Line2 -from pygerber.gerberx3.parser2.commands2.region2 import Region2 - -if TYPE_CHECKING: - from io import BytesIO - - -class Renderer2: - """Rendering backend base class for Parser2 based Gerber data structures.""" - - def __init__(self, hooks: Renderer2HooksABC) -> None: - self.hooks = hooks - - def render(self, command_buffer: ReadonlyCommandBuffer2) -> ImageRef: - """Render Gerber structures.""" - for _ in self.render_iter(command_buffer): - pass - - return self.get_image_ref() - - def get_image_ref(self) -> ImageRef: - """Get reference to render image.""" - return self.hooks.get_image_ref() - - def render_iter( - self, - command_buffer: ReadonlyCommandBuffer2, - ) -> Generator[Command2, None, None]: - """Iterate over commands in buffer and render image for each command.""" - self.hooks.init(self, command_buffer) - for command in command_buffer: - yield from command.render_iter(self) - self.hooks.finalize() - - -class Renderer2HooksABC: - """Hooks for implementing rendering of Gerber structures to a target format.""" - - def init(self, renderer: Renderer2, command_buffer: ReadonlyCommandBuffer2) -> None: - """Initialize rendering.""" - self.renderer = renderer - self.command_buffer = command_buffer - - def render_line(self, command: Line2) -> None: - """Render line to target image.""" - - def render_arc(self, command: Arc2) -> None: - """Render arc to target image.""" - - def render_cc_arc(self, command: CCArc2) -> None: - """Render arc to target image.""" - - def render_flash_circle(self, command: Flash2, aperture: Circle2) -> None: - """Render flash circle to target image.""" - - def render_flash_no_circle(self, command: Flash2, aperture: NoCircle2) -> None: - """Render flash no circle aperture to target image.""" - - def render_flash_rectangle(self, command: Flash2, aperture: Rectangle2) -> None: - """Render flash rectangle to target image.""" - - def render_flash_obround(self, command: Flash2, aperture: Obround2) -> None: - """Render flash obround to target image.""" - - def render_flash_polygon(self, command: Flash2, aperture: Polygon2) -> None: - """Render flash polygon to target image.""" - - def render_flash_macro(self, command: Flash2, aperture: Macro2) -> None: - """Render flash macro aperture to target image.""" - - def render_buffer(self, command: BufferCommand2) -> Generator[Command2, None, None]: - """Render buffer command, performing no writes.""" - for cmd in command: - cmd.render(self.renderer) - yield cmd - - def render_region(self, command: Region2) -> None: - """Render region to target image.""" - - def get_image_ref(self) -> ImageRef: - """Get reference to render image.""" - raise NotImplementedError - - def finalize(self) -> None: - """Finalize rendering.""" - - -class ImageRef: - """Generic container for reference to rendered image.""" - - def save_to( - self, - dest: BytesIO | Path | str, - options: Optional[FormatOptions] = None, - ) -> None: - """Save rendered image.""" - if isinstance(dest, str): - dest = Path(dest) - if isinstance(dest, Path): - with dest.open("wb") as output: - return self._save_to_io(output, options) - else: - return self._save_to_io(dest, options) - - def _save_to_io( - self, - output: BinaryIO, - options: Optional[FormatOptions] = None, - ) -> None: - """Save rendered image to bytes stream buffer.""" - raise NotImplementedError - - -class FormatOptions: - """Base class for representing of possible format options.""" diff --git a/src/pygerber/gerberx3/renderer2/errors2.py b/src/pygerber/gerberx3/renderer2/errors2.py deleted file mode 100644 index 9c77af61..00000000 --- a/src/pygerber/gerberx3/renderer2/errors2.py +++ /dev/null @@ -1,24 +0,0 @@ -"""Module contains exceptions raised by rendering backends.""" - -from __future__ import annotations - - -class Renderer2Error(Exception): - """Base class for exceptions raised by rendering backends.""" - - -class SvgRenderer2Error(Renderer2Error): - """Error raised by SVG rendering backend.""" - - -class DRAWSVGNotAvailableError(SvgRenderer2Error): - """Raised when `drawsvg` can't be imported, probably because it was not installed. - - You can install it with `pip install pygerber[svg]`. - """ - - def __init__(self) -> None: - super().__init__( - "`drawsvg` library is not available. " - "Install it with `pip install pygerber[svg]`." - ) diff --git a/src/pygerber/gerberx3/renderer2/raster.py b/src/pygerber/gerberx3/renderer2/raster.py deleted file mode 100644 index 9e744759..00000000 --- a/src/pygerber/gerberx3/renderer2/raster.py +++ /dev/null @@ -1,976 +0,0 @@ -"""Module contains implementation of Gerber rendering backend outputting raster -images. -""" - -from __future__ import annotations - -import gc -import math -from contextlib import contextmanager -from decimal import Decimal -from enum import Enum -from typing import TYPE_CHECKING, Any, BinaryIO, ContextManager, Generator, Optional - -from PIL import Image, ImageDraw - -from pygerber.backend.rasterized_2d.color_scheme import ColorScheme -from pygerber.common.error import throw -from pygerber.gerberx3.math.bounding_box import BoundingBox -from pygerber.gerberx3.math.offset import Offset -from pygerber.gerberx3.math.vector_2d import Vector2D -from pygerber.gerberx3.parser2.apertures2.aperture2 import Aperture2 -from pygerber.gerberx3.parser2.apertures2.circle2 import Circle2, NoCircle2 -from pygerber.gerberx3.parser2.apertures2.macro2 import Macro2 -from pygerber.gerberx3.parser2.apertures2.obround2 import Obround2 -from pygerber.gerberx3.parser2.apertures2.polygon2 import Polygon2 -from pygerber.gerberx3.parser2.apertures2.rectangle2 import Rectangle2 -from pygerber.gerberx3.parser2.command_buffer2 import ReadonlyCommandBuffer2 -from pygerber.gerberx3.parser2.commands2.arc2 import Arc2, CCArc2 -from pygerber.gerberx3.parser2.commands2.command2 import Command2 -from pygerber.gerberx3.parser2.commands2.flash2 import Flash2 -from pygerber.gerberx3.parser2.commands2.line2 import Line2 -from pygerber.gerberx3.parser2.commands2.region2 import Region2 -from pygerber.gerberx3.parser2.state2 import ApertureTransform -from pygerber.gerberx3.renderer2.abstract import ( - FormatOptions, - ImageRef, - Renderer2, - Renderer2HooksABC, -) -from pygerber.gerberx3.state_enums import Polarity - -if TYPE_CHECKING: - from typing_extensions import Self - - -class RasterRenderer2(Renderer2): - """Rendering backend for creating raster images.""" - - def __init__( - self, - hooks: Optional[RasterRenderer2Hooks] = None, - ) -> None: - hooks = RasterRenderer2Hooks() if hooks is None else hooks - super().__init__(hooks) - - -HALF = Decimal("0.5") - - -def custom_round(value: Decimal | float) -> int: - """Round value to jason integer.""" - int_val = int(value) - diff = abs(int_val - Decimal(value)) - - if diff >= HALF: - return int_val - - return int_val - - -class RasterRenderingFrameBuilder: - """Builder for RasterRenderingFrame.""" - - def __init__(self) -> None: - self.command_buffer: Optional[ReadonlyCommandBuffer2] = None - self.bounding_box: Optional[BoundingBox] = None - self.image: Optional[Image.Image] = None - self.mask: Optional[Image.Image] = None - self.color_scheme: Optional[ColorScheme] = None - self.is_region: bool = False - self.dpmm = 1 - self.scale = Decimal("1") - self.polarity: Optional[Polarity] = None - self.x_offset = 0 - self.y_offset = 0 - - def set_command_buffer(self, command_buffer: ReadonlyCommandBuffer2) -> Self: - """Specify source buffer.""" - self.command_buffer = command_buffer - return self - - def set_command_buffer_from_list(self, commands: list[Command2]) -> Self: - """Specify source buffer.""" - self.command_buffer = ReadonlyCommandBuffer2(commands=commands) - return self - - def set_dpmm(self, dpmm: int) -> Self: - """Specify image dpmm.""" - self.dpmm = dpmm - return self - - def set_scale(self, scale: Decimal) -> Self: - """Specify rendering scale.""" - self.scale = scale - return self - - def set_image(self, image: Image.Image) -> Self: - """Specify image.""" - self.image = image - return self - - def set_mask(self, mask: Image.Image) -> Self: - """Specify mask.""" - self.mask = mask - return self - - def set_region(self, *, is_region: bool) -> Self: - """Specify region.""" - self.is_region = is_region - return self - - def set_color_scheme(self, color_scheme: ColorScheme) -> Self: - """Specify color scheme.""" - self.color_scheme = color_scheme - return self - - def set_polarity(self, polarity: Polarity) -> Self: - """Specify polarity.""" - self.polarity = polarity - return self - - def set_pixel_dimension_offsets(self, x: int = 0, y: int = 0) -> Self: - """Set pixel dimension offsets.""" - self.x_offset = x - self.y_offset = y - return self - - def build(self, *, with_mask: bool = True) -> RasterRenderingFrame: - """Build final rendering frame container.""" - command_buffer = ( - self.command_buffer - if self.command_buffer is not None - else throw(RuntimeError("Command buffer not set.")) - ) - bbox = ( - command_buffer.get_bounding_box() - if self.bounding_box is None - else self.bounding_box - ) - dimensions = ( - max(custom_round(bbox.width.as_millimeters() * self.dpmm * self.scale), 1) - + self.x_offset, - max(custom_round(bbox.height.as_millimeters() * self.dpmm * self.scale), 1) - + self.y_offset, - ) - image = ( - Image.new("RGBA", dimensions, (0, 0, 0, 0)) - if self.image is None - else self.image - ) - mask = ( - ( - Image.new("RGBA", dimensions, (0, 0, 0, 0)) - if self.mask is None - else self.mask - ) - if with_mask - else None - ) - color_scheme = self.color_scheme or throw(RuntimeError("Missing color schema.")) - polarity = self.polarity or throw(RuntimeError("Missing polarity.")) - # Unset command buffer to prevent unintended reuse. - self.command_buffer = None - self.polarity = None - - return RasterRenderingFrame( - command_buffer=command_buffer, - bounding_box=bbox, - image=image, - mask=mask, - color_scheme=color_scheme, - polarity=polarity, - is_region=self.is_region, - ) - - -class RasterRenderingFrame: - """Container for rendering variables.""" - - def __init__( - self, - command_buffer: ReadonlyCommandBuffer2, - bounding_box: BoundingBox, - image: Image.Image, - mask: Optional[Image.Image], - color_scheme: ColorScheme, - polarity: Polarity, - *, - is_region: bool = False, - ) -> None: - self.command_buffer = command_buffer - self.bounding_box = bounding_box - self.image = image - self.layer = ImageDraw.ImageDraw(image) - self.mask = mask - self.mask_draw = None if mask is None else ImageDraw.ImageDraw(mask) - self.color_scheme = color_scheme - self.polarity = polarity - self.is_region = is_region - - def get_aperture(self) -> RasterAperture: - """Return aperture.""" - if self.mask is None: - msg = "Invalid aperture mask." - raise RuntimeError(msg) - return RasterAperture(image=self.image, mask=self.mask) - - def get_color(self, polarity: Polarity) -> str: - """Get color for specified polarity.""" - if self.polarity == Polarity.Dark: - return self._get_color(polarity) - return self._get_color(polarity.invert()) - - def _get_color(self, polarity: Polarity) -> str: - if self.is_region: - if polarity == Polarity.Dark: - return self.color_scheme.solid_region_color.to_hex() - return self.color_scheme.clear_region_color.to_hex() - - if polarity == Polarity.Dark: - return self.color_scheme.solid_color.to_hex() - return self.color_scheme.clear_color.to_hex() - - def get_mask_color(self, polarity: Polarity) -> str: - """Get color for specified polarity.""" - if polarity == Polarity.Dark: - return "#FFFFFFFF" - return "#00000000" - - def line(self, polarity: Polarity, *args: Any, **kwargs: Any) -> None: - """Draw line on image.""" - kwargs["fill"] = self.get_color(polarity) - self.layer.line(*args, **kwargs) - if self.mask_draw is not None: - kwargs["fill"] = self.get_mask_color(polarity) - self.mask_draw.line(*args, **kwargs) - - def arc(self, polarity: Polarity, *args: Any, **kwargs: Any) -> None: - """Draw arc on image.""" - kwargs["fill"] = self.get_color(polarity) - self.layer.arc(*args, **kwargs) - if self.mask_draw is not None: - kwargs["fill"] = self.get_mask_color(polarity) - self.mask_draw.arc(*args, **kwargs) - - def ellipse(self, polarity: Polarity, *args: Any, **kwargs: Any) -> None: - """Draw ellipse on image.""" - kwargs["fill"] = self.get_color(polarity) - self.layer.ellipse(*args, **kwargs) - if self.mask_draw is not None: - kwargs["fill"] = self.get_mask_color(polarity) - self.mask_draw.ellipse(*args, **kwargs) - - def rectangle(self, polarity: Polarity, *args: Any, **kwargs: Any) -> None: - """Draw rectangle on image.""" - kwargs["fill"] = self.get_color(polarity) - self.layer.rectangle(*args, **kwargs) - if self.mask_draw is not None: - kwargs["fill"] = self.get_mask_color(polarity) - self.mask_draw.rectangle(*args, **kwargs) - - def rounded_rectangle(self, polarity: Polarity, *args: Any, **kwargs: Any) -> None: - """Draw rounded rectangle on image.""" - kwargs["fill"] = self.get_color(polarity) - self.layer.rounded_rectangle(*args, **kwargs) - if self.mask_draw is not None: - kwargs["fill"] = self.get_mask_color(polarity) - self.mask_draw.rounded_rectangle(*args, **kwargs) - - def regular_polygon(self, polarity: Polarity, *args: Any, **kwargs: Any) -> None: - """Draw regular polygon on image.""" - kwargs["fill"] = self.get_color(polarity) - self.layer.regular_polygon(*args, **kwargs) - if self.mask_draw is not None: - kwargs["fill"] = self.get_mask_color(polarity) - self.mask_draw.regular_polygon(*args, **kwargs) - - def polygon(self, polarity: Polarity, *args: Any, **kwargs: Any) -> None: - """Draw polygon on image.""" - kwargs["fill"] = self.get_color(polarity) - self.layer.polygon(*args, **kwargs) - if self.mask_draw is not None: - kwargs["fill"] = self.get_mask_color(polarity) - self.mask_draw.polygon(*args, **kwargs) - - def paste(self, *args: Any, **kwargs: Any) -> None: - """Draw polygon on image.""" - self.image.paste(*args, **kwargs) - if self.mask is not None: - self.mask.paste(*args, **kwargs) - - def region_mode(self) -> ContextManager[None]: - """Set rendering mode to region.""" - - @contextmanager - def _with() -> Generator[None, None, None]: - self.is_region = True - yield - self.is_region = False - - return _with() - - -class RasterAperture: - """Raster Aperture model.""" - - def __init__(self, image: Image.Image, mask: Image.Image) -> None: - self.image = image - self.mask = mask - - -class RasterRenderer2Hooks(Renderer2HooksABC): - """Class implementing rendering hooks to output raster images.""" - - def __init__( - self, - color_scheme: ColorScheme = ColorScheme.DEBUG_1_ALPHA, - scale: Decimal = Decimal("1"), - dpmm: int = 20, - *, - flip_y: bool = True, - ) -> None: - self.color_scheme = color_scheme - self.scale = scale - self.dpmm = dpmm - self.flip_y = flip_y - self.frame_builder = ( - RasterRenderingFrameBuilder() - .set_dpmm(self.dpmm) - .set_scale(self.scale) - .set_color_scheme(self.color_scheme) - ) - - def init( - self, - renderer: Renderer2, - command_buffer: ReadonlyCommandBuffer2, - ) -> None: - """Initialize renderer.""" - if not isinstance(renderer, RasterRenderer2): - raise NotImplementedError - - self.renderer = renderer - self.command_buffer = command_buffer - self.rendering_stack: list[RasterRenderingFrame] = [] - self.push_render_frame( - self.frame_builder.set_polarity(Polarity.Dark) - .set_command_buffer(command_buffer) - .build(with_mask=False), - ) - self.apertures: dict[str, RasterAperture] = {} - - def push_render_frame(self, cmd: RasterRenderingFrame) -> None: - """Push new segment render frame.""" - self.rendering_stack.append(cmd) - - def pop_render_frame(self) -> RasterRenderingFrame: - """Pop segment render frame.""" - if len(self.rendering_stack) <= 1: - raise RuntimeError - return self.rendering_stack.pop() - - @property - def frame(self) -> RasterRenderingFrame: - """Get current rendering stack frame.""" - return self.rendering_stack[-1] - - def convert_xy(self, v: Vector2D) -> tuple[int, int]: - """Convert vector coordinates to coordinates in image space.""" - return ( - self.convert_x(v.x), - self.convert_y(v.y), - ) - - def convert_x(self, x: Offset) -> int: - """Convert y offset to y coordinate in image space.""" - origin_offset_x = self.frame.bounding_box.min_x.as_millimeters() - corrected_position_x = x.as_millimeters() - origin_offset_x - return custom_round( - corrected_position_x * self.scale * self.dpmm - Decimal(0.5), - ) - - def convert_y(self, y: Offset) -> int: - """Convert y offset to y coordinate in image space.""" - origin_offset_y = self.frame.bounding_box.min_y.as_millimeters() - corrected_position_y = y.as_millimeters() - origin_offset_y - return custom_round( - corrected_position_y * self.scale * self.dpmm - Decimal(0.5), - ) - - def convert_size(self, diameter: Offset) -> int: - """Convert y offset to pixel y coordinate.""" - return max(custom_round(diameter.as_millimeters() * self.scale * self.dpmm), 1) - - def convert_bbox(self, bbox: BoundingBox) -> tuple[int, int, int, int]: - """Convert bounding box region to pixel coordinates bbox.""" - return ( - self.convert_x(bbox.min_x), - self.convert_y(bbox.min_y), - self.convert_x(bbox.max_x), - self.convert_y(bbox.max_y), - ) - - def get_aperture(self, aperture_id: str) -> Optional[RasterAperture]: - """Get SVG group representing aperture.""" - return self.apertures.get(aperture_id) - - def set_aperture( - self, - aperture_id: str, - raster_aperture: RasterAperture, - ) -> None: - """Set SVG group representing aperture.""" - self.apertures[aperture_id] = raster_aperture - - def get_aperture_id(self, aperture: Aperture2, transform: ApertureTransform) -> str: - """Return combined ID for listed aperture.""" - return ( - f"{aperture.identifier}%{transform.polarity.value}" - f"%{transform.get_transform_key()}" - ) - - def render_line(self, command: Line2) -> None: - """Render line to target image.""" - command.aperture.render_flash( - self.renderer, - Flash2( - transform=command.transform, - attributes=command.attributes, - aperture=command.aperture, - flash_point=command.start_point, - ), - ) - - self.frame.line( - command.transform.polarity, - ( - self.convert_x(command.start_point.x), - self.convert_y(command.start_point.y), - self.convert_x(command.end_point.x), - self.convert_y(command.end_point.y), - ), - width=self.convert_size(command.aperture.get_stroke_width()), - ) - - command.aperture.render_flash( - self.renderer, - Flash2( - transform=command.transform, - attributes=command.attributes, - aperture=command.aperture, - flash_point=command.end_point, - ), - ) - - def render_arc(self, command: Arc2) -> None: - """Render arc to target image.""" - command.aperture.render_flash( - self.renderer, - Flash2( - transform=command.transform, - attributes=command.attributes, - aperture=command.aperture, - flash_point=command.start_point, - ), - ) - - start_angle = ( - command.get_relative_start_point().angle_between( - Vector2D.UNIT_X, - ) - % 360 - ) - end_angle = ( - command.get_relative_end_point().angle_between( - Vector2D.UNIT_X, - ) - % 360 - ) - bbox = self.convert_bbox( - BoundingBox.from_diameter( - (command.get_radius() * 2) + (command.aperture.get_stroke_width()), - ) - + command.center_point, - ) - - if end_angle < start_angle: - end_angle += 360 - - if end_angle == start_angle: - start_angle = 360 - end_angle = 0 - - self.frame.arc( - command.transform.polarity, - bbox, - end_angle, - start_angle, - width=self.convert_size(command.aperture.get_stroke_width()), - ) - - command.aperture.render_flash( - self.renderer, - Flash2( - transform=command.transform, - attributes=command.attributes, - aperture=command.aperture, - flash_point=command.end_point, - ), - ) - - def render_cc_arc(self, command: CCArc2) -> None: - """Render arc to target image.""" - command.aperture.render_flash( - self.renderer, - Flash2( - transform=command.transform, - attributes=command.attributes, - aperture=command.aperture, - flash_point=command.start_point, - ), - ) - - start_angle = ( - command.get_relative_start_point().angle_between( - Vector2D.UNIT_X, - ) - % 360 - ) - end_angle = ( - command.get_relative_end_point().angle_between( - Vector2D.UNIT_X, - ) - % 360 - ) - bbox = self.convert_bbox( - BoundingBox.from_diameter( - (command.get_radius() * 2) + (command.aperture.get_stroke_width()), - ) - + command.center_point, - ) - - if end_angle <= start_angle: - end_angle += 360 - - self.frame.arc( - command.transform.polarity, - bbox, - start_angle, - end_angle, - width=self.convert_size(command.aperture.get_stroke_width()), - ) - - command.aperture.render_flash( - self.renderer, - Flash2( - transform=command.transform, - attributes=command.attributes, - aperture=command.aperture, - flash_point=command.end_point, - ), - ) - - def render_flash_circle(self, command: Flash2, aperture: Circle2) -> None: - """Render flash circle to target image.""" - aperture_id = self.get_aperture_id(aperture, command.transform) - raster_aperture = self.get_aperture(aperture_id) - - if raster_aperture is None: - bbox = list(self.convert_bbox(command.get_bounding_box())) - # Circles which are drawn with small amount of pixels are offset by 1 pixel - # for some reason. This is a first part of workaround for that. 30 pixels is - # an empirically determined threshold after which the offset is not needed - # anymore. We need to increase size of the bounding box by 1 pixel to - # fit a circle which size will also be increased by 1 pixel. - if abs(bbox[0] - bbox[2]) <= 35: # noqa: PLR2004 - self.frame_builder.set_pixel_dimension_offsets(x=1, y=1) - - # Unfortunately workaround implemented just above forces frame generation - # to be deferred to here. - frame_builder = self.frame_builder.set_polarity( - command.transform.polarity - ).set_command_buffer_from_list([command]) - self.push_render_frame(frame_builder.build()) - # Additionally we have to clean up frame_builder state we have altered. - self.frame_builder.set_pixel_dimension_offsets() - - # We have to recalculate a bounding box after jumping into new frame as - # dimensions of the frame likely changed, therefore relative position of - # bounding box also changed. - bbox = list(self.convert_bbox(command.get_bounding_box())) - # This is a second part of workaround for circles which are drawn with small - # amount of pixels. We need to increase size of the circle itself. We - # couldn't do it earlier because we need to recalculate bbox for new frame. - if abs(bbox[0] - bbox[2]) <= 35: # noqa: PLR2004 - bbox[2] += 1 - - self.frame.ellipse( - Polarity.Dark, - bbox, - ) - self._make_hole(command, aperture) - - frame = self.pop_render_frame() - raster_aperture = frame.get_aperture() - self.set_aperture(aperture_id, raster_aperture) - - self._paste_aperture(command, raster_aperture) - - def _make_hole( - self, - command: Flash2, - aperture: Circle2 | Rectangle2 | Obround2 | Polygon2, - ) -> None: - if aperture.hole_diameter is None: - return - self.frame.ellipse( - Polarity.Clear, - self.convert_bbox( - BoundingBox( - min_x=-(aperture.hole_diameter / 2), - min_y=-(aperture.hole_diameter / 2), - max_x=aperture.hole_diameter / 2, - max_y=aperture.hole_diameter / 2, - ) - + command.flash_point, - ), - ) - - def _paste_aperture(self, command: Flash2, aperture_image: RasterAperture) -> None: - bbox = command.get_bounding_box() - origin_x, origin_y = self.convert_bbox(bbox)[0:2] - self.frame.paste( - aperture_image.image, - (origin_x, origin_y), - mask=aperture_image.mask, - ) - - def render_flash_no_circle(self, command: Flash2, aperture: NoCircle2) -> None: - """Render flash no circle aperture to target image.""" - - def render_flash_rectangle(self, command: Flash2, aperture: Rectangle2) -> None: - """Render flash rectangle to target image.""" - aperture_id = self.get_aperture_id(aperture, command.transform) - raster_aperture = self.get_aperture(aperture_id) - - if raster_aperture is None: - self.push_render_frame( - self.frame_builder.set_polarity(command.transform.polarity) - .set_command_buffer_from_list([command]) - .build(), - ) - edge_offset_vector = Vector2D( - x=aperture.x_size / 2, - y=Offset.new(0), - ).get_rotated(aperture.rotation) - - max_xy = command.flash_point + edge_offset_vector - min_xy = command.flash_point - edge_offset_vector - - start_xy = min_xy - end_xy = max_xy - - tangent_vector = Vector2D( - x=Offset.new(0), - y=aperture.y_size / 2, - ).get_rotated(aperture.rotation) - - self.frame.polygon( - Polarity.Dark, - ( - (self.convert_xy(start_xy + tangent_vector)), - (self.convert_xy(start_xy - tangent_vector)), - (self.convert_xy(end_xy - tangent_vector)), - (self.convert_xy(end_xy + tangent_vector)), - ), - ) - self._make_hole(command, aperture) - - frame = self.pop_render_frame() - raster_aperture = frame.get_aperture() - self.set_aperture(aperture_id, raster_aperture) - - self._paste_aperture(command, raster_aperture) - - def render_flash_obround(self, command: Flash2, aperture: Obround2) -> None: - """Render flash obround to target image.""" - aperture_id = self.get_aperture_id(aperture, command.transform) - aperture_image = self.get_aperture(aperture_id) - - if aperture_image is None: - self.push_render_frame( - self.frame_builder.set_polarity(command.transform.polarity) - .set_command_buffer_from_list([command]) - .build(), - ) - - self.frame.rounded_rectangle( - Polarity.Dark, - self.convert_bbox( - BoundingBox.from_rectangle(aperture.x_size, aperture.y_size) - + command.flash_point, - ), - radius=min( - self.convert_size(aperture.x_size), - self.convert_size(aperture.y_size), - ) - / 2, - ) - self._make_hole(command, aperture) - - frame = self.pop_render_frame() - aperture_image = frame.get_aperture() - self.set_aperture(aperture_id, aperture_image) - - self._paste_aperture(command, aperture_image) - - def render_flash_polygon(self, command: Flash2, aperture: Polygon2) -> None: - """Render flash polygon to target image.""" - aperture_id = self.get_aperture_id(aperture, command.transform) - aperture_image = self.get_aperture(aperture_id) - - if aperture_image is None: - self.push_render_frame( - self.frame_builder.set_polarity(command.transform.polarity) - .set_command_buffer_from_list([command]) - .build(), - ) - - outer_diameter = aperture.outer_diameter - radius = self.convert_size(outer_diameter / 2) - # In PIL rotation angle goes in opposite direction than in Gerber and - # starts from different orientation. - rotation = -float(aperture.rotation) - 90.0 - bbox = command.get_bounding_box() - - self.frame.regular_polygon( - Polarity.Dark, - ( - self.convert_x(bbox.min_x) + radius, - self.convert_y(bbox.min_y) + radius, - radius, - ), - n_sides=aperture.number_vertices, - rotation=rotation, - ) - self._make_hole(command, aperture) - - frame = self.pop_render_frame() - aperture_image = frame.get_aperture() - self.set_aperture(aperture_id, aperture_image) - - self._paste_aperture(command, aperture_image) - - def render_flash_macro(self, command: Flash2, aperture: Macro2) -> None: - """Render flash macro aperture to target image.""" - aperture_id = self.get_aperture_id(aperture, command.transform) - aperture_image = self.get_aperture(aperture_id) - - if aperture_image is None: - self.push_render_frame( - self.frame_builder.set_polarity(command.transform.polarity) - .set_command_buffer(aperture.command_buffer) - .build(), - ) - - for cmd in aperture.command_buffer: - cmd.render(self.renderer) - - frame = self.pop_render_frame() - aperture_image = frame.get_aperture() - self.set_aperture(aperture_id, aperture_image) - - self._paste_aperture(command, aperture_image) - - def render_region(self, command: Region2) -> None: - """Render region to target image.""" - if len(command.command_buffer) == 0: - return - - with self.frame.region_mode(): - points: list[tuple[int, int]] = [] - - for cmd in command.command_buffer: - if isinstance(cmd, Line2): - self.generate_line_points(cmd, points) - elif isinstance(cmd, Arc2): - self.generate_arc_points(cmd, points) - elif isinstance(cmd, CCArc2): - self.generate_cc_arc_points(cmd, points) - else: - raise NotImplementedError - - self.frame.polygon(command.transform.polarity, points) - - def generate_line_points( - self, - command: Line2, - points: list[tuple[int, int]], - ) -> None: - """Generate points of line region boundary.""" - points.append( - ( - self.convert_x(command.start_point.x), - self.convert_y(command.start_point.y), - ), - ) - points.append( - ( - self.convert_x(command.end_point.x), - self.convert_y(command.end_point.y), - ), - ) - - def generate_arc_points(self, command: Arc2, points: list[tuple[int, int]]) -> None: - """Generate points of arc region boundary.""" - points.append( - ( - self.convert_x(command.start_point.x), - self.convert_y(command.start_point.y), - ), - ) - angle = command.get_relative_start_point().angle_between( - command.get_relative_end_point(), - ) - angle_ratio = angle / 360 - arc_length = (command.get_radius() * 2 * math.pi) * angle_ratio - point_count = self.convert_size(arc_length / 1.618) - angle_step = Decimal(angle) / Decimal(point_count) - - current_point = command.get_relative_start_point() - for i in range(point_count - 1): - rotated_current_point = current_point.rotate_around_origin( - -(i * angle_step), - ) - absolute_current_point = command.center_point + rotated_current_point - points.append( - ( - self.convert_x(absolute_current_point.x), - self.convert_y(absolute_current_point.y), - ), - ) - - points.append( - ( - self.convert_x(command.end_point.x), - self.convert_y(command.end_point.y), - ), - ) - - def generate_cc_arc_points( - self, - command: CCArc2, - points: list[tuple[int, int]], - ) -> None: - """Generate points of counter clockwise arc region boundary.""" - points.append( - ( - self.convert_x(command.start_point.x), - self.convert_y(command.start_point.y), - ), - ) - angle = command.get_relative_start_point().angle_between( - command.get_relative_end_point(), - ) - angle_ratio = angle / 360 - arc_length = (command.get_radius() * 2 * math.pi) * angle_ratio - point_count = self.convert_size(arc_length / 2) - angle_step = Decimal(angle) / Decimal(point_count) - - current_point = command.get_relative_start_point() - for i in range(point_count - 1): - rotated_current_point = current_point.rotate_around_origin(i * angle_step) - absolute_current_point = command.center_point + rotated_current_point - points.append( - ( - self.convert_x(absolute_current_point.x), - self.convert_y(absolute_current_point.y), - ), - ) - - points.append( - ( - self.convert_x(command.end_point.x), - self.convert_y(command.end_point.y), - ), - ) - - def finalize(self) -> None: - """Finalize renderer.""" - self.apertures.clear() - gc.collect(0) - gc.collect(1) - gc.collect(2) - self.frame.image = self.frame.image.transpose(Image.Transpose.FLIP_TOP_BOTTOM) - gc.collect(0) - gc.collect(1) - gc.collect(2) - - def get_image_ref(self) -> ImageRef: - """Get reference to render image.""" - return RasterImageRef(self.frame.image) - - -class RasterImageRef(ImageRef): - """Reference to raster image.""" - - def __init__(self, image: Image.Image) -> None: - self.image = image - - def _save_to_io( - self, - output: BinaryIO, - options: FormatOptions | None = None, - ) -> None: - if isinstance(options, RasterFormatOptions): - if self.image.mode.casefold() != options.pixel_format.value.casefold(): - image = self.image.convert(options.pixel_format.value) - else: - image = self.image - - kwargs = {} - - if options.image_format != ImageFormat.AUTO: - kwargs["format"] = options.image_format.value - - if options.quality is not None: - kwargs["quality"] = options.quality - - image.save(output, **kwargs) - return - - self.image.save(output) - - def get_image(self) -> Image.Image: - """Get image reference.""" - return self.image - - -class ImageFormat(Enum): - """List of officially supported raster image formats.""" - - PNG = "png" - JPEG = "jpg" - AUTO = "auto" - - -class PixelFormat(Enum): - """List of officially supported pixel formats.""" - - RGB = "RGB" - RGBA = "RGBA" - - -class RasterFormatOptions(FormatOptions): - """Raster Format specific options.""" - - def __init__( - self, - image_format: ImageFormat = ImageFormat.AUTO, - pixel_format: PixelFormat = PixelFormat.RGBA, - quality: int = 85, - ) -> None: - self.image_format = image_format - self.pixel_format = pixel_format - self.quality = quality diff --git a/src/pygerber/gerberx3/renderer2/svg.py b/src/pygerber/gerberx3/renderer2/svg.py deleted file mode 100644 index 8fc85c2b..00000000 --- a/src/pygerber/gerberx3/renderer2/svg.py +++ /dev/null @@ -1,811 +0,0 @@ -"""Module contains implementation of Gerber rendering backend outputting SVG files.""" - -from __future__ import annotations - -import importlib.util -from dataclasses import dataclass, field -from decimal import Decimal -from typing import BinaryIO, Optional - -from pygerber.backend.rasterized_2d.color_scheme import ColorScheme -from pygerber.gerberx3.math.bounding_box import BoundingBox -from pygerber.gerberx3.math.offset import Offset -from pygerber.gerberx3.math.vector_2d import Vector2D -from pygerber.gerberx3.parser2.apertures2.aperture2 import Aperture2 -from pygerber.gerberx3.parser2.apertures2.circle2 import Circle2, NoCircle2 -from pygerber.gerberx3.parser2.apertures2.macro2 import Macro2 -from pygerber.gerberx3.parser2.apertures2.obround2 import Obround2 -from pygerber.gerberx3.parser2.apertures2.polygon2 import Polygon2 -from pygerber.gerberx3.parser2.apertures2.rectangle2 import Rectangle2 -from pygerber.gerberx3.parser2.command_buffer2 import ReadonlyCommandBuffer2 -from pygerber.gerberx3.parser2.commands2.arc2 import Arc2, CCArc2 -from pygerber.gerberx3.parser2.commands2.flash2 import Flash2 -from pygerber.gerberx3.parser2.commands2.line2 import Line2 -from pygerber.gerberx3.parser2.commands2.region2 import Region2 -from pygerber.gerberx3.parser2.state2 import ApertureTransform -from pygerber.gerberx3.renderer2.abstract import ( - FormatOptions, - ImageRef, - Renderer2, - Renderer2HooksABC, -) -from pygerber.gerberx3.renderer2.errors2 import DRAWSVGNotAvailableError -from pygerber.gerberx3.state_enums import Polarity - -IS_SVG_BACKEND_AVAILABLE: bool = False - -try: - _spec_drawsvg = importlib.util.find_spec("drawsvg") - - IS_SVG_BACKEND_AVAILABLE = _spec_drawsvg is not None -except (ImportError, ValueError): - IS_SVG_BACKEND_AVAILABLE = False - - -if IS_SVG_BACKEND_AVAILABLE: - import drawsvg - - -class SvgRenderer2(Renderer2): - """Rendering backend class for rendering SVG images.""" - - def __init__( - self, - hooks: Optional[SvgRenderer2Hooks] = None, - ) -> None: - hooks = SvgRenderer2Hooks() if hooks is None else hooks - super().__init__(hooks) - - -if IS_SVG_BACKEND_AVAILABLE: - import drawsvg - - @dataclass - class SvgRenderingFrame: - """Rendering variable container.""" - - bounding_box: BoundingBox - normalize_origin_to_0_0: bool - mask: drawsvg.Mask = field(default_factory=drawsvg.Mask) - group: drawsvg.Group = field(default_factory=drawsvg.Group) - polarity: Optional[Polarity] = None - is_region: bool = False - flip_y: bool = True - - def get_group_or_mask( - self, - is_group: bool, # noqa: FBT001 - ) -> drawsvg.Group | drawsvg.Mask: - """Get group or mask.""" - if is_group: - return self.group - return self.mask - - -class SvgRenderer2Hooks(Renderer2HooksABC): - """Rendering backend hooks used to render SVG images.""" - - renderer: SvgRenderer2 - - def __init__( - self, - color_scheme: ColorScheme = ColorScheme.DEBUG_1, - scale: Decimal = Decimal("1"), - *, - flip_y: bool = True, - ) -> None: - if not IS_SVG_BACKEND_AVAILABLE: - raise DRAWSVGNotAvailableError - self.color_scheme = color_scheme - self.scale = scale - self.flip_y = flip_y - - def init( - self, - renderer: Renderer2, - command_buffer: ReadonlyCommandBuffer2, - ) -> None: - """Initialize rendering hooks.""" - if not isinstance(renderer, SvgRenderer2): - raise NotImplementedError - - self.renderer = renderer - self.command_buffer = command_buffer - self.rendering_stack: list[SvgRenderingFrame] = [ - SvgRenderingFrame( - bounding_box=self.command_buffer.get_bounding_box(), - normalize_origin_to_0_0=True, - flip_y=self.flip_y, - ), - ] - self.apertures: dict[str, drawsvg.Group] = {} - - def push_render_frame( - self, - bbox: BoundingBox, - *, - normalize_origin_to_0_0: bool, - flip_y: bool, - ) -> None: - """Push new segment render frame.""" - self.rendering_stack.append( - SvgRenderingFrame( - bounding_box=bbox, - normalize_origin_to_0_0=normalize_origin_to_0_0, - flip_y=flip_y, - ), - ) - - def pop_render_frame(self) -> SvgRenderingFrame: - """Pop segment render frame.""" - if len(self.rendering_stack) <= 1: - raise RuntimeError - return self.rendering_stack.pop() - - @property - def base_frame(self) -> SvgRenderingFrame: - """Get base rendering stack frame.""" - return self.rendering_stack[0] - - @property - def current_frame(self) -> SvgRenderingFrame: - """Get current rendering stack frame.""" - return self.rendering_stack[-1] - - def add_element_to_frame( - self, - polarity: Polarity, - element: drawsvg.DrawingBasicElement, - ) -> None: - """Add element to current frame.""" - self.get_layer(polarity).append(element) - - def get_layer(self, polarity: Polarity) -> drawsvg.Group | drawsvg.Mask: - """Get SVG layer object corresponding to polarity.""" - # In general what we want to do is to have a layer made of group with mask. - # First we fill the group with dark command, then after meeting first clear - # command we start filling mask with consecutive clear command until - # we meed dark command again. Then we create new group-mask layer to repeat the - # cycle. - - # If frame is not initialized, initialize it. - if self.current_frame.polarity is None: - self.current_frame.polarity = polarity - self.add_masked_group_to_frame() - - return self.current_frame.get_group_or_mask(polarity.is_solid()) - - if polarity.is_solid() != self.current_frame.polarity.is_solid(): - # If polarity of frame is solid it means that mask for this group is still - # empty and can be filled. - if self.current_frame.polarity.is_solid(): - self.current_frame.polarity = polarity - return self.current_frame.mask - # If polarity of frame is clear, it means that we already filled - # both group and mask and we need to create new group-mask layer. - self.add_masked_group_to_frame() - self.current_frame.polarity = polarity - return self.current_frame.group - - # We have the same polarity as layer (and as previous commands) so we can - # simply add it to current layer. - return self.current_frame.get_group_or_mask(polarity.is_solid()) - - def add_masked_group_to_frame(self) -> None: - """Create new layer including previous layer.""" - self.current_frame.mask = self.create_full_mask() - new_layer = drawsvg.Group(mask=self.current_frame.mask) - new_layer.append(self.current_frame.group) - self.current_frame.group = new_layer - - def create_full_mask(self) -> drawsvg.Mask: - """Create mask covering whole image.""" - bbox = self.base_frame.bounding_box - mask = drawsvg.Mask() - mask.append( - drawsvg.Rectangle( - x=self.convert_size(-bbox.width / 2), - y=self.convert_size(-bbox.height / 2), - width=self.convert_size(bbox.width * 2), - height=self.convert_size(bbox.height * 2), - fill="white", - ), - ) - return mask - - def create_mask(self, bbox: BoundingBox) -> drawsvg.Mask: - """Create mask covering specified bounding box.""" - mask = drawsvg.Mask() - mask.append( - drawsvg.Rectangle( - x=self.convert_size(bbox.min_x), - y=self.convert_size(bbox.min_y), - width=self.convert_size(bbox.width), - height=self.convert_size(bbox.height), - fill="white", - ), - ) - return mask - - def convert_x(self, x: Offset) -> Decimal: - """Convert y offset to y coordinate in image space.""" - if self.current_frame.normalize_origin_to_0_0: - origin_offset_x = self.current_frame.bounding_box.min_x.as_millimeters() - else: - origin_offset_x = Decimal(0) - - corrected_position_x = x.as_millimeters() - origin_offset_x - - return corrected_position_x * self.scale - - def convert_y(self, y: Offset) -> Decimal: - """Convert y offset to y coordinate in image space.""" - return self._convert_y( - y, - normalize_origin_to_0_0=self.current_frame.normalize_origin_to_0_0, - flip_y=self.current_frame.flip_y, - ) - - def _convert_y( - self, - y: Offset, - *, - normalize_origin_to_0_0: bool, - flip_y: bool, - ) -> Decimal: - """Convert y offset to pixel y coordinate.""" - if normalize_origin_to_0_0: - origin_offset_y = self.current_frame.bounding_box.min_y.as_millimeters() - else: - origin_offset_y = Decimal(0) - - corrected_position_y = y.as_millimeters() - origin_offset_y - - if flip_y: - flipped_position_y = ( - self.current_frame.bounding_box.height.as_millimeters() - - corrected_position_y - ) - return flipped_position_y * self.scale - return corrected_position_y * self.scale - - def convert_size(self, diameter: Offset) -> Decimal: - """Convert y offset to pixel y coordinate.""" - return diameter.as_millimeters() * self.scale - - def get_color(self, polarity: Polarity) -> str: - """Get color for specified polarity.""" - if self.current_frame.is_region: - if polarity.is_solid(): - return self.color_scheme.solid_region_color.to_hex() - return "black" - - if polarity.is_solid(): - return self.color_scheme.solid_color.to_hex() - return "black" - - def get_aperture_id(self, aperture: Aperture2, transform: ApertureTransform) -> str: - """Get unique ID for aperture.""" - return ( - f"{aperture.identifier}%" - f"{transform.get_transform_key()}%{transform.polarity}" - ) - - def get_aperture(self, aperture_id: str) -> Optional[drawsvg.Group]: - """Get SVG group representing aperture.""" - return self.apertures.get(aperture_id) - - def set_aperture( - self, - aperture_id: str, - aperture: drawsvg.Group, - ) -> None: - """Set SVG group representing aperture.""" - self.apertures[aperture_id] = aperture - - def render_line(self, command: Line2) -> None: - """Render line to target image.""" - color = self.get_color(command.transform.polarity) - - command.aperture.render_flash( - self.renderer, - Flash2( - transform=command.transform, - attributes=command.attributes, - aperture=command.aperture, - flash_point=command.start_point, - ), - ) - - parallel_vector = command.start_point - command.end_point - perpendicular_vector = parallel_vector.perpendicular() - normalized_perpendicular_vector = perpendicular_vector.normalize() - point_offset = normalized_perpendicular_vector * ( - command.aperture.get_stroke_width() / 2.0 - ) - - p0 = command.start_point - point_offset - p1 = command.start_point + point_offset - p2 = command.end_point + point_offset - p3 = command.end_point - point_offset - - rectangle = drawsvg.Lines( - f"{self.convert_x(p0.x):.8f}", - f"{self.convert_y(p0.y):.8f}", - f"{self.convert_x(p1.x):.8f}", - f"{self.convert_y(p1.y):.8f}", - f"{self.convert_x(p2.x):.8f}", - f"{self.convert_y(p2.y):.8f}", - f"{self.convert_x(p3.x):.8f}", - f"{self.convert_y(p3.y):.8f}", - fill=color, - close=True, - ) - self.add_element_to_frame(command.transform.polarity, rectangle) - - command.aperture.render_flash( - self.renderer, - Flash2( - transform=command.transform, - attributes=command.attributes, - aperture=command.aperture, - flash_point=command.end_point, - ), - ) - - def render_arc(self, command: Arc2) -> None: - """Render arc to target image.""" - color = self.get_color(command.transform.polarity) - # Arcs which start and end point overlaps are completely invisible in SVG. - # Therefore we need to replace them with two half-full-arcs. - # THB spec recommends doing it when exporting Gerber files, to avoid problems - # with floating point numbers, but I guess nobody does that. - if command.start_point == command.end_point: - # This is a vector from center to start point, so we can invert it and - # apply it twice to get the point on the opposite side of the center point. - relative = command.get_relative_start_point() - # Now we cen recursively invoke self with two modified copies of this - # command. - self.render_arc( - command.model_copy( - update={ - "start_point": command.start_point, - "end_point": command.start_point - (relative * 2), - }, - ), - ) - self.render_arc( - command.model_copy( - update={ - "start_point": command.start_point - (relative * 2), - "end_point": command.start_point, - }, - ), - ) - return - - command.aperture.render_flash( - self.renderer, - Flash2( - transform=command.transform, - attributes=command.attributes, - aperture=command.aperture, - flash_point=command.start_point, - ), - ) - # First we calculate perpendicular vector. This vector is always pointing - # from the center, thus it is perpendicular to arc. - # Then we can normalize it and multiply by half of aperture diameter, - # effectively giving us vector pointing to inner/outer edge of line. - # We can ignore the fact that we don't know which point (inner/outer) we - # have, as long as we get the same every time, then we can pair it with - # corresponding vector made from end point and create single arc, - # Then invert both vectors and draw second arc. - start_perpendicular_vector = command.get_relative_start_point() - start_normalized_perpendicular_vector = start_perpendicular_vector.normalize() - start_point_offset = start_normalized_perpendicular_vector * ( - command.aperture.get_stroke_width() / 2.0 - ) - - end_perpendicular_vector = command.get_relative_end_point() - end_normalized_perpendicular_vector = end_perpendicular_vector.normalize() - end_point_offset = end_normalized_perpendicular_vector * ( - command.aperture.get_stroke_width() / 2.0 - ) - - arc_path = drawsvg.Path(fill=color) - - # Determine start point of inner arc. - start_inner = command.start_point + start_point_offset - end_inner = command.end_point + end_point_offset - # Move path ptr to inner arc start point. - arc_path.M( - f"{self.convert_x(start_inner.x):.8f}", - f"{self.convert_y(start_inner.y):.8f}", - ) - self.render_arc_to_path( - command.model_copy( - update={ - "start_point": start_inner, - "end_point": end_inner, - }, - ), - arc_path, - ) - # Determine start point of outer arc. - # This arc have to be in reverse direction, so we swap start/end points. - start_outer = command.end_point - end_point_offset - end_outer = command.start_point - start_point_offset - # Draw line between end of inner arc and start of outer arc. - arc_path.L( - f"{self.convert_x(start_outer.x):.8f}", - f"{self.convert_y(start_outer.y):.8f}", - ) - self.render_cc_arc_to_path( - CCArc2( - transform=command.transform, - attributes=command.attributes, - aperture=command.aperture, - start_point=start_outer, - center_point=command.center_point, - end_point=end_outer, - ), - arc_path, - ) - # Close arc box by drawing line between end of outer arc and start of inner - arc_path.Z() - self.add_element_to_frame(command.transform.polarity, arc_path) - - command.aperture.render_flash( - self.renderer, - Flash2( - transform=command.transform, - attributes=command.attributes, - aperture=command.aperture, - flash_point=command.end_point, - ), - ) - - def render_cc_arc(self, command: CCArc2) -> None: - """Render arc to target image.""" - self.render_arc( - command.model_copy( - update={ - "start_point": command.end_point, - "end_point": command.start_point, - }, - ), - ) - - def render_flash_circle(self, command: Flash2, aperture: Circle2) -> None: - """Render flash circle to target image.""" - color = self.get_color(command.transform.polarity) - transform = command.transform - aperture_id = self.get_aperture_id(aperture, transform) - aperture_group = self.get_aperture(aperture_id) - - if aperture_group is None: - aperture_group = self.create_group_for_aperture( - aperture.get_bounding_box(), - aperture.hole_diameter, - ) - aperture_group.append( - drawsvg.Circle( - cx=0, - cy=0, - r=self.convert_size(aperture.diameter) / Decimal("2.0"), - fill=color, - ), - ) - self.set_aperture(aperture_id, aperture_group) - - self.add_element_to_frame( - command.transform.polarity, - drawsvg.Use( - aperture_group, - x=self.convert_x(command.flash_point.x), - y=self.convert_y(command.flash_point.y), - ), - ) - - def create_group_for_aperture( - self, - bbox: BoundingBox, - hole_diameter: Optional[Offset], - ) -> drawsvg.Group: - """Create SVG group for aperture.""" - if hole_diameter is None: - return drawsvg.Group() - - mask = self.create_mask(bbox) - central_circle = drawsvg.Circle( - cx=0, - cy=0, - r=self.convert_size(hole_diameter) / 2, - fill="black", - ) - mask.append(central_circle) - - return drawsvg.Group(mask=mask) - - def render_flash_no_circle(self, command: Flash2, aperture: NoCircle2) -> None: - """Render flash no circle aperture to target image.""" - - def render_flash_rectangle(self, command: Flash2, aperture: Rectangle2) -> None: - """Render flash rectangle to target image.""" - color = self.get_color(command.transform.polarity) - transform = command.transform - aperture_id = self.get_aperture_id(aperture, transform) - aperture_group = self.get_aperture(aperture_id) - - if aperture_group is None: - aperture_group = self.create_group_for_aperture( - aperture.get_bounding_box(), - aperture.hole_diameter, - ) - aperture_group.append( - drawsvg.Rectangle( - -self.convert_size(aperture.x_size) / 2, - -self.convert_size(aperture.y_size) / 2, - self.convert_size(aperture.x_size), - self.convert_size(aperture.y_size), - fill=color, - transform=f"rotate(-{aperture.rotation})", - ), - ) - self.set_aperture(aperture_id, aperture_group) - - self.add_element_to_frame( - command.transform.polarity, - drawsvg.Use( - aperture_group, - self.convert_x(command.flash_point.x), - self.convert_y(command.flash_point.y), - ), - ) - - def render_flash_obround(self, command: Flash2, aperture: Obround2) -> None: - """Render flash obround to target image.""" - color = self.get_color(command.transform.polarity) - transform = command.transform - aperture_id = self.get_aperture_id(aperture, transform) - aperture_group = self.get_aperture(aperture_id) - - if aperture_group is None: - aperture_group = self.create_group_for_aperture( - aperture.get_bounding_box(), - aperture.hole_diameter, - ) - x_size = self.convert_size(aperture.x_size) - y_size = self.convert_size(aperture.y_size) - radius = x_size.min(y_size) / Decimal("2.0") - - aperture_group.append( - drawsvg.Rectangle( - -self.convert_size(aperture.x_size) / 2, - -self.convert_size(aperture.y_size) / 2, - x_size, - y_size, - fill=color, - rx=radius, - ry=radius, - transform=f"rotate(-{aperture.rotation})", - ), - ) - self.set_aperture(aperture_id, aperture_group) - - self.add_element_to_frame( - command.transform.polarity, - drawsvg.Use( - aperture_group, - self.convert_x(command.flash_point.x), - self.convert_y(command.flash_point.y), - ), - ) - - def render_flash_polygon(self, command: Flash2, aperture: Polygon2) -> None: - """Render flash polygon to target image.""" - color = self.get_color(command.transform.polarity) - transform = command.transform - aperture_id = self.get_aperture_id(aperture, transform) - aperture_group = self.get_aperture(aperture_id) - - if aperture_group is None: - aperture_group = self.create_group_for_aperture( - aperture.get_bounding_box(), - aperture.hole_diameter, - ) - - number_of_vertices = aperture.number_vertices - initial_angle = aperture.rotation - inner_angle = Decimal("360") / Decimal(number_of_vertices) - - radius_vector = Vector2D.UNIT_X * (aperture.outer_diameter / Decimal("2.0")) - rotated_radius_vector = radius_vector.rotate_around_origin(initial_angle) - - p = drawsvg.Path(fill=color) - p.M( - f"{self.convert_size(rotated_radius_vector.x):.8f}", - f"{self.convert_size(rotated_radius_vector.y):.8f}", - ) - - for i in range(1, number_of_vertices): - rotation_angle = inner_angle * i + initial_angle - rotated_radius_vector = radius_vector.rotate_around_origin( - rotation_angle, - ) - p.L( - f"{self.convert_size(rotated_radius_vector.x):.8f}", - f"{self.convert_size(rotated_radius_vector.y):.8f}", - ) - - p.Z() - - aperture_group.append(p) - self.set_aperture(aperture_id, aperture_group) - - self.add_element_to_frame( - command.transform.polarity, - drawsvg.Use( - aperture_group, - self.convert_x(command.flash_point.x), - self.convert_y(command.flash_point.y), - ), - ) - - def render_flash_macro(self, command: Flash2, aperture: Macro2) -> None: - """Render flash macro aperture to target image.""" - transform = command.transform - aperture_id = self.get_aperture_id(aperture, transform) - aperture_group = self.get_aperture(aperture_id) - - if aperture_group is None: - self.push_render_frame( - command.get_bounding_box(), - normalize_origin_to_0_0=False, - flip_y=False, - ) - for cmd in aperture.command_buffer: - cmd.render(self.renderer) - - frame = self.pop_render_frame() - aperture_group = frame.group - self.set_aperture(aperture_id, aperture_group) - - self.add_element_to_frame( - command.transform.polarity, - drawsvg.Use( - aperture_group, - x=self.convert_x(command.flash_point.x), - y=self.convert_y(command.flash_point.y), - ), - ) - - def render_region(self, command: Region2) -> None: - """Render region to target image.""" - if len(command.command_buffer) == 0: - return - - self.current_frame.is_region = True - - color = self.get_color(command.transform.polarity) - region = drawsvg.Path(fill=color) - - for cmd in command.command_buffer: - if isinstance(cmd, (Line2, Arc2, CCArc2)): - region.M( - f"{self.convert_x(cmd.start_point.x):.8f}", - f"{self.convert_y(cmd.start_point.y):.8f}", - ) - break - - for cmd in command.command_buffer: - if isinstance(cmd, Line2): - self.render_line_to_path(cmd, region) - elif isinstance(cmd, Arc2): - self.render_arc_to_path(cmd, region) - elif isinstance(cmd, CCArc2): - self.render_cc_arc_to_path(cmd, region) - else: - raise NotImplementedError - - region.Z() - self.add_element_to_frame(command.transform.polarity, region) - - self.current_frame.is_region = False - - def render_line_to_path(self, command: Line2, path: drawsvg.Path) -> None: - """Render line region boundary.""" - path.L( - f"{self.convert_x(command.end_point.x):.8f}", - f"{self.convert_y(command.end_point.y):.8f}", - ) - - def render_arc_to_path(self, command: Arc2, path: drawsvg.Path) -> None: - """Render line region boundary.""" - relative_start_vector = command.get_relative_start_point() - relative_end_vector = command.get_relative_end_point() - - angle_clockwise = relative_start_vector.angle_between(relative_end_vector) - angle_counter_clockwise = relative_start_vector.angle_between_cc( - relative_end_vector, - ) - # We want to render clockwise angle, so if cc angle is bigger, we need to - # choose small angle. - large_arc = angle_clockwise >= angle_counter_clockwise - sweep = 1 - - path.A( - rx=f"{self.convert_size(command.get_radius()):.8f}", - ry=f"{self.convert_size(command.get_radius()):.8f}", - ex=f"{self.convert_x(command.end_point.x):.8f}", - ey=f"{self.convert_y(command.end_point.y):.8f}", - rot=0, - large_arc=large_arc, - sweep=sweep, - ) - - def render_cc_arc_to_path(self, command: CCArc2, path: drawsvg.Path) -> None: - """Render line region boundary.""" - relative_start_vector = command.get_relative_start_point() - relative_end_vector = command.get_relative_end_point() - - angle_clockwise = relative_start_vector.angle_between(relative_end_vector) - angle_counter_clockwise = relative_start_vector.angle_between_cc( - relative_end_vector, - ) - # We want to render clockwise angle, so if cc angle is bigger, we need to - # choose small angle. - large_arc = not (angle_clockwise >= angle_counter_clockwise) - sweep = 0 - - path.A( - rx=f"{self.convert_size(command.get_radius()):.8f}", - ry=f"{self.convert_size(command.get_radius()):.8f}", - ex=f"{self.convert_x(command.end_point.x):.8f}", - ey=f"{self.convert_y(command.end_point.y):.8f}", - rot=0, - large_arc=large_arc, - sweep=sweep, - ) - - def get_image_ref(self) -> ImageRef: - """Get reference to render image.""" - return SvgImageRef(self.drawing) - - def finalize(self) -> None: - """Finalize rendering.""" - if len(self.rendering_stack) > 1: - self.rendering_stack = [self.rendering_stack[0]] - elif len(self.rendering_stack) < 1: - raise RuntimeError - - width = self.convert_size(self.current_frame.bounding_box.width) - height = self.convert_size(self.current_frame.bounding_box.height) - self.drawing = drawsvg.Drawing( - width=width, - height=height, - ) - self.drawing.append(self.get_layer(Polarity.Dark)) - - -class SvgImageRef(ImageRef): - """Generic container for reference to rendered image.""" - - def __init__(self, image: drawsvg.Drawing) -> None: - self.image = image - - def _save_to_io( - self, - output: BinaryIO, - options: Optional[FormatOptions] = None, # noqa: ARG002 - ) -> None: - """Save rendered image to bytes stream buffer.""" - svg = self.image.as_svg() - if svg is None: - return - output.write(svg.encode("utf-8")) - - -class SvgFormatOptions: - """Format options for SVG format.""" diff --git a/src/pygerber/gerberx3/revisions.py b/src/pygerber/gerberx3/revisions.py deleted file mode 100644 index 510b251a..00000000 --- a/src/pygerber/gerberx3/revisions.py +++ /dev/null @@ -1,291 +0,0 @@ -"""Gerber format metadata.""" - -from __future__ import annotations - -from dataclasses import dataclass -from enum import Enum - -from pygerber.common.frozen_general_model import FrozenGeneralModel - - -class Gerber(Enum): - """Gerber specification major versions.""" - - X1 = 0x01FF - X2 = 0x02FF - X3 = 0x03FF - - -class RevisionData(FrozenGeneralModel): - """Container for Gerber format revision metadata.""" - - name: str - spec: Gerber - - -class Revision(Enum): - """List of known Gerber format revisions.""" - - Revision_Legacy = RevisionData(name="Revision Legacy", spec=Gerber.X1) - # Gerber X2 - Revision_2012_12 = RevisionData(name="Revision I1 (2012 12)", spec=Gerber.X2) - Revision_2013_04 = RevisionData(name="Revision I2 (2013 04)", spec=Gerber.X2) - Revision_2013_06 = RevisionData(name="Revision I3 (2013 06)", spec=Gerber.X2) - Revision_2013_10 = RevisionData(name="Revision I4 (2013 10)", spec=Gerber.X2) - Revision_2014_02 = RevisionData(name="Revision J1 (2014 02) - X2", spec=Gerber.X2) - Revision_2014_07 = RevisionData(name="Revision J2 (2014 07)", spec=Gerber.X2) - Revision_2014_10 = RevisionData(name="Revision J3 (2014 10)", spec=Gerber.X2) - Revision_2015_02 = RevisionData(name="Revision J4 (2015 02)", spec=Gerber.X2) - Revision_2015_06 = RevisionData(name="Revision 2015.06", spec=Gerber.X2) - Revision_2015_07 = RevisionData(name="Revision 2015.07", spec=Gerber.X2) - Revision_2015_10 = RevisionData(name="Revision 2015.10", spec=Gerber.X2) - Revision_2016_01 = RevisionData(name="Revision 2016.01", spec=Gerber.X2) - Revision_2016_04 = RevisionData(name="Revision 2016.04", spec=Gerber.X2) - Revision_2016_06 = RevisionData(name="Revision 2016.06", spec=Gerber.X2) - Revision_2016_09 = RevisionData(name="Revision 2016.09", spec=Gerber.X2) - Revision_2016_11 = RevisionData(name="Revision 2016.11", spec=Gerber.X2) - Revision_2016_12 = RevisionData( - name="Revision 2016.12 - Nested step and repeat", - spec=Gerber.X2, - ) - Revision_2017_03 = RevisionData(name="Revision 2017.03", spec=Gerber.X2) - Revision_2017_05 = RevisionData(name="Revision 2017.05", spec=Gerber.X2) - Revision_2017_11 = RevisionData(name="Revision 2017.11", spec=Gerber.X2) - Revision_2018_05 = RevisionData(name="Revision 2018.05", spec=Gerber.X2) - Revision_2018_06 = RevisionData(name="Revision 2018.06", spec=Gerber.X2) - Revision_2018_09 = RevisionData(name="Revision 2018.09", spec=Gerber.X2) - Revision_2018_11 = RevisionData(name="Revision 2018.11", spec=Gerber.X2) - Revision_2019_06 = RevisionData(name="Revision 2019.06", spec=Gerber.X2) - Revision_2019_09 = RevisionData(name="Revision 2019.09", spec=Gerber.X2) - # Gerber X3 - Revision_2020_09 = RevisionData(name="Revision 2020.09", spec=Gerber.X3) - Revision_2021_02 = RevisionData(name="Revision 2021.02", spec=Gerber.X3) - Revision_2021_04 = RevisionData(name="Revision 2021.04", spec=Gerber.X3) - Revision_2021_11 = RevisionData(name="Revision 2021.11", spec=Gerber.X3) - Revision_2022_02 = RevisionData(name="Revision 2022.02", spec=Gerber.X3) - Revision_2023_03 = RevisionData(name="Revision 2023.03", spec=Gerber.X3) - Revision_2023_08 = RevisionData(name="Revision 2023.08", spec=Gerber.X3) - - -@dataclass -class SpecSec: - """Gerber specification section.""" - - sec_id: str - name: str - page: int - - -REVISION_2023_08_SOURCE_URL: str = "https://www.ucamco.com/files/downloads/file_en/456/gerber-layer-format-specification-revision-2023-08_en.pdf" - - -class Revision202308(Enum): - """Enumeration of all known sections in Gerber specification, revision 2023.08.""" - - Contents = SpecSec("", "Contents", 2) - Preface = SpecSec("", "Preface", 7) - S_1 = SpecSec("1", "Introduction", 8) - S_1_1 = SpecSec("1.1", "Scope and Target Audience", 8) - S_1_2 = SpecSec("1.2", "Further Resources", 8) - S_1_3 = SpecSec("1.3", "Reference Gerber Viewer", 8) - S_1_4 = SpecSec("1.4", "Copyright and Intellectual Property", 9) - S_2 = SpecSec("2", "Overview", 10) - S_2_1 = SpecSec("2.1", "File Structure", 10) - S_2_2 = SpecSec("2.2", "Apertures", 10) - S_2_3 = SpecSec("2.3", "Graphical objects", 11) - S_2_4 = SpecSec("2.4", "Draws and Arcs", 12) - S_2_5 = SpecSec("2.5", "Operations (D01, D02, D03)", 13) - S_2_6 = SpecSec("2.6", "Graphics State", 16) - S_2_7 = SpecSec("2.7", "Polarity", 13) - S_2_8 = SpecSec("2.8", "Blocks", 14) - S_2_9 = SpecSec("2.9", "Attributes", 14) - S_2_10 = SpecSec("2.10", "Commands Overview", 18) - S_2_11 = SpecSec("2.11", "Processing a Gerber File", 19) - S_2_12 = SpecSec("2.12", "Glossary", 21) - S_2_13 = SpecSec("2.13", "Annotated Example Files", 24) - S_2_13_1 = SpecSec("", "Example: Two Square Boxes", 24) - S_2_13_2 = SpecSec("", "Example: Polarities and Apertures", 26) - S_2_14 = SpecSec("2.14", "Conformance", 30) - S_3 = SpecSec("3", "Syntax", 31) - S_3_1 = SpecSec("3.1", "Character Set", 31) - S_3_2 = SpecSec("3.2", "Grammar Syntax", 31) - S_3_3 = SpecSec("3.3", "Commands", 33) - S_3_4 = SpecSec("3.4", "Data Types", 35) - S_3_4_1 = SpecSec("", "Integers", 35) - S_3_4_2 = SpecSec("", "Decimals", 35) - S_3_4_3 = SpecSec("", "Strings", 35) - S_3_4_4 = SpecSec("", "Fields", 36) - S_3_4_5 = SpecSec("", "Names", 36) - S_3_5 = SpecSec("3.5", "Grammar of the Gerber Layer Format", 37) - S_3_6 = SpecSec("3.6", "File Extension, MIME Type and UTI", 43) - S_4 = SpecSec("4", "Graphics", 44) - S_4_1 = SpecSec("4.1", "Comment (G04)", 44) - S_4_2 = SpecSec("4.2", "Coordinate Commands", 45) - S_4_2_1 = SpecSec("", "Unit (MO)", 46) - S_4_2_2 = SpecSec("", "Format Specification (FS)", 47) - S_4_3 = SpecSec("4.3", "Aperture Definition (AD)", 48) - S_4_3_1 = SpecSec("", "AD Command", 48) - S_4_3_2 = SpecSec("", "Zero-size Apertures", 48) - S_4_3_3 = SpecSec("", "Examples", 49) - S_4_4 = SpecSec("4.4", "Standard Aperture Templates", 50) - S_4_4_1 = SpecSec("", "Overview", 50) - S_4_4_2 = SpecSec("", "Circle", 50) - S_4_4_3 = SpecSec("", "Rectangle", 52) - S_4_4_4 = SpecSec("", "Obround", 53) - S_4_4_5 = SpecSec("", "Polygon", 54) - S_4_4_6 = SpecSec("", "Transparency of Holes", 55) - S_4_5 = SpecSec("4.5", "Aperture Macro (AM)", 56) - S_4_5_1 = SpecSec("", "Primitives", 58) - S_4_5_2 = SpecSec("", "Exposure Parameter", 67) - S_4_5_3 = SpecSec("", "Rotation Parameter", 68) - S_4_5_4 = SpecSec("", "Macro Variables and Expressions", 70) - S_4_5_5 = SpecSec("", "Examples", 72) - S_4_6 = SpecSec("4.6", "Set Current Aperture (Dnn)", 75) - S_4_7 = SpecSec("4.7", "Plot State Commands (G01,G02,G03,G75)", 76) - S_4_7_1 = SpecSec("", "Linear Plotting (G01)", 76) - S_4_7_2 = SpecSec("", "Circular Plotting (G02, G03, G75)", 77) - S_4_8 = SpecSec("4.8", "Operations (D01/D02/D03)", 81) - S_4_8_1 = SpecSec("", "Overview", 81) - S_4_8_2 = SpecSec("", "Plot (D01)", 83) - S_4_8_3 = SpecSec("", "Move (D02)", 83) - S_4_8_4 = SpecSec("", "Flash (D03)", 83) - S_4_8_5 = SpecSec("", "Example", 84) - S_4_9 = SpecSec("4.9", "Aperture Transformations (LP, LM, LR, LS)", 85) - S_4_9_1 = SpecSec("", "Overview", 85) - S_4_9_2 = SpecSec("", "Load Polarity (LP)", 87) - S_4_9_3 = SpecSec("", "Load Mirroring (LM)", 87) - S_4_9_4 = SpecSec("", "Load Rotation (LR)", 87) - S_4_9_5 = SpecSec("", "Load Scaling (LS)", 88) - S_4_9_6 = SpecSec("", "Example", 88) - S_4_10 = SpecSec("4.10", "Region Statement (G36/G37)", 90) - S_4_10_1 = SpecSec("", "Region Overview", 90) - S_4_10_2 = SpecSec("", "Region Statement Syntax", 90) - S_4_10_3 = SpecSec("", "Valid Contours", 91) - S_4_10_4 = SpecSec("", "Examples", 93) - S_4_10_5 = SpecSec("", "Copper Pours, Power and Ground Planes", 108) - S_4_11 = SpecSec("4.11", "Block Aperture (AB)", 111) - S_4_11_1 = SpecSec("", "Overview of block apertures", 111) - S_4_11_2 = SpecSec("", "AB Statement Syntax", 111) - S_4_11_3 = SpecSec("", "Usage of Block Apertures", 113) - S_4_11_4 = SpecSec("", "Example", 114) - S_4_12 = SpecSec("4.12", "Step and Repeat (SR)", 116) - S_4_13 = SpecSec("4.13", "End-of-file (M02)", 119) - S_4_14 = SpecSec("4.14", "Numerical Accuracy", 120) - S_4_14_1 = SpecSec("", "Visualization", 120) - S_4_14_2 = SpecSec("", "Image Processing", 120) - S_5 = SpecSec("5", "Attributes", 122) - S_5_1 = SpecSec("5.1", "Attributes Overview", 122) - S_5_2 = SpecSec("5.2", "File Attributes (TF)", 124) - S_5_3 = SpecSec("5.3", "Aperture Attributes (TA)", 124) - S_5_3_1 = SpecSec("", "Aperture Attributes on Regions", 125) - S_5_4 = SpecSec("5.4", "Object Attributes (TO)", 125) - S_5_5 = SpecSec("5.5", "Delete Attribute (TD)", 126) - S_5_6 = SpecSec("5.6", "Standard Attributes", 127) - S_5_6_1 = SpecSec("", "Overview", 127) - S_5_6_2 = SpecSec("", ".Part", 129) - S_5_6_3 = SpecSec("", ".FileFunction", 130) - S_5_6_4 = SpecSec("", ".FilePolarity", 135) - S_5_6_5 = SpecSec("", ".SameCoordinates", 136) - S_5_6_6 = SpecSec("", ".CreationDate", 136) - S_5_6_7 = SpecSec("", ".GenerationSoftware", 137) - S_5_6_8 = SpecSec("", ".ProjectId", 137) - S_5_6_9 = SpecSec("", ".MD5", 138) - S_5_6_10 = SpecSec("", ".AperFunction", 139) - S_5_6_11 = SpecSec("", ".DrillTolerance", 147) - S_5_6_12 = SpecSec("", ".FlashText", 147) - S_5_6_13 = SpecSec("", ".N (Net)", 149) - S_5_6_14 = SpecSec("", ".P (Pin)", 151) - S_5_6_15 = SpecSec("", ".C (Component Refdes)", 152) - S_5_6_16 = SpecSec("", ".Cxxx (Component Characteristics)", 153) - S_5_7 = SpecSec("5.7", "Text in the Image", 155) - S_5_8 = SpecSec("5.8", "Examples", 156) - S_6 = SpecSec("6", "PCB Fabrication and Assembly Data", 158) - S_6_1 = SpecSec("6.1", "Structure", 158) - S_6_2 = SpecSec("6.2", "Mandatory Attributes", 158) - S_6_3 = SpecSec("6.3", "Alignment", 158) - S_6_4 = SpecSec("6.4", "Pads", 158) - S_6_5 = SpecSec("6.5", "The Profile", 158) - S_6_6 = SpecSec("6.6", "Drill/rout files", 159) - S_6_6_1 = SpecSec("", "Backdrilling", 159) - S_6_6_2 = SpecSec("", "Example Drill File", 160) - S_6_7 = SpecSec("6.7", "Drawings and Data", 163) - S_6_8 = SpecSec("6.8", "The CAD Netlist", 164) - S_6_8_1 = SpecSec("", "Benefits of Including the CAD Netlist", 164) - S_6_8_2 = SpecSec("", "IP Considerations", 165) - S_6_9 = SpecSec("6.9", "PCB Assembly Data", 166) - S_6_9_1 = SpecSec("", "Overview", 166) - S_6_9_2 = SpecSec("", "Assembly Data Set", 166) - S_6_9_3 = SpecSec("", "Annotated Example Component Layer", 167) - S_7 = SpecSec("7", "Errors and Bad Practices", 169) - S_7_1 = SpecSec("7.1", "Errors", 169) - S_7_2 = SpecSec("7.2", "Bad Practices", 171) - S_8 = SpecSec("8", "Deprecated Format Elements", 173) - S_8_1 = SpecSec("8.1", "Deprecated Commands", 173) - S_8_1_1 = SpecSec("", "Overview", 173) - S_8_1_2 = SpecSec("", "Axis Select (AS)", 175) - S_8_1_3 = SpecSec("", "Image Name (IN)", 176) - S_8_1_4 = SpecSec("", "Image Polarity (IP)", 177) - S_8_1_5 = SpecSec("", "Image Rotation (IR)", 178) - S_8_1_6 = SpecSec("", "Load Name (LN)", 179) - S_8_1_7 = SpecSec("", "Mirror Image (MI)", 180) - S_8_1_8 = SpecSec("", "Offset (OF)", 181) - S_8_1_9 = SpecSec("", "Scale Factor (SF)", 182) - S_8_1_10 = SpecSec("", "Single-quadrant arc mode (G74)", 183) - S_8_2 = SpecSec("8.2", "Deprecated Command Options", 187) - S_8_2_1 = SpecSec("", "Format Specification (FS) Options", 187) - S_8_2_2 = SpecSec("", "Rectangular Hole in Standard Apertures", 188) - S_8_2_3 = SpecSec("", "Draws and Arcs with Rectangular Apertures", 189) - S_8_2_4 = SpecSec("", "Macro Primitive Code 2, Vector Line", 190) - S_8_2_5 = SpecSec("", "Macro Primitive Code 22, Lower Left Line", 190) - S_8_2_6 = SpecSec("", "Macro Primitive Code 6, Moiré", 191) - S_8_3 = SpecSec("8.3", "Deprecated Syntax Variations", 192) - S_8_3_1 = SpecSec("", "Combining G01/G02/G03 and D01 in a single command.", 192) - S_8_3_2 = SpecSec("", "Coordinate Data without Operation Code", 192) - S_8_3_3 = SpecSec("", "Style Variations in Command Codes", 192) - S_8_3_4 = SpecSec("", "Deprecated usage of SR", 192) - S_8_4 = SpecSec("8.4", "Deprecated Attribute Values", 193) - S_8_5 = SpecSec("8.5", "Standard Gerber (RS-274-D)", 194) - S_9 = SpecSec("9", "References", 195) - S_10 = SpecSec("10", "History", 196) - S_11 = SpecSec("11", "Revisions", 198) - S_11_1 = SpecSec("11.1", "Revision 2023.08", 198) - S_11_2 = SpecSec("11.2", "Revision 2023.03", 198) - S_11_3 = SpecSec("11.3", "Revision 2022.02", 198) - S_11_4 = SpecSec("11.4", "Revision 2021.11", 198) - S_11_5 = SpecSec("11.5", "Revision 2021.04", 198) - S_11_6 = SpecSec("11.6", "Revision 2021.02 - Formal grammar", 199) - S_11_7 = SpecSec("11.7", "Revision 2020.09 - X3", 199) - S_11_8 = SpecSec("11.8", "Revision 2019.09", 199) - S_11_9 = SpecSec("11.9", "Revision 2019.06", 199) - S_11_10 = SpecSec("11.10", "Revision 2018.11", 200) - S_11_11 = SpecSec("11.11", "Revision 2018.09", 200) - S_11_12 = SpecSec("11.12", "Revision 2018.06", 200) - S_11_13 = SpecSec("11.13", "Revision 2018.05", 200) - S_11_14 = SpecSec("11.14", "Revision 2017.11", 200) - S_11_15 = SpecSec("11.15", "Revision 2017.05", 201) - S_11_16 = SpecSec("11.16", "Revision 2017.03", 201) - S_11_17 = SpecSec("11.17", "Revision 2016.12 - Nested step and repeat", 201) - S_11_18 = SpecSec("11.18", "Revision 2016.11", 201) - S_11_19 = SpecSec("11.19", "Revision 2016.09", 202) - S_11_20 = SpecSec("11.20", "Revision 2016.06", 202) - S_11_21 = SpecSec("11.21", "Revision 2016.04", 202) - S_11_22 = SpecSec("11.22", "Revision 2016.01", 202) - S_11_23 = SpecSec("11.23", "Revision 2015.10", 203) - S_11_24 = SpecSec("11.24", "Revision 2015.07", 203) - S_11_25 = SpecSec("11.25", "Revision 2015.06", 203) - S_11_26 = SpecSec("11.26", "Revision J3 (2014 10)", 203) - S_11_27 = SpecSec("11.27", "Revision J4 (2015 02)", 203) - S_11_28 = SpecSec("11.28", "Revision J2 (2014 07)", 203) - S_11_29 = SpecSec("11.29", "Revision J1 (2014 02) - X2", 204) - S_11_30 = SpecSec("11.30", "Revision I4 (2013 10)", 204) - S_11_31 = SpecSec("11.31", "Revision I3 (2013 06)", 204) - S_11_32 = SpecSec("11.32", "Revision I2 (2013 04)", 204) - S_11_33 = SpecSec("11.33", "Revision I1 (2012 12)", 204) - - def get_url(self) -> str: - """Get url to this section.""" - return f"{REVISION_2023_08_SOURCE_URL}page={self.value.page}" - - def get_sec_id(self) -> str: - """Get section id.""" - return self.value.sec_id or self.value.name diff --git a/src/pygerber/gerberx3/state_enums.py b/src/pygerber/gerberx3/state_enums.py deleted file mode 100644 index 8c0b2b41..00000000 --- a/src/pygerber/gerberx3/state_enums.py +++ /dev/null @@ -1,166 +0,0 @@ -"""All state-defining enumerations.""" - -from __future__ import annotations - -from pygerber.gerberx3.tokenizer.helpers.gerber_code_enum import GerberCodeEnum - - -class DrawMode(GerberCodeEnum): - """Drawing mode.""" - - Linear = "G01" - """In linear plot mode a D01 operation generates a linear segment, from the current - point to the (X, Y) coordinates in the command. The current point is then set to the - (X, Y) coordinates.Outside a region statement the segment is stroked with the - current aperture to create a draw graphical object. In a region statement the - segment is added to the contour under construction.""" - - ClockwiseCircular = "G02" - """In circular plot mode a D01 operation generates an arc segment, from the current - point to the (X, Y) coordinates in the command. The current point is then set to the - (X, Y) coordinates. Outside a region statement the segment is stroked with the - current aperture to create an arc graphical object. In a region statement the - segment is added to the contour under construction. For compatibility with older - versions of the Gerber format, a G75* must be issued before the first D01 in - circular mode.""" - - CounterclockwiseCircular = "G03" - """In circular plot mode a D01 operation generates an arc segment, from the current - point to the (X, Y) coordinates in the command. The current point is then set to the - (X, Y) coordinates. Outside a region statement the segment is stroked with the - current aperture to create an arc graphical object. In a region statement the - segment is added to the contour under construction. For compatibility with older - versions of the Gerber format, a G75* must be issued before the first D01 in - circular mode.""" - - def to_human_readable(self) -> str: - """Convert to human friendly string.""" - return _to_what_draw_message[self] - - -_to_what_draw_message = { - DrawMode.Linear: "line", - DrawMode.ClockwiseCircular: "clockwise arc", - DrawMode.CounterclockwiseCircular: "counterclockwise arc", -} - - -class Polarity(GerberCodeEnum): - """Aperture polarity.""" - - Clear = "C" - Dark = "D" - ClearRegion = "ClearRegion" - DarkRegion = "DarkRegion" - Background = "Background" - DEBUG = "DBG" - DEBUG2 = "DBG2" - - def invert(self) -> Polarity: - """Return opposite polarity.""" - return _polarity_invert_map[self] - - def to_region_variant(self) -> Polarity: - """Return region variant of polarity.""" - return _to_region_variant_map[self] - - def get_2d_rasterized_color(self) -> int: - """Get color for "1" mode image.""" - return _2d_rasterized_color_map[self] - - def is_solid(self) -> bool: - """Check if polarity represents solid surface.""" - return _is_solid_map[self] - - -_to_region_variant_map = { - Polarity.Clear: Polarity.ClearRegion, - Polarity.Dark: Polarity.DarkRegion, -} - -_polarity_invert_map = { - Polarity.Clear: Polarity.Dark, - Polarity.Dark: Polarity.Clear, - Polarity.ClearRegion: Polarity.DarkRegion, - Polarity.DarkRegion: Polarity.ClearRegion, - Polarity.DEBUG: Polarity.DEBUG2, - Polarity.DEBUG2: Polarity.DEBUG, -} - -_2d_rasterized_color_map = { - "RESERVED_BLACK": 0, - "RESERVED_WHITE": 255, - Polarity.Dark: 240, - Polarity.Clear: 15, - Polarity.DarkRegion: 230, - Polarity.ClearRegion: 30, - Polarity.Background: 0, - Polarity.DEBUG: 127, - Polarity.DEBUG2: 75, -} - -_is_solid_map = { - Polarity.Clear: False, - Polarity.Dark: True, - Polarity.ClearRegion: False, - Polarity.DarkRegion: True, - Polarity.Background: True, - Polarity.DEBUG: True, - Polarity.DEBUG2: True, -} - - -class Mirroring(GerberCodeEnum): - """Aperture mirroring.""" - - NoMirroring = "N" - XY = "XY" - X = "X" - Y = "Y" - - -class Unit(GerberCodeEnum): - """Aperture unit.""" - - Millimeters = "MM" - Inches = "IN" - - -class ImagePolarityEnum(GerberCodeEnum): - """Image polarity. - - ### Image Polarity (IP) - - Note: The IP command is deprecated. - - The `IP` command is responsible for setting the polarity for the entire image. It is - designed to be used only once, preferably at the very beginning of the file. - - #### 7.1.3.1 Positive Image Polarity - Under the positive image polarity: - - The image is generated in accordance with the specifications provided elsewhere in - this document. - - It's worth noting that, by default, image generation has always assumed a positive - image polarity. - - #### 7.1.3.2 Negative Image Polarity - When the negative image polarity is in use: - - The intent is to produce an image that portrays clear areas against a dark - backdrop. - - The initial state of the entire image plane is dark, as opposed to being clear. - - The polarity effects between dark and clear regions are interchanged. Essentially, - what was dark becomes white and vice-versa. - - For negative image polarity, the very first graphics object that gets produced - must possess a dark polarity. As a result, it takes on the role of clearing the - dark backdrop. - """ - - POSITIVE = "POS" - NEGATIVE = "NEG" - - -class AxisCorrespondence(GerberCodeEnum): - """Possible values of AS command axis correspondence.""" - - AXBY = "AXBY" - AYBX = "AYBX" diff --git a/src/pygerber/gerberx3/tokenizer/__init__.py b/src/pygerber/gerberx3/tokenizer/__init__.py deleted file mode 100644 index 11bc5a36..00000000 --- a/src/pygerber/gerberx3/tokenizer/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Gerber X3 tokenizer.""" diff --git a/src/pygerber/gerberx3/tokenizer/aperture_id.py b/src/pygerber/gerberx3/tokenizer/aperture_id.py deleted file mode 100644 index ca3fb9a7..00000000 --- a/src/pygerber/gerberx3/tokenizer/aperture_id.py +++ /dev/null @@ -1,35 +0,0 @@ -"""Defines a class used to identify aperture objects created by parser.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING, Any - -from pydantic_core import CoreSchema, core_schema - -from pygerber.gerberx3.tokenizer.tokens.bases.gerber_code import GerberCode - -if TYPE_CHECKING: - from pydantic import GetCoreSchemaHandler - - -class ApertureID(str, GerberCode): - """Aperture ID wrapper.""" - - __slots__ = () - - @classmethod - def __get_pydantic_core_schema__( - cls, - source_type: Any, - handler: GetCoreSchemaHandler, - ) -> CoreSchema: - """Generate the pydantic-core schema.""" - return core_schema.no_info_after_validator_function(cls, handler(str)) - - def get_gerber_code( - self, - indent: str = "", # noqa: ARG002 - endline: str = "\n", # noqa: ARG002 - ) -> str: - """Get gerber code represented by this token.""" - return f"{self}" diff --git a/src/pygerber/gerberx3/tokenizer/decorators.py b/src/pygerber/gerberx3/tokenizer/decorators.py deleted file mode 100644 index 55cc2a55..00000000 --- a/src/pygerber/gerberx3/tokenizer/decorators.py +++ /dev/null @@ -1,33 +0,0 @@ -"""Decorators for reducing boilerplate required to implement token features.""" - -from __future__ import annotations - -from typing import Generic, TypeVar - -from pygerber.gerberx3.revisions import Revision, Revision202308 -from pygerber.gerberx3.tokenizer.tokens.bases.token import Token - -T = TypeVar("T") -TokenT = TypeVar("TokenT", bound=Token) - - -class UseType(Generic[T]): - """Placeholder for passing type parameter specification as param.""" - - -class AnnotateSpecSection: - """Add Gerber specification reference link to docstring.""" - - def __init__(self, spec_section: Revision202308) -> None: - self.spec_section = spec_section - - def __call__(self, class_: type[TokenT]) -> type[TokenT]: - """Update docstring with specification reference.""" - class_.__doc__ = (class_.__doc__ or "") + ( - "\n\n" - f"See section {self.spec_section.get_sec_id()} of " - "The Gerber Layer Format Specification " - f"{Revision.Revision_2023_08} - " - f"{self.spec_section.get_url()}" - ) - return class_ diff --git a/src/pygerber/gerberx3/tokenizer/errors.py b/src/pygerber/gerberx3/tokenizer/errors.py deleted file mode 100644 index 3ec8d4c8..00000000 --- a/src/pygerber/gerberx3/tokenizer/errors.py +++ /dev/null @@ -1,7 +0,0 @@ -"""Base error classes used in this module.""" - -from __future__ import annotations - - -class TokenizerError(ValueError): - """Base class for tokenizer errors.""" diff --git a/src/pygerber/gerberx3/tokenizer/grammar.ebnf b/src/pygerber/gerberx3/tokenizer/grammar.ebnf deleted file mode 100644 index 5bfca71f..00000000 --- a/src/pygerber/gerberx3/tokenizer/grammar.ebnf +++ /dev/null @@ -1,210 +0,0 @@ - -# KT, 29-Jan-2022 -@@grammar :: Gerber_2022_02_ -@@nameguard :: False -@@whitespace :: /\n/ - -start = - { - | G04 - | MO - | FS - | AD - | AM - | Dnn - | D01 - | D02 - | D03 - | G01 - | G02 - | G03 - | G75 - | LP - | LM - | LR - | LS - | region_statement - | AB_statement - | SR_statement - | TF - | TA - | TO - | TD - }* - M02 - $; - - -# Graphics commands -#------------------ - -FS = '%FS' 'LA' 'X' coordinate_digits 'Y' coordinate_digits '*%'; -coordinate_digits = /[1-6][56]/; -MO = '%MO' ('MM'|'IN') '*%'; - -D01 = ['X' integer] ['Y' integer] ['I' integer 'J' integer] 'D01*'; -D02 = ['X' integer] ['Y' integer] 'D02*'; -D03 = ['X' integer] ['Y' integer] 'D03*'; - -G01 = 'G01*'; -G02 = 'G02*'; -G03 = 'G03*'; -G75 = 'G75*'; - -Dnn = aperture_identifier '*'; - -G04 = 'G04' string '*'; - -M02 = 'M02*'; - -LP = '%LP' ('C'|'D') '*%'; -LM = '%LM' ('N'|'XY'|'Y'|'X') '*%'; -LR = '%LR' decimal '*%'; -LS = '%LS' decimal '*%'; - -AD = '%AD' - aperture_identifier - ( - | 'C' ',' decimal ['X' decimal] - | 'R' ',' decimal 'X' decimal ['X' decimal] - | 'O' ',' decimal 'X' decimal ['X' decimal] - | 'P' ',' decimal 'X' decimal ['X' decimal ['X' decimal]] - | name [',' decimal {'X' decimal}*] - ) - '*%'; - -AM = '%AM' name '*' macro_body '%'; -macro_body = { primitive | variable_definition }+; -variable_definition = macro_variable '=' expr '*'; -primitive = - | '0' string '*' - | '1' ',' expr ',' expr ',' expr ',' expr [',' expr] '*' - | '20' ',' expr ',' expr ',' expr ',' expr ',' expr ',' expr ',' expr '*' - | '21' ',' expr ',' expr ',' expr ',' expr ',' expr ',' expr '*' - | '4' ',' expr ',' expr ',' expr ',' expr {',' expr ',' expr}+ ',' expr '*' - | '5' ',' expr ',' expr ',' expr ',' expr ',' expr ',' expr '*' - | '7' ',' expr ',' expr ',' expr ',' expr ',' expr ',' expr '*' - ; -macro_variable = /\$[0-9]*[1-9][0-9]*/; -expr = - |{/[+-]/ term}+ - |expr /[+-]/ term - |term - ; -term = - |term /[x\/]/ factor - |factor - ; -factor = - | '(' expr ')' - |macro_variable - |unsigned_decimal - ; - -region_statement = G36 {contour}+ G37; -contour = D02 {D01|G01|G02|G03}*; -G36 = 'G36*'; -G37 = 'G37*'; - -AB_statement = AB_open block AB_close; -AB_open = '%AB' aperture_identifier '*%'; -AB_close = '%AB' '*%'; - -SR_statement = SR_open block SR_close; -SR_open = '%SR' 'X' positive_integer 'Y' positive_integer 'I' decimal 'J' decimal '*%'; -SR_close = '%SR' '*%'; - -block = - { - | G04 - | AD - | AM - | Dnn - | D01 - | D02 - | D03 - | G01 - | G02 - | G03 - | G75 - | LP - | LM - | LR - | LS - | region_statement - | AB_statement - | TF - | TA - | TO - | TD - }* - ; - - -# Attribute commands -#------------------- - -TF = '%TF' file_attribute_name {',' field}* '*%'; -TA = '%TA' aperture_attribute_name {',' field}* '*%'; -TO = '%TO' object_attribute_name {',' field}* '*%'; -TD = '%TD' - [ - | file_attribute_name - | aperture_attribute_name - | object_attribute_name - | user_name - ] - '*%'; - -file_attribute_name = - | '.Part' - | '.FileFunction' - | '.FilePolarity' - | '.SameCoordinates' - | '.CreationDate' - | '.GenerationSoftware' - | '.ProjectId' - | '.MD5' - | user_name - ; -aperture_attribute_name = - | '.AperFunction' - | '.DrillTolerance' - | '.FlashText' - | user_name - ; -object_attribute_name = - | '.N' - | '.P' - | '.C' &',' # To avoid this rule also parses .CRot etc - | '.CRot' - | '.CMfr' - | '.CMPN' - | '.CVal' - | '.CMnt' - | '.CFtp' - | '.CPgN' - | '.CPgD' - | '.CHgt' - | '.CLbN' - | '.CLbD' - | '.CSup' - | user_name - ; - - -# Tokens, by regex -#----------------- - -unsigned_integer = /[0-9]+/; -positive_integer = /[0-9]*[1-9][0-9]*/; -integer = /[+-]?[0-9]+/; -unsigned_decimal = /((([0-9]+)(\.[0-9]*)?)|(\.[0-9]+))/; -decimal = /[+-]?((([0-9]+)(\.[0-9]*)?)|(\.[0-9]+))/; - -aperture_identifier = /D[0]*[1-9][0-9]+/; - -name = /[._a-zA-Z$][._a-zA-Z0-9]*/; -user_name = /[_a-zA-Z$][._a-zA-Z0-9]*/; # Cannot start with a dot -string = /[^%*]*/; # All characters except * % -field = /[^%*,]*/; # All characters except * % , diff --git a/src/pygerber/gerberx3/tokenizer/grammar.py b/src/pygerber/gerberx3/tokenizer/grammar.py deleted file mode 100644 index af177574..00000000 --- a/src/pygerber/gerberx3/tokenizer/grammar.py +++ /dev/null @@ -1,1096 +0,0 @@ -"""GerberX3 grammar.""" - -from __future__ import annotations - -from dataclasses import dataclass -from typing import TYPE_CHECKING, Any, Optional, Type, TypeVar - -from pyparsing import ( - CharsNotIn, - Combine, - Forward, - Literal, - OneOrMore, - OpAssoc, - Opt, - ParserElement, - Regex, - Suppress, - Word, - ZeroOrMore, - infix_notation, - nums, - oneOf, - ungroup, -) - -from pygerber.gerberx3.tokenizer.tokens.ab_block_aperture import ( - BlockApertureBegin, - BlockApertureEnd, -) -from pygerber.gerberx3.tokenizer.tokens.ad_define_aperture import ( - DefineCircle, - DefineMacro, - DefineObround, - DefinePolygon, - DefineRectangle, -) -from pygerber.gerberx3.tokenizer.tokens.as_axis_select import AxisSelect -from pygerber.gerberx3.tokenizer.tokens.bases.token import Token -from pygerber.gerberx3.tokenizer.tokens.d01_draw import D01Draw -from pygerber.gerberx3.tokenizer.tokens.d02_move import D02Move -from pygerber.gerberx3.tokenizer.tokens.d03_flash import D03Flash -from pygerber.gerberx3.tokenizer.tokens.dnn_select_aperture import DNNSelectAperture -from pygerber.gerberx3.tokenizer.tokens.end_of_expression import EndOfExpression -from pygerber.gerberx3.tokenizer.tokens.fs_coordinate_format import CoordinateFormat -from pygerber.gerberx3.tokenizer.tokens.g01_set_linear import SetLinear -from pygerber.gerberx3.tokenizer.tokens.g02_set_clockwise_circular import ( - SetClockwiseCircular, -) -from pygerber.gerberx3.tokenizer.tokens.g03_set_counterclockwise_circular import ( - SetCounterclockwiseCircular, -) -from pygerber.gerberx3.tokenizer.tokens.g04_comment import Comment -from pygerber.gerberx3.tokenizer.tokens.g36_begin_region import BeginRegion -from pygerber.gerberx3.tokenizer.tokens.g37_end_region import EndRegion -from pygerber.gerberx3.tokenizer.tokens.g54_select_aperture import G54SelectAperture -from pygerber.gerberx3.tokenizer.tokens.g70_set_unit_inch import SetUnitInch -from pygerber.gerberx3.tokenizer.tokens.g71_set_unit_mm import SetUnitMillimeters -from pygerber.gerberx3.tokenizer.tokens.g74_single_quadrant import SetSingleQuadrantMode -from pygerber.gerberx3.tokenizer.tokens.g75_multi_quadrant import SetMultiQuadrantMode -from pygerber.gerberx3.tokenizer.tokens.g90_set_coordinate_absolute import ( - SetAbsoluteNotation, -) -from pygerber.gerberx3.tokenizer.tokens.g91_set_coordinate_incremental import ( - SetIncrementalNotation, -) -from pygerber.gerberx3.tokenizer.tokens.groups.ast import AST -from pygerber.gerberx3.tokenizer.tokens.groups.statement import ( - Statement, -) -from pygerber.gerberx3.tokenizer.tokens.in_image_name import ImageName -from pygerber.gerberx3.tokenizer.tokens.invalid_token import InvalidToken -from pygerber.gerberx3.tokenizer.tokens.ip_image_polarity import ImagePolarity -from pygerber.gerberx3.tokenizer.tokens.lm_load_mirroring import LoadMirroring -from pygerber.gerberx3.tokenizer.tokens.ln_load_name import LoadName -from pygerber.gerberx3.tokenizer.tokens.lp_load_polarity import LoadPolarity -from pygerber.gerberx3.tokenizer.tokens.lr_load_rotation import LoadRotation -from pygerber.gerberx3.tokenizer.tokens.ls_load_scaling import LoadScaling -from pygerber.gerberx3.tokenizer.tokens.m00_program_stop import M00ProgramStop -from pygerber.gerberx3.tokenizer.tokens.m01_optional_stop import M01OptionalStop -from pygerber.gerberx3.tokenizer.tokens.m02_end_of_file import M02EndOfFile -from pygerber.gerberx3.tokenizer.tokens.macro.am_macro import MacroDefinition -from pygerber.gerberx3.tokenizer.tokens.macro.expressions.binary import ( - AdditionOperator, - DivisionOperator, - MultiplicationOperator, - SubtractionOperator, -) -from pygerber.gerberx3.tokenizer.tokens.macro.expressions.numeric_constant import ( - NumericConstant, -) -from pygerber.gerberx3.tokenizer.tokens.macro.expressions.unary import ( - NegationOperator, - PositiveOperator, -) -from pygerber.gerberx3.tokenizer.tokens.macro.expressions.variable_name import ( - MacroVariableName, -) -from pygerber.gerberx3.tokenizer.tokens.macro.macro_begin import MacroBegin -from pygerber.gerberx3.tokenizer.tokens.macro.point import Point -from pygerber.gerberx3.tokenizer.tokens.macro.statements.code_1_circle import ( - Code1CircleToken, -) -from pygerber.gerberx3.tokenizer.tokens.macro.statements.code_4_outline import ( - Code4OutlineToken, -) -from pygerber.gerberx3.tokenizer.tokens.macro.statements.code_5_polygon import ( - Code5PolygonToken, -) -from pygerber.gerberx3.tokenizer.tokens.macro.statements.code_7_thermal import ( - Code7ThermalToken, -) -from pygerber.gerberx3.tokenizer.tokens.macro.statements.code_20_vector_line import ( - Code20VectorLineToken, -) -from pygerber.gerberx3.tokenizer.tokens.macro.statements.code_21_center_line import ( - Code21CenterLineToken, -) -from pygerber.gerberx3.tokenizer.tokens.macro.statements.comment import MacroComment -from pygerber.gerberx3.tokenizer.tokens.macro.statements.variable_assignment import ( - MacroVariableAssignment, -) -from pygerber.gerberx3.tokenizer.tokens.mo_unit_mode import UnitMode -from pygerber.gerberx3.tokenizer.tokens.of_image_offset import ImageOffset -from pygerber.gerberx3.tokenizer.tokens.sr_step_repeat import ( - StepRepeatBegin, - StepRepeatEnd, -) -from pygerber.gerberx3.tokenizer.tokens.ta_aperture_attribute import ApertureAttribute -from pygerber.gerberx3.tokenizer.tokens.td_delete_attribute import DeleteAttribute -from pygerber.gerberx3.tokenizer.tokens.tf_file_attribute import FileAttribute -from pygerber.gerberx3.tokenizer.tokens.to_object_attribute import ObjectAttribute - -if TYPE_CHECKING: - from typing_extensions import Self - - -T1 = TypeVar("T1") -T2 = TypeVar("T2") - - -class TokenWrapper: - """Class for wrapping ParserElements with Token classes.""" - - def __init__(self, *, is_raw: bool) -> None: - self.is_raw = is_raw - - def __call__( - self, - token_cls: Type[Token], - parser_element: ParserElement, - ) -> ParserElement: - """Wrap ParserElement with Token class.""" - if self.is_raw: - return parser_element - - return token_cls.wrap(parser_element) - - @classmethod - def build( - cls, - wrapper: Optional[Self] = None, - *, - is_raw: bool = False, - ) -> Self: - """Build TokenWrapper instance.""" - if wrapper is not None: - return wrapper - - return cls(is_raw=is_raw) - - -class GrammarBuilderOptions: - """Grammar builder options.""" - - -@dataclass -class GrammarBuilder: - """Base class for all grammar builder classes.""" - - def __init__( - self, - wrapper: Optional[TokenWrapper] = None, - *, - is_raw: bool = False, - options: Optional[GrammarBuilderOptions] = None, - ) -> None: - self.wrapper = TokenWrapper.build(wrapper=wrapper, is_raw=is_raw) - self.options = options if options is not None else GrammarBuilderOptions() - - -@dataclass -class GerberGrammarBuilderOptions(GrammarBuilderOptions): - """Grammar builder options.""" - - ast_token_cls: Type[Token] = AST - block_aperture_begin_token_cls: Type[Token] = BlockApertureBegin - block_aperture_end_token_cls: Type[Token] = BlockApertureEnd - define_circle_token_cls: Type[Token] = DefineCircle - define_macro_token_cls: Type[Token] = DefineMacro - define_obround_token_cls: Type[Token] = DefineObround - define_polygon_token_cls: Type[Token] = DefinePolygon - define_rectangle_token_cls: Type[Token] = DefineRectangle - axis_select_token_cls: Type[Token] = AxisSelect - d01_draw_token_cls: Type[Token] = D01Draw - d02_move_token_cls: Type[Token] = D02Move - d03_flash_token_cls: Type[Token] = D03Flash - dnn_select_aperture_token_cls: Type[Token] = DNNSelectAperture - end_of_expression_token_cls: Type[Token] = EndOfExpression - fs_coordinate_format_token_cls: Type[Token] = CoordinateFormat - g01_set_linear_token_cls: Type[Token] = SetLinear - g02_set_clockwise_circular_token_cls: Type[Token] = SetClockwiseCircular - g03_set_counterclockwise_circular_token_cls: Type[Token] = ( - SetCounterclockwiseCircular - ) - g04_comment_token_cls: Type[Token] = Comment - g36_begin_region_token_cls: Type[Token] = BeginRegion - g37_end_region_token_cls: Type[Token] = EndRegion - g54_select_aperture_token_cls: Type[Token] = G54SelectAperture - g70_set_unit_inch_token_cls: Type[Token] = SetUnitInch - g71_set_unit_mm_token_cls: Type[Token] = SetUnitMillimeters - g74_single_quadrant_token_cls: Type[Token] = SetSingleQuadrantMode - g75_multi_quadrant_token_cls: Type[Token] = SetMultiQuadrantMode - g90_set_coordinate_absolute_token_cls: Type[Token] = SetAbsoluteNotation - g91_set_coordinate_incremental_token_cls: Type[Token] = SetIncrementalNotation - statement_token_cls: Type[Token] = Statement - in_image_name_token_cls: Type[Token] = ImageName - invalid_token_cls: Type[Token] = InvalidToken - ip_image_polarity_token_cls: Type[Token] = ImagePolarity - lm_load_mirroring_token_cls: Type[Token] = LoadMirroring - ln_load_name_token_cls: Type[Token] = LoadName - lp_load_polarity_token_cls: Type[Token] = LoadPolarity - lr_load_rotation_token_cls: Type[Token] = LoadRotation - ls_load_scaling_token_cls: Type[Token] = LoadScaling - of_image_offset_token_cls: Type[Token] = ImageOffset - as_axes_select_token_cls: Type[Token] = AxisSelect - m00_program_stop_token_cls: Type[Token] = M00ProgramStop - m01_optional_stop_token_cls: Type[Token] = M01OptionalStop - m02_end_of_file_token_cls: Type[Token] = M02EndOfFile - macro_definition_token_cls: Type[Token] = MacroDefinition - macro_addition_operator_token_cls: Type[Token] = AdditionOperator - macro_division_operator_token_cls: Type[Token] = DivisionOperator - macro_multiplication_operator_token_cls: Type[Token] = MultiplicationOperator - macro_negation_operator_token_cls: Type[Token] = NegationOperator - macro_positive_operator_token_cls: Type[Token] = PositiveOperator - macro_subtraction_operator_token_cls: Type[Token] = SubtractionOperator - macro_comment_token_cls: Type[Token] = MacroComment - macro_begin_token_cls: Type[Token] = MacroBegin - macro_numeric_constant_token_cls: Type[Token] = NumericConstant - macro_point_token_cls: Type[Token] = Point - macro_primitive_center_line_token_cls: Type[Token] = Code21CenterLineToken - macro_primitive_circle_token_cls: Type[Token] = Code1CircleToken - macro_primitive_outline_token_cls: Type[Token] = Code4OutlineToken - macro_primitive_polygon_token_cls: Type[Token] = Code5PolygonToken - macro_primitive_thermal_token_cls: Type[Token] = Code7ThermalToken - macro_primitive_vector_line_token_cls: Type[Token] = Code20VectorLineToken - macro_variable_definition_token_cls: Type[Token] = MacroVariableAssignment - macro_variable_name_token_cls: Type[Token] = MacroVariableName - mo_unit_mode_token_cls: Type[Token] = UnitMode - step_repeat_begin_token_cls: Type[Token] = StepRepeatBegin - step_repeat_end_token_cls: Type[Token] = StepRepeatEnd - ta_aperture_attribute_token_cls: Type[Token] = ApertureAttribute - td_delete_attribute_token_cls: Type[Token] = DeleteAttribute - tf_file_attribute_token_cls: Type[Token] = FileAttribute - to_object_attribute_token_cls: Type[Token] = ObjectAttribute - - -class GerberGrammarBuilder(GrammarBuilder): - """Base class for all Gerber grammar builders.""" - - options: GerberGrammarBuilderOptions - - def __init__( - self, - wrapper: Optional[TokenWrapper] = None, - *, - is_raw: bool = False, - options: Optional[GerberGrammarBuilderOptions] = None, - ) -> None: - super().__init__( - wrapper=wrapper, - is_raw=is_raw, - options=options if options is not None else GerberGrammarBuilderOptions(), - ) - - def build(self) -> GerberGrammar: - """Build grammar object.""" - wrapper = self.wrapper - eoex = self._build_eoex() - - load_commands = self._build_load_commands() - # Sets the polarity of the whole image. - ip = self._build_stmt( - wrapper( - self.options.ip_image_polarity_token_cls, - Literal("IP") + oneOf("POS NEG").set_results_name("image_polarity"), - ), - ) - # End of file. - m02 = wrapper( - self.options.m02_end_of_file_token_cls, - Literal("M02").set_name("End of file") + eoex, - ) - # Optional stop. - m01 = wrapper( - self.options.m01_optional_stop_token_cls, - Literal("M01").set_name("Optional stop") + eoex, - ) - # Program stop. - m00 = wrapper( - self.options.m00_program_stop_token_cls, - Literal("M00").set_name("Program stop") + eoex, - ) - - dnn = wrapper( - self.options.dnn_select_aperture_token_cls, - self._build_aperture_identifier() + eoex, - ) - """Sets the current aperture to D code nn.""" - - fs = self._build_format_specifier() - # Sets the unit to mm or inch. - mo = self._build_stmt( - wrapper( - self.options.mo_unit_mode_token_cls, - Literal("MO") - + oneOf("MM IN").set_results_name("unit").set_name("unit"), - ), - ) - - # Open a step and repeat statement. - sr = self._build_step_repeat() - - # Opens a block aperture statement and assigns its aperture number - ab_open = self._build_stmt( - wrapper( - self.options.block_aperture_begin_token_cls, - Literal("AB") + self._build_aperture_identifier(), - ), - ) - # Closes a block aperture statement. - ab_close = self._build_stmt( - wrapper(self.options.block_aperture_end_token_cls, Literal("AB")), - ) - - g_codes = self._build_g_codes() - d_codes = self._build_d_codes() - comment = self._build_comment_token() - attributes = self._build_attribute_tokens() - macro = self._build_macro_tokens() - define_aperture = self._build_define_aperture() - - common = ( - mo - | fs - | macro - | define_aperture - | dnn - | (d_codes + eoex) - | (g_codes + d_codes + eoex) - | (g_codes + eoex) - | load_commands - | ip - | ab_open - | ab_close - | sr - | attributes - | m01 - | eoex - | comment - ) - - invalid_token = self.wrapper( - self.options.invalid_token_cls, - Regex(".+").set_results_name("content"), - ) - - resilient = self.wrapper( - self.options.ast_token_cls, - (common | m02 | m00 | invalid_token)[0, ...], - ) - expressions = self.wrapper( - self.options.ast_token_cls, - (common | m02 | m00)[0, ...], - ) - grammar = self.wrapper( - self.options.ast_token_cls, - common[0, ...] + (m02 | m00), - ) - - return GerberGrammar(grammar, expressions, resilient) - - def _build_load_commands(self) -> ParserElement: - wrapper = self.wrapper - - lm = self._build_stmt( - wrapper( - self.options.lm_load_mirroring_token_cls, - Literal("LM") + oneOf("N XY Y X").set_results_name("mirroring"), - ), - ) - lp = self._build_stmt( - wrapper( - self.options.lp_load_polarity_token_cls, - Literal("LP") + oneOf("C D").set_results_name("polarity"), - ), - ) - ls = self._build_stmt( - wrapper( - self.options.ls_load_scaling_token_cls, - Literal("LS") + self._build_decimal("scaling"), - ), - ) - lr = self._build_stmt( - wrapper( - self.options.lr_load_rotation_token_cls, - Literal("LR") + self._build_decimal("rotation"), - ), - ) - ln = self._build_stmt( - wrapper( - self.options.ln_load_name_token_cls, - Literal("LN") + self._build_string(), - ), - ) - in_ = self._build_stmt( - wrapper( - self.options.in_image_name_token_cls, - Literal("IN") + self._build_string(), - ), - ) - as_ = self._build_stmt( - wrapper( - self.options.as_axes_select_token_cls, - Literal("AS") + oneOf("AXBY AYBX").set_results_name("correspondence"), - ), - ) - of = self._build_stmt( - wrapper( - self.options.of_image_offset_token_cls, - Literal("OF") - + Opt(Literal("A") + self._build_decimal("a")) - + Opt(Literal("B") + self._build_decimal("b")), - ), - ) - - return lm | lp | ls | lr | as_ | of | in_ | ln - - def _build_format_specifier(self) -> ParserElement: - wrapper = self.wrapper - - coord_digits = Regex(r"[1-9][1-9]") - - # Sets the coordinate format, e.g. the number of decimals. - return self._build_stmt( - wrapper( - self.options.fs_coordinate_format_token_cls, - Literal("FS") - + oneOf("L T").set_results_name("zeros_mode").set_name("zeros mode") - + oneOf("A I") - .set_results_name("coordinate_mode") - .set_name("coordinate mode") - + "X" - + coord_digits.set_results_name("x_format").set_name( - "X coordinate format", - ) - + "Y" - + coord_digits.set_results_name("y_format").set_name( - "Y coordinate format", - ), - ), - ) - - def _build_step_repeat(self) -> ParserElement: - wrapper = self.wrapper - - sr_open = self._build_stmt( - wrapper( - self.options.step_repeat_begin_token_cls, - Literal("SR") - + "X" - + self._build_integer("x_repeat") - + "Y" - + self._build_integer("y_repeat") - + "I" - + self._build_decimal("x_step") - + "J" - + self._build_decimal("y_step"), - ), - ) - # Closes a step and repeat statement. - sr_close = self._build_stmt( - wrapper(self.options.step_repeat_end_token_cls, Literal("SR")), - ) - - return sr_open | sr_close - - def _build_g_codes(self) -> ParserElement: - wrapper = self.wrapper - - g54dnn = wrapper( - self.options.g54_select_aperture_token_cls, - Regex("G0*54") + self._build_aperture_identifier(), - ) - g01 = wrapper(self.options.g01_set_linear_token_cls, Regex("G0*1")) - g02 = wrapper(self.options.g02_set_clockwise_circular_token_cls, Regex("G0*2")) - g03 = wrapper( - self.options.g03_set_counterclockwise_circular_token_cls, - Regex("G0*3"), - ) - g36 = wrapper(self.options.g36_begin_region_token_cls, Regex("G0*36")) - g37 = wrapper(self.options.g37_end_region_token_cls, Regex("G0*37")) - g70 = wrapper(self.options.g70_set_unit_inch_token_cls, Regex("G0*70")) - g71 = wrapper(self.options.g71_set_unit_mm_token_cls, Regex("G0*71")) - g74 = wrapper(self.options.g74_single_quadrant_token_cls, Regex("G0*74")) - g75 = wrapper(self.options.g75_multi_quadrant_token_cls, Regex("G0*75")) - g90 = wrapper( - self.options.g90_set_coordinate_absolute_token_cls, - Regex("G0*90"), - ) - g91 = wrapper( - self.options.g91_set_coordinate_incremental_token_cls, - Regex("G0*91"), - ) - - # Order is important, as g03 would match g36 if checked before g36 regex. - return g54dnn | g36 | g37 | g70 | g71 | g74 | g75 | g90 | g91 | g01 | g02 | g03 - - def _build_d_codes(self) -> ParserElement: - wrapper = self.wrapper - - x_coordinate = Literal("X") + self._build_integer("x", "X coordinate") - y_coordinate = Literal("Y") + self._build_integer("y", "Y coordinate") - - i_coordinate = Literal("I") + self._build_integer("i", "I offset") - j_coordinate = Literal("J") + self._build_integer("j", "J offset") - - xy = (x_coordinate + Opt(y_coordinate)) | (Opt(x_coordinate) + y_coordinate) - ij = (i_coordinate + Opt(j_coordinate)) | (Opt(i_coordinate) + j_coordinate) - - d01 = wrapper( - self.options.d01_draw_token_cls, - ((Opt(xy) + Opt(ij) + Regex("D0*1")) | (xy + Opt(ij))), - ) - d02 = wrapper( - self.options.d02_move_token_cls, - Opt(xy) + Regex("D0*2"), - ) - d03 = wrapper( - self.options.d03_flash_token_cls, - Opt(xy) + Regex("D0*3"), - ) - - return d03 | d02 | d01 - - def _build_comment_token(self) -> ParserElement: - wrapper = self.wrapper - eoex = self._build_eoex() - - # A human readable comment, does not affect the image. - return wrapper( - self.options.g04_comment_token_cls, - Regex("G0*4") + Opt(self._build_string(), default="") + eoex, - ) - - def _build_define_aperture(self) -> ParserElement: - wrapper = self.wrapper - - ad = Literal("AD").set_name("AD code") - - circle = wrapper( - self.options.define_circle_token_cls, - ad - + self._build_aperture_identifier() - + Literal("C").set_results_name("aperture_type") - + "," - + self._build_decimal("diameter") - + Opt("X" + self._build_decimal("hole_diameter")), - ).set_name("define aperture circle") - - rectangle = wrapper( - self.options.define_rectangle_token_cls, - ad - + self._build_aperture_identifier() - + Literal("R").set_results_name("aperture_type") - + "," - + self._build_decimal("x_size") - + "X" - + self._build_decimal("y_size") - + Opt("X" + self._build_decimal("hole_diameter")), - ).set_name("define aperture rectangle") - - obround = wrapper( - self.options.define_obround_token_cls, - ad - + self._build_aperture_identifier() - + Literal("O").set_results_name("aperture_type") - + "," - + self._build_decimal("x_size") - + "X" - + self._build_decimal("y_size") - + Opt("X" + self._build_decimal("hole_diameter")), - ).set_name("define aperture obround") - - polygon = wrapper( - self.options.define_polygon_token_cls, - ad - + self._build_aperture_identifier() - + Literal("P").set_results_name("aperture_type") - + "," - + self._build_decimal("outer_diameter") - + "X" - + self._build_decimal("number_of_vertices") - + Opt( - "X" - + self._build_decimal("rotation") - + Opt("X" + self._build_decimal("hole_diameter")), - ), - ).set_name("define aperture polygon") - - am_param = self._build_decimal("am_param", list_all_matches=True) - # Defines a template-based aperture, assigns a D code to it. - - macro = wrapper( - self.options.define_macro_token_cls, - ad - + self._build_aperture_identifier() - + self._build_name("aperture_type") - + Opt("," + am_param + ZeroOrMore("X" + am_param)), - ).set_name("define aperture macro") - - return self._build_stmt(circle | rectangle | obround | polygon | macro) - - def _build_macro_tokens(self) -> ParserElement: - wrapper = self.wrapper - - primitive = self._build_macro_primitive() - variable_definition = self._build_macro_variable_definition() - comment = self._build_comment_token() - - macro_body = ( - (primitive | variable_definition | comment) - .set_results_name("macro_body", list_all_matches=True) - .set_name("macro body expression") - )[1, ...] - - am_start = ( - self._annotate_parser_element( - wrapper( - self.options.macro_begin_token_cls, - Literal("AM") + self._build_name("macro_name"), - ), - "macro_begin", - ) - + self._build_eoex() - ) - - # Defines a macro aperture template. - return self._build_stmt( - wrapper( - self.options.macro_definition_token_cls, - am_start + macro_body, - ), - eoex=False, - ).set_name("macro definition") - - def _build_macro_variable_definition(self) -> ParserElement: - return self.wrapper( - self.options.macro_variable_definition_token_cls, - self._build_macro_variable() - + "=" - + self._build_macro_expr("value") - + self._build_eoex(), - ) - - def _build_macro_primitive(self) -> ParserElement: - cs = Suppress(Literal(",").set_name("comma")) - primitive_comment = self.wrapper( - self.options.macro_comment_token_cls, - "0" + self._build_string(), - ) - primitive_circle = self.wrapper( - self.options.macro_primitive_circle_token_cls, - "1" # Circle - + cs - + self._build_macro_expr("exposure") # Exposure - + cs - + self._build_macro_expr("diameter") # Diameter - + cs - + self._build_macro_expr("center_x") # Center X - + cs - + self._build_macro_expr("center_y") # Center Y - + Opt(cs + self._build_macro_expr("rotation")), # Rotation - ) - primitive_vector_line = self.wrapper( - self.options.macro_primitive_vector_line_token_cls, - "20" # Vector Line - + cs - + self._build_macro_expr("exposure") # Exposure - + cs - + self._build_macro_expr("width") # Width - + cs - + self._build_macro_expr("start_x") # Start X - + cs - + self._build_macro_expr("start_y") # Start Y - + cs - + self._build_macro_expr("end_x") # End X - + cs - + self._build_macro_expr("end_y") # End Y - + cs - + self._build_macro_expr("rotation"), # Rotation - ) - primitive_center_line = self.wrapper( - self.options.macro_primitive_center_line_token_cls, - "21" # Center Line - + cs - + self._build_macro_expr("exposure") # Exposure - + cs - + self._build_macro_expr("width") # Width - + cs - + self._build_macro_expr("height") # height - + cs - + self._build_macro_expr("center_x") # Center X - + cs - + self._build_macro_expr("center_y") # Center Y - + cs - + self._build_macro_expr("rotation"), # Rotation - ) - primitive_outline = self.wrapper( - self.options.macro_primitive_outline_token_cls, - "4" # Outline - + cs - + self._build_macro_expr("exposure") # Exposure - + cs - + self._build_macro_expr("number_of_vertices") # Number of vertices - + cs - + self._build_macro_expr("start_x") # Start X - + cs - + self._build_macro_expr("start_y") # Start Y - + OneOrMore( # Subsequent points... - self._build_macro_point().set_results_name( - "point", - list_all_matches=True, - ), - ) - + cs - + self._build_macro_expr("rotation"), # Rotation - ) - primitive_polygon = self.wrapper( - self.options.macro_primitive_polygon_token_cls, - "5" # Polygon - + cs - + self._build_macro_expr("exposure") # Exposure - + cs - + self._build_macro_expr("number_of_vertices") # Number of vertices - + cs - + self._build_macro_expr("center_x") # Center X - + cs - + self._build_macro_expr("center_y") # Center Y - + cs - + self._build_macro_expr("diameter") # Diameter - + cs - + self._build_macro_expr("rotation"), # Rotation - ) - primitive_thermal = self.wrapper( - self.options.macro_primitive_thermal_token_cls, - "7" # Thermal - + cs - + self._build_macro_expr("center_x") # Center X - + cs - + self._build_macro_expr("center_y") # Center Y - + cs - + self._build_macro_expr("outer_diameter") # Outer diameter - + cs - + self._build_macro_expr("inner_diameter") # Inner diameter - + cs - + self._build_macro_expr("gap") # Gap - + cs - + self._build_macro_expr("rotation"), # Rotation - ) - - return ( - (primitive_comment + self._build_eoex()).set_name("primitive comment") - | (primitive_circle + self._build_eoex()).set_name("primitive circle") - | (primitive_vector_line + self._build_eoex()).set_name( - "primitive vector line", - ) - | (primitive_center_line + self._build_eoex()).set_name( - "primitive center line", - ) - | (primitive_outline + self._build_eoex()).set_name("primitive outline") - | (primitive_polygon + self._build_eoex()).set_name("primitive polygon") - | (primitive_thermal + self._build_eoex()).set_name("primitive thermal") - ) - - def _build_macro_point(self) -> ParserElement: - cs = Suppress(Literal(",").set_name("comma")) - return self.wrapper( - self.options.macro_point_token_cls, - cs + self._build_macro_expr("x") + cs + self._build_macro_expr("y"), - ) - - _macro_expr: Optional[ParserElement] = None - - def _build_macro_expr(self, expr_name: str = "expr") -> ParserElement: - macro_variable = self._build_macro_variable() - numeric_constant = self.wrapper( - self.options.macro_numeric_constant_token_cls, - Regex(r"((([0-9]+)(\.[0-9]*)?)|(\.[0-9]+))").set_results_name( - "numeric_constant_value", - ), - ) - - arithmetic_expression = Forward() - - factor = macro_variable | numeric_constant - - if self.wrapper.is_raw: - arithmetic_expression <<= ungroup( - infix_notation( - factor, - [ - ("-", 1, OpAssoc.RIGHT), - ("+", 1, OpAssoc.RIGHT), - ("/", 2, OpAssoc.RIGHT), - (oneOf("x X"), 2, OpAssoc.RIGHT), - ("-", 2, OpAssoc.RIGHT), - ("+", 2, OpAssoc.RIGHT), - ], - ), - ) - else: - arithmetic_expression <<= ungroup( - infix_notation( - factor, - [ - ( - Suppress("-"), - 1, - OpAssoc.RIGHT, - self.options.macro_negation_operator_token_cls.new, - ), - ( - Suppress("+"), - 1, - OpAssoc.RIGHT, - self.options.macro_positive_operator_token_cls.new, - ), - ( - Suppress("/"), - 2, - OpAssoc.RIGHT, - self.options.macro_division_operator_token_cls.new, - ), - ( - Suppress(oneOf("x X")), - 2, - OpAssoc.RIGHT, - self.options.macro_multiplication_operator_token_cls.new, - ), - ( - Suppress("-"), - 2, - OpAssoc.RIGHT, - self.options.macro_subtraction_operator_token_cls.new, - ), - ( - Suppress("+"), - 2, - OpAssoc.RIGHT, - self.options.macro_addition_operator_token_cls.new, - ), - ], - ), - ) - - expr = arithmetic_expression | factor - - return expr.set_results_name(expr_name).set_name(expr_name) - - def _build_macro_variable(self) -> ParserElement: - return self.wrapper( - self.options.macro_variable_name_token_cls, - Regex(r"\$[0-9]*[1-9][0-9]*")("macro_variable_name"), - ) - - def _build_attribute_tokens(self) -> ParserElement: - wrapper = self.wrapper - - file_attribute_name = self._build_name().set_name("file attribute name") - aperture_attribute_name = self._build_name().set_name("aperture attribute name") - object_attribute_name = self._build_name().set_name("object attribute name") - - comma = Literal(",") - comma_with_field = comma + self._build_field() - maybe_comma_or_maybe_comma_with_field = Opt(comma_with_field | comma) - - # Set a file attribute. - tf = wrapper( - self.options.tf_file_attribute_token_cls, - Literal("TF") - + file_attribute_name.set_results_name("attribute_name") - + maybe_comma_or_maybe_comma_with_field, - ) - # Add an aperture attribute to the dictionary or modify it. - ta = wrapper( - self.options.ta_aperture_attribute_token_cls, - Literal("TA") - + aperture_attribute_name.set_results_name("attribute_name") - + maybe_comma_or_maybe_comma_with_field, - ) - # Add an object attribute to the dictionary or modify it. - to = wrapper( - self.options.to_object_attribute_token_cls, - Literal("TO") - + object_attribute_name.set_results_name("attribute_name") - + maybe_comma_or_maybe_comma_with_field, - ) - # Delete one or all attributes in the dictionary. - td = wrapper( - self.options.td_delete_attribute_token_cls, - Literal("TD") - + Opt( - file_attribute_name | aperture_attribute_name | object_attribute_name, - ).set_results_name("attribute_name"), - ) - - # Set a file attribute. - tf_comment = wrapper( - self.options.tf_file_attribute_token_cls, - Regex("G0*4") - + Literal("#@!") - + Literal("TF") - + file_attribute_name.set_results_name("attribute_name") - + maybe_comma_or_maybe_comma_with_field, - ) - # Add an aperture attribute to the dictionary or modify it. - ta_comment = wrapper( - self.options.ta_aperture_attribute_token_cls, - Regex("G0*4") - + Literal("#@!") - + Literal("TA") - + aperture_attribute_name.set_results_name("attribute_name") - + maybe_comma_or_maybe_comma_with_field, - ) - # Add an object attribute to the dictionary or modify it. - to_comment = wrapper( - self.options.to_object_attribute_token_cls, - Regex("G0*4") - + Literal("#@!") - + Literal("TO") - + object_attribute_name.set_results_name("attribute_name") - + maybe_comma_or_maybe_comma_with_field, - ) - # Delete one or all attributes in the dictionary. - td_comment = wrapper( - self.options.td_delete_attribute_token_cls, - Regex("G0*4") - + Literal("#@!") - + Literal("TD") - + Opt( - file_attribute_name | aperture_attribute_name | object_attribute_name, - ).set_results_name("attribute_name"), - ) - - attrs = tf | ta | to | td - comment_attrs = tf_comment | ta_comment | to_comment | td_comment - - return self._build_stmt(attrs) | comment_attrs - - _build_eoex_cache: Optional[ParserElement] = None - - def _build_eoex(self) -> ParserElement: - if self._build_eoex_cache is None: - self._build_eoex_cache = self.wrapper( - self.options.end_of_expression_token_cls, - Literal("*").set_name("end of expression"), - ) - return self._build_eoex_cache - - def _build_stmt( - self, - expr: ParserElement, - *, - eoex: bool = True, - ) -> ParserElement: - begin_stmt = Literal("%") - end_stmt = Literal("%") - - return self.wrapper( - self.options.statement_token_cls, - begin_stmt + expr + ((self._build_eoex() + end_stmt) if eoex else end_stmt), - ) - - def _build_integer( - self, - result_name: str = "integer", - name: Optional[str] = None, - **kwargs: Any, - ) -> ParserElement: - return self._annotate_parser_element( - Combine(Opt(oneOf("+ -")) + Word(nums)), - result_name, - name, - **kwargs, - ) - - def _annotate_parser_element( - self, - element: ParserElement, - result_name: str, - name: Optional[str] = None, - **kwargs: Any, - ) -> ParserElement: - if name is None: - name = result_name - return element.set_name(name).set_results_name(result_name, **kwargs) - - def _build_decimal( - self, - result_name: str = "decimal", - name: Optional[str] = None, - **kwargs: Any, - ) -> ParserElement: - return self._annotate_parser_element( - Regex(r"[+-]?((([0-9]+)(\.[0-9]*)?)|(\.[0-9]+))"), - result_name, - name, - **kwargs, - ) - - def _build_aperture_identifier( - self, - result_name: str = "aperture_identifier", - name: Optional[str] = None, - **kwargs: Any, - ) -> ParserElement: - return self._annotate_parser_element( - Combine("D" + Regex(r"[1-9][0-9]+")), - result_name, - name, - **kwargs, - ) - - def _build_name( - self, - result_name: str = "name", - name: Optional[str] = None, - **kwargs: Any, - ) -> ParserElement: - return self._annotate_parser_element( - Regex(r"[._a-zA-Z$][\._a-zA-Z0-9]*"), - result_name, - name, - **kwargs, - ) - - def _build_string( - self, - result_name: str = "string", - name: Optional[str] = None, - **kwargs: Any, - ) -> ParserElement: - return self._annotate_parser_element( - CharsNotIn("%*"), - result_name, - name, - **kwargs, - ) - - def _build_field( - self, - result_name: str = "field", - name: Optional[str] = None, - **kwargs: Any, - ) -> ParserElement: - return self._annotate_parser_element( - CharsNotIn("%*"), - result_name, - name, - **kwargs, - ) - - -class GerberGrammar: - """Gerber grammar container.""" - - def __init__( - self, - strict_grammar: ParserElement, - expression_grammar: ParserElement, - resilient_grammar: ParserElement, - ) -> None: - self.strict_grammar = strict_grammar - self.expression_grammar = expression_grammar - self.resilient_grammar = resilient_grammar diff --git a/src/pygerber/gerberx3/tokenizer/helpers/__init__.py b/src/pygerber/gerberx3/tokenizer/helpers/__init__.py deleted file mode 100644 index 8b3d1948..00000000 --- a/src/pygerber/gerberx3/tokenizer/helpers/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Helper classes.""" diff --git a/src/pygerber/gerberx3/tokenizer/helpers/gerber_code_enum.py b/src/pygerber/gerberx3/tokenizer/helpers/gerber_code_enum.py deleted file mode 100644 index 498b1d77..00000000 --- a/src/pygerber/gerberx3/tokenizer/helpers/gerber_code_enum.py +++ /dev/null @@ -1,19 +0,0 @@ -"""Enum with GerberCode interface implementation.""" - -from __future__ import annotations - -from enum import Enum - -from pygerber.gerberx3.tokenizer.tokens.bases.gerber_code import GerberCode - - -class GerberCodeEnum(GerberCode, Enum): - """Enum with GerberCode interface implementation.""" - - def get_gerber_code( - self, - indent: str = "", - endline: str = "\n", # noqa: ARG002 - ) -> str: - """Get gerber code represented by this token.""" - return f"{indent}{self.value}" diff --git a/src/pygerber/gerberx3/tokenizer/tokenizer.py b/src/pygerber/gerberx3/tokenizer/tokenizer.py deleted file mode 100644 index c36d7dce..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokenizer.py +++ /dev/null @@ -1,87 +0,0 @@ -"""GerberX3 format tokenizer. - -Parser is based on GerberX3 format described in Ucamco's `The Gerber Layer Format -Specification`. - -""" - -from __future__ import annotations - -import logging -from dataclasses import dataclass, field -from typing import TYPE_CHECKING, Optional - -from pygerber.gerberx3.tokenizer.grammar import ( - GerberGrammarBuilder, - GerberGrammarBuilderOptions, -) -from pygerber.gerberx3.tokenizer.tokens.groups.ast import AST - -if TYPE_CHECKING: - from pyparsing import ParserElement - - -@dataclass -class TokenizerOptions: - """Tokenizer options.""" - - grammar_options: GerberGrammarBuilderOptions = field( - default_factory=GerberGrammarBuilderOptions, - ) - - -class Tokenizer: - """GerberX3 format tokenizer.""" - - def __init__(self, options: Optional[TokenizerOptions] = None) -> None: - """GerberX3 format tokenizer.""" - logging.debug("Created %s GerberX3 tokenizer.") - self.options = TokenizerOptions() if options is None else options - self.grammar = GerberGrammarBuilder( - options=self.options.grammar_options, - ).build() - - def tokenize(self, source: str) -> AST: - """Convert source code into token stack. - - Supports only full, valid GerberX3 files. - """ - return self._tokenize_grammar( - source, - self.grammar.strict_grammar, - parse_all=False, - ) - - def tokenize_expressions(self, source: str) -> AST: - """Convert source code into token stack. - - Supports arbitrary sequences of valid GerberX3 expressions. - """ - return self._tokenize_grammar( - source, - self.grammar.expression_grammar, - parse_all=True, - ) - - def tokenize_resilient(self, source: str) -> AST: - """Convert source code into token stack. - - Supports arbitrary sequences of valid GerberX3 expressions. - """ - return self._tokenize_grammar( - source, - self.grammar.resilient_grammar, - parse_all=True, - ) - - def _tokenize_grammar( - self, - source: str, - grammar: ParserElement, - *, - parse_all: bool, - ) -> AST: - ast = grammar.parse_string(source, parse_all=parse_all)[0] - if not isinstance(ast, AST): - raise TypeError(ast) - return ast diff --git a/src/pygerber/gerberx3/tokenizer/tokens/__init__.py b/src/pygerber/gerberx3/tokenizer/tokens/__init__.py deleted file mode 100644 index 014b64d5..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Gerber X3 token wrappers.""" diff --git a/src/pygerber/gerberx3/tokenizer/tokens/ab_block_aperture.py b/src/pygerber/gerberx3/tokenizer/tokens/ab_block_aperture.py deleted file mode 100644 index b62517b9..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/ab_block_aperture.py +++ /dev/null @@ -1,142 +0,0 @@ -"""Block Aperture (AB) logic.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from pygerber.gerberx3.tokenizer.tokens.bases.command import CommandToken -from pygerber.gerberx3.tokenizer.tokens.dnn_select_aperture import ApertureID - -if TYPE_CHECKING: - from pyparsing import ParseResults - from typing_extensions import Self - - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class BlockApertureBegin(CommandToken): - """## 4.11 Block Aperture (AB). - - ### 4.11.1 Overview of block apertures - - The AB command creates a block aperture. The command stream between the opening and - closing AB command defines a block aperture which is stored in the aperture dictionary. Thus - the AB commands add an aperture to the dictionary directly, without needing an AD command. - The LM, LR, LS and LP commands affect the flashes of block apertures as any other aperture: - when a block aperture is flashed, it is first transformed according to the transformation - parameters in the graphics state and then added to the object stream. - - The origin of the block aperture is the (0,0) point of the file. - - A block aperture is not a single graphical object but a set of objects. While a standard or macro - aperture always adds a single graphical object to the stream, a block aperture can add any - number of objects, each with their own polarity. Standard and macro apertures always have a - single polarity while block apertures can contain both dark and clear objects. - - If the polarity is dark (LPD) when the block is flashed then the block aperture is inserted as is. If - the polarity is clear (LPC) then the polarity of all objects in the block is toggled (clear becomes - dark, and dark becomes clear). This toggle propagates through all nesting levels. In the - following example the polarity of objects in the flash of block D12 will be toggled. - - ```gerber - %ABD12*% - … - %AB*% - … - D12* - %LPC*% - X-2500000Y-1000000D03* - ``` - - A D03 of a block aperture updates the current point but otherwise leaves the graphics state - unmodified, as with any other aperture. - - --- - - See section 4.11 of [The Gerber Layer Format Specification](https://www.ucamco.com/files/downloads/file_en/456/gerber-layer-format-specification-revision-2023-08_en.pdf#page=111) - - """ # noqa: E501 - - def __init__(self, string: str, location: int, identifier: ApertureID) -> None: - super().__init__(string, location) - self.identifier = identifier - - @classmethod - def new(cls, string: str, location: int, tokens: ParseResults) -> Self: - """Create instance of this class. - - Created to be used as callback in `ParserElement.set_parse_action()`. - """ - return cls(string, location, ApertureID(tokens["aperture_identifier"])) - - def parser2_visit_token(self, context: Parser2Context) -> None: - """Perform actions on the context implicated by this token.""" - context.get_hooks().begin_block_aperture.pre_parser_visit_token(self, context) - context.get_hooks().begin_block_aperture.on_parser_visit_token(self, context) - context.get_hooks().begin_block_aperture.post_parser_visit_token(self, context) - - def get_gerber_code( - self, - indent: str = "", - endline: str = "\n", # noqa: ARG002 - ) -> str: - """Get gerber code represented by this token.""" - return f"{indent}AB{self.identifier.get_gerber_code()}" - - -class BlockApertureEnd(CommandToken): - """## 4.11 Block Aperture (AB). - - ### 4.11.1 Overview of block apertures - - The AB command creates a block aperture. The command stream between the opening and - closing AB command defines a block aperture which is stored in the aperture dictionary. Thus - the AB commands add an aperture to the dictionary directly, without needing an AD command. - The LM, LR, LS and LP commands affect the flashes of block apertures as any other aperture: - when a block aperture is flashed, it is first transformed according to the transformation - parameters in the graphics state and then added to the object stream. - - The origin of the block aperture is the (0,0) point of the file. - - A block aperture is not a single graphical object but a set of objects. While a standard or macro - aperture always adds a single graphical object to the stream, a block aperture can add any - number of objects, each with their own polarity. Standard and macro apertures always have a - single polarity while block apertures can contain both dark and clear objects. - - If the polarity is dark (LPD) when the block is flashed then the block aperture is inserted as is. If - the polarity is clear (LPC) then the polarity of all objects in the block is toggled (clear becomes - dark, and dark becomes clear). This toggle propagates through all nesting levels. In the - following example the polarity of objects in the flash of block D12 will be toggled. - - ```gerber - %ABD12*% - … - %AB*% - … - D12* - %LPC*% - X-2500000Y-1000000D03* - ``` - - A D03 of a block aperture updates the current point but otherwise leaves the graphics state - unmodified, as with any other aperture. - - --- - - See section 4.11 of [The Gerber Layer Format Specification](https://www.ucamco.com/files/downloads/file_en/456/gerber-layer-format-specification-revision-2023-08_en.pdf#page=111) - - """ # noqa: E501 - - def parser2_visit_token(self, context: Parser2Context) -> None: - """Perform actions on the context implicated by this token.""" - context.get_hooks().end_block_aperture.pre_parser_visit_token(self, context) - context.get_hooks().end_block_aperture.on_parser_visit_token(self, context) - context.get_hooks().end_block_aperture.post_parser_visit_token(self, context) - - def get_gerber_code( - self, - indent: str = "", - endline: str = "\n", # noqa: ARG002 - ) -> str: - """Get gerber code represented by this token.""" - return f"{indent}AB" diff --git a/src/pygerber/gerberx3/tokenizer/tokens/ad_define_aperture.py b/src/pygerber/gerberx3/tokenizer/tokens/ad_define_aperture.py deleted file mode 100644 index 419d3e44..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/ad_define_aperture.py +++ /dev/null @@ -1,670 +0,0 @@ -"""AD Command logic. - -Generally, apertures with size zero are invalid, and so are objects created with them. -There is one exception. The C (circular) standard aperture with zero diameter is -allowed, and so are objects created with it. Attributes can be attached to them. For the -avoidance of doubt, zero size is only allowed for the C aperture, not another aperture -type whose shape is fortuitously circular. - -Zero-size objects do not affect the image. They can be used to provide meta-information -to locations in the image plane. - -Allowed does not mean recommended, quite the contrary. If you are tempted to use a -zero-size object, consider whether it is useful, and whether there is no proper way to -convey the meta information. Certainly do not abuse a zero-size object to indicate the -absence of an object, e.g. by flashing a zero-size aperture to indicate the absence of -a pad. This is just confusing. If there is nothing, put nothing. -""" - -from __future__ import annotations - -from decimal import Decimal -from typing import TYPE_CHECKING, List, Optional - -from pygerber.gerberx3.tokenizer.tokens.bases.extended_command import ( - ExtendedCommandToken, -) -from pygerber.gerberx3.tokenizer.tokens.dnn_select_aperture import ApertureID - -if TYPE_CHECKING: - from pyparsing import ParseResults - from typing_extensions import Self - - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class DefineAperture(ExtendedCommandToken): - """## 4.3.1 AD Command. - - The AD command creates an aperture, attaches the aperture attributes at that moment in the - attribute dictionary to it and adds it to the apertures dictionary. - - ```ebnf - AD = '%' ('AD' aperture_ident template_call) '*%'; - template_call = template_name [',' parameter {'X' parameter}*]; - ``` - - The AD command must precede the first use of the aperture. It is recommended to put all AD - commands in the file header. - - --- - - ## Example - - ```gerber - %ADD10C,.025*% - %ADD10C,0.5X0.25*% - ``` - - --- - - See section 4.3 of [The Gerber Layer Format Specification](https://www.ucamco.com/files/downloads/file_en/456/gerber-layer-format-specification-revision-2023-08_en.pdf#page=48) - - """ # noqa: E501 - - -class DefineCircle(DefineAperture): - """## 4.3.1 AD Command. - - The AD command creates an aperture, attaches the aperture attributes at that moment in the - attribute dictionary to it and adds it to the apertures dictionary. - - ```ebnf - AD = '%' ('AD' aperture_ident template_call) '*%'; - template_call = template_name [',' parameter {'X' parameter}*]; - ``` - - The AD command must precede the first use of the aperture. It is recommended to put all AD - commands in the file header. - - --- - - ## Example - - ```gerber - %ADD10C,.025*% - %ADD10C,0.5X0.25*% - ``` - - --- - - See section 4.3 of [The Gerber Layer Format Specification](https://www.ucamco.com/files/downloads/file_en/456/gerber-layer-format-specification-revision-2023-08_en.pdf#page=48) - - --- - - ## 4.4.2 Circle - - The syntax of the circle standard template call is: - - ```ebnf - template_call = 'C' ',' diameter 'X' hole_diameter - ``` - - - `C` - Indicates the circle aperture template. - - `diameter` - Diameter. A decimal ≥0. - - `hole_diameter` - Diameter of a round hole. A decimal >0. If omitted the aperture is solid. See also section [4.4.6](https://www.ucamco.com/files/downloads/file_en/456/gerber-layer-format-specification-revision-2023-08_en.pdf#page=55). - - --- - - See section 4.4.2 of [The Gerber Layer Format Specification](https://www.ucamco.com/files/downloads/file_en/456/gerber-layer-format-specification-revision-2023-08_en.pdf#page=50) - - """ # noqa: E501 - - def __init__( - self, - string: str, - location: int, - aperture_id: ApertureID, - diameter: Decimal, - hole_diameter: Optional[Decimal], - ) -> None: - super().__init__(string, location) - self.aperture_id = aperture_id - self.diameter = diameter - self.hole_diameter = hole_diameter - - @classmethod - def new(cls, string: str, location: int, tokens: ParseResults) -> Self: - """Create instance of this class. - - Created to be used as callback in `ParserElement.set_parse_action()`. - """ - aperture_id = ApertureID(tokens["aperture_identifier"]) - diameter: Decimal = Decimal(str(tokens["diameter"])) - hole_diameter: Optional[Decimal] = ( - Decimal(str(tokens["hole_diameter"])) - if tokens.get("hole_diameter") is not None - else None - ) - return cls( - string=string, - location=location, - aperture_id=aperture_id, - diameter=diameter, - hole_diameter=hole_diameter, - ) - - def parser2_visit_token(self, context: Parser2Context) -> None: - """Perform actions on the context implicated by this token.""" - context.get_hooks().define_circle_aperture.pre_parser_visit_token(self, context) - context.get_hooks().define_aperture.pre_parser_visit_token(self, context) - - context.get_hooks().define_circle_aperture.on_parser_visit_token(self, context) - context.get_hooks().define_aperture.on_parser_visit_token(self, context) - - context.get_hooks().define_circle_aperture.post_parser_visit_token( - self, - context, - ) - context.get_hooks().define_aperture.post_parser_visit_token(self, context) - - def get_gerber_code( - self, - indent: str = "", - endline: str = "\n", - ) -> str: - """Get gerber code represented by this token.""" - suffix = "" - if self.hole_diameter is not None: - suffix += f"X{self.hole_diameter}" - return ( - f"AD{self.aperture_id.get_gerber_code(indent, endline)}C," - f"{self.diameter}{suffix}" - ) - - -class DefineRectangle(DefineAperture): - """## 4.3.1 AD Command. - - The AD command creates an aperture, attaches the aperture attributes at that moment in the - attribute dictionary to it and adds it to the apertures dictionary. - - ```ebnf - AD = '%' ('AD' aperture_ident template_call) '*%'; - template_call = template_name [',' parameter {'X' parameter}*]; - ``` - - The AD command must precede the first use of the aperture. It is recommended to put all AD - commands in the file header. - - --- - - ### Example: - - ```gerber - %ADD22R,0.044X0.025*% - %ADD23R,0.044X0.025X0.019*% - ``` - - --- - - See section 4.3 of [The Gerber Layer Format Specification](https://www.ucamco.com/files/downloads/file_en/456/gerber-layer-format-specification-revision-2023-08_en.pdf#page=48) - - --- - - ## 4.4.3 Rectangle - - The syntax of the rectangle or square standard template call is: - - ```ebnf - template_call = 'R' ',' x_size 'X' y_size 'X' hole_diameter - ``` - - - `T` - Indicates the rectangle aperture template. - - `x_size`, `x_size` - X and Y sizes of the rectangle. Decimals >0. If x_size = y_size the effective shape is a square - - `hole_diameter` - Diameter of a round hole. A decimal >0. If omitted the aperture is solid. See also section [4.4.6](https://www.ucamco.com/files/downloads/file_en/456/gerber-layer-format-specification-revision-2023-08_en.pdf#page=55). - - --- - - See section 4.4.3 of [The Gerber Layer Format Specification](https://www.ucamco.com/files/downloads/file_en/456/gerber-layer-format-specification-revision-2023-08_en.pdf#page=52) - - """ # noqa: E501 - - def __init__( - self, - string: str, - location: int, - aperture_id: ApertureID, - x_size: Decimal, - y_size: Decimal, - hole_diameter: Optional[Decimal], - ) -> None: - super().__init__(string, location) - self.aperture_id = aperture_id - self.x_size = x_size - self.y_size = y_size - self.hole_diameter = hole_diameter - - @classmethod - def new(cls, string: str, location: int, tokens: ParseResults) -> Self: - """Create instance of this class. - - Created to be used as callback in `ParserElement.set_parse_action()`. - """ - aperture_id = ApertureID(tokens["aperture_identifier"]) - x_size: Decimal = Decimal(str(tokens["x_size"])) - y_size: Decimal = Decimal(str(tokens["y_size"])) - hole_diameter: Optional[Decimal] = ( - Decimal(str(tokens["hole_diameter"])) - if tokens.get("hole_diameter") is not None - else None - ) - return cls( - string=string, - location=location, - aperture_id=aperture_id, - x_size=x_size, - y_size=y_size, - hole_diameter=hole_diameter, - ) - - def parser2_visit_token(self, context: Parser2Context) -> None: - """Perform actions on the context implicated by this token.""" - context.get_hooks().define_rectangle_aperture.pre_parser_visit_token( - self, - context, - ) - context.get_hooks().define_aperture.pre_parser_visit_token(self, context) - - context.get_hooks().define_rectangle_aperture.on_parser_visit_token( - self, - context, - ) - context.get_hooks().define_aperture.on_parser_visit_token(self, context) - - context.get_hooks().define_rectangle_aperture.post_parser_visit_token( - self, - context, - ) - context.get_hooks().define_aperture.post_parser_visit_token(self, context) - - def get_gerber_code( - self, - indent: str = "", - endline: str = "\n", - ) -> str: - """Get gerber code represented by this token.""" - suffix = "" - if self.hole_diameter is not None: - suffix += f"X{self.hole_diameter}" - return ( - f"AD{self.aperture_id.get_gerber_code(indent, endline)}R," - f"{self.x_size}X{self.y_size}{suffix}" - ) - - -class DefineObround(DefineAperture): - """## 4.3.1 AD Command. - - The AD command creates an aperture, attaches the aperture attributes at that moment in the - attribute dictionary to it and adds it to the apertures dictionary. - - ```ebnf - AD = '%' ('AD' aperture_ident template_call) '*%'; - template_call = template_name [',' parameter {'X' parameter}*]; - ``` - - The AD command must precede the first use of the aperture. It is recommended to put all AD - commands in the file header. - - --- - - ### Example: - - ```gerber - %ADD22O,0.046X0.026*% - %ADD22O,0.046X0.026X0.019*% - ``` - - --- - - See section 4.3 of [The Gerber Layer Format Specification](https://www.ucamco.com/files/downloads/file_en/456/gerber-layer-format-specification-revision-2023-08_en.pdf#page=48) - - --- - - ## 4.4.4 Obround - - Obround (oval) is a rectangle where the smallest side is rounded to a half-circle. The syntax is: - - ```ebnf - template_call = 'O' ',' x_size 'X' y_size 'X' hole_diameter - ``` - - - `O` - Indicates the obround aperture template. - - `x_size`, `x_size` - X and Y sizes of enclosing box. Decimals >0. If x_size = y_size the effective shape is a circle. - - `hole_diameter` - Diameter of a round hole. A decimal >0. If omitted the aperture is solid. See also section [4.4.6](https://www.ucamco.com/files/downloads/file_en/456/gerber-layer-format-specification-revision-2023-08_en.pdf#page=55). - - --- - - See section 4.4.4 of [The Gerber Layer Format Specification](https://www.ucamco.com/files/downloads/file_en/456/gerber-layer-format-specification-revision-2023-08_en.pdf#page=53) - - """ # noqa: E501 - - def __init__( - self, - string: str, - location: int, - aperture_id: ApertureID, - x_size: Decimal, - y_size: Decimal, - hole_diameter: Optional[Decimal], - ) -> None: - super().__init__(string, location) - self.aperture_id = aperture_id - self.x_size = x_size - self.y_size = y_size - self.hole_diameter = hole_diameter - - @classmethod - def new(cls, string: str, location: int, tokens: ParseResults) -> Self: - """Create instance of this class. - - Created to be used as callback in `ParserElement.set_parse_action()`. - """ - aperture_id = ApertureID(tokens["aperture_identifier"]) - x_size: Decimal = Decimal(str(tokens["x_size"])) - y_size: Decimal = Decimal(str(tokens["y_size"])) - hole_diameter: Optional[Decimal] = ( - Decimal(str(tokens["hole_diameter"])) - if tokens.get("hole_diameter") is not None - else None - ) - return cls( - string=string, - location=location, - aperture_id=aperture_id, - x_size=x_size, - y_size=y_size, - hole_diameter=hole_diameter, - ) - - def parser2_visit_token(self, context: Parser2Context) -> None: - """Perform actions on the context implicated by this token.""" - context.get_hooks().define_obround_aperture.pre_parser_visit_token( - self, - context, - ) - context.get_hooks().define_aperture.pre_parser_visit_token(self, context) - - context.get_hooks().define_obround_aperture.on_parser_visit_token(self, context) - context.get_hooks().define_aperture.on_parser_visit_token(self, context) - - context.get_hooks().define_obround_aperture.post_parser_visit_token( - self, - context, - ) - context.get_hooks().define_aperture.post_parser_visit_token(self, context) - - def get_gerber_code( - self, - indent: str = "", - endline: str = "\n", - ) -> str: - """Get gerber code represented by this token.""" - suffix = "" - if self.hole_diameter is not None: - suffix += f"X{self.hole_diameter}" - return ( - f"AD{self.aperture_id.get_gerber_code(indent, endline)}O," - f"{self.x_size}X{self.y_size}{suffix}" - ) - - -class DefinePolygon(DefineAperture): - """## 4.3.1 AD Command. - - The AD command creates an aperture, attaches the aperture attributes at that moment in the - attribute dictionary to it and adds it to the apertures dictionary. - - ```ebnf - AD = '%' ('AD' aperture_ident template_call) '*%'; - template_call = template_name [',' parameter {'X' parameter}*]; - ``` - - The AD command must precede the first use of the aperture. It is recommended to put all AD - commands in the file header. - - --- - - ### Example: - - ```gerber - %ADD17P,.040X6*% - %ADD17P,.040X6X0.0X0.019*% - ``` - - --- - - See section 4.3 of [The Gerber Layer Format Specification](https://www.ucamco.com/files/downloads/file_en/456/gerber-layer-format-specification-revision-2023-08_en.pdf#page=48) - - --- - - ## 4.4.5 Polygon - - Creates a regular polygon aperture. The syntax of the polygon template is: - - ```ebnf - template_call = 'P' ',' outer_diameter 'X' vertices 'X' rotation 'X' hole_diameter - ``` - - - `P` - Indicates the polygon aperture template. - - `outer_diameter` - Diameter of the circle circumscribing the regular polygon, i.e. the circle through the polygon vertices. A decimal > 0. - - `vertices` - Number of vertices n, 3 ≤ n ≤ 12. An integer. - - `rotation` - The rotation angle, in degrees counterclockwise. A decimal. With rotation angle zero there is a vertex on the positive X-axis through the aperture center. - - `hole_diameter` - Diameter of a round hole. A decimal >0. If omitted the aperture is solid. See also section [4.4.6](https://www.ucamco.com/files/downloads/file_en/456/gerber-layer-format-specification-revision-2023-08_en.pdf#page=55). - - --- - - See section 4.4.5 of [The Gerber Layer Format Specification](https://www.ucamco.com/files/downloads/file_en/456/gerber-layer-format-specification-revision-2023-08_en.pdf#page=54) - - """ # noqa: E501 - - def __init__( - self, - string: str, - location: int, - aperture_id: ApertureID, - outer_diameter: Decimal, - number_of_vertices: int, - rotation: Optional[Decimal], - hole_diameter: Optional[Decimal], - ) -> None: - super().__init__(string, location) - self.aperture_id = aperture_id - self.outer_diameter = outer_diameter - self.number_of_vertices = number_of_vertices - self.rotation = rotation - self.hole_diameter = hole_diameter - - @classmethod - def new(cls, string: str, location: int, tokens: ParseResults) -> Self: - """Create instance of this class. - - Created to be used as callback in `ParserElement.set_parse_action()`. - """ - aperture_id = ApertureID(tokens["aperture_identifier"]) - outer_diameter: Decimal = Decimal(str(tokens["outer_diameter"])) - number_of_vertices: int = int(str(tokens["number_of_vertices"])) - rotation: Optional[Decimal] = ( - Decimal(str(tokens["rotation"])) - if tokens.get("rotation") is not None - else None - ) - hole_diameter: Optional[Decimal] = ( - Decimal(str(tokens["hole_diameter"])) - if tokens.get("hole_diameter") is not None - else None - ) - return cls( - string=string, - location=location, - aperture_id=aperture_id, - outer_diameter=outer_diameter, - number_of_vertices=number_of_vertices, - rotation=rotation, - hole_diameter=hole_diameter, - ) - - def parser2_visit_token(self, context: Parser2Context) -> None: - """Perform actions on the context implicated by this token.""" - context.get_hooks().define_polygon_aperture.pre_parser_visit_token( - self, - context, - ) - context.get_hooks().define_aperture.pre_parser_visit_token(self, context) - - context.get_hooks().define_polygon_aperture.on_parser_visit_token(self, context) - context.get_hooks().define_aperture.on_parser_visit_token(self, context) - - context.get_hooks().define_polygon_aperture.post_parser_visit_token( - self, - context, - ) - context.get_hooks().define_aperture.post_parser_visit_token(self, context) - - def get_gerber_code( - self, - indent: str = "", - endline: str = "\n", - ) -> str: - """Get gerber code represented by this token.""" - suffix = "" - if self.hole_diameter is not None: - suffix += f"X{self.hole_diameter}" - return ( - f"AD{self.aperture_id.get_gerber_code(indent, endline)}P," - f"{self.outer_diameter}X{self.number_of_vertices}X{self.rotation}{suffix}" - ) - - -class DefineMacro(DefineAperture): - """## 4.3.1 AD Command. - - The AD command creates an aperture, attaches the aperture attributes at that moment in the - attribute dictionary to it and adds it to the apertures dictionary. - - ```ebnf - AD = '%' ('AD' aperture_ident template_call) '*%'; - template_call = template_name [',' parameter {'X' parameter}*]; - ``` - - The AD command must precede the first use of the aperture. It is recommended to put all AD - commands in the file header. - - --- - - ## Example - - ```gerber - %ADD10C,.025*% - %ADD10C,0.5X0.25*% - ``` - - --- - - See section 4.3 of [The Gerber Layer Format Specification](https://www.ucamco.com/files/downloads/file_en/456/gerber-layer-format-specification-revision-2023-08_en.pdf#page=48) - - --- - - ## 4.5 Aperture Macro (AM) - - The AM command creates a macro aperture template and adds it to the aperture template - dictionary (see 2.2). A template is a parametrized shape. The AD command instantiates a - template into an aperture by supplying values to the template parameters. - - Templates of any shape or parametrization can be created. Multiple simple shapes called - primitives can be combined in a single template. An aperture macro can contain variables - whose actual values are defined by: - - - Values provided by the AD command, - - Arithmetic expressions with other variables. - - The template is created by positioning primitives in a coordinate space. The origin of that - coordinate space will be the origin of all apertures created with the state. - - A template must be defined before the first AD that refers to it. The AM command can be used - multiple times in a file. - - Attributes are not attached to templates. They are attached to the aperture at the time of its - creation with the AD command. - - An AM command contains the following words: - - - The AM declaration with the macro name - - Primitives with their comma-separated parameters - - Macro variables, defined by an arithmetic expression - - ```ebnf - AM = '%' ('AM' macro_name macro_body) '%'; - macro_name = name '*'; - macro_body = {in_macro_block}+; - in_macro_block = - |primitive - |variable_definition - ; - variable_definition = (macro_variable '=' expression) '*'; - macro_variable = '$' positive_integer; - primitive = primitive_code {',' par}* - par = ',' (expression); - ``` - - --- - - See section 4.5 of [The Gerber Layer Format Specification](https://www.ucamco.com/files/downloads/file_en/456/gerber-layer-format-specification-revision-2023-08_en.pdf#page=56) - - """ # noqa: E501 - - def __init__( - self, - string: str, - location: int, - aperture_type: str, - aperture_id: ApertureID, - am_param: List[str], - ) -> None: - super().__init__(string, location) - self.aperture_type = aperture_type - self.aperture_id = aperture_id - self.am_param = am_param - - @classmethod - def new(cls, string: str, location: int, tokens: ParseResults) -> Self: - """Create instance of this class. - - Created to be used as callback in `ParserElement.set_parse_action()`. - """ - aperture_type: str = str(tokens["aperture_type"]) - aperture_id = ApertureID(tokens["aperture_identifier"]) - - raw_am_param = e if (e := tokens.get("am_param")) is not None else [] - am_param: List[str] = [str(p) for p in raw_am_param] - - return cls( - string=string, - location=location, - aperture_type=aperture_type, - aperture_id=aperture_id, - am_param=am_param, - ) - - def parser2_visit_token(self, context: Parser2Context) -> None: - """Perform actions on the context implicated by this token.""" - context.get_hooks().define_macro_aperture.pre_parser_visit_token(self, context) - context.get_hooks().define_aperture.pre_parser_visit_token(self, context) - - context.get_hooks().define_macro_aperture.on_parser_visit_token(self, context) - context.get_hooks().define_aperture.on_parser_visit_token(self, context) - - context.get_hooks().define_macro_aperture.post_parser_visit_token(self, context) - context.get_hooks().define_aperture.post_parser_visit_token(self, context) - - def get_gerber_code( - self, - indent: str = "", - endline: str = "\n", - ) -> str: - """Get gerber code represented by this token.""" - return ( - f"AD{self.aperture_id.get_gerber_code(indent, endline)}" - f"{self.aperture_type},{'X'.join(self.am_param)}" - ) diff --git a/src/pygerber/gerberx3/tokenizer/tokens/as_axis_select.py b/src/pygerber/gerberx3/tokenizer/tokens/as_axis_select.py deleted file mode 100644 index f07425dd..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/as_axis_select.py +++ /dev/null @@ -1,120 +0,0 @@ -"""### Load Name (LN). - -Note: The LN command was deprecated in revision I4 from October 2013. - -The historic `LN` command doesn't influence the image in any manner and can safely be -overlooked. - -Function of the `LN` command: -- `LN` is designed to allocate a name to the following section of the file. -- It was originally conceptualized to serve as a human-readable comment. -- For creating human-readable comments, it's advisable to utilize the standard `G04` - command. -- The `LN` command has the flexibility to be executed multiple times within a file. - -SPEC: `2023.03` SECTION: `8.1.6` -""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from pygerber.gerberx3.state_enums import AxisCorrespondence -from pygerber.gerberx3.tokenizer.tokens.bases.extended_command import ( - ExtendedCommandToken, -) - -if TYPE_CHECKING: - from pyparsing import ParseResults - from typing_extensions import Self - - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class AxisSelect(ExtendedCommandToken): - """## 8.1.2 Axis Select (AS). - - The AS command is deprecated since revision I1 from December 2012. - - The historic AS command sets the correspondence between the X, Y data axes and the - A, B output device axes. It does not affect the image in computer to computer data - exchange. It only has an effect how the image is positioned on an output device. - - The order of execution is always MI, SF, OF, IR and AS, independent of their order - of appearance in the file. - - The AS command can only be used once, at the beginning of the file. - - ### 8.1.2.1 AS Command. - - The syntax for the AS command is: - - ```ebnf - AS = '%' (AS' ('AXBY'|'AYBX')) '*%'; - ``` - - - `AS` - AS for Axis Select - - `AXBY` - Assign output device axis A to data axis X, output device axis B to data axis Y. This is the default. - - `AYBX` - Assign output device axis A to data axis Y, output device axis B to data axis X. - - --- - - ## Example - - Assign output device axis A to data axis X and output device axis B - to data axis Y - - ```gerber - %ASAXBY*% - ``` - - Assign output device axis A to data axis Y and output device axis B - to data axis X - - ```gerber - %ASAYBX*% - ``` - - --- - - See section 8.1.2 of [The Gerber Layer Format Specification](https://www.ucamco.com/files/downloads/file_en/456/gerber-layer-format-specification-revision-2023-08_en.pdf#page=175) - - """ # noqa: E501 - - def __init__( - self, - string: str, - location: int, - correspondence: AxisCorrespondence, - ) -> None: - super().__init__(string, location) - self.correspondence = correspondence - - @classmethod - def new(cls, string: str, location: int, tokens: ParseResults) -> Self: - """Create instance of this class. - - Created to be used as callback in `ParserElement.set_parse_action()`. - """ - correspondence = tokens["correspondence"] - if not isinstance(correspondence, str): - raise TypeError(correspondence) - return cls( - string=string, - location=location, - correspondence=AxisCorrespondence(correspondence), - ) - - def parser2_visit_token(self, context: Parser2Context) -> None: - """Perform actions on the context implicated by this token.""" - context.get_hooks().axis_select.pre_parser_visit_token(self, context) - context.get_hooks().axis_select.on_parser_visit_token(self, context) - context.get_hooks().axis_select.post_parser_visit_token(self, context) - - def get_gerber_code( - self, - indent: str = "", # noqa: ARG002 - endline: str = "\n", # noqa: ARG002 - ) -> str: - """Get gerber code represented by this token.""" - return f"IN{self.correspondence}" diff --git a/src/pygerber/gerberx3/tokenizer/tokens/attribute_token.py b/src/pygerber/gerberx3/tokenizer/tokens/attribute_token.py deleted file mode 100644 index 83bafe82..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/attribute_token.py +++ /dev/null @@ -1,116 +0,0 @@ -"""Base class for attribute tokens.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING, Optional - -from pygerber.gerberx3.tokenizer.tokens.bases.extended_command import ( - ExtendedCommandToken, -) - -if TYPE_CHECKING: - from pyparsing import ParseResults - from typing_extensions import Self - - -class AttributeToken(ExtendedCommandToken): - """## 5.1 Attributes Overview,. - - Attributes add meta-information to a Gerber file. Attributes are akin to labels providing - information about the file or features within them. Examples of meta-information conveyed by - attributes are: - - - The function of the file in the layer structure. Is the file the top solder mask, the bottom - copper layer, …? - - The function of a pad. Is the pad an SMD pad, or a via pad, or a fiducial, ...? - - The attribute syntax provides a flexible and standardized way to add meta-information to a - Gerber file, independent of the specific semantics or application. - - Attributes do not affect the image. A Gerber reader will generate the correct image if it simply - ignores the attributes. - - Each attribute consists of an attribute name and an optional attribute value: - - ```ebnf - = [,]* - ``` - - Attribute names follow the name syntax in section 3.4.3. - - The attribute value consists of one or more comma-separated fields, see section 3.4.4. - - ```ebnf - = {,} - ``` - - There are three types of attributes by the item they attach to: - - - `Attachment type` - The item to which they attach meta-information. - - `File attributes` - Attach meta-information to the file as a whole. - - `Aperture attributes` - Attach meta-information to an aperture or a region. Objects created by - the aperture inherit the aperture meta-information. - - `Object attributes` - Attach meta-information to on object directly. - - There are two types of attributes by the scope of their use: - - - `Standard attributes`. Standard attribute names, values and semantics are defined in this - specification and are part of it. As they are standardized, they can exchange meta information between all applications. - - - `User attributes`. User attributes can be chosen freely by users to extend the format with - custom meta-information. Use custom attributes only for unequivocally defined - machine-readable information, use G04 for mere human-readable comments. - - In accordance with the general rule in 3.4.3 standard attribute names must begin with a dot "." - while user attribute names cannot begin with a dot. The dot, if present, is part of the attribute - name and indicates that it is a standard attribute whose syntax and semantics are defined in - section 5.6. - - --- - - ## Example - - ```gerber - %TFMyAttribute,Yes*% - %TFZap*% - %TFZonk*% - ``` - - --- - - See section 5.1 of [The Gerber Layer Format Specification](https://www.ucamco.com/files/downloads/file_en/456/gerber-layer-format-specification-revision-2023-08_en.pdf#page=122) - - """ # noqa: E501 - - -class SetAttributeToken(AttributeToken): - """Base class for all classes which set some kind of attribute.""" - - def __init__( - self, - string: str, - location: int, - name: str, - value: Optional[str], - ) -> None: - super().__init__(string, location) - self.name = name - self.value = value - - @classmethod - def new(cls, string: str, location: int, tokens: ParseResults) -> Self: - """Create instance of this class. - - Created to be used as callback in `ParserElement.set_parse_action()`. - """ - name: str = str(tokens["attribute_name"]) - value = tokens.get("field", None) - if value is not None: - value = str(value) - - return cls( - string=string, - location=location, - name=name, - value=value, - ) diff --git a/src/pygerber/gerberx3/tokenizer/tokens/bases/__init__.py b/src/pygerber/gerberx3/tokenizer/tokens/bases/__init__.py deleted file mode 100644 index 25ad8fda..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/bases/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""All generic base classes for tokens.""" diff --git a/src/pygerber/gerberx3/tokenizer/tokens/bases/command.py b/src/pygerber/gerberx3/tokenizer/tokens/bases/command.py deleted file mode 100644 index 6e6a77ac..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/bases/command.py +++ /dev/null @@ -1,91 +0,0 @@ -"""Base class for all commands.""" - -from __future__ import annotations - -from pygerber.gerberx3.tokenizer.tokens.bases.token import Token - - -class CommandToken(Token): - """## 3.3 Commands. - - Commands are the core syntactic element of the Gerber format. A Gerber file is a stream of - commands. Commands define the graphics state, create graphical objects, defines apertures, - manage attributes and so on. - - Commands are built with words, the basic syntactic building block of a Gerber file. A word is a - non-empty character string, excluding the reserved characters '*' and '%', terminated with an '*' - - ```ebnf - free_character = /[^%*]/; # All characters but * and % - word = {free_character}+ '*'; - ``` - - For historic reasons, there are two command syntax styles: word commands and extended - commands. - - ```ebnf - command = - | extended_command - | word_command - ; - word_command = word; - extended_command = '%' {word}+ '%'; - ``` - - - Word commands are identified by a command code, the letter G, D or M followed by a positive - integer, e.g. `G02`. Most word commands only consist of the command code, some also contain - coordinates. - - Extended commands are identified by a two-character command code that is followed by - parameters specific to the code. An extended command is enclosed by a pair of "%" delimiters - - An overview of all commands is in section 2.8, a full description in chapters 3.5 and 5. - - - --- - - ## Example 1 - - The example below shows a stream of Gerber commands. - - ```gerber - G04 Different command styles* - G75* - G02* - D10* - X0Y0D02* - X2000000Y0I1000000J0D01* - D11* - X0Y2000000D03* - M02* - ``` - - - --- - - ## Example 2 - - The example below shows a stream of Gerber extended commands. - - ```gerber - %FSLAX26Y26*% - %MOMM*% - %AMDonut* - 1,1,$1,$2,$3* - $4=$1x0.75* - 1,0,$4,$2,$3* - % - %ADD11Donut,0.30X0X0*% - %ADD10C,0.1*% - ``` - - --- - - See section 3.3 of [The Gerber Layer Format Specification](https://www.ucamco.com/files/downloads/file_en/456/gerber-layer-format-specification-revision-2023-08_en.pdf#page=33) - - """ # noqa: E501 - - def get_gerber_code_one_line_pretty_display(self) -> str: - """Get gerber code represented by this token.""" - return f"{self.get_gerber_code()}*" diff --git a/src/pygerber/gerberx3/tokenizer/tokens/bases/extended_command.py b/src/pygerber/gerberx3/tokenizer/tokens/bases/extended_command.py deleted file mode 100644 index e0246402..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/bases/extended_command.py +++ /dev/null @@ -1,91 +0,0 @@ -"""Base class for all extended commands.""" - -from __future__ import annotations - -from pygerber.gerberx3.tokenizer.tokens.bases.token import Token - - -class ExtendedCommandToken(Token): - """## 3.3 Commands. - - Commands are the core syntactic element of the Gerber format. A Gerber file is a stream of - commands. Commands define the graphics state, create graphical objects, defines apertures, - manage attributes and so on. - - Commands are built with words, the basic syntactic building block of a Gerber file. A word is a - non-empty character string, excluding the reserved characters '*' and '%', terminated with an '*' - - ```ebnf - free_character = /[^%*]/; # All characters but * and % - word = {free_character}+ '*'; - ``` - - For historic reasons, there are two command syntax styles: word commands and extended - commands. - - ```ebnf - command = - | extended_command - | word_command - ; - word_command = word; - extended_command = '%' {word}+ '%'; - ``` - - - Word commands are identified by a command code, the letter G, D or M followed by a positive - integer, e.g. `G02`. Most word commands only consist of the command code, some also contain - coordinates. - - Extended commands are identified by a two-character command code that is followed by - parameters specific to the code. An extended command is enclosed by a pair of "%" delimiters - - An overview of all commands is in section 2.8, a full description in chapters 3.5 and 5. - - - --- - - ## Example 1 - - The example below shows a stream of Gerber commands. - - ```gerber - G04 Different command styles* - G75* - G02* - D10* - X0Y0D02* - X2000000Y0I1000000J0D01* - D11* - X0Y2000000D03* - M02* - ``` - - - --- - - ## Example 2 - - The example below shows a stream of Gerber extended commands. - - ```gerber - %FSLAX26Y26*% - %MOMM*% - %AMDonut* - 1,1,$1,$2,$3* - $4=$1x0.75* - 1,0,$4,$2,$3* - % - %ADD11Donut,0.30X0X0*% - %ADD10C,0.1*% - ``` - - --- - - See section 3.3 of [The Gerber Layer Format Specification](https://www.ucamco.com/files/downloads/file_en/456/gerber-layer-format-specification-revision-2023-08_en.pdf#page=33) - - """ # noqa: E501 - - def get_gerber_code_one_line_pretty_display(self) -> str: - """Get gerber code represented by this token.""" - return f"%{self.get_gerber_code()}*%" diff --git a/src/pygerber/gerberx3/tokenizer/tokens/bases/gerber_code.py b/src/pygerber/gerberx3/tokenizer/tokens/bases/gerber_code.py deleted file mode 100644 index b7907c7b..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/bases/gerber_code.py +++ /dev/null @@ -1,30 +0,0 @@ -"""Base class providing GerberCode interface.""" - -from __future__ import annotations - -from typing import Iterable - - -class GerberCode: - """Interface of object which can be converted to gerber code.""" - - def get_gerber_code( - self, - indent: str = "", - endline: str = "\n", # noqa: ARG002 - ) -> str: - """Get gerber code represented by this token.""" - return f"{indent}G04 {self.__class__.__qualname__} no formatting available*" - - def get_gerber_code_one_line_pretty_display(self) -> str: - """Get gerber code represented by this token.""" - return f"G04 {self.__class__.__qualname__} no formatting available*" - - -def get_gerber_code( - tokens: Iterable[GerberCode], - indent: str = "", - endline: str = "\n", -) -> str: - """Get gerber code from iterable of tokens.""" - return endline.join(t.get_gerber_code(indent, endline) for t in tokens) diff --git a/src/pygerber/gerberx3/tokenizer/tokens/bases/group.py b/src/pygerber/gerberx3/tokenizer/tokens/bases/group.py deleted file mode 100644 index 667e9b62..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/bases/group.py +++ /dev/null @@ -1,146 +0,0 @@ -"""Wrapper for flash operation token.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING, Iterable, Iterator, Optional, Sequence - -from pygerber.common.position import Position -from pygerber.gerberx3.tokenizer.tokens.bases.token import Token -from pygerber.gerberx3.tokenizer.tokens.bases.token_accessor import TokenAccessor - -if TYPE_CHECKING: - from pyparsing import ParseResults - from typing_extensions import Self - - -class TokenGroup(Token): - """Token consisting of multiple nested tokens.""" - - def __init__( - self, - string: str, - location: int, - tokens: Sequence[Token], - ) -> None: - super().__init__(string, location) - self.tokens = tokens - - @classmethod - def new(cls, string: str, location: int, tokens: ParseResults) -> Self: - """Create instance of this class. - - Created to be used as callback in `ParserElement.set_parse_action()`. - """ - - def _() -> Iterable[Token]: - for token in tokens.as_list(): - if isinstance(token, Token): - yield token - - return cls( - string=string, - location=location, - tokens=list(_()), - ) - - def get_gerber_code( - self, - indent: str = "", - endline: str = "\n", - ) -> str: - """Get gerber code represented by this token.""" - return endline.join(t.get_gerber_code(indent) for t in self.tokens) - - def __str__(self) -> str: - prefix = super().__str__() - tokens = ", ".join(str(t) for t in self.tokens) - return f"{prefix}[{tokens}]" - - def find_closest_token( - self, - pos: Position, - parent: Optional[TokenAccessor] = None, - ) -> TokenAccessor: - """Find token closest to specified position.""" - if parent is None: - parent = TokenAccessor(self) - - token_accessor = self._find_closest_token(pos, parent) - token = token_accessor.token - - if token and isinstance(token, TokenGroup): - token_accessor = token.find_closest_token(pos, token_accessor) - - return token_accessor - - def _find_closest_token( - self, - pos: Position, - parent: TokenAccessor, - ) -> TokenAccessor: - threshold = 10 - if len(self.tokens) > threshold: - return self._find_closest_token_binary(pos, parent) - - return self._find_closest_token_linear(pos, parent) - - def _find_closest_token_binary( - self, - pos: Position, - parent: TokenAccessor, - ) -> TokenAccessor: - left = 0 - right = len(self.tokens) - 1 - center = (left + right) // 2 - - while left <= right: - center = (left + right) // 2 - token = self.tokens[center] - token_pos = token.get_token_position() - - if token_pos < pos: - left = center + 1 - elif token_pos > pos: - right = center - 1 - else: - break - - while ((token := self.tokens[center]).get_token_position()) > pos: - center -= 1 - - return TokenAccessor( - self.tokens[center], - parent, - self.tokens, - center, - ) - - def _find_closest_token_linear( - self, - pos: Position, - parent: TokenAccessor, - ) -> TokenAccessor: - i = 0 - search_pos = pos - token = prev_token = self.tokens[i] - - for i, token in enumerate(self.tokens): - token_pos = token.get_token_position() - if token_pos > search_pos: - return TokenAccessor( - prev_token, - parent, - self.tokens, - i, - ) - - prev_token = token - - return TokenAccessor(prev_token, parent, self.tokens, i) - - def __iter__(self) -> Iterator[Token]: - for token in self.tokens: - yield from token - - def __len__(self) -> int: - return self.tokens.__len__() diff --git a/src/pygerber/gerberx3/tokenizer/tokens/bases/token.py b/src/pygerber/gerberx3/tokenizer/tokens/bases/token.py deleted file mode 100644 index 48ab67ca..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/bases/token.py +++ /dev/null @@ -1,107 +0,0 @@ -"""Base class for creating token classes.""" - -from __future__ import annotations - -from functools import cached_property -from typing import TYPE_CHECKING, Any, Iterable, Iterator - -from pyparsing import col, lineno - -from pygerber.common.position import Position -from pygerber.gerberx3.linter import diagnostic -from pygerber.gerberx3.tokenizer.tokens.bases.gerber_code import GerberCode - -if TYPE_CHECKING: - from pyparsing import ParserElement, ParseResults - from typing_extensions import Self - - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class Token(GerberCode): - """Base class for creating Gerber token classes.""" - - @classmethod - def wrap(cls, expr: ParserElement) -> ParserElement: - """Set parse result to be instance of this class.""" - return expr.set_parse_action(cls.new) - - @classmethod - def new(cls, string: str, location: int, _tokens: ParseResults) -> Self: - """Create instance of this class. - - Created to be used as callback in `ParserElement.set_parse_action()`. - """ - return cls(string, location) - - def __init__(self, string: str, location: int) -> None: - """Initialize token instance.""" - self.string = string - self.location = location - - def __str__(self) -> str: - return f"GerberCode::Token::{self.__class__.__qualname__}" - - def __repr__(self) -> str: - """Return pretty representation of comment token.""" - return self.__str__() - - def get_debug_format(self) -> str: - """Return debug formatted token object.""" - return super().__repr__() - - @classmethod - def ensure_type(cls, thing: Any) -> Self: - """Ensure that is a instance of this class. - - Raise TypeError otherwise. - """ - if not isinstance(thing, cls): - raise TypeError(thing) - - return thing - - def parser2_visit_token( - self, - context: Parser2Context, - ) -> None: - """Update drawing state for Gerber AST parser, version 2.""" - - def get_token_position(self) -> Position: - """Get position of token.""" - return self._token_position - - @cached_property - def _token_position(self) -> Position: - return Position( - lineno(self.location, self.string), - col(self.location, self.string), - ) - - def get_gerber_code_one_line_pretty_display(self) -> str: - """Get gerber code represented by this token.""" - return self.get_gerber_code() - - def get_token_diagnostics(self) -> Iterable[diagnostic.Diagnostic]: - """Get diagnostics for this token.""" - return - yield # type: ignore[unreachable] - - def get_token_end_position(self) -> Position: - """Get position of the end of the token.""" - s = str(self) - first, *_ = s.split("\n") - lines_offset = 0 - column_offset = len(first) - return self.get_token_position().offset(lines_offset, column_offset) - - def __iter__(self) -> Iterator[Token]: - yield self - - def __hash__(self) -> int: - return id(self) - - def __eq__(self, __value: object) -> bool: - if not isinstance(__value, Token): - return NotImplemented - return id(__value) == id(self) diff --git a/src/pygerber/gerberx3/tokenizer/tokens/bases/token_accessor.py b/src/pygerber/gerberx3/tokenizer/tokens/bases/token_accessor.py deleted file mode 100644 index 391ff355..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/bases/token_accessor.py +++ /dev/null @@ -1,24 +0,0 @@ -"""Accessor for token objects in AST.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING, Optional, Sequence - -if TYPE_CHECKING: - from pygerber.gerberx3.tokenizer.tokens.bases.token import Token - - -class TokenAccessor: - """Accessor containing reference to token and its parent group.""" - - def __init__( - self, - token: Optional[Token], - parent: Optional[TokenAccessor] = None, - tokens: Sequence[Token] = (), - token_index: int = 0, - ) -> None: - self.token = token - self.parent = parent - self.tokens = tokens - self.token_index = token_index diff --git a/src/pygerber/gerberx3/tokenizer/tokens/coordinate.py b/src/pygerber/gerberx3/tokenizer/tokens/coordinate.py deleted file mode 100644 index 022cfd74..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/coordinate.py +++ /dev/null @@ -1,102 +0,0 @@ -"""Coordinate wrapper class.""" - -from __future__ import annotations - -from enum import Enum -from typing import TYPE_CHECKING, Optional - -from pygerber.common.frozen_general_model import FrozenGeneralModel -from pygerber.gerberx3.tokenizer.tokens.bases.gerber_code import GerberCode - -if TYPE_CHECKING: - from typing_extensions import Self - - -class Coordinate(GerberCode, FrozenGeneralModel): - """## Coordinate data. - - A number whose interpretation is determined by the FS command. It is used to specify - the X and Y coordinates of a point in the image plane and a distance - or offset in the X and Y direction. - """ - - coordinate_type: CoordinateType - sign: CoordinateSign - offset: str - - @classmethod - def new(cls, coordinate_type: CoordinateType, offset: Optional[str]) -> Self: - """Create new Coordinate object.""" - if offset is None: - coordinate_type = coordinate_type.to_missing() - offset = "" - sign = CoordinateSign.Positive - - elif len(offset) > 0 and offset[0] in "+-": - sign = CoordinateSign(offset[0]) - offset = offset[1:].ljust(1, "0") - - else: - sign = CoordinateSign.Positive - - return cls(coordinate_type=coordinate_type, sign=sign, offset=offset) - - def get_gerber_code( - self, - indent: str = "", - endline: str = "\n", # noqa: ARG002 - ) -> str: - """Get gerber code represented by this token.""" - return ( - "" - if self.coordinate_type.is_missing() - else f"{indent}{self.coordinate_type}{self.sign}{self.offset}" - ) - - -class CoordinateType(Enum): - """Type of coordinate axis/meaning.""" - - X = "X" - Y = "Y" - I = "I" # noqa: E741 - J = "J" - NULL = "" - MISSING_X = "MISSING_X" - MISSING_Y = "MISSING_Y" - MISSING_I = "MISSING_I" - MISSING_J = "MISSING_J" - - def to_missing(self) -> CoordinateType: - """Map to MISSING_.""" - return _coordinate_type_to_missing_map[self] - - def is_missing(self) -> bool: - """Check if coordinate is one of variants of missing coordinates.""" - return self in ( - CoordinateType.MISSING_X, - CoordinateType.MISSING_Y, - CoordinateType.MISSING_I, - CoordinateType.MISSING_J, - ) - - def __str__(self) -> str: - return self.value - - -_coordinate_type_to_missing_map = { - CoordinateType.X: CoordinateType.MISSING_X, - CoordinateType.Y: CoordinateType.MISSING_Y, - CoordinateType.I: CoordinateType.MISSING_I, - CoordinateType.J: CoordinateType.MISSING_J, -} - - -class CoordinateSign(Enum): - """Coordinate sign.""" - - Positive = "+" - Negative = "-" - - def __str__(self) -> str: - return "-" if self == CoordinateSign.Negative else "" diff --git a/src/pygerber/gerberx3/tokenizer/tokens/d01_draw.py b/src/pygerber/gerberx3/tokenizer/tokens/d01_draw.py deleted file mode 100644 index 9e35ff31..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/d01_draw.py +++ /dev/null @@ -1,140 +0,0 @@ -"""Plot (D01) logic.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from pygerber.gerberx3.tokenizer.tokens.bases.command import CommandToken -from pygerber.gerberx3.tokenizer.tokens.coordinate import Coordinate, CoordinateType - -if TYPE_CHECKING: - from pyparsing import ParseResults - from typing_extensions import Self - - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class D01Draw(CommandToken): - """## 4.8.2 Plot (D01). - - Performs a plotting operation, creating a draw or an arc segment. The plot state defines which - type of segment is created, see 4.7. The syntax depends on the required parameters, and, - hence, on the plot state. - - D01 creates a linear or circular line segment by plotting from the current point to the - coordinate pair in the command. Outside a region statement (see 2.3.2) these segments - are converted to draw or arc objects by stroking them with the current aperture (see 2.3.1). - Within a region statement these segments form a contour defining a region (see 4.10). The - effect of D01, e.g. whether a straight or circular segment is created, depends on the - graphics state (see 2.3.2). - - ### Syntax - - For linear (G01): - - ```ebnf - D01 = (['X' x_coordinate] ['Y' y_coordinate] 'D01') '*'; - ``` - - For Circular (G02|G03) - - ```ebnf - D01 = (['X' x_coordinate] ['Y' y_coordinate] 'I' x_offset 'J' y-offset ) 'D01' '*'; - ``` - - - x_coordinate - `` is coordinate data - see section 0. It defines the X coordinate of the - new current point. The default is the X coordinate of the old current point. - - --- - - ## Example - - ```gerber - X275000Y115000D02* - G01* - X2512000Y115000D01* - G75* - G03* - X5005000Y3506000I3000J0D01* - G01* - X15752000D01* - Y12221000D01* - ``` - - --- - - See section 4.8.2 of [The Gerber Layer Format Specification](https://www.ucamco.com/files/downloads/file_en/456/gerber-layer-format-specification-revision-2023-08_en.pdf#page=83) - - """ # noqa: E501 - - def __init__( - self, - string: str, - location: int, - x: Coordinate, - y: Coordinate, - i: Coordinate, - j: Coordinate, - ) -> None: - super().__init__(string, location) - self.x = x - self.y = y - self.i = i - self.j = j - - @classmethod - def new(cls, string: str, location: int, tokens: ParseResults) -> Self: - """Create instance of this class. - - Created to be used as callback in `ParserElement.set_parse_action()`. - """ - x = tokens.get("x") - x = Coordinate.new( - coordinate_type=CoordinateType.X, - offset=str(x) if x is not None else None, - ) - y = tokens.get("y") - y = Coordinate.new( - coordinate_type=CoordinateType.Y, - offset=str(y) if y is not None else None, - ) - i = tokens.get("i") - i = Coordinate.new( - coordinate_type=CoordinateType.I, - offset=str(i) if i is not None else None, - ) - j = tokens.get("j") - j = Coordinate.new( - coordinate_type=CoordinateType.J, - offset=str(j) if j is not None else None, - ) - - return cls( - string=string, - location=location, - x=x, - y=y, - i=i, - j=j, - ) - - def parser2_visit_token(self, context: Parser2Context) -> None: - """Perform actions on the context implicated by this token.""" - context.get_hooks().command_draw.pre_parser_visit_token(self, context) - context.get_hooks().command_draw.on_parser_visit_token(self, context) - context.get_hooks().command_draw.post_parser_visit_token(self, context) - - def get_gerber_code( - self, - indent: str = "", - endline: str = "\n", - ) -> str: - """Get gerber code represented by this token.""" - return ( - f"{indent}" - f"{self.x.get_gerber_code(indent, endline)}" - f"{self.y.get_gerber_code(indent, endline)}" - f"{self.i.get_gerber_code(indent, endline)}" - f"{self.j.get_gerber_code(indent, endline)}" - "D01" - ) diff --git a/src/pygerber/gerberx3/tokenizer/tokens/d02_move.py b/src/pygerber/gerberx3/tokenizer/tokens/d02_move.py deleted file mode 100644 index a7f1478a..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/d02_move.py +++ /dev/null @@ -1,98 +0,0 @@ -"""Wrapper for move operation token.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from pygerber.gerberx3.tokenizer.tokens.bases.command import CommandToken -from pygerber.gerberx3.tokenizer.tokens.coordinate import Coordinate, CoordinateType - -if TYPE_CHECKING: - from pyparsing import ParseResults - from typing_extensions import Self - - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class D02Move(CommandToken): - """## 4.8.3 Move (D02). - - Moves the current point to the (X,Y) in the comment. The syntax is: - - ```ebnf - D02 = (['X' x_coordinate] ['Y' y_coordinate] 'D02') '*'; - ``` - - - x_coordinate - `` is coordinate data - see section 0. It defines the X - coordinate of the new current point. The default is the X coordinate of - the old current point. - - y_coordinate - As above, but for the Y coordinate. - - D02 - Move operation code - - --- - - ## Example - - ```gerber - X2152000Y1215000D02* - ``` - - --- - - See section 4.8.3 of [The Gerber Layer Format Specification](https://www.ucamco.com/files/downloads/file_en/456/gerber-layer-format-specification-revision-2023-08_en.pdf#page=83) - - """ - - def __init__( - self, - string: str, - location: int, - x: Coordinate, - y: Coordinate, - ) -> None: - super().__init__(string, location) - self.x = x - self.y = y - - @classmethod - def new(cls, string: str, location: int, tokens: ParseResults) -> Self: - """Create instance of this class. - - Created to be used as callback in `ParserElement.set_parse_action()`. - """ - x = tokens.get("x") - x = Coordinate.new( - coordinate_type=CoordinateType.X, - offset=str(x) if x is not None else None, - ) - y = tokens.get("y") - y = Coordinate.new( - coordinate_type=CoordinateType.Y, - offset=str(y) if y is not None else None, - ) - - return cls( - string=string, - location=location, - x=x, - y=y, - ) - - def parser2_visit_token(self, context: Parser2Context) -> None: - """Perform actions on the context implicated by this token.""" - context.get_hooks().command_move.pre_parser_visit_token(self, context) - context.get_hooks().command_move.on_parser_visit_token(self, context) - context.get_hooks().command_move.post_parser_visit_token(self, context) - - def get_gerber_code( - self, - indent: str = "", - endline: str = "\n", - ) -> str: - """Get gerber code represented by this token.""" - return ( - f"{indent}" - f"{self.x.get_gerber_code(indent, endline)}" - f"{self.y.get_gerber_code(indent, endline)}" - "D02" - ) diff --git a/src/pygerber/gerberx3/tokenizer/tokens/d03_flash.py b/src/pygerber/gerberx3/tokenizer/tokens/d03_flash.py deleted file mode 100644 index 8c7e74a9..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/d03_flash.py +++ /dev/null @@ -1,78 +0,0 @@ -"""Wrapper for flash operation token.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from pygerber.gerberx3.tokenizer.tokens.bases.command import CommandToken -from pygerber.gerberx3.tokenizer.tokens.coordinate import Coordinate, CoordinateType - -if TYPE_CHECKING: - from pyparsing import ParseResults - from typing_extensions import Self - - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class D03Flash(CommandToken): - """Wrapper for flash operation token. - - Creates a flash object with the current aperture. The current point is moved to the - flash point. - - See section 4.8.4 of The Gerber Layer Format Specification Revision 2023.03 - https://argmaster.github.io/pygerber/latest/gerber_specification/revision_2023_03.html - """ - - def __init__( - self, - string: str, - location: int, - x: Coordinate, - y: Coordinate, - ) -> None: - super().__init__(string, location) - self.x = x - self.y = y - - @classmethod - def new(cls, string: str, location: int, tokens: ParseResults) -> Self: - """Create instance of this class. - - Created to be used as callback in `ParserElement.set_parse_action()`. - """ - x = tokens.get("x") - x = Coordinate.new( - coordinate_type=CoordinateType.X, - offset=str(x) if x is not None else None, - ) - y = tokens.get("y") - y = Coordinate.new( - coordinate_type=CoordinateType.Y, - offset=str(y) if y is not None else None, - ) - - return cls( - string=string, - location=location, - x=x, - y=y, - ) - - def get_gerber_code( - self, - indent: str = "", - endline: str = "\n", - ) -> str: - """Get gerber code represented by this token.""" - return ( - f"{indent}" - f"{self.x.get_gerber_code(indent, endline)}" - f"{self.y.get_gerber_code(indent, endline)}" - "D03" - ) - - def parser2_visit_token(self, context: Parser2Context) -> None: - """Perform actions on the context implicated by this token.""" - context.get_hooks().command_flash.pre_parser_visit_token(self, context) - context.get_hooks().command_flash.on_parser_visit_token(self, context) - context.get_hooks().command_flash.post_parser_visit_token(self, context) diff --git a/src/pygerber/gerberx3/tokenizer/tokens/dnn_select_aperture.py b/src/pygerber/gerberx3/tokenizer/tokens/dnn_select_aperture.py deleted file mode 100644 index 25339dd2..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/dnn_select_aperture.py +++ /dev/null @@ -1,74 +0,0 @@ -"""Wrapper for aperture select token.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from pygerber.gerberx3.tokenizer.aperture_id import ApertureID -from pygerber.gerberx3.tokenizer.tokens.bases.command import CommandToken - -if TYPE_CHECKING: - from pyparsing import ParseResults - from typing_extensions import Self - - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class DNNSelectAperture(CommandToken): - """## 4.6 Current Aperture (Dnn). - - The command Dnn (nn≥10) sets the current aperture graphics state parameter. The syntax is: - - ```ebnf - Dnn = 'D unsigned_integer '*'; - ``` - - - `D` - Command code. - - `` - The aperture number (integer ≥10). An aperture with that number must be in the apertures dictionary. - - D-commands 0 to 9 are reserved and cannot be used for apertures. The D01 and D03 - commands use the current aperture to create track and flash graphical objects. - - --- - - ## Example - - ```gerber - D10* - ``` - - --- - - See section 4.6 of [The Gerber Layer Format Specification](https://www.ucamco.com/files/downloads/file_en/456/gerber-layer-format-specification-revision-2023-08_en.pdf#page=75) - - """ # noqa: E501 - - def __init__(self, string: str, location: int, aperture_id: ApertureID) -> None: - super().__init__(string, location) - self.aperture_id = aperture_id - - @classmethod - def new(cls, string: str, location: int, tokens: ParseResults) -> Self: - """Create instance of this class. - - Created to be used as callback in `ParserElement.set_parse_action()`. - """ - return cls( - string=string, - location=location, - aperture_id=ApertureID(tokens["aperture_identifier"]), - ) - - def parser2_visit_token(self, context: Parser2Context) -> None: - """Perform actions on the context implicated by this token.""" - context.get_hooks().select_aperture.pre_parser_visit_token(self, context) - context.get_hooks().select_aperture.on_parser_visit_token(self, context) - context.get_hooks().select_aperture.post_parser_visit_token(self, context) - - def get_gerber_code( - self, - indent: str = "", - endline: str = "\n", - ) -> str: - """Get gerber code represented by this token.""" - return f"{indent}{self.aperture_id.get_gerber_code(indent, endline)}" diff --git a/src/pygerber/gerberx3/tokenizer/tokens/end_of_expression.py b/src/pygerber/gerberx3/tokenizer/tokens/end_of_expression.py deleted file mode 100644 index d00fe733..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/end_of_expression.py +++ /dev/null @@ -1,57 +0,0 @@ -"""Wrapper for G74 token.""" - -from __future__ import annotations - -from pygerber.gerberx3.tokenizer.tokens.bases.token import Token - - -class EndOfExpression(Token): - """## 3.3 Commands (`'*' - end of command`). - - Commands are the core syntactic element of the Gerber format. A Gerber file is a stream of - commands. Commands define the graphics state, create graphical objects, defines apertures, - manage attributes and so on. - - Commands are built with words, the basic syntactic building block of a Gerber file. A word is a - non-empty character string, excluding the reserved characters '*' and '%', terminated with an '*' - - ```ebnf - word = {free_character}+ '*'; - ``` - - For historic reasons, there are two command syntax styles: word commands and extended - commands. - - (...) - - The example below shows a stream of Gerber commands. - - --- - - ## Example - - ```gerber - G04 Different command styles* - G75* - G02* - D10* - X0Y0D02* - X2000000Y0I1000000J0D01* - D11* - X0Y2000000D03* - M02* - ``` - - --- - - See section 3.3 of [The Gerber Layer Format Specification](https://www.ucamco.com/files/downloads/file_en/456/gerber-layer-format-specification-revision-2023-08_en.pdf#page=33) - - """ # noqa: E501 - - def get_gerber_code( - self, - indent: str = "", # noqa: ARG002 - endline: str = "\n", # noqa: ARG002 - ) -> str: - """Get gerber code represented by this token.""" - return "*" diff --git a/src/pygerber/gerberx3/tokenizer/tokens/fs_coordinate_format.py b/src/pygerber/gerberx3/tokenizer/tokens/fs_coordinate_format.py deleted file mode 100644 index 6c93e61c..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/fs_coordinate_format.py +++ /dev/null @@ -1,207 +0,0 @@ -"""Coordinate format token.""" - -from __future__ import annotations - -import logging -from decimal import Decimal -from typing import TYPE_CHECKING - -from pygerber.common.frozen_general_model import FrozenGeneralModel -from pygerber.gerberx3.parser2.errors2 import ( - IncrementalCoordinatesNotSupported2Error, - InvalidCoordinateLength2Error, - UnsupportedCoordinateType2Error, - ZeroOmissionNotSupported2Error, -) -from pygerber.gerberx3.tokenizer.helpers.gerber_code_enum import GerberCodeEnum -from pygerber.gerberx3.tokenizer.tokens.bases.extended_command import ( - ExtendedCommandToken, -) -from pygerber.gerberx3.tokenizer.tokens.bases.gerber_code import GerberCode -from pygerber.gerberx3.tokenizer.tokens.coordinate import ( - Coordinate, - CoordinateSign, - CoordinateType, -) - -if TYPE_CHECKING: - from pyparsing import ParseResults - from typing_extensions import Self - - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -RECOMMENDED_MINIMAL_DECIMAL_PLACES = 5 - - -class CoordinateFormat(ExtendedCommandToken): - """Description of coordinate format token. - - See: - - section 4.1.1 of The Gerber Layer Format Specification Revision 2023.03 - https://argmaster.github.io/pygerber/latest/gerber_specification/revision_2023_03.html - - section 4.2.2 of The Gerber Layer Format Specification Revision 2023.03 - https://argmaster.github.io/pygerber/latest/gerber_specification/revision_2023_03.html - """ - - def __init__( - self, - string: str, - location: int, - zeros_mode: TrailingZerosMode, - coordinate_mode: CoordinateMode, - x_format: AxisFormat, - y_format: AxisFormat, - ) -> None: - super().__init__(string, location) - self.zeros_mode = zeros_mode - self.coordinate_mode = coordinate_mode - self.x_format = x_format - self.y_format = y_format - - @classmethod - def new(cls, string: str, location: int, tokens: ParseResults) -> Self: - """Create instance of this class. - - Created to be used as callback in `ParserElement.set_parse_action()`. - """ - zeros_mode = TrailingZerosMode(tokens["zeros_mode"]) - coordinate_mode = CoordinateMode(tokens["coordinate_mode"]) - x_format = AxisFormat( - integer=int(str(tokens["x_format"][0])), - decimal=int(str(tokens["x_format"][1])), - ) - y_format = AxisFormat( - integer=int(str(tokens["y_format"][0])), - decimal=int(str(tokens["y_format"][1])), - ) - return cls( - string=string, - location=location, - zeros_mode=zeros_mode, - coordinate_mode=coordinate_mode, - x_format=x_format, - y_format=y_format, - ) - - def parser2_visit_token(self, context: Parser2Context) -> None: - """Perform actions on the context implicated by this token.""" - context.get_hooks().coordinate_format.pre_parser_visit_token(self, context) - context.get_hooks().coordinate_format.on_parser_visit_token(self, context) - context.get_hooks().coordinate_format.post_parser_visit_token(self, context) - - def get_gerber_code( - self, - indent: str = "", - endline: str = "\n", - ) -> str: - """Get gerber code represented by this token.""" - return ( - f"FS" - f"{self.zeros_mode.get_gerber_code(indent, endline)}" - f"{self.coordinate_mode.get_gerber_code(indent, endline)}" - f"X{self.x_format.get_gerber_code(indent, endline)}" - f"Y{self.y_format.get_gerber_code(indent, endline)}" - ) - - -class TrailingZerosMode(GerberCodeEnum): - """Coordinate format mode. - - GerberX3 supports only one, L, the other is required for backwards compatibility. - """ - - OmitLeading = "L" - OmitTrailing = "T" - - -class CoordinateMode(GerberCodeEnum): - """Coordinate format mode. - - GerberX3 supports only one, A, the other required for backwards compatibility. - """ - - Absolute = "A" - Incremental = "I" - - -class AxisFormat(FrozenGeneralModel, GerberCode): - """Wrapper for single axis format.""" - - integer: int - decimal: int - - @property - def total_length(self) -> int: - """Total format length.""" - return self.integer + self.decimal - - def __str__(self) -> str: - return f"{self.integer}{self.decimal}" - - def get_gerber_code( - self, - indent: str = "", - endline: str = "\n", # noqa: ARG002 - ) -> str: - """Get gerber code represented by this token.""" - return f"{indent}{self.integer}{self.decimal}" - - -class CoordinateParser(FrozenGeneralModel): - """Coordinate Parser class.""" - - x_format: AxisFormat - y_format: AxisFormat - - @classmethod - def new( - cls, - x_format: AxisFormat, - y_format: AxisFormat, - coordinate_mode: CoordinateMode = CoordinateMode.Absolute, - zeros_mode: TrailingZerosMode = TrailingZerosMode.OmitLeading, - ) -> Self: - """Update coordinate parser format configuration.""" - if coordinate_mode != CoordinateMode.Absolute: - raise IncrementalCoordinatesNotSupported2Error - - if zeros_mode != TrailingZerosMode.OmitLeading: - raise ZeroOmissionNotSupported2Error - - for axis, axis_format in (("X", x_format), ("Y", y_format)): - if axis_format.decimal < RECOMMENDED_MINIMAL_DECIMAL_PLACES: - logging.warning( - "It is recommended to use at least 5 decimal places for coordinate " - "data when using metric units and 6 decimal places for imperial " - "units. (Detected for %s)" - "(See 4.2.2 in Gerber Layer Format Specification)", - axis, - ) - - return cls(x_format=x_format, y_format=y_format) - - def parse(self, coordinate: Coordinate) -> Decimal: - """Parse raw coordinate data.""" - if coordinate.coordinate_type in (CoordinateType.X, CoordinateType.I): - return self._parse(self.x_format, coordinate.offset, coordinate.sign) - - if coordinate.coordinate_type in (CoordinateType.Y, CoordinateType.J): - return self._parse(self.y_format, coordinate.offset, coordinate.sign) - - raise UnsupportedCoordinateType2Error(coordinate.coordinate_type) - - def _parse( - self, - axis_format: AxisFormat, - offset: str, - sign: CoordinateSign, - ) -> Decimal: - total_length = axis_format.total_length - - if len(offset) > total_length: - msg = f"Got {offset!r} with length {len(offset)} expected {total_length}." - raise InvalidCoordinateLength2Error(msg) - - offset = offset.rjust(axis_format.total_length, "0") - integer, decimal = offset[: axis_format.integer], offset[axis_format.integer :] - - return Decimal(f"{sign.value}{integer}.{decimal}") diff --git a/src/pygerber/gerberx3/tokenizer/tokens/g01_set_linear.py b/src/pygerber/gerberx3/tokenizer/tokens/g01_set_linear.py deleted file mode 100644 index dd8ba281..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/g01_set_linear.py +++ /dev/null @@ -1,34 +0,0 @@ -"""Wrapper for G01 mode set token.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from pygerber.gerberx3.tokenizer.tokens.bases.command import CommandToken - -if TYPE_CHECKING: - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class SetLinear(CommandToken): - """Wrapper for G01 mode set token. - - Sets linear/circular mode to linear. - See: - - section 4.8 of The Gerber Layer Format Specification Revision 2020.09 - https://argmaster.github.io/pygerber/latest/gerber_specification/revision_2020_09.html - - section 4.7 of The Gerber Layer Format Specification Revision 2023.03 - https://argmaster.github.io/pygerber/latest/gerber_specification/revision_2023_03.html - """ - - def parser2_visit_token(self, context: Parser2Context) -> None: - """Perform actions on the context implicated by this token.""" - context.get_hooks().set_linear.pre_parser_visit_token(self, context) - context.get_hooks().set_linear.on_parser_visit_token(self, context) - context.get_hooks().set_linear.post_parser_visit_token(self, context) - - def get_gerber_code( - self, - indent: str = "", - endline: str = "\n", # noqa: ARG002 - ) -> str: - """Get gerber code represented by this token.""" - return f"{indent}G01" diff --git a/src/pygerber/gerberx3/tokenizer/tokens/g02_set_clockwise_circular.py b/src/pygerber/gerberx3/tokenizer/tokens/g02_set_clockwise_circular.py deleted file mode 100644 index 0ccd0cb6..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/g02_set_clockwise_circular.py +++ /dev/null @@ -1,38 +0,0 @@ -"""Wrapper for G01 mode set token.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from pygerber.gerberx3.tokenizer.tokens.bases.command import CommandToken - -if TYPE_CHECKING: - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class SetClockwiseCircular(CommandToken): - """Wrapper for G02 mode set token. - - Sets linear/circular mode to clockwise circular. - - See: - - section 4.8 of The Gerber Layer Format Specification Revision 2020.09 - https://argmaster.github.io/pygerber/latest/gerber_specification/revision_2020_09.html - - section 4.7 of The Gerber Layer Format Specification Revision 2023.03 - https://argmaster.github.io/pygerber/latest/gerber_specification/revision_2023_03.html - """ - - def parser2_visit_token(self, context: Parser2Context) -> None: - """Perform actions on the context implicated by this token.""" - context.get_hooks().set_clockwise_circular.pre_parser_visit_token(self, context) - context.get_hooks().set_clockwise_circular.on_parser_visit_token(self, context) - context.get_hooks().set_clockwise_circular.post_parser_visit_token( - self, - context, - ) - - def get_gerber_code( - self, - indent: str = "", - endline: str = "\n", # noqa: ARG002 - ) -> str: - """Get gerber code represented by this token.""" - return f"{indent}G02" diff --git a/src/pygerber/gerberx3/tokenizer/tokens/g03_set_counterclockwise_circular.py b/src/pygerber/gerberx3/tokenizer/tokens/g03_set_counterclockwise_circular.py deleted file mode 100644 index 2683690f..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/g03_set_counterclockwise_circular.py +++ /dev/null @@ -1,44 +0,0 @@ -"""Wrapper for G01 mode set token.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from pygerber.gerberx3.tokenizer.tokens.bases.command import CommandToken - -if TYPE_CHECKING: - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class SetCounterclockwiseCircular(CommandToken): - """Wrapper for G03 mode set token. - - Sets linear/circular mode to counterclockwise circular. - - See: - - section 4.8 of The Gerber Layer Format Specification Revision 2020.09 - https://argmaster.github.io/pygerber/latest/gerber_specification/revision_2020_09.html - - section 4.7 of The Gerber Layer Format Specification Revision 2023.03 - https://argmaster.github.io/pygerber/latest/gerber_specification/revision_2023_03.html - """ - - def parser2_visit_token(self, context: Parser2Context) -> None: - """Perform actions on the context implicated by this token.""" - context.get_hooks().set_counter_clockwise_circular.pre_parser_visit_token( - self, - context, - ) - context.get_hooks().set_counter_clockwise_circular.on_parser_visit_token( - self, - context, - ) - context.get_hooks().set_counter_clockwise_circular.post_parser_visit_token( - self, - context, - ) - - def get_gerber_code( - self, - indent: str = "", - endline: str = "\n", # noqa: ARG002 - ) -> str: - """Get gerber code represented by this token.""" - return f"{indent}G03" diff --git a/src/pygerber/gerberx3/tokenizer/tokens/g04_comment.py b/src/pygerber/gerberx3/tokenizer/tokens/g04_comment.py deleted file mode 100644 index bb57c692..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/g04_comment.py +++ /dev/null @@ -1,68 +0,0 @@ -"""Comment token.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from pygerber.gerberx3.tokenizer.tokens.bases.command import CommandToken - -if TYPE_CHECKING: - from pyparsing import ParseResults - from typing_extensions import Self - - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class Comment(CommandToken): - """## 4.1 Comment (G04). - - The G04 command is used for human readable comments. It does not affect the image. - The syntax for G04 is as follows. - - ```ebnf - G04 = ('G04' string) '*'; - ``` - - The string must follow the string syntax in [3.4.3](https://www.ucamco.com/files/downloads/file_en/456/gerber-layer-format-specification-revision-2023-08_en.pdf#page=35). - - --- - - ## Example - - ```gerber - G04 This is a comment* - G04 The space characters as well as "," and ";" are allowed here.* - ``` - - --- - - See section 4.1 of [The Gerber Layer Format Specification](https://www.ucamco.com/files/downloads/file_en/456/gerber-layer-format-specification-revision-2023-08_en.pdf#page=44) - - """ - - def __init__(self, string: str, location: int, content: str) -> None: - super().__init__(string, location) - self.content = content - - @classmethod - def new(cls, string: str, location: int, tokens: ParseResults) -> Self: - """Create instance of this class. - - Created to be used as callback in `ParserElement.set_parse_action()`. - """ - content: str = str(tokens["string"]) - return cls(string=string, location=location, content=content) - - def parser2_visit_token(self, context: Parser2Context) -> None: - """Perform actions on the context implicated by this token.""" - context.get_hooks().comment.pre_parser_visit_token(self, context) - context.get_hooks().comment.on_parser_visit_token(self, context) - context.get_hooks().comment.post_parser_visit_token(self, context) - - def get_gerber_code( - self, - indent: str = "", - endline: str = "\n", # noqa: ARG002 - ) -> str: - """Get gerber code represented by this token.""" - return f"{indent}G04 {self.content}" diff --git a/src/pygerber/gerberx3/tokenizer/tokens/g36_begin_region.py b/src/pygerber/gerberx3/tokenizer/tokens/g36_begin_region.py deleted file mode 100644 index c8df3b1b..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/g36_begin_region.py +++ /dev/null @@ -1,33 +0,0 @@ -"""Wrapper for aperture select token.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from pygerber.gerberx3.tokenizer.tokens.bases.command import CommandToken - -if TYPE_CHECKING: - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class BeginRegion(CommandToken): - """Wrapper for G36 token. - - Starts a region statement which creates a region by defining its contours. - - See section 4.10 of The Gerber Layer Format Specification Revision 2023.03 - https://argmaster.github.io/pygerber/latest/gerber_specification/revision_2023_03.html - """ - - def parser2_visit_token(self, context: Parser2Context) -> None: - """Perform actions on the context implicated by this token.""" - context.get_hooks().begin_region.pre_parser_visit_token(self, context) - context.get_hooks().begin_region.on_parser_visit_token(self, context) - context.get_hooks().begin_region.post_parser_visit_token(self, context) - - def get_gerber_code( - self, - indent: str = "", - endline: str = "\n", # noqa: ARG002 - ) -> str: - """Get gerber code represented by this token.""" - return f"{indent}G36" diff --git a/src/pygerber/gerberx3/tokenizer/tokens/g37_end_region.py b/src/pygerber/gerberx3/tokenizer/tokens/g37_end_region.py deleted file mode 100644 index d9c31517..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/g37_end_region.py +++ /dev/null @@ -1,33 +0,0 @@ -"""Wrapper for aperture select token.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from pygerber.gerberx3.tokenizer.tokens.bases.command import CommandToken - -if TYPE_CHECKING: - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class EndRegion(CommandToken): - """Wrapper for G37 token. - - Ends the region statement. - - See section 4.10 of The Gerber Layer Format Specification Revision 2023.03 - https://argmaster.github.io/pygerber/latest/gerber_specification/revision_2023_03.html - """ - - def parser2_visit_token(self, context: Parser2Context) -> None: - """Perform actions on the context implicated by this token.""" - context.get_hooks().end_region.pre_parser_visit_token(self, context) - context.get_hooks().end_region.on_parser_visit_token(self, context) - context.get_hooks().end_region.post_parser_visit_token(self, context) - - def get_gerber_code( - self, - indent: str = "", - endline: str = "\n", # noqa: ARG002 - ) -> str: - """Get gerber code represented by this token.""" - return f"{indent}G37" diff --git a/src/pygerber/gerberx3/tokenizer/tokens/g54_select_aperture.py b/src/pygerber/gerberx3/tokenizer/tokens/g54_select_aperture.py deleted file mode 100644 index ee4ae290..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/g54_select_aperture.py +++ /dev/null @@ -1,42 +0,0 @@ -"""Wrapper for G70 token.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from pygerber.gerberx3.tokenizer.tokens.dnn_select_aperture import DNNSelectAperture - -if TYPE_CHECKING: - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class G54SelectAperture(DNNSelectAperture): - """Wrapper for G54DNN token. - - Select aperture. - - This historic code optionally precedes an aperture selection Dnn command. It has no - effect. Sometimes used. Deprecated in 2012. - - See section 8.1.1 of The Gerber Layer Format Specification Revision 2023.03 - https://argmaster.github.io/pygerber/latest/gerber_specification/revision_2023_03.html - """ - - def parser2_visit_token(self, context: Parser2Context) -> None: - """Perform actions on the context implicated by this token.""" - context.get_hooks().prepare_select_aperture.pre_parser_visit_token( - self, - context, - ) - context.get_hooks().prepare_select_aperture.on_parser_visit_token(self, context) - context.get_hooks().prepare_select_aperture.post_parser_visit_token( - self, - context, - ) - - def get_gerber_code( - self, - indent: str = "", - endline: str = "\n", # noqa: ARG002 - ) -> str: - """Get gerber code represented by this token.""" - return f"{indent}G54{self.aperture_id}" diff --git a/src/pygerber/gerberx3/tokenizer/tokens/g70_set_unit_inch.py b/src/pygerber/gerberx3/tokenizer/tokens/g70_set_unit_inch.py deleted file mode 100644 index 0ea71823..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/g70_set_unit_inch.py +++ /dev/null @@ -1,36 +0,0 @@ -"""Wrapper for G70 token.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from pygerber.gerberx3.tokenizer.tokens.bases.command import CommandToken - -if TYPE_CHECKING: - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class SetUnitInch(CommandToken): - """Wrapper for G70 token. - - Set the `Unit` to inch. - - This historic codes perform a function handled by the MO command. See 4.2.1. - Sometimes used. Deprecated in 2012 - - See section 8.1 of The Gerber Layer Format Specification Revision 2023.03 - https://argmaster.github.io/pygerber/latest/gerber_specification/revision_2023_03.html - """ - - def parser2_visit_token(self, context: Parser2Context) -> None: - """Perform actions on the context implicated by this token.""" - context.get_hooks().set_unit_inch.pre_parser_visit_token(self, context) - context.get_hooks().set_unit_inch.on_parser_visit_token(self, context) - context.get_hooks().set_unit_inch.post_parser_visit_token(self, context) - - def get_gerber_code( - self, - indent: str = "", - endline: str = "\n", # noqa: ARG002 - ) -> str: - """Get gerber code represented by this token.""" - return f"{indent}G70" diff --git a/src/pygerber/gerberx3/tokenizer/tokens/g71_set_unit_mm.py b/src/pygerber/gerberx3/tokenizer/tokens/g71_set_unit_mm.py deleted file mode 100644 index 0ccc7921..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/g71_set_unit_mm.py +++ /dev/null @@ -1,36 +0,0 @@ -"""Wrapper for G71 token.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from pygerber.gerberx3.tokenizer.tokens.bases.command import CommandToken - -if TYPE_CHECKING: - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class SetUnitMillimeters(CommandToken): - """Wrapper for G71 token. - - Set the `Unit` to millimeter. - - This historic codes perform a function handled by the MO command. - Sometimes used. Deprecated in 2012 - - See section 4.2.1 of The Gerber Layer Format Specification Revision 2023.03 - https://argmaster.github.io/pygerber/latest/gerber_specification/revision_2023_03.html - """ - - def parser2_visit_token(self, context: Parser2Context) -> None: - """Perform actions on the context implicated by this token.""" - context.get_hooks().set_unit_millimeters.pre_parser_visit_token(self, context) - context.get_hooks().set_unit_millimeters.on_parser_visit_token(self, context) - context.get_hooks().set_unit_millimeters.post_parser_visit_token(self, context) - - def get_gerber_code( - self, - indent: str = "", - endline: str = "\n", # noqa: ARG002 - ) -> str: - """Get gerber code represented by this token.""" - return f"{indent}G71" diff --git a/src/pygerber/gerberx3/tokenizer/tokens/g74_single_quadrant.py b/src/pygerber/gerberx3/tokenizer/tokens/g74_single_quadrant.py deleted file mode 100644 index 72c7b14a..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/g74_single_quadrant.py +++ /dev/null @@ -1,47 +0,0 @@ -"""Wrapper for G74 token.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from pygerber.gerberx3.tokenizer.tokens.bases.command import CommandToken - -if TYPE_CHECKING: - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class SetSingleQuadrantMode(CommandToken): - """Wrapper for G74 token. - - Sets single quadrant mode - Rarely used, and then typically without effect. - Deprecated in 2020. - - In single quadrant mode the arc is not allowed to extend over more than 90°. - - See: - - section 4.7 of The Gerber Layer Format Specification Revision 2023.03 - https://argmaster.github.io/pygerber/latest/gerber_specification/revision_2023_03.html - - section 8.1.10 of The Gerber Layer Format Specification Revision 2023.03 - https://argmaster.github.io/pygerber/latest/gerber_specification/revision_2023_03.html - """ - - def parser2_visit_token(self, context: Parser2Context) -> None: - """Perform actions on the context implicated by this token.""" - context.get_hooks().set_single_quadrant_mode.pre_parser_visit_token( - self, - context, - ) - context.get_hooks().set_single_quadrant_mode.on_parser_visit_token( - self, - context, - ) - context.get_hooks().set_single_quadrant_mode.post_parser_visit_token( - self, - context, - ) - - def get_gerber_code( - self, - indent: str = "", - endline: str = "\n", # noqa: ARG002 - ) -> str: - """Get gerber code represented by this token.""" - return f"{indent}G74" diff --git a/src/pygerber/gerberx3/tokenizer/tokens/g75_multi_quadrant.py b/src/pygerber/gerberx3/tokenizer/tokens/g75_multi_quadrant.py deleted file mode 100644 index bd6737bc..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/g75_multi_quadrant.py +++ /dev/null @@ -1,54 +0,0 @@ -"""Wrapper for G74 token.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from pygerber.gerberx3.tokenizer.tokens.bases.command import CommandToken - -if TYPE_CHECKING: - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class SetMultiQuadrantMode(CommandToken): - """Wrapper for G75 token. - - In multi quadrant mode the arc is allowed to extend over more than 90°. - To avoid ambiguity between 0° and 360° arcs the following relation must hold: - - 0° < A ≤360°, where A is the arc angle - - If the start point of the arc is equal to the - end point, the arc is a full circle of 360°. - - 0° ≤A ≤90°, where A is the arc angle - - angleIf the start point of the arc is equal to the end point, the arc has length - zero, i.e. it covers 0°. A separate operation is required for each quadrant. A - minimum of four operations is required for a full circle. - - See: - - section 4.8 of The Gerber Layer Format Specification Revision 2020.09 - https://argmaster.github.io/pygerber/latest/gerber_specification/revision_2020_09.html - - section 4.7 of The Gerber Layer Format Specification Revision 2023.03 - https://argmaster.github.io/pygerber/latest/gerber_specification/revision_2023_03.html - - section 8.1.10 of The Gerber Layer Format Specification Revision 2023.03 - https://argmaster.github.io/pygerber/latest/gerber_specification/revision_2023_03.html - """ - - def parser2_visit_token(self, context: Parser2Context) -> None: - """Perform actions on the context implicated by this token.""" - context.get_hooks().set_multi_quadrant_mode.pre_parser_visit_token( - self, - context, - ) - context.get_hooks().set_multi_quadrant_mode.on_parser_visit_token(self, context) - context.get_hooks().set_multi_quadrant_mode.post_parser_visit_token( - self, - context, - ) - - def get_gerber_code( - self, - indent: str = "", - endline: str = "\n", # noqa: ARG002 - ) -> str: - """Get gerber code represented by this token.""" - return f"{indent}G75" diff --git a/src/pygerber/gerberx3/tokenizer/tokens/g90_set_coordinate_absolute.py b/src/pygerber/gerberx3/tokenizer/tokens/g90_set_coordinate_absolute.py deleted file mode 100644 index ff6aa9dd..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/g90_set_coordinate_absolute.py +++ /dev/null @@ -1,42 +0,0 @@ -"""Wrapper for G90 token.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from pygerber.gerberx3.tokenizer.tokens.bases.command import CommandToken - -if TYPE_CHECKING: - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class SetAbsoluteNotation(CommandToken): - """Wrapper for G90 token. - - Set the `Coordinate format` to `Absolute notation`. - - This historic code performs a function handled by the FS command. See 4.1. Very - rarely used nowadays. Deprecated in 2012. - - See section 8.1 of The Gerber Layer Format Specification Revision 2023.03 - https://argmaster.github.io/pygerber/latest/gerber_specification/revision_2023_03.html - """ - - def parser2_visit_token(self, context: Parser2Context) -> None: - """Perform actions on the context implicated by this token.""" - context.get_hooks().set_coordinate_absolute.pre_parser_visit_token( - self, - context, - ) - context.get_hooks().set_coordinate_absolute.on_parser_visit_token(self, context) - context.get_hooks().set_coordinate_absolute.post_parser_visit_token( - self, - context, - ) - - def get_gerber_code( - self, - indent: str = "", - endline: str = "\n", # noqa: ARG002 - ) -> str: - """Get gerber code represented by this token.""" - return f"{indent}G90" diff --git a/src/pygerber/gerberx3/tokenizer/tokens/g91_set_coordinate_incremental.py b/src/pygerber/gerberx3/tokenizer/tokens/g91_set_coordinate_incremental.py deleted file mode 100644 index 97a2596a..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/g91_set_coordinate_incremental.py +++ /dev/null @@ -1,45 +0,0 @@ -"""Wrapper for G91 token.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from pygerber.gerberx3.tokenizer.tokens.bases.command import CommandToken - -if TYPE_CHECKING: - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class SetIncrementalNotation(CommandToken): - """Wrapper for G91 token. - - Set the `Coordinate format` to `Incremental notation`. - - This historic code performs a function handled by the FS command. See 4.1. Very - rarely used nowadays. Deprecated in 2012. - - See section 8.1 of The Gerber Layer Format Specification Revision 2023.03 - https://argmaster.github.io/pygerber/latest/gerber_specification/revision_2023_03.html - """ - - def parser2_visit_token(self, context: Parser2Context) -> None: - """Perform actions on the context implicated by this token.""" - context.get_hooks().set_coordinate_incremental.pre_parser_visit_token( - self, - context, - ) - context.get_hooks().set_coordinate_incremental.on_parser_visit_token( - self, - context, - ) - context.get_hooks().set_coordinate_incremental.post_parser_visit_token( - self, - context, - ) - - def get_gerber_code( - self, - indent: str = "", - endline: str = "\n", # noqa: ARG002 - ) -> str: - """Get gerber code represented by this token.""" - return f"{indent}G91" diff --git a/src/pygerber/gerberx3/tokenizer/tokens/groups/__init__.py b/src/pygerber/gerberx3/tokenizer/tokens/groups/__init__.py deleted file mode 100644 index b8f53ec3..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/groups/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Group tokens.""" diff --git a/src/pygerber/gerberx3/tokenizer/tokens/groups/ast.py b/src/pygerber/gerberx3/tokenizer/tokens/groups/ast.py deleted file mode 100644 index 070856e9..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/groups/ast.py +++ /dev/null @@ -1,9 +0,0 @@ -"""Wrapper for G74 token.""" - -from __future__ import annotations - -from pygerber.gerberx3.tokenizer.tokens.bases.group import TokenGroup - - -class AST(TokenGroup): - """Gerber format abstract syntax tree.""" diff --git a/src/pygerber/gerberx3/tokenizer/tokens/groups/statement.py b/src/pygerber/gerberx3/tokenizer/tokens/groups/statement.py deleted file mode 100644 index ecfae805..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/groups/statement.py +++ /dev/null @@ -1,72 +0,0 @@ -"""Wrapper for G74 token.""" - -from __future__ import annotations - -from pygerber.gerberx3.tokenizer.tokens.bases.group import TokenGroup - - -class Statement(TokenGroup): - """## 3.3 Commands (`'%%' - extended command`). - - Commands are the core syntactic element of the Gerber format. A Gerber file is a stream of - commands. Commands define the graphics state, create graphical objects, defines apertures, - manage attributes and so on. - - Commands are built with words, the basic syntactic building block of a Gerber file. A word is a - non-empty character string, excluding the reserved characters '*' and '%', terminated with an '*' - - ```ebnf - free_character = /[^%*]/; # All characters but * and % - word = {free_character}+ '*'; - ``` - - For historic reasons, there are two command syntax styles: word commands and extended - commands. - - ```ebnf - command = - | extended_command - | word_command - ; - word_command = word; - extended_command = '%' {word}+ '%'; - ``` - - (...) - - Extended commands are identified by a two-character command code that is followed by - parameters specific to the code. An extended command is enclosed by a pair of "%" delimiters. - - An overview of all commands is in section 2.8, a full description in chapters 3.5 and 5. - - The example below shows a stream of Gerber extended commands. - - --- - - ## Example - - ```gerber - %FSLAX26Y26*% - %MOMM*% - %AMDonut* - 1,1,$1,$2,$3* - $4=$1x0.75* - 1,0,$4,$2,$3* - % - %ADD11Donut,0.30X0X0*% - %ADD10C,0.1*% - ``` - - --- - - See section 3.3 of [The Gerber Layer Format Specification](https://www.ucamco.com/files/downloads/file_en/456/gerber-layer-format-specification-revision-2023-08_en.pdf#page=33) - - """ # noqa: E501 - - def get_gerber_code( - self, - indent: str = "", - endline: str = "\n", # noqa: ARG002 - ) -> str: - """Get gerber code represented by this token.""" - return "%" + "".join(t.get_gerber_code(indent) for t in self.tokens) + "%" diff --git a/src/pygerber/gerberx3/tokenizer/tokens/in_image_name.py b/src/pygerber/gerberx3/tokenizer/tokens/in_image_name.py deleted file mode 100644 index 366fd9c3..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/in_image_name.py +++ /dev/null @@ -1,73 +0,0 @@ -"""### Load Name (LN). - -Note: The LN command was deprecated in revision I4 from October 2013. - -The historic `LN` command doesn't influence the image in any manner and can safely be -overlooked. - -Function of the `LN` command: -- `LN` is designed to allocate a name to the following section of the file. -- It was originally conceptualized to serve as a human-readable comment. -- For creating human-readable comments, it's advisable to utilize the standard `G04` - command. -- The `LN` command has the flexibility to be executed multiple times within a file. - -SPEC: `2023.03` SECTION: `8.1.6` -""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from pygerber.gerberx3.tokenizer.tokens.bases.extended_command import ( - ExtendedCommandToken, -) - -if TYPE_CHECKING: - from pyparsing import ParseResults - from typing_extensions import Self - - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class ImageName(ExtendedCommandToken): - """### Image Name (IN). - - The IN command is deprecated since revision I4 from October 2013. - - The historic IN command gives a name to the image contained in the Gerber file. - The name must comply with the syntax rules for a string as described in section - 3.4.3. This command can only be used once, at the beginning of the file. - - IN has no effect on the image. A reader can ignore this command. The informal - information provide by IN can also be put a G04 comment. - - See section 8.1.3 of The Gerber Layer Format Specification Revision 2023.03 - https://argmaster.github.io/pygerber/latest/gerber_specification/revision_2023_03.html - """ - - def __init__(self, string: str, location: int, content: str) -> None: - super().__init__(string, location) - self.content = content - - @classmethod - def new(cls, string: str, location: int, tokens: ParseResults) -> Self: - """Create instance of this class. - - Created to be used as callback in `ParserElement.set_parse_action()`. - """ - content: str = str(tokens["string"]) - return cls(string=string, location=location, content=content) - - def parser2_visit_token(self, context: Parser2Context) -> None: - """Perform actions on the context implicated by this token.""" - context.get_hooks().image_name.pre_parser_visit_token(self, context) - context.get_hooks().image_name.on_parser_visit_token(self, context) - context.get_hooks().image_name.post_parser_visit_token(self, context) - - def get_gerber_code( - self, - indent: str = "", # noqa: ARG002 - endline: str = "\n", # noqa: ARG002 - ) -> str: - """Get gerber code represented by this token.""" - return f"IN{self.content}" diff --git a/src/pygerber/gerberx3/tokenizer/tokens/invalid_token.py b/src/pygerber/gerberx3/tokenizer/tokens/invalid_token.py deleted file mode 100644 index a4bb87a0..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/invalid_token.py +++ /dev/null @@ -1,61 +0,0 @@ -"""Wrapper for G74 token.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING, Iterable - -from pygerber.gerberx3.linter import diagnostic -from pygerber.gerberx3.tokenizer.tokens.bases.token import Token - -if TYPE_CHECKING: - from pyparsing import ParseResults - from typing_extensions import Self - - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class InvalidToken(Token): - """Invalid syntax. - - This is not a valid Gerber X3/X2 expression. - """ - - def __init__(self, string: str, location: int, content: str) -> None: - super().__init__(string, location) - self.content = content - - @classmethod - def new(cls, string: str, location: int, tokens: ParseResults) -> Self: - """Create instance of this class. - - Created to be used as callback in `ParserElement.set_parse_action()`. - """ - content: str = str(tokens["content"]) - return cls(string=string, location=location, content=content) - - def get_token_diagnostics(self) -> Iterable[diagnostic.Diagnostic]: - """Get diagnostics for this token.""" - yield diagnostic.Diagnostic( - range=( - diagnostic.Range( - start=self.get_token_position(), - end=self.get_token_end_position(), - ) - ), - message="Invalid syntax.", - severity=diagnostic.DiagnosticSeverity.Error, - ) - - def parser2_visit_token(self, context: Parser2Context) -> None: - """Perform actions on the context implicated by this token.""" - context.get_hooks().invalid_token.pre_parser_visit_token(self, context) - context.get_hooks().invalid_token.on_parser_visit_token(self, context) - context.get_hooks().invalid_token.post_parser_visit_token(self, context) - - def get_gerber_code( - self, - indent: str = "", # noqa: ARG002 - endline: str = "\n", # noqa: ARG002 - ) -> str: - """Get gerber code represented by this token.""" - return self.content diff --git a/src/pygerber/gerberx3/tokenizer/tokens/ip_image_polarity.py b/src/pygerber/gerberx3/tokenizer/tokens/ip_image_polarity.py deleted file mode 100644 index 2a09f79d..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/ip_image_polarity.py +++ /dev/null @@ -1,64 +0,0 @@ -"""Wrapper for image polarity token.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from pygerber.gerberx3.state_enums import ImagePolarityEnum -from pygerber.gerberx3.tokenizer.tokens.bases.extended_command import ( - ExtendedCommandToken, -) - -if TYPE_CHECKING: - from pyparsing import ParseResults - from typing_extensions import Self - - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class ImagePolarity(ExtendedCommandToken): - """Wrapper for image polarity token. - - The IP command is deprecated. - - IP sets positive or negative polarity for the entire image. It can only be used - once, at the beginning of the file. - - See section 8.1.4 of The Gerber Layer Format Specification Revision 2023.03 - https://argmaster.github.io/pygerber/latest/gerber_specification/revision_2023_03.html - """ - - def __init__( - self, - string: str, - location: int, - image_polarity: ImagePolarityEnum, - ) -> None: - super().__init__(string, location) - self.image_polarity = image_polarity - - @classmethod - def new(cls, string: str, location: int, tokens: ParseResults) -> Self: - """Create instance of this class. - - Created to be used as callback in `ParserElement.set_parse_action()`. - """ - image_polarity = ImagePolarityEnum(tokens["image_polarity"]) - return cls( - string=string, - location=location, - image_polarity=image_polarity, - ) - - def parser2_visit_token(self, context: Parser2Context) -> None: - """Perform actions on the context implicated by this token.""" - context.get_hooks().image_polarity.pre_parser_visit_token(self, context) - context.get_hooks().image_polarity.on_parser_visit_token(self, context) - context.get_hooks().image_polarity.post_parser_visit_token(self, context) - - def get_gerber_code( - self, - indent: str = "", - endline: str = "\n", - ) -> str: - """Get gerber code represented by this token.""" - return f"IP{self.image_polarity.get_gerber_code(indent, endline)}" diff --git a/src/pygerber/gerberx3/tokenizer/tokens/lm_load_mirroring.py b/src/pygerber/gerberx3/tokenizer/tokens/lm_load_mirroring.py deleted file mode 100644 index acf88b01..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/lm_load_mirroring.py +++ /dev/null @@ -1,57 +0,0 @@ -"""Wrapper for load mirroring token.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from pygerber.gerberx3.state_enums import Mirroring -from pygerber.gerberx3.tokenizer.tokens.bases.extended_command import ( - ExtendedCommandToken, -) - -if TYPE_CHECKING: - from pyparsing import ParseResults - from typing_extensions import Self - - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class LoadMirroring(ExtendedCommandToken): - """Wrapper for load mirroring token. - - Loads the mirror object transformation parameter. - - See section 4.9.3 of The Gerber Layer Format Specification Revision 2023.03 - https://argmaster.github.io/pygerber/latest/gerber_specification/revision_2023_03.html - """ - - def __init__( - self, - string: str, - location: int, - mirroring: Mirroring, - ) -> None: - super().__init__(string, location) - self.mirroring = mirroring - - @classmethod - def new(cls, string: str, location: int, tokens: ParseResults) -> Self: - """Create instance of this class. - - Created to be used as callback in `ParserElement.set_parse_action()`. - """ - mirroring = Mirroring(tokens["mirroring"]) - return cls(string=string, location=location, mirroring=mirroring) - - def parser2_visit_token(self, context: Parser2Context) -> None: - """Perform actions on the context implicated by this token.""" - context.get_hooks().load_mirroring.pre_parser_visit_token(self, context) - context.get_hooks().load_mirroring.on_parser_visit_token(self, context) - context.get_hooks().load_mirroring.post_parser_visit_token(self, context) - - def get_gerber_code( - self, - indent: str = "", # noqa: ARG002 - endline: str = "\n", # noqa: ARG002 - ) -> str: - """Get gerber code represented by this token.""" - return f"LM{self.mirroring.value}" diff --git a/src/pygerber/gerberx3/tokenizer/tokens/ln_load_name.py b/src/pygerber/gerberx3/tokenizer/tokens/ln_load_name.py deleted file mode 100644 index 5a1fe15a..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/ln_load_name.py +++ /dev/null @@ -1,78 +0,0 @@ -"""### Load Name (LN). - -Note: The LN command was deprecated in revision I4 from October 2013. - -The historic `LN` command doesn't influence the image in any manner and can safely be -overlooked. - -Function of the `LN` command: -- `LN` is designed to allocate a name to the following section of the file. -- It was originally conceptualized to serve as a human-readable comment. -- For creating human-readable comments, it's advisable to utilize the standard `G04` - command. -- The `LN` command has the flexibility to be executed multiple times within a file. - -SPEC: `2023.03` SECTION: `8.1.6` -""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from pygerber.gerberx3.tokenizer.tokens.bases.extended_command import ( - ExtendedCommandToken, -) - -if TYPE_CHECKING: - from pyparsing import ParseResults - from typing_extensions import Self - - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class LoadName(ExtendedCommandToken): - """Comment token. - - ### Load Name (LN) - - Note: The LN command was deprecated in revision I4 from October 2013. - - The historic `LN` command doesn't influence the image in any manner and can safely - be overlooked. - - Function of the `LN` command: - - `LN` is designed to allocate a name to the following section of the file. - - It was originally conceptualized to serve as a human-readable comment. - - For creating human-readable comments, it's advisable to utilize the standard `G04` - command. - - The `LN` command has the flexibility to be executed multiple times within a file. - - See section 8.1.6 of The Gerber Layer Format Specification Revision 2023.03 - https://argmaster.github.io/pygerber/latest/gerber_specification/revision_2023_03.html - """ - - def __init__(self, string: str, location: int, content: str) -> None: - super().__init__(string, location) - self.content = content - - @classmethod - def new(cls, string: str, location: int, tokens: ParseResults) -> Self: - """Create instance of this class. - - Created to be used as callback in `ParserElement.set_parse_action()`. - """ - content: str = str(tokens["string"]) - return cls(string=string, location=location, content=content) - - def parser2_visit_token(self, context: Parser2Context) -> None: - """Perform actions on the context implicated by this token.""" - context.get_hooks().load_name.pre_parser_visit_token(self, context) - context.get_hooks().load_name.on_parser_visit_token(self, context) - context.get_hooks().load_name.post_parser_visit_token(self, context) - - def get_gerber_code( - self, - indent: str = "", # noqa: ARG002 - endline: str = "\n", # noqa: ARG002 - ) -> str: - """Get gerber code represented by this token.""" - return f"LN{self.content}" diff --git a/src/pygerber/gerberx3/tokenizer/tokens/lp_load_polarity.py b/src/pygerber/gerberx3/tokenizer/tokens/lp_load_polarity.py deleted file mode 100644 index 1723873b..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/lp_load_polarity.py +++ /dev/null @@ -1,57 +0,0 @@ -"""Wrapper for load polarity token.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from pygerber.gerberx3.state_enums import Polarity -from pygerber.gerberx3.tokenizer.tokens.bases.extended_command import ( - ExtendedCommandToken, -) - -if TYPE_CHECKING: - from pyparsing import ParseResults - from typing_extensions import Self - - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class LoadPolarity(ExtendedCommandToken): - """Wrapper for load polarity token. - - Loads the scale object transformation parameter. - - See section 4.9.2 of The Gerber Layer Format Specification Revision 2023.03 - https://argmaster.github.io/pygerber/latest/gerber_specification/revision_2023_03.html - """ - - def __init__( - self, - string: str, - location: int, - polarity: Polarity, - ) -> None: - super().__init__(string, location) - self.polarity = polarity - - @classmethod - def new(cls, string: str, location: int, tokens: ParseResults) -> Self: - """Create instance of this class. - - Created to be used as callback in `ParserElement.set_parse_action()`. - """ - polarity = Polarity(tokens["polarity"]) - return cls(string=string, location=location, polarity=polarity) - - def parser2_visit_token(self, context: Parser2Context) -> None: - """Perform actions on the context implicated by this token.""" - context.get_hooks().load_polarity.pre_parser_visit_token(self, context) - context.get_hooks().load_polarity.on_parser_visit_token(self, context) - context.get_hooks().load_polarity.post_parser_visit_token(self, context) - - def get_gerber_code( - self, - indent: str = "", # noqa: ARG002 - endline: str = "\n", # noqa: ARG002 - ) -> str: - """Get gerber code represented by this token.""" - return f"LP{self.polarity.value}" diff --git a/src/pygerber/gerberx3/tokenizer/tokens/lr_load_rotation.py b/src/pygerber/gerberx3/tokenizer/tokens/lr_load_rotation.py deleted file mode 100644 index e9057fac..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/lr_load_rotation.py +++ /dev/null @@ -1,73 +0,0 @@ -"""Wrapper for load rotation token.""" - -from __future__ import annotations - -from decimal import Decimal -from typing import TYPE_CHECKING - -from pygerber.gerberx3.tokenizer.tokens.bases.extended_command import ( - ExtendedCommandToken, -) - -if TYPE_CHECKING: - from pyparsing import ParseResults - from typing_extensions import Self - - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class LoadRotation(ExtendedCommandToken): - """Wrapper for load rotation token. - - ### LR Command: Rotation Graphics State Parameter - - The `LR` command is utilized to configure the rotation graphics state parameter. - - Functionality: - - This command specifies the rotation angle to be applied when crafting objects. - - The aperture is rotated centered on its origin, which might either coincide with - or differ from its geometric center. - - Usage and Persistence: - - The `LR` command can be invoked numerous times throughout a file. - - Once defined, the object rotation retains its configuration unless overridden by - an ensuing `LR` command. - - Rotation is strictly determined by the exact value mentioned in the command and - doesn't integrate with any prior rotation values. - - The LR command was introduced in revision 2016.12. - - See section 4.9.4 of The Gerber Layer Format Specification Revision 2023.03 - https://argmaster.github.io/pygerber/latest/gerber_specification/revision_2023_03.html - """ - - def __init__( - self, - string: str, - location: int, - rotation: Decimal, - ) -> None: - super().__init__(string, location) - self.rotation = rotation - - @classmethod - def new(cls, string: str, location: int, tokens: ParseResults) -> Self: - """Create instance of this class. - - Created to be used as callback in `ParserElement.set_parse_action()`. - """ - rotation = Decimal(str(tokens["rotation"])) - return cls(string=string, location=location, rotation=rotation) - - def parser2_visit_token(self, context: Parser2Context) -> None: - """Perform actions on the context implicated by this token.""" - context.get_hooks().load_rotation.pre_parser_visit_token(self, context) - context.get_hooks().load_rotation.on_parser_visit_token(self, context) - context.get_hooks().load_rotation.post_parser_visit_token(self, context) - - def get_gerber_code( - self, - indent: str = "", # noqa: ARG002 - endline: str = "\n", # noqa: ARG002 - ) -> str: - """Get gerber code represented by this token.""" - return f"LR{self.rotation}" diff --git a/src/pygerber/gerberx3/tokenizer/tokens/ls_load_scaling.py b/src/pygerber/gerberx3/tokenizer/tokens/ls_load_scaling.py deleted file mode 100644 index be239fe8..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/ls_load_scaling.py +++ /dev/null @@ -1,73 +0,0 @@ -"""Wrapper for load scaling token.""" - -from __future__ import annotations - -from decimal import Decimal -from typing import TYPE_CHECKING - -from pygerber.gerberx3.tokenizer.tokens.bases.extended_command import ( - ExtendedCommandToken, -) - -if TYPE_CHECKING: - from pyparsing import ParseResults - from typing_extensions import Self - - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class LoadScaling(ExtendedCommandToken): - """Wrapper for load scaling token. - - ### LS Command: Scaling Graphics State Parameter - - The `LS` command is employed to establish the scaling graphics state parameter. - - Functionality: - - The command dictates the scale factor utilized during object creation. - - The aperture undergoes scaling, anchored at its origin. It's crucial to note that - this origin might not always align with its geometric center. - - Usage and Persistence: - - The `LS` command can be invoked multiple times within a single file. - - Once set, the object scaling retains its value unless a subsequent `LS` command - modifies it. - - The scaling gets adjusted based on the specific value mentioned in the command and - doesn't accumulate with the preceding scale factor. - - The LS command was introduced in revision 2016.12. - - See section 4.9.5 of The Gerber Layer Format Specification Revision 2023.03 - https://argmaster.github.io/pygerber/latest/gerber_specification/revision_2023_03.html - """ - - def __init__( - self, - string: str, - location: int, - scaling: Decimal, - ) -> None: - super().__init__(string, location) - self.scaling = scaling - - @classmethod - def new(cls, string: str, location: int, tokens: ParseResults) -> Self: - """Create instance of this class. - - Created to be used as callback in `ParserElement.set_parse_action()`. - """ - scaling = Decimal(str(tokens["scaling"])) - return cls(string=string, location=location, scaling=scaling) - - def parser2_visit_token(self, context: Parser2Context) -> None: - """Perform actions on the context implicated by this token.""" - context.get_hooks().load_scaling.pre_parser_visit_token(self, context) - context.get_hooks().load_scaling.on_parser_visit_token(self, context) - context.get_hooks().load_scaling.post_parser_visit_token(self, context) - - def get_gerber_code( - self, - indent: str = "", # noqa: ARG002 - endline: str = "\n", # noqa: ARG002 - ) -> str: - """Get gerber code represented by this token.""" - return f"LS{self.scaling}" diff --git a/src/pygerber/gerberx3/tokenizer/tokens/m00_program_stop.py b/src/pygerber/gerberx3/tokenizer/tokens/m00_program_stop.py deleted file mode 100644 index dd53eed8..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/m00_program_stop.py +++ /dev/null @@ -1,31 +0,0 @@ -"""Wrapper for program stop token.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from pygerber.gerberx3.tokenizer.tokens.bases.command import CommandToken - -if TYPE_CHECKING: - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class M00ProgramStop(CommandToken): - """Wrapper for program stop token. - - See section 8.1.1 of The Gerber Layer Format Specification Revision 2023.03 - https://argmaster.github.io/pygerber/latest/gerber_specification/revision_2023_03.html - """ - - def parser2_visit_token(self, context: Parser2Context) -> None: - """Perform actions on the context implicated by this token.""" - context.get_hooks().program_stop.pre_parser_visit_token(self, context) - context.get_hooks().program_stop.on_parser_visit_token(self, context) - context.get_hooks().program_stop.post_parser_visit_token(self, context) - - def get_gerber_code( - self, - indent: str = "", - endline: str = "\n", # noqa: ARG002 - ) -> str: - """Get gerber code represented by this token.""" - return f"{indent}M00" diff --git a/src/pygerber/gerberx3/tokenizer/tokens/m01_optional_stop.py b/src/pygerber/gerberx3/tokenizer/tokens/m01_optional_stop.py deleted file mode 100644 index b7fc76a9..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/m01_optional_stop.py +++ /dev/null @@ -1,31 +0,0 @@ -"""Wrapper for optional stop token.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from pygerber.gerberx3.tokenizer.tokens.bases.command import CommandToken - -if TYPE_CHECKING: - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class M01OptionalStop(CommandToken): - """Wrapper for optional stop token. - - See section 8.1.1 of The Gerber Layer Format Specification Revision 2023.03 - https://argmaster.github.io/pygerber/latest/gerber_specification/revision_2023_03.html - """ - - def parser2_visit_token(self, context: Parser2Context) -> None: - """Perform actions on the context implicated by this token.""" - context.get_hooks().optional_stop.pre_parser_visit_token(self, context) - context.get_hooks().optional_stop.on_parser_visit_token(self, context) - context.get_hooks().optional_stop.post_parser_visit_token(self, context) - - def get_gerber_code( - self, - indent: str = "", - endline: str = "\n", # noqa: ARG002 - ) -> str: - """Get gerber code represented by this token.""" - return f"{indent}M01" diff --git a/src/pygerber/gerberx3/tokenizer/tokens/m02_end_of_file.py b/src/pygerber/gerberx3/tokenizer/tokens/m02_end_of_file.py deleted file mode 100644 index fc4b3a7d..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/m02_end_of_file.py +++ /dev/null @@ -1,31 +0,0 @@ -"""Wrapper for end of file token.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from pygerber.gerberx3.tokenizer.tokens.bases.command import CommandToken - -if TYPE_CHECKING: - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class M02EndOfFile(CommandToken): - """Wrapper for end of file token. - - See section 4.10 of The Gerber Layer Format Specification Revision 2023.03 - https://argmaster.github.io/pygerber/latest/gerber_specification/revision_2023_03.html - """ - - def parser2_visit_token(self, context: Parser2Context) -> None: - """Perform actions on the context implicated by this token.""" - context.get_hooks().end_of_file.pre_parser_visit_token(self, context) - context.get_hooks().end_of_file.on_parser_visit_token(self, context) - context.get_hooks().end_of_file.post_parser_visit_token(self, context) - - def get_gerber_code( - self, - indent: str = "", - endline: str = "\n", # noqa: ARG002 - ) -> str: - """Get gerber code represented by this token.""" - return f"{indent}M02" diff --git a/src/pygerber/gerberx3/tokenizer/tokens/macro/__init__.py b/src/pygerber/gerberx3/tokenizer/tokens/macro/__init__.py deleted file mode 100644 index d1f9c1cf..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/macro/__init__.py +++ /dev/null @@ -1,29 +0,0 @@ -"""The AM command creates a macro aperture template and adds it to the aperture template -dictionary (see 2.2). A template is a parametrized shape. The AD command instantiates a -template into an aperture by supplying values to the template parameters. - -Templates of any shape or parametrization can be created. Multiple simple shapes called -primitives can be combined in a single template. An aperture macro can contain variables -whose actual values are defined by: - -- Values provided by the AD command - -- Arithmetic expressions with other variables - -The template is created by positioning primitives in a coordinate space. The origin of -that coordinate space will be the origin of all apertures created with the state. - -A template must be defined before the first AD that refers to it. The AM command can be -used multiple times in a file. - -Attributes are not attached to templates. They are attached to the aperture at the time -of its creation with the AD command. - -An AM command contains the following words: - -- The AM declaration with the macro name - -- Primitives with their comma-separated parameters - -- Macro variables, defined by an arithmetic expression -""" diff --git a/src/pygerber/gerberx3/tokenizer/tokens/macro/am_macro.py b/src/pygerber/gerberx3/tokenizer/tokens/macro/am_macro.py deleted file mode 100644 index 1eb7970b..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/macro/am_macro.py +++ /dev/null @@ -1,174 +0,0 @@ -"""Container token for macro definition.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING, Iterator, List - -from pygerber.gerberx3.tokenizer.tokens.bases.group import TokenGroup -from pygerber.gerberx3.tokenizer.tokens.bases.token import Token -from pygerber.gerberx3.tokenizer.tokens.macro.macro_begin import MacroBegin - -if TYPE_CHECKING: - from pyparsing import ParseResults - from typing_extensions import Self - - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class MacroDefinition(TokenGroup): - """## 4.5 Aperture Macro (AM). - - The AM command creates a macro aperture template and adds it to the aperture template - dictionary (see 2.2). A template is a parametrized shape. The AD command instantiates a - template into an aperture by supplying values to the template parameters. - - Templates of any shape or parametrization can be created. Multiple simple shapes called - primitives can be combined in a single template. An aperture macro can contain variables - whose actual values are defined by: - - - Values provided by the AD command, - - Arithmetic expressions with other variables. - - The template is created by positioning primitives in a coordinate space. The origin of that - coordinate space will be the origin of all apertures created with the state. - - A template must be defined before the first AD that refers to it. The AM command can be used - multiple times in a file. - - Attributes are not attached to templates. They are attached to the aperture at the time of its - creation with the AD command. - - An AM command contains the following words: - - - The AM declaration with the macro name - - Primitives with their comma-separated parameters - - Macro variables, defined by an arithmetic expression - - --- - - ### Syntax - - ```ebnf - AM = '%' ('AM' macro_name macro_body) '%'; - macro_name = name '*'; - macro_body = {in_macro_block}+; - in_macro_block = - |primitive - |variable_definition - ; - variable_definition = (macro_variable '=' expression) '*'; - macro_variable = '$' positive_integer; - primitive = primitive_code {',' par}* - par = ',' (expression); - ``` - - - `AM` - AM for Aperture Macro - - `` - Name of the aperture macro. The name must be unique, it - cannot be reused for another macro. See [3.4.5](https://www.ucamco.com/files/downloads/file_en/456/gerber-layer-format-specification-revision-2023-08_en.pdf#page=36) - for the syntax rules. - - `` - The macro body contains the primitives generating the image - and the calculation of their parameters. - - `` - `$n=`. An arithmetic expression may - use arithmetic operators (described later), constants and - variables $m defined previously. - - `` - A primitive is a basic shape to create the macro. It includes - primitive code identifying the primitive and primitive-specific - parameters (e.g. center of a circle). See [4.5.1](https://www.ucamco.com/files/downloads/file_en/456/gerber-layer-format-specification-revision-2023-08_en.pdf#page=58). - The primitives are positioned in a coordinates system whose origin is the - origin of the resulting apertures. - - `` - A code specifying the primitive (e.g. polygon). - - `` - Parameter can be a decimal number (e.g. `0.050`), a variable - (e.g. `$1`) or an arithmetic expression based on numbers and - variables. The actual value is calculated as explained in - [4.5.4.3](https://www.ucamco.com/files/downloads/file_en/456/gerber-layer-format-specification-revision-2023-08_en.pdf#page=71). - - Coordinates and sizes are expressed in the unit set by the MO command. - - A parameter can be either: - - - A decimal number - - A macro variable - - An arithmetic expression - - A macro variable name must be a `$` character followed by an integer >0, for example `$12`. - (This is a subset of names allowed in [3.4.3](https://www.ucamco.com/files/downloads/file_en/456/gerber-layer-format-specification-revision-2023-08_en.pdf#page=35).) - - **Note:** New lines can be added between words of a single command to enhance - readability. They do not affect the macro definition. - - ### Example - - The following AM command defines an aperture macro named 'Triangle_30'. - - ```gerber - %AMTriangle_30* - 4,1,3, - 1,-1, - 1,1, - 2,1, - 1,-1, - 30* - % - ``` - - --- - - See section 4.5 of [The Gerber Layer Format Specification](https://www.ucamco.com/files/downloads/file_en/456/gerber-layer-format-specification-revision-2023-08_en.pdf#page=56) - - """ # noqa: E501 - - begin: MacroBegin - tokens: List[Token] - - def __init__( - self, - string: str, - location: int, - macro_begin: MacroBegin, - tokens: List[Token], - ) -> None: - super().__init__(string, location, tokens) - self.macro_begin = macro_begin - - @property - def macro_name(self) -> str: - """Name of macro item.""" - return self.macro_begin.name - - @classmethod - def new(cls, string: str, location: int, tokens: ParseResults) -> Self: - """Create instance of this class. - - Created to be used as callback in `ParserElement.set_parse_action()`. - """ - macro_begin = tokens["macro_begin"] - if not isinstance(macro_begin, MacroBegin): - raise TypeError(macro_begin) - - macro_body_raw = tokens["macro_body"] - macro_body_tokens: list[Token] = [] - - for e in macro_body_raw: - token = e[0] - if not isinstance(token, Token): - raise TypeError(token) - macro_body_tokens.append(token) - - return cls( - string=string, - location=location, - macro_begin=macro_begin, - tokens=macro_body_tokens, - ) - - def parser2_visit_token(self, context: Parser2Context) -> None: - """Perform actions on the context implicated by this token.""" - context.get_hooks().macro_definition.pre_parser_visit_token(self, context) - context.get_hooks().macro_definition.on_parser_visit_token(self, context) - context.get_hooks().macro_definition.post_parser_visit_token(self, context) - - def __iter__(self) -> Iterator[Token]: - yield self.macro_begin - for token in self.tokens: - yield from token - yield self diff --git a/src/pygerber/gerberx3/tokenizer/tokens/macro/expressions/__init__.py b/src/pygerber/gerberx3/tokenizer/tokens/macro/expressions/__init__.py deleted file mode 100644 index e6d2e738..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/macro/expressions/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""`expressions` package contains macro expression tokens.""" diff --git a/src/pygerber/gerberx3/tokenizer/tokens/macro/expressions/binary.py b/src/pygerber/gerberx3/tokenizer/tokens/macro/expressions/binary.py deleted file mode 100644 index cb261140..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/macro/expressions/binary.py +++ /dev/null @@ -1,159 +0,0 @@ -"""Arithmetic expression token.""" - -from __future__ import annotations - -from operator import add, mul, sub, truediv -from typing import TYPE_CHECKING, Any, Callable - -from pygerber.gerberx3.tokenizer.tokens.macro.expressions.macro_expression import ( - MacroExpressionToken, -) - -if TYPE_CHECKING: - from pyparsing import ParseResults - from typing_extensions import Self - - from pygerber.gerberx3.parser2.context2 import Parser2Context - from pygerber.gerberx3.parser2.macro2.expressions2.expression2 import Expression2 - - -class BinaryOperator(MacroExpressionToken): - """Operation with two operands.""" - - def __init__( - self, - string: str, - location: int, - left: MacroExpressionToken, - right: MacroExpressionToken, - ) -> None: - super().__init__(string, location) - self.left = left - self.right = right - self.operator: Callable[[Any, Any], Any] = add - self.sign = "+" - - @classmethod - def new( - cls, - string: str, - location: int, - tokens: ParseResults, - ) -> Self: - """Create instance of this class. - - Created to be used as callback in `ParserElement.set_parse_action()`. - """ - left, right = tokens[0] - return cls( - string, - location, - MacroExpressionToken.ensure_type(left), - MacroExpressionToken.ensure_type(right), - ) - - def to_parser2_expression(self, context: Parser2Context) -> Expression2: - """Convert to `Expression2` descendant class.""" - raise NotImplementedError - - def get_gerber_code( - self, - indent: str = "", - endline: str = "\n", - ) -> str: - """Get gerber code from iterable of tokens.""" - return ( - f"{self.left.get_gerber_code(indent=indent, endline=endline)}{self.sign}" - f"{self.right.get_gerber_code(indent=indent, endline=endline)}" - ) - - def __str__(self) -> str: - return f"{super().__str__()}::[{self.left}, {self.right}]" - - -class MultiplicationOperator(BinaryOperator): - """Operation with two operands.""" - - def __init__( - self, - string: str, - location: int, - left: MacroExpressionToken, - right: MacroExpressionToken, - ) -> None: - super().__init__(string, location, left, right) - self.operator: Callable[[Any, Any], Any] = mul - self.sign = "x" - - def to_parser2_expression(self, context: Parser2Context) -> Expression2: - """Convert to `Expression2` descendant class.""" - return context.macro_expressions.multiplication( - lhs=self.left.to_parser2_expression(context), - rhs=self.right.to_parser2_expression(context), - ) - - -class DivisionOperator(BinaryOperator): - """Operation with two operands.""" - - def __init__( - self, - string: str, - location: int, - left: MacroExpressionToken, - right: MacroExpressionToken, - ) -> None: - super().__init__(string, location, left, right) - self.operator: Callable[[Any, Any], Any] = truediv - self.sign = "/" - - def to_parser2_expression(self, context: Parser2Context) -> Expression2: - """Convert to `Expression2` descendant class.""" - return context.macro_expressions.division( - lhs=self.left.to_parser2_expression(context), - rhs=self.right.to_parser2_expression(context), - ) - - -class AdditionOperator(BinaryOperator): - """Operation with two operands.""" - - def __init__( - self, - string: str, - location: int, - left: MacroExpressionToken, - right: MacroExpressionToken, - ) -> None: - super().__init__(string, location, left, right) - self.operator: Callable[[Any, Any], Any] = add - self.sign = "+" - - def to_parser2_expression(self, context: Parser2Context) -> Expression2: - """Convert to `Expression2` descendant class.""" - return context.macro_expressions.addition( - lhs=self.left.to_parser2_expression(context), - rhs=self.right.to_parser2_expression(context), - ) - - -class SubtractionOperator(BinaryOperator): - """Operation with two operands.""" - - def __init__( - self, - string: str, - location: int, - left: MacroExpressionToken, - right: MacroExpressionToken, - ) -> None: - super().__init__(string, location, left, right) - self.operator: Callable[[Any, Any], Any] = sub - self.sign = "-" - - def to_parser2_expression(self, context: Parser2Context) -> Expression2: - """Convert to `Expression2` descendant class.""" - return context.macro_expressions.subtraction( - lhs=self.left.to_parser2_expression(context), - rhs=self.right.to_parser2_expression(context), - ) diff --git a/src/pygerber/gerberx3/tokenizer/tokens/macro/expressions/errors.py b/src/pygerber/gerberx3/tokenizer/tokens/macro/expressions/errors.py deleted file mode 100644 index 652477cc..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/macro/expressions/errors.py +++ /dev/null @@ -1,9 +0,0 @@ -"""Arithmetic expression token.""" - -from __future__ import annotations - -from pygerber.gerberx3.tokenizer.errors import TokenizerError - - -class InvalidArithmeticExpressionError(TokenizerError): - """Raised when it's not possible to construct valid arithmetic expression.""" diff --git a/src/pygerber/gerberx3/tokenizer/tokens/macro/expressions/macro_expression.py b/src/pygerber/gerberx3/tokenizer/tokens/macro/expressions/macro_expression.py deleted file mode 100644 index 0e3ab0ad..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/macro/expressions/macro_expression.py +++ /dev/null @@ -1,44 +0,0 @@ -"""In-macro numeric expression token.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from pygerber.gerberx3.tokenizer.tokens.bases.token import Token - -if TYPE_CHECKING: - from pygerber.gerberx3.parser2.context2 import Parser2Context - from pygerber.gerberx3.parser2.macro2.expressions2.expression2 import Expression2 - - -class MacroExpressionToken(Token): - """## 4.5.4.2 Arithmetic Expressions. - - A parameter value can also be defined by an arithmetic expression consisting of integer and - decimal constants, other variables, arithmetic operators and the brackets "(" and ")". The - standard arithmetic precedence rules apply. The following arithmetic operators are available: - - --- - - ## Example - - ```gerber - %AMRect* - 21,1,$1,$2-2x$3,-$4,-$5+$2,0*% - ``` - - The corresponding AD command could be: - - ```gerber - %ADD146Rect,0.0807087X0.1023622X0.0118110X0.5000000X0.3000000*% - ``` - - --- - - See section 4.5.4.2 of [The Gerber Layer Format Specification](https://www.ucamco.com/files/downloads/file_en/456/gerber-layer-format-specification-revision-2023-08_en.pdf#page=70) - - """ # noqa: E501 - - def to_parser2_expression(self, context: Parser2Context) -> Expression2: - """Convert to `Expression2` descendant class.""" - raise NotImplementedError diff --git a/src/pygerber/gerberx3/tokenizer/tokens/macro/expressions/numeric_constant.py b/src/pygerber/gerberx3/tokenizer/tokens/macro/expressions/numeric_constant.py deleted file mode 100644 index 9a43126d..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/macro/expressions/numeric_constant.py +++ /dev/null @@ -1,61 +0,0 @@ -"""Arithmetic expression token.""" - -from __future__ import annotations - -from decimal import Decimal -from typing import TYPE_CHECKING - -from pygerber.gerberx3.tokenizer.tokens.macro.expressions.macro_expression import ( - MacroExpressionToken, -) - -if TYPE_CHECKING: - from pyparsing import ParseResults - from typing_extensions import Self - - from pygerber.gerberx3.parser2.context2 import Parser2Context - from pygerber.gerberx3.parser2.macro2.expressions2.expression2 import Expression2 - - -class NumericConstant(MacroExpressionToken): - """Wrapper around numeric constant expression token.""" - - def __init__( - self, - string: str, - location: int, - value: Decimal, - ) -> None: - super().__init__(string, location) - self.value = value - - @classmethod - def new(cls, string: str, location: int, tokens: ParseResults) -> Self: - """Create instance of this class. - - Created to be used as callback in `ParserElement.set_parse_action()`. - """ - value = Decimal(str(tokens["numeric_constant_value"])) - - return cls( - string=string, - location=location, - value=value, - ) - - def to_parser2_expression(self, context: Parser2Context) -> Expression2: - """Convert to `Expression2` descendant class.""" - return context.macro_expressions.constant( - value=self.value, - ) - - def get_gerber_code( - self, - indent: str = "", # noqa: ARG002 - endline: str = "\n", # noqa: ARG002 - ) -> str: - """Get gerber code from iterable of tokens.""" - return f"{self.value}" - - def __str__(self) -> str: - return f"{super().__str__()}::[{self.value}]" diff --git a/src/pygerber/gerberx3/tokenizer/tokens/macro/expressions/unary.py b/src/pygerber/gerberx3/tokenizer/tokens/macro/expressions/unary.py deleted file mode 100644 index 91f70887..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/macro/expressions/unary.py +++ /dev/null @@ -1,107 +0,0 @@ -"""Arithmetic expression token.""" - -from __future__ import annotations - -from operator import neg, pos -from typing import TYPE_CHECKING, Any, Callable - -from pygerber.gerberx3.tokenizer.tokens.macro.expressions.macro_expression import ( - MacroExpressionToken, -) - -if TYPE_CHECKING: - from pyparsing import ParseResults - from typing_extensions import Self - - from pygerber.gerberx3.parser2.context2 import Parser2Context - from pygerber.gerberx3.parser2.macro2.expressions2.expression2 import Expression2 - - -class UnaryOperator(MacroExpressionToken): - """Operator with one operand.""" - - def __init__( - self, - string: str, - location: int, - operand: MacroExpressionToken, - ) -> None: - super().__init__(string, location) - self.operand = operand - self.operator: Callable[[Any], Any] = neg - self.sign = "-" - - @classmethod - def new( - cls, - string: str, - location: int, - tokens: ParseResults, - ) -> Self: - """Create instance of this class. - - Created to be used as callback in `ParserElement.set_parse_action()`. - """ - operand, *_ = tokens.as_list()[0] - - if not isinstance(operand, MacroExpressionToken): - raise TypeError(operand) - - return cls(string, location, operand) - - def to_parser2_expression(self, context: Parser2Context) -> Expression2: - """Convert to `Expression2` descendant class.""" - raise NotImplementedError - - def get_gerber_code( - self, - indent: str = "", - endline: str = "\n", - ) -> str: - """Get gerber code from iterable of tokens.""" - return ( - f"{self.sign}{self.operand.get_gerber_code(indent=indent, endline=endline)}" - ) - - def __str__(self) -> str: - return f"{super().__str__()}::[{self.operand}]" - - -class NegationOperator(UnaryOperator): - """Negation operation.""" - - def __init__( - self, - string: str, - location: int, - operand: MacroExpressionToken, - ) -> None: - super().__init__(string, location, operand) - self.operator = neg - self.sign = "-" - - def to_parser2_expression(self, context: Parser2Context) -> Expression2: - """Convert to `UnaryMinusExpression2`.""" - return context.macro_expressions.negation( - op=self.operand.to_parser2_expression(context), - ) - - -class PositiveOperator(UnaryOperator): - """Negation operation.""" - - def __init__( - self, - string: str, - location: int, - operand: MacroExpressionToken, - ) -> None: - super().__init__(string, location, operand) - self.operator = pos - self.sign = "+" - - def to_parser2_expression(self, context: Parser2Context) -> Expression2: - """Convert to `UnaryPlusExpression2`.""" - return context.macro_expressions.positive( - op=self.operand.to_parser2_expression(context), - ) diff --git a/src/pygerber/gerberx3/tokenizer/tokens/macro/expressions/variable_name.py b/src/pygerber/gerberx3/tokenizer/tokens/macro/expressions/variable_name.py deleted file mode 100644 index 7f15348f..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/macro/expressions/variable_name.py +++ /dev/null @@ -1,75 +0,0 @@ -"""Macro variable use token.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from pygerber.gerberx3.tokenizer.tokens.macro.expressions.macro_expression import ( - MacroExpressionToken, -) - -if TYPE_CHECKING: - from pyparsing import ParseResults - from typing_extensions import Self - - from pygerber.gerberx3.parser2.context2 import Parser2Context - from pygerber.gerberx3.parser2.macro2.expressions2.expression2 import Expression2 - - -class MacroVariableName(MacroExpressionToken): - """## 4.5.4.1 Variable Values from the AD Command. - - description - - --- - - ## Example - - ```gerber - %AMDONUTVAR*1,1,$1,$2,$3*1,0,$4,$2,$3*% - ``` - `$1`, `$2`, `$3` and `$4` are macro variables. With the following calling AD. - - ```gerber - %ADD34DONUTVAR,0.100X0X0X0.080*% - ``` - - the variables take the following values: - - ```yaml - $1 = 0.100 - $2 = 0 - $3 = 0 - $4 = 0.080 - ``` - - --- - - See section 4.5.4.1 of [The Gerber Layer Format Specification](https://www.ucamco.com/files/downloads/file_en/456/gerber-layer-format-specification-revision-2023-08_en.pdf#page=70) - - """ - - def __init__(self, string: str, location: int, name: str) -> None: - super().__init__(string, location) - self.name = name - - @classmethod - def new(cls, string: str, location: int, tokens: ParseResults) -> Self: - """Create instance of this class. - - Created to be used as callback in `ParserElement.set_parse_action()`. - """ - name = str(tokens["macro_variable_name"]) - return cls(string=string, location=location, name=name) - - def to_parser2_expression(self, context: Parser2Context) -> Expression2: - """Convert to `Expression2` descendant class.""" - return context.macro_expressions.variable_name(name=self.name) - - def get_gerber_code( - self, - indent: str = "", # noqa: ARG002 - endline: str = "\n", # noqa: ARG002 - ) -> str: - """Get gerber code represented by this token.""" - return self.name diff --git a/src/pygerber/gerberx3/tokenizer/tokens/macro/macro_begin.py b/src/pygerber/gerberx3/tokenizer/tokens/macro/macro_begin.py deleted file mode 100644 index bd4ff2d1..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/macro/macro_begin.py +++ /dev/null @@ -1,108 +0,0 @@ -"""Comment token.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from pygerber.gerberx3.tokenizer.tokens.bases.token import Token - -if TYPE_CHECKING: - from pyparsing import ParseResults - from typing_extensions import Self - - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class MacroBegin(Token): - """## 4.5 Aperture Macro (AM). - - The AM command creates a macro aperture `template` and adds it to the aperture `template` - dictionary (see 2.2). A `template` is a parametrized shape. The AD command instantiates a - `template` into an aperture by supplying values to the template `parameters`. - - Templates of any shape or parametrization can be created. Multiple simple shapes called - `primitives` can be combined in a single `template`. An aperture macro can contain `variables` - whose actual values are defined by: - - - Values provided by the AD command - - `Arithmetic expressions` with other `variables` - - The template is created by positioning `primitives` in a coordinate space. The origin of that - coordinate space will be the origin of all apertures created with the state. - - A `template` must be defined before the first AD that refers to it. The AM command can be used - multiple times in a file. - - `Attributes` are not attached to templates. They are attached to the aperture at the time of its - creation with the AD command. - - An AM command contains the following words: - - - The AM declaration with the macro name - - `Primitives` with their comma-separated `parameters` - - `Macro variables`, defined by an `arithmetic expression` - - Coordinates and sizes are expressed in the unit set by the MO command. - - A parameter can be either: - - - A `decimal` number - - A `macro variable` - - An `arithmetic expression` - - A `macro variable` name must be a "$" character followed by an integer >0, for example `$12`. - (This is a subset of names allowed in 3.4.3.) - - --- - - ## Example - - The following AM command defines an aperture macro named "Triangle_30". - - ```gerber - %AMTriangle_30* - 4,1,3, - 1,-1, - 1,1, - 2,1, - 1,-1, - 30* - % - ``` - - --- - - See section 4.5 of [The Gerber Layer Format Specification](https://www.ucamco.com/files/downloads/file_en/456/gerber-layer-format-specification-revision-2023-08_en.pdf#page=56) - - """ # noqa: E501 - - def __init__(self, string: str, location: int, name: str) -> None: - super().__init__(string, location) - self.name = name - - @classmethod - def new(cls, string: str, location: int, tokens: ParseResults) -> Self: - """Create instance of this class. - - Created to be used as callback in `ParserElement.set_parse_action()`. - """ - content: str = str(tokens["macro_name"]) - return cls(string=string, location=location, name=content) - - def parser2_visit_token(self, context: Parser2Context) -> None: - """Perform actions on the context implicated by this token.""" - context.get_hooks().macro_begin.pre_parser_visit_token(self, context) - context.get_hooks().macro_begin.on_parser_visit_token(self, context) - context.get_hooks().macro_begin.post_parser_visit_token(self, context) - - def get_gerber_code( - self, - indent: str = "", # noqa: ARG002 - endline: str = "\n", # noqa: ARG002 - ) -> str: - """Get gerber code represented by this token.""" - return f"AM{self.name}" - - def get_gerber_code_one_line_pretty_display(self) -> str: - """Get gerber code represented by this token.""" - return f"%{self.get_gerber_code()}*" diff --git a/src/pygerber/gerberx3/tokenizer/tokens/macro/macro_context.py b/src/pygerber/gerberx3/tokenizer/tokens/macro/macro_context.py deleted file mode 100644 index 6c019e08..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/macro/macro_context.py +++ /dev/null @@ -1,15 +0,0 @@ -"""Module contains MacroContext class definition.""" - -from __future__ import annotations - -from typing import Dict - -from pydantic import BaseModel, Field - -from pygerber.gerberx3.math.offset import Offset - - -class MacroContext(BaseModel): - """Macro context object used during macro evaluation.""" - - variables: Dict[str, Offset] = Field(default_factory=dict) diff --git a/src/pygerber/gerberx3/tokenizer/tokens/macro/point.py b/src/pygerber/gerberx3/tokenizer/tokens/macro/point.py deleted file mode 100644 index 175b705e..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/macro/point.py +++ /dev/null @@ -1,69 +0,0 @@ -"""Point wrapper token.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from pygerber.gerberx3.parser2.macro2.point2 import Point2 -from pygerber.gerberx3.tokenizer.tokens.bases.token import Token -from pygerber.gerberx3.tokenizer.tokens.macro.expressions.macro_expression import ( - MacroExpressionToken, -) - -if TYPE_CHECKING: - from pyparsing import ParseResults - from typing_extensions import Self - - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class Point(Token): - """Point wrapper token.""" - - def __init__( - self, - string: str, - location: int, - x: MacroExpressionToken, - y: MacroExpressionToken, - ) -> None: - super().__init__(string, location) - self.x = x - self.y = y - - @classmethod - def new(cls, string: str, location: int, tokens: ParseResults) -> Self: - """Create instance of this class. - - Created to be used as callback in `ParserElement.set_parse_action()`. - """ - x = MacroExpressionToken.ensure_type(tokens["x"]) - y = MacroExpressionToken.ensure_type(tokens["y"]) - - return cls( - string=string, - location=location, - x=x, - y=y, - ) - - def to_parser2_point2(self, context: Parser2Context) -> Point2: - """Convert to `Expression2` descendant class.""" - return Point2( - x=self.x.to_parser2_expression(context=context), - y=self.y.to_parser2_expression(context=context), - ) - - def get_gerber_code( - self, - indent: str = "", - endline: str = "\n", - ) -> str: - """Get gerber code from iterable of tokens.""" - return ( - f"{self.x.get_gerber_code(indent, endline)}," - f"{self.y.get_gerber_code(indent, endline)}" - ) - - def __str__(self) -> str: - return f"{super().__str__()}::[{self.x}, {self.y}]" diff --git a/src/pygerber/gerberx3/tokenizer/tokens/macro/statements/__init__.py b/src/pygerber/gerberx3/tokenizer/tokens/macro/statements/__init__.py deleted file mode 100644 index 63e42d81..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/macro/statements/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""`statements` package contains macro statement tokens.""" diff --git a/src/pygerber/gerberx3/tokenizer/tokens/macro/statements/code_1_circle.py b/src/pygerber/gerberx3/tokenizer/tokens/macro/statements/code_1_circle.py deleted file mode 100644 index ce661498..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/macro/statements/code_1_circle.py +++ /dev/null @@ -1,136 +0,0 @@ -"""Macro primitives tokens.""" - -from __future__ import annotations - -from decimal import Decimal -from typing import TYPE_CHECKING, ClassVar, Optional - -from pygerber.gerberx3.tokenizer.tokens.macro.expressions.macro_expression import ( - MacroExpressionToken, -) -from pygerber.gerberx3.tokenizer.tokens.macro.expressions.numeric_constant import ( - NumericConstant, -) -from pygerber.gerberx3.tokenizer.tokens.macro.statements.primitive import ( - MacroPrimitiveToken, -) - -if TYPE_CHECKING: - from pyparsing import ParseResults - from typing_extensions import Self - - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class Code1CircleToken(MacroPrimitiveToken): - """## 4.5.1.3 Circle, Code 1. - - A circle primitive is defined by its center point and diameter. - - --- - - ## Example - - ```gerber - %AMCircle* - 1,1,1.5,0,0,0*% - ``` - - --- - - See section 4.5.1.3 of [The Gerber Layer Format Specification](https://www.ucamco.com/files/downloads/file_en/456/gerber-layer-format-specification-revision-2023-08_en.pdf#page=60) - - """ - - symbol: ClassVar[str] = "1" - - def __init__( - self, - string: str, - location: int, - exposure: MacroExpressionToken, - diameter: MacroExpressionToken, - center_x: MacroExpressionToken, - center_y: MacroExpressionToken, - rotation: MacroExpressionToken, - ) -> None: - super().__init__(string, location) - self.exposure = exposure - self.diameter = diameter - self.center_x = center_x - self.center_y = center_y - self.rotation = rotation - - @classmethod - def new(cls, string: str, location: int, tokens: ParseResults) -> Self: - """Create instance of this class. - - Created to be used as callback in `ParserElement.set_parse_action()`. - """ - exposure = MacroExpressionToken.ensure_type(tokens["exposure"]) - diameter = MacroExpressionToken.ensure_type(tokens["diameter"]) - center_x = MacroExpressionToken.ensure_type(tokens["center_x"]) - center_y = MacroExpressionToken.ensure_type(tokens["center_y"]) - - r = tokens.get("rotation") - rotation: Optional[MacroExpressionToken] = ( - MacroExpressionToken.ensure_type(r) if r is not None else None - ) - if rotation is None: - rotation = NumericConstant(string, location, value=Decimal("0.0")) - - return cls( - string=string, - location=location, - exposure=exposure, - diameter=diameter, - center_x=center_x, - center_y=center_y, - rotation=rotation, - ) - - def parser2_visit_token(self, context: Parser2Context) -> None: - """Perform actions on the context implicated by this token.""" - context.get_hooks().macro_code_1_circle.pre_parser_visit_token( - self, - context, - ) - context.get_hooks().macro_code_1_circle.on_parser_visit_token( - self, - context, - ) - context.get_hooks().macro_code_1_circle.post_parser_visit_token( - self, - context, - ) - - def get_gerber_code( - self, - indent: str = "", - endline: str = "\n", - ) -> str: - """Get gerber code represented by this token.""" - string = self.symbol - - string += f",{self.exposure.get_gerber_code(indent, endline)}" - string += f",{self.diameter.get_gerber_code(indent, endline)}" - string += f",{self.center_x.get_gerber_code(indent, endline)}" - string += f",{self.center_y.get_gerber_code(indent, endline)}" - - if self.rotation is not None: - string += f",{self.rotation.get_gerber_code(indent, endline)}" - - return indent + string - - def __str__(self) -> str: - string = super().__str__() - - string += f"\n {self.exposure}" - string += f"\n {self.diameter}" - string += f"\n {self.center_x}" - string += f"\n {self.center_y}" - - if self.rotation is not None: - string += f"\n {self.rotation}" - - return string diff --git a/src/pygerber/gerberx3/tokenizer/tokens/macro/statements/code_20_vector_line.py b/src/pygerber/gerberx3/tokenizer/tokens/macro/statements/code_20_vector_line.py deleted file mode 100644 index bcf02140..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/macro/statements/code_20_vector_line.py +++ /dev/null @@ -1,136 +0,0 @@ -"""Macro primitive vector line.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING, ClassVar - -from pygerber.gerberx3.tokenizer.tokens.macro.expressions.macro_expression import ( - MacroExpressionToken, -) -from pygerber.gerberx3.tokenizer.tokens.macro.statements.primitive import ( - MacroPrimitiveToken, -) - -if TYPE_CHECKING: - from pyparsing import ParseResults - from typing_extensions import Self - - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class Code20VectorLineToken(MacroPrimitiveToken): - """## 4.5.1.4 Vector Line, Code 20. - - A vector line is a rectangle defined by its line width, start and end points. The - line ends are rectangular. - - --- - - ## Example - - ```gerber - %AMLine* - 20,1,0.9,0,0.45,12,0.45,0* - % - ``` - - --- - - See section 4.5.1.4 of [The Gerber Layer Format Specification](https://www.ucamco.com/files/downloads/file_en/456/gerber-layer-format-specification-revision-2023-08_en.pdf#page=61) - - """ - - symbol: ClassVar[str] = "20" - - def __init__( # noqa: PLR0913 - self, - string: str, - location: int, - exposure: MacroExpressionToken, - width: MacroExpressionToken, - start_x: MacroExpressionToken, - start_y: MacroExpressionToken, - end_x: MacroExpressionToken, - end_y: MacroExpressionToken, - rotation: MacroExpressionToken, - ) -> None: - super().__init__(string, location) - self.exposure = exposure - self.width = width - self.start_x = start_x - self.start_y = start_y - self.end_x = end_x - self.end_y = end_y - self.rotation = rotation - - @classmethod - def new(cls, string: str, location: int, tokens: ParseResults) -> Self: - """Create instance of this class. - - Created to be used as callback in `ParserElement.set_parse_action()`. - """ - exposure = MacroExpressionToken.ensure_type(tokens["exposure"]) - width = MacroExpressionToken.ensure_type(tokens["width"]) - start_x = MacroExpressionToken.ensure_type(tokens["start_x"]) - start_y = MacroExpressionToken.ensure_type(tokens["start_y"]) - end_x = MacroExpressionToken.ensure_type(tokens["end_x"]) - end_y = MacroExpressionToken.ensure_type(tokens["end_y"]) - rotation = MacroExpressionToken.ensure_type(tokens["rotation"]) - - return cls( - string=string, - location=location, - exposure=exposure, - width=width, - start_x=start_x, - start_y=start_y, - end_x=end_x, - end_y=end_y, - rotation=rotation, - ) - - def parser2_visit_token(self, context: Parser2Context) -> None: - """Perform actions on the context implicated by this token.""" - context.get_hooks().macro_code_20_vector_line.pre_parser_visit_token( - self, - context, - ) - context.get_hooks().macro_code_20_vector_line.on_parser_visit_token( - self, - context, - ) - context.get_hooks().macro_code_20_vector_line.post_parser_visit_token( - self, - context, - ) - - def get_gerber_code( - self, - indent: str = "", - endline: str = "\n", - ) -> str: - """Get gerber code represented by this token.""" - string = self.symbol - - string += f",{self.exposure.get_gerber_code(indent, endline)}" - string += f",{self.width.get_gerber_code(indent, endline)}" - string += f",{self.start_x.get_gerber_code(indent, endline)}" - string += f",{self.start_y.get_gerber_code(indent, endline)}" - string += f",{self.end_x.get_gerber_code(indent, endline)}" - string += f",{self.end_y.get_gerber_code(indent, endline)}" - string += f",{self.rotation.get_gerber_code(indent, endline)}" - - return indent + string - - def __str__(self) -> str: - string = super().__str__() - - string += f"\n {self.exposure}" - string += f"\n {self.width}" - string += f"\n {self.start_x}" - string += f"\n {self.start_y}" - string += f"\n {self.end_x}" - string += f"\n {self.end_y}" - string += f"\n {self.rotation}" - - return string diff --git a/src/pygerber/gerberx3/tokenizer/tokens/macro/statements/code_21_center_line.py b/src/pygerber/gerberx3/tokenizer/tokens/macro/statements/code_21_center_line.py deleted file mode 100644 index 84452667..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/macro/statements/code_21_center_line.py +++ /dev/null @@ -1,129 +0,0 @@ -"""Macro primitive center line.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING, ClassVar - -from pygerber.gerberx3.tokenizer.tokens.macro.expressions.macro_expression import ( - MacroExpressionToken, -) -from pygerber.gerberx3.tokenizer.tokens.macro.statements.primitive import ( - MacroPrimitiveToken, -) - -if TYPE_CHECKING: - from pyparsing import ParseResults - from typing_extensions import Self - - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class Code21CenterLineToken(MacroPrimitiveToken): - """## 4.5.1.5 Center Line, Code 21. - - A center line primitive is a rectangle defined by its width, height, and center - point. - - --- - - ## Example - - ```gerber - %AMRECTANGLE* - 21,1,6.8,1.2,3.4,0.6,30*% - ``` - - --- - - See section 4.5.1.5 of [The Gerber Layer Format Specification](https://www.ucamco.com/files/downloads/file_en/456/gerber-layer-format-specification-revision-2023-08_en.pdf#page=62) - - """ - - symbol: ClassVar[str] = "21" - - def __init__( - self, - string: str, - location: int, - exposure: MacroExpressionToken, - width: MacroExpressionToken, - height: MacroExpressionToken, - center_x: MacroExpressionToken, - center_y: MacroExpressionToken, - rotation: MacroExpressionToken, - ) -> None: - super().__init__(string, location) - self.exposure = exposure - self.width = width - self.height = height - self.center_x = center_x - self.center_y = center_y - self.rotation = rotation - - @classmethod - def new(cls, string: str, location: int, tokens: ParseResults) -> Self: - """Create instance of this class. - - Created to be used as callback in `ParserElement.set_parse_action()`. - """ - exposure = MacroExpressionToken.ensure_type(tokens["exposure"]) - width = MacroExpressionToken.ensure_type(tokens["width"]) - height = MacroExpressionToken.ensure_type(tokens["height"]) - center_x = MacroExpressionToken.ensure_type(tokens["center_x"]) - center_y = MacroExpressionToken.ensure_type(tokens["center_y"]) - rotation = MacroExpressionToken.ensure_type(tokens["rotation"]) - - return cls( - string=string, - location=location, - exposure=exposure, - width=width, - height=height, - center_x=center_x, - center_y=center_y, - rotation=rotation, - ) - - def parser2_visit_token(self, context: Parser2Context) -> None: - """Perform actions on the context implicated by this token.""" - context.get_hooks().macro_code_21_center_line.pre_parser_visit_token( - self, - context, - ) - context.get_hooks().macro_code_21_center_line.on_parser_visit_token( - self, - context, - ) - context.get_hooks().macro_code_21_center_line.post_parser_visit_token( - self, - context, - ) - - def get_gerber_code( - self, - indent: str = "", - endline: str = "\n", - ) -> str: - """Get gerber code represented by this token.""" - string = self.symbol - - string += f",{self.exposure.get_gerber_code(indent, endline)}" - string += f",{self.width.get_gerber_code(indent, endline)}" - string += f",{self.height.get_gerber_code(indent, endline)}" - string += f",{self.center_x.get_gerber_code(indent, endline)}" - string += f",{self.center_y.get_gerber_code(indent, endline)}" - string += f",{self.rotation.get_gerber_code(indent, endline)}" - - return indent + string - - def __str__(self) -> str: - string = super().__str__() - - string += f"\n {self.exposure}" - string += f"\n {self.width}" - string += f"\n {self.height}" - string += f"\n {self.center_x}" - string += f"\n {self.center_y}" - string += f"\n {self.rotation}" - - return string diff --git a/src/pygerber/gerberx3/tokenizer/tokens/macro/statements/code_22_lower_left_line.py b/src/pygerber/gerberx3/tokenizer/tokens/macro/statements/code_22_lower_left_line.py deleted file mode 100644 index 9c3014b0..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/macro/statements/code_22_lower_left_line.py +++ /dev/null @@ -1,31 +0,0 @@ -"""Code 22 lower left line macro primitive.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from pygerber.gerberx3.tokenizer.tokens.macro.statements.primitive import ( - MacroPrimitiveToken, -) - -if TYPE_CHECKING: - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class Code22LowerLeftLineToken(MacroPrimitiveToken): - """Code 22 lower left line macro primitive.""" - - def parser2_visit_token(self, context: Parser2Context) -> None: - """Perform actions on the context implicated by this token.""" - context.get_hooks().macro_code_22_lower_left_line.pre_parser_visit_token( - self, - context, - ) - context.get_hooks().macro_code_22_lower_left_line.on_parser_visit_token( - self, - context, - ) - context.get_hooks().macro_code_22_lower_left_line.post_parser_visit_token( - self, - context, - ) diff --git a/src/pygerber/gerberx3/tokenizer/tokens/macro/statements/code_2_vector_line.py b/src/pygerber/gerberx3/tokenizer/tokens/macro/statements/code_2_vector_line.py deleted file mode 100644 index 7bf77716..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/macro/statements/code_2_vector_line.py +++ /dev/null @@ -1,31 +0,0 @@ -"""Macro primitives tokens.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from pygerber.gerberx3.tokenizer.tokens.macro.statements.primitive import ( - MacroPrimitiveToken, -) - -if TYPE_CHECKING: - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class Code2VectorLineToken(MacroPrimitiveToken): - """Vector line macro primitive.""" - - def parser2_visit_token(self, context: Parser2Context) -> None: - """Perform actions on the context implicated by this token.""" - context.get_hooks().macro_code_2_vector_line.pre_parser_visit_token( - self, - context, - ) - context.get_hooks().macro_code_2_vector_line.on_parser_visit_token( - self, - context, - ) - context.get_hooks().macro_code_2_vector_line.post_parser_visit_token( - self, - context, - ) diff --git a/src/pygerber/gerberx3/tokenizer/tokens/macro/statements/code_4_outline.py b/src/pygerber/gerberx3/tokenizer/tokens/macro/statements/code_4_outline.py deleted file mode 100644 index 52ced7fc..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/macro/statements/code_4_outline.py +++ /dev/null @@ -1,154 +0,0 @@ -"""Macro primitives tokens.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING, ClassVar, List - -from pygerber.gerberx3.tokenizer.tokens.macro.expressions.macro_expression import ( - MacroExpressionToken, -) -from pygerber.gerberx3.tokenizer.tokens.macro.point import Point -from pygerber.gerberx3.tokenizer.tokens.macro.statements.primitive import ( - MacroPrimitiveToken, -) - -if TYPE_CHECKING: - from pyparsing import ParseResults - from typing_extensions import Self - - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class Code4OutlineToken(MacroPrimitiveToken): - """## 4.5.1.6 Outline, Code 4. - - An outline primitive is an area defined by its outline or contour. The outline is a - polygon, consisting of linear segments only, defined by its start vertex and n - subsequent vertices. The outline must be closed, i.e. the last vertex must be equal - to the start vertex. The outline must comply with all the requirements of a contour - according to [4.10.3](https://www.ucamco.com/files/downloads/file_en/456/gerber-layer-format-specification-revision-2023-08_en.pdf#page=91). - - The maximum number of vertices is 5000. The purpose of this primitive is to create - apertures to flash pads with special shapes. The purpose is not to create copper - pours. Use the region statement for copper pours; see - [4.10](https://www.ucamco.com/files/downloads/file_en/456/gerber-layer-format-specification-revision-2023-08_en.pdf#page=90). - - --- - - ## Example - - ```gerber - %AMTriangle_30* - 4,1,3, - 1,-1, - 1,1, - 2,1, - 1,-1, - 30* - % - ``` - - --- - - See section 4.5.1.6 of [The Gerber Layer Format Specification](https://www.ucamco.com/files/downloads/file_en/456/gerber-layer-format-specification-revision-2023-08_en.pdf#page=63) - - """ - - symbol: ClassVar[str] = "4" - - def __init__( - self, - string: str, - location: int, - exposure: MacroExpressionToken, - number_of_vertices: MacroExpressionToken, - start_x: MacroExpressionToken, - start_y: MacroExpressionToken, - rotation: MacroExpressionToken, - point: List[Point], - ) -> None: - super().__init__(string, location) - self.exposure = exposure - self.number_of_vertices = number_of_vertices - self.start_x = start_x - self.start_y = start_y - self.rotation = rotation - self.point = point - - @classmethod - def new(cls, string: str, location: int, tokens: ParseResults) -> Self: - """Create instance of this class. - - Created to be used as callback in `ParserElement.set_parse_action()`. - """ - exposure = MacroExpressionToken.ensure_type(tokens["exposure"]) - number_of_vertices = MacroExpressionToken.ensure_type( - tokens["number_of_vertices"], - ) - start_x = MacroExpressionToken.ensure_type(tokens["start_x"]) - start_y = MacroExpressionToken.ensure_type(tokens["start_y"]) - rotation = MacroExpressionToken.ensure_type(tokens["rotation"]) - - p = p if (p := tokens.get("point")) is not None else [] - point = [Point.ensure_type(e) for e in p] - - return cls( - string=string, - location=location, - exposure=exposure, - number_of_vertices=number_of_vertices, - start_x=start_x, - start_y=start_y, - rotation=rotation, - point=point, - ) - - def parser2_visit_token(self, context: Parser2Context) -> None: - """Perform actions on the context implicated by this token.""" - context.get_hooks().macro_code_4_outline.pre_parser_visit_token( - self, - context, - ) - context.get_hooks().macro_code_4_outline.on_parser_visit_token( - self, - context, - ) - context.get_hooks().macro_code_4_outline.post_parser_visit_token( - self, - context, - ) - - def get_gerber_code( - self, - indent: str = "", - endline: str = "\n", - ) -> str: - """Get gerber code represented by this token.""" - string = self.symbol - - string += f",{self.exposure.get_gerber_code(indent, endline)}" - string += f",{self.number_of_vertices.get_gerber_code(indent, endline)}" - string += f",{self.start_x.get_gerber_code(indent, endline)}" - string += f",{self.start_y.get_gerber_code(indent, endline)}" - - for point in self.point: - string += f",{point.get_gerber_code(indent, endline)}" - - string += f",{self.rotation.get_gerber_code(indent, endline)}" - - return indent + string - - def __str__(self) -> str: - string = super().__str__() - - string += f"\n {self.exposure}" - string += f"\n {self.number_of_vertices}" - string += f"\n {self.start_x}" - string += f"\n {self.start_y}" - - for point in self.point: - string += f"\n {point}" - - string += f"\n {self.rotation}" - - return string diff --git a/src/pygerber/gerberx3/tokenizer/tokens/macro/statements/code_5_polygon.py b/src/pygerber/gerberx3/tokenizer/tokens/macro/statements/code_5_polygon.py deleted file mode 100644 index cd8587c8..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/macro/statements/code_5_polygon.py +++ /dev/null @@ -1,130 +0,0 @@ -"""Macro primitive polygon token.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING, ClassVar - -from pygerber.gerberx3.tokenizer.tokens.macro.expressions.macro_expression import ( - MacroExpressionToken, -) -from pygerber.gerberx3.tokenizer.tokens.macro.statements.primitive import ( - MacroPrimitiveToken, -) - -if TYPE_CHECKING: - from pyparsing import ParseResults - from typing_extensions import Self - - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class Code5PolygonToken(MacroPrimitiveToken): - """## 4.5.1.7 Polygon, Code 5. - - A polygon primitive is a regular polygon defined by the number of vertices n, the - center point and the diameter of the circumscribed circle. - - --- - - ## Example - - ```gerber - %AMPolygon* - 5,1,8,0,0,8,0*% - ``` - - --- - - See section 4.5.1.7 of [The Gerber Layer Format Specification](https://www.ucamco.com/files/downloads/file_en/456/gerber-layer-format-specification-revision-2023-08_en.pdf#page=65) - - """ - - symbol: ClassVar[str] = "5" - - def __init__( - self, - string: str, - location: int, - exposure: MacroExpressionToken, - number_of_vertices: MacroExpressionToken, - center_x: MacroExpressionToken, - center_y: MacroExpressionToken, - diameter: MacroExpressionToken, - rotation: MacroExpressionToken, - ) -> None: - super().__init__(string, location) - self.exposure = exposure - self.number_of_vertices = number_of_vertices - self.center_x = center_x - self.center_y = center_y - self.diameter = diameter - self.rotation = rotation - - @classmethod - def new(cls, string: str, location: int, tokens: ParseResults) -> Self: - """Create instance of this class. - - Created to be used as callback in `ParserElement.set_parse_action()`. - """ - exposure = MacroExpressionToken.ensure_type(tokens["exposure"]) - number_of_vertices = MacroExpressionToken.ensure_type( - tokens["number_of_vertices"], - ) - center_x = MacroExpressionToken.ensure_type(tokens["center_x"]) - center_y = MacroExpressionToken.ensure_type(tokens["center_y"]) - diameter = MacroExpressionToken.ensure_type(tokens["diameter"]) - rotation = MacroExpressionToken.ensure_type(tokens["rotation"]) - return cls( - string=string, - location=location, - exposure=exposure, - number_of_vertices=number_of_vertices, - center_x=center_x, - center_y=center_y, - diameter=diameter, - rotation=rotation, - ) - - def parser2_visit_token(self, context: Parser2Context) -> None: - """Perform actions on the context implicated by this token.""" - context.get_hooks().macro_code_5_polygon.pre_parser_visit_token( - self, - context, - ) - context.get_hooks().macro_code_5_polygon.on_parser_visit_token( - self, - context, - ) - context.get_hooks().macro_code_5_polygon.post_parser_visit_token( - self, - context, - ) - - def get_gerber_code( - self, - indent: str = "", - endline: str = "\n", - ) -> str: - """Get gerber code represented by this token.""" - string = self.symbol - - string += f",{self.exposure.get_gerber_code(indent, endline)}" - string += f",{self.number_of_vertices.get_gerber_code(indent, endline)}" - string += f",{self.center_x.get_gerber_code(indent, endline)}" - string += f",{self.center_y.get_gerber_code(indent, endline)}" - string += f",{self.diameter.get_gerber_code(indent, endline)}" - string += f",{self.rotation.get_gerber_code(indent, endline)}" - - return indent + string - - def __str__(self) -> str: - string = super().__str__() - - string += f"\n {self.exposure}" - string += f"\n {self.number_of_vertices}" - string += f"\n {self.center_x}" - string += f"\n {self.center_y}" - string += f"\n {self.diameter}" - string += f"\n {self.rotation}" - - return string diff --git a/src/pygerber/gerberx3/tokenizer/tokens/macro/statements/code_6_moire.py b/src/pygerber/gerberx3/tokenizer/tokens/macro/statements/code_6_moire.py deleted file mode 100644 index 1fc72583..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/macro/statements/code_6_moire.py +++ /dev/null @@ -1,31 +0,0 @@ -"""Macro primitive Moire.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from pygerber.gerberx3.tokenizer.tokens.macro.statements.primitive import ( - MacroPrimitiveToken, -) - -if TYPE_CHECKING: - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class Code6MoireToken(MacroPrimitiveToken): - """Moire macro primitive.""" - - def parser2_visit_token(self, context: Parser2Context) -> None: - """Perform actions on the context implicated by this token.""" - context.get_hooks().macro_code_6_moire.pre_parser_visit_token( - self, - context, - ) - context.get_hooks().macro_code_6_moire.on_parser_visit_token( - self, - context, - ) - context.get_hooks().macro_code_6_moire.post_parser_visit_token( - self, - context, - ) diff --git a/src/pygerber/gerberx3/tokenizer/tokens/macro/statements/code_7_thermal.py b/src/pygerber/gerberx3/tokenizer/tokens/macro/statements/code_7_thermal.py deleted file mode 100644 index 99f5c922..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/macro/statements/code_7_thermal.py +++ /dev/null @@ -1,126 +0,0 @@ -"""Macro primitives tokens.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING, ClassVar - -from pygerber.gerberx3.tokenizer.tokens.macro.expressions.macro_expression import ( - MacroExpressionToken, -) -from pygerber.gerberx3.tokenizer.tokens.macro.statements.primitive import ( - MacroPrimitiveToken, -) - -if TYPE_CHECKING: - from pyparsing import ParseResults - from typing_extensions import Self - - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class Code7ThermalToken(MacroPrimitiveToken): - """## 4.5.1.8 Thermal, Code 7. - - The thermal primitive is a ring (annulus) interrupted by four gaps. Exposure is - always on. - - --- - - ## Example - - ```gerber - %AMThermal* - 7,0,0,0.95,0.75,0.175,0.0*% - ``` - - --- - - See section 4.5.1.8 of [The Gerber Layer Format Specification](https://www.ucamco.com/files/downloads/file_en/456/gerber-layer-format-specification-revision-2023-08_en.pdf#page=66) - - """ - - symbol: ClassVar[str] = "7" - - def __init__( - self, - string: str, - location: int, - center_x: MacroExpressionToken, - center_y: MacroExpressionToken, - outer_diameter: MacroExpressionToken, - inner_diameter: MacroExpressionToken, - gap: MacroExpressionToken, - rotation: MacroExpressionToken, - ) -> None: - super().__init__(string, location) - self.center_x = center_x - self.center_y = center_y - self.outer_diameter = outer_diameter - self.inner_diameter = inner_diameter - self.gap = gap - self.rotation = rotation - - @classmethod - def new(cls, string: str, location: int, tokens: ParseResults) -> Self: - """Initialize token object.""" - center_x = MacroExpressionToken.ensure_type(tokens["center_x"]) - center_y = MacroExpressionToken.ensure_type(tokens["center_y"]) - outer_diameter = MacroExpressionToken.ensure_type(tokens["outer_diameter"]) - inner_diameter = MacroExpressionToken.ensure_type(tokens["inner_diameter"]) - gap = MacroExpressionToken.ensure_type(tokens["gap"]) - rotation = MacroExpressionToken.ensure_type(tokens["rotation"]) - - return cls( - string, - location, - center_x=center_x, - center_y=center_y, - outer_diameter=outer_diameter, - inner_diameter=inner_diameter, - gap=gap, - rotation=rotation, - ) - - def parser2_visit_token(self, context: Parser2Context) -> None: - """Perform actions on the context implicated by this token.""" - context.get_hooks().macro_code_7_thermal.pre_parser_visit_token( - self, - context, - ) - context.get_hooks().macro_code_7_thermal.on_parser_visit_token( - self, - context, - ) - context.get_hooks().macro_code_7_thermal.post_parser_visit_token( - self, - context, - ) - - def get_gerber_code( - self, - indent: str = "", - endline: str = "\n", - ) -> str: - """Get gerber code represented by this token.""" - string = self.symbol - - string += f",{self.center_x.get_gerber_code(indent, endline)}" - string += f",{self.center_y.get_gerber_code(indent, endline)}" - string += f",{self.outer_diameter.get_gerber_code(indent, endline)}" - string += f",{self.inner_diameter.get_gerber_code(indent, endline)}" - string += f",{self.gap.get_gerber_code(indent, endline)}" - string += f",{self.rotation.get_gerber_code(indent, endline)}" - - return indent + string - - def __str__(self) -> str: - string = super().__str__() - - string += f"\n {self.center_x}" - string += f"\n {self.center_y}" - string += f"\n {self.outer_diameter}" - string += f"\n {self.inner_diameter}" - string += f"\n {self.gap}" - string += f"\n {self.rotation}" - - return string diff --git a/src/pygerber/gerberx3/tokenizer/tokens/macro/statements/comment.py b/src/pygerber/gerberx3/tokenizer/tokens/macro/statements/comment.py deleted file mode 100644 index 608cc92e..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/macro/statements/comment.py +++ /dev/null @@ -1,73 +0,0 @@ -"""Comment token.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from pygerber.gerberx3.tokenizer.tokens.macro.statements.statement import ( - MacroStatementToken, -) - -if TYPE_CHECKING: - from pyparsing import ParseResults - from typing_extensions import Self - - -class MacroComment(MacroStatementToken): - """## 4.5.1.2 Comment, Code 0. - - The comment primitive has no effect on the image but adds human-readable comments - in an AM command. The comment primitive starts with the '0' code followed by a space - and then a single-line text string. The text string follows the syntax for strings - in section 3.4.3. - - --- - - ## Example - - ```gerber - %AMBox* - 0 Rectangle with rounded corners, with rotation* - 0 The origin of the aperture is its center* - 0 $1 X-size* - 0 $2 Y-size* - 0 $3 Rounding radius* - 0 $4 Rotation angle, in degrees counterclockwise* - 0 Add two overlapping rectangle primitives as box body* - 21,1,$1, $2-$3-$3,0,0,$4 - 21,1,$1-$3-$3,$2,0,0,$4* - 0 Add four circle primitives for the rounded corners* - $5-$1/2* - $6-$2/2* - $7=2x$3* - 1,1, $7, $5-$3, $6-$3, $4* - 1,1, $7-$5+$3,$6-$3, $4* - 1,1, $7-$5+$3,-$6+$3, $4* - 1,1, $7, $5-$3,-$6+$3, $4*% - ``` - - --- - - See section 4.5.1.2 of [The Gerber Layer Format Specification Revision 2023.03](https://www.ucamco.com/files/downloads/file_en/456/gerber-layer-format-specification-revision-2023-03_en.pdf#page=59) - """ - - def __init__(self, string: str, location: int, content: str) -> None: - super().__init__(string, location) - self.content = content - - @classmethod - def new(cls, string: str, location: int, tokens: ParseResults) -> Self: - """Create instance of this class. - - Created to be used as callback in `ParserElement.set_parse_action()`. - """ - content: str = str(tokens["string"]) - return cls(string=string, location=location, content=content) - - def get_gerber_code( - self, - indent: str = "", - endline: str = "\n", # noqa: ARG002 - ) -> str: - """Get gerber code represented by this token.""" - return f"{indent}0 {self.content}" diff --git a/src/pygerber/gerberx3/tokenizer/tokens/macro/statements/primitive.py b/src/pygerber/gerberx3/tokenizer/tokens/macro/statements/primitive.py deleted file mode 100644 index 5e312606..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/macro/statements/primitive.py +++ /dev/null @@ -1,15 +0,0 @@ -"""Macro primitives tokens.""" - -from __future__ import annotations - -from typing import ClassVar - -from pygerber.gerberx3.tokenizer.tokens.macro.statements.statement import ( - MacroStatementToken, -) - - -class MacroPrimitiveToken(MacroStatementToken): - """Wrapper for macro primitive token, common base class for specialized tokens.""" - - symbol: ClassVar[str] = "X" diff --git a/src/pygerber/gerberx3/tokenizer/tokens/macro/statements/statement.py b/src/pygerber/gerberx3/tokenizer/tokens/macro/statements/statement.py deleted file mode 100644 index bc923243..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/macro/statements/statement.py +++ /dev/null @@ -1,17 +0,0 @@ -"""Macro statement token.""" - -from __future__ import annotations - -from pygerber.gerberx3.tokenizer.tokens.bases.command import CommandToken - - -class MacroStatementToken(CommandToken): - """Wrapper for in-macro expression.""" - - def get_gerber_code( - self, - indent: str = "", - endline: str = "\n", # noqa: ARG002 - ) -> str: - """Get gerber code represented by this token.""" - return f"{indent}0 {self.__class__.__qualname__} no formatting available" diff --git a/src/pygerber/gerberx3/tokenizer/tokens/macro/statements/variable_assignment.py b/src/pygerber/gerberx3/tokenizer/tokens/macro/statements/variable_assignment.py deleted file mode 100644 index cf536ed1..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/macro/statements/variable_assignment.py +++ /dev/null @@ -1,144 +0,0 @@ -"""Macro variable definition token.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from pygerber.gerberx3.tokenizer.tokens.macro.expressions.macro_expression import ( - MacroExpressionToken, -) -from pygerber.gerberx3.tokenizer.tokens.macro.expressions.variable_name import ( - MacroVariableName, -) -from pygerber.gerberx3.tokenizer.tokens.macro.statements.statement import ( - MacroStatementToken, -) - -if TYPE_CHECKING: - from pyparsing import ParseResults - from typing_extensions import Self - - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class MacroVariableAssignment(MacroStatementToken): - """## 4.5.4.3 Definition of New Variable. - - New variables can be defined by an assign statement as follows: `$4=$1x1.25-$3`. The - right-hand side is any arithmetic expression as in the previous section. - - The variable values are determined as follows: - - - `$1`, `$2`, ..., `$n` take the values of the n parameters of the calling AD command. - - New variables get their value from their defining expression. - - The undefined variables are 0. - - Macro variables cannot be redefined. - - --- - - ## Example #1 - - ```gerber - %AMDONUTCAL* - 1,1,$1,$2,$3* - $4=$1x1.25* - 1,0,$4,$2,$3*% - ``` - - The AD command contains four parameters which define the first four macro variables: - - ```yaml - $1 = 0.02 - $2 = 0 - $3 = 0 - $4 = 0.06 - ``` - - The variable `$5` is defined in the macro body and becomes - - ```yaml - $5 = 0.06 x 0.25 = 0.015 - ``` - - ## Example #2 - - ```gerber - %AMTEST1* - 1,1,$1,$2,$3* - $4=$1x0.75* - $5=($2+100)x1.75* - 1,0,$4,$5,$3*% - ``` - - ## Example #3 - - ``` - %AMTEST2* - $4=$1x0.75* - $5=100+$3* - 1,1,$1,$2,$3* - 1,0,$4,$2,$5* - $6=$4x0.5* - 1,0,$6,$2,$5*% - ``` - - --- - - See section 4.5.4.3 of [The Gerber Layer Format Specification](https://www.ucamco.com/files/downloads/file_en/456/gerber-layer-format-specification-revision-2023-08_en.pdf#page=71) - - """ # noqa: E501 - - def __init__( - self, - string: str, - location: int, - variable: MacroVariableName, - value: MacroExpressionToken, - ) -> None: - super().__init__(string, location) - self.variable = variable - self.value = value - - @classmethod - def new(cls, string: str, location: int, tokens: ParseResults) -> Self: - """Create instance of this class. - - Created to be used as callback in `ParserElement.set_parse_action()`. - """ - variable = MacroVariableName.ensure_type(tokens["macro_variable_name"]) - value = MacroExpressionToken.ensure_type(tokens["value"]) - return cls( - string=string, - location=location, - variable=variable, - value=value, - ) - - def parser2_visit_token(self, context: Parser2Context) -> None: - """Perform actions on the context implicated by this token.""" - context.get_hooks().macro_variable_assignment.pre_parser_visit_token( - self, - context, - ) - context.get_hooks().macro_variable_assignment.on_parser_visit_token( - self, - context, - ) - context.get_hooks().macro_variable_assignment.post_parser_visit_token( - self, - context, - ) - - def get_gerber_code( - self, - indent: str = "", - endline: str = "\n", - ) -> str: - """Get gerber code represented by this token.""" - return ( - f"{indent}{self.variable.get_gerber_code(endline=endline)}=" - f"{self.value.get_gerber_code(endline=endline)}" - ) - - def __str__(self) -> str: - return f"{super().__str__()}::[{self.variable} = {self.value}]" diff --git a/src/pygerber/gerberx3/tokenizer/tokens/mo_unit_mode.py b/src/pygerber/gerberx3/tokenizer/tokens/mo_unit_mode.py deleted file mode 100644 index ea599f5e..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/mo_unit_mode.py +++ /dev/null @@ -1,50 +0,0 @@ -"""Wrapper for set unit mode token.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from pygerber.gerberx3.state_enums import Unit -from pygerber.gerberx3.tokenizer.tokens.bases.extended_command import ( - ExtendedCommandToken, -) - -if TYPE_CHECKING: - from pyparsing import ParseResults - from typing_extensions import Self - - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class UnitMode(ExtendedCommandToken): - """Wrapper for set unit mode token. - - Sets the unit to mm or inch. - """ - - def __init__(self, string: str, location: int, unit: Unit) -> None: - super().__init__(string, location) - self.unit = unit - - @classmethod - def new(cls, string: str, location: int, tokens: ParseResults) -> Self: - """Create instance of this class. - - Created to be used as callback in `ParserElement.set_parse_action()`. - """ - unit: Unit = Unit(tokens["unit"]) - return cls(string=string, location=location, unit=unit) - - def parser2_visit_token(self, context: Parser2Context) -> None: - """Perform actions on the context implicated by this token.""" - context.get_hooks().unit_mode.pre_parser_visit_token(self, context) - context.get_hooks().unit_mode.on_parser_visit_token(self, context) - context.get_hooks().unit_mode.post_parser_visit_token(self, context) - - def get_gerber_code( - self, - indent: str = "", # noqa: ARG002 - endline: str = "\n", # noqa: ARG002 - ) -> str: - """Get gerber code represented by this token.""" - return f"MO{self.unit.value}" diff --git a/src/pygerber/gerberx3/tokenizer/tokens/of_image_offset.py b/src/pygerber/gerberx3/tokenizer/tokens/of_image_offset.py deleted file mode 100644 index 7971e831..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/of_image_offset.py +++ /dev/null @@ -1,85 +0,0 @@ -"""### Load Name (LN). - -Note: The LN command was deprecated in revision I4 from October 2013. - -The historic `LN` command doesn't influence the image in any manner and can safely be -overlooked. - -Function of the `LN` command: -- `LN` is designed to allocate a name to the following section of the file. -- It was originally conceptualized to serve as a human-readable comment. -- For creating human-readable comments, it's advisable to utilize the standard `G04` - command. -- The `LN` command has the flexibility to be executed multiple times within a file. - -SPEC: `2023.03` SECTION: `8.1.6` -""" - -from __future__ import annotations - -from decimal import Decimal -from typing import TYPE_CHECKING, Optional - -from pygerber.gerberx3.tokenizer.tokens.bases.extended_command import ( - ExtendedCommandToken, -) - -if TYPE_CHECKING: - from pyparsing import ParseResults - from typing_extensions import Self - - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class ImageOffset(ExtendedCommandToken): - """### Image Offset (OF). - - The OF command is deprecated since revision I1 from December 2012. - - OF moves the final image up to plus or minus 99999.99999 units from the imaging - device (0, 0) point. The image can be moved along the imaging device A or B axis, - or both. The offset values used by OF command are absolute. If the A or B part is - missing, the corresponding offset is 0. The offset values are expressed in units - specified by MO command. This command affects the entire image. It can only be - used once, at the beginning of the file. The order of execution is always MI, SF, - OF, IR and AS, independent of their order of appearance in the file. - - See section 8.1.8 of The Gerber Layer Format Specification Revision 2023.03 - https://argmaster.github.io/pygerber/latest/gerber_specification/revision_2023_03.html - """ - - def __init__( - self, - string: str, - location: int, - a: Optional[Decimal], - b: Optional[Decimal], - ) -> None: - super().__init__(string, location) - self.a = a - self.b = b - - @classmethod - def new(cls, string: str, location: int, tokens: ParseResults) -> Self: - """Create instance of this class. - - Created to be used as callback in `ParserElement.set_parse_action()`. - """ - a = Decimal(str(tmp)) if (tmp := tokens.get("a")) is not None else None - b = Decimal(str(tmp)) if (tmp := tokens.get("b")) is not None else None - return cls(string=string, location=location, a=a, b=b) - - def parser2_visit_token(self, context: Parser2Context) -> None: - """Perform actions on the context implicated by this token.""" - context.get_hooks().image_offset.pre_parser_visit_token(self, context) - context.get_hooks().image_offset.on_parser_visit_token(self, context) - context.get_hooks().image_offset.post_parser_visit_token(self, context) - - def get_gerber_code( - self, - indent: str = "", # noqa: ARG002 - endline: str = "\n", # noqa: ARG002 - ) -> str: - """Get gerber code represented by this token.""" - a = f"A{self.a}" if self.a is not None else "" - b = f"B{self.b}" if self.b is not None else "" - return f"OF{a}{b}" diff --git a/src/pygerber/gerberx3/tokenizer/tokens/sr_step_repeat.py b/src/pygerber/gerberx3/tokenizer/tokens/sr_step_repeat.py deleted file mode 100644 index 4bf2a51b..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/sr_step_repeat.py +++ /dev/null @@ -1,92 +0,0 @@ -"""Wrapper for aperture select token.""" - -from __future__ import annotations - -from decimal import Decimal -from typing import TYPE_CHECKING - -from pygerber.gerberx3.tokenizer.tokens.bases.command import CommandToken - -if TYPE_CHECKING: - from pyparsing import ParseResults - from typing_extensions import Self - - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class StepRepeatBegin(CommandToken): - """Wrapper for SR begin token. - - Opens an SR statement and starts block accumulation. - - See section 4.10 of The Gerber Layer Format Specification Revision 2023.03 - https://argmaster.github.io/pygerber/latest/gerber_specification/revision_2023_03.html - """ - - def __init__( - self, - string: str, - location: int, - x_repeat: int, - y_repeat: int, - x_step: Decimal, - y_step: Decimal, - ) -> None: - super().__init__(string, location) - self.x_repeat = x_repeat - self.y_repeat = y_repeat - self.x_step = x_step - self.y_step = y_step - - @classmethod - def new(cls, string: str, location: int, tokens: ParseResults) -> Self: - """Create instance of this class. - - Created to be used as callback in `ParserElement.set_parse_action()`. - """ - x_repeat = int(str(tokens.get("x_repeat", "0"))) - y_repeat = int(str(tokens.get("y_repeat", "0"))) - x_step = Decimal(str(tokens.get("x_step", "0"))) - y_step = Decimal(str(tokens.get("y_step", "0"))) - return cls( - string=string, - location=location, - x_repeat=x_repeat, - y_repeat=y_repeat, - x_step=x_step, - y_step=y_step, - ) - - def parser2_visit_token(self, context: Parser2Context) -> None: - """Perform actions on the context implicated by this token.""" - context.get_hooks().step_repeat_begin.pre_parser_visit_token(self, context) - context.get_hooks().step_repeat_begin.on_parser_visit_token(self, context) - context.get_hooks().step_repeat_begin.post_parser_visit_token(self, context) - - def get_gerber_code( - self, - indent: str = "", # noqa: ARG002 - endline: str = "\n", # noqa: ARG002 - ) -> str: - """Get gerber code represented by this token.""" - return f"SRX{self.x_repeat}Y{self.y_repeat}I{self.x_step}J{self.y_step}" - - -class StepRepeatEnd(CommandToken): - """Wrapper for SR end token. - - Ends step and repeat statement. - """ - - def parser2_visit_token(self, context: Parser2Context) -> None: - """Perform actions on the context implicated by this token.""" - context.get_hooks().step_repeat_end.pre_parser_visit_token(self, context) - context.get_hooks().step_repeat_end.on_parser_visit_token(self, context) - context.get_hooks().step_repeat_end.post_parser_visit_token(self, context) - - def get_gerber_code( - self, - indent: str = "", # noqa: ARG002 - endline: str = "\n", # noqa: ARG002 - ) -> str: - """Get gerber code represented by this token.""" - return "SR" diff --git a/src/pygerber/gerberx3/tokenizer/tokens/ta_aperture_attribute.py b/src/pygerber/gerberx3/tokenizer/tokens/ta_aperture_attribute.py deleted file mode 100644 index e0e3c37b..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/ta_aperture_attribute.py +++ /dev/null @@ -1,45 +0,0 @@ -"""Comment token.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from pygerber.gerberx3.tokenizer.tokens.attribute_token import ( - SetAttributeToken, -) - -if TYPE_CHECKING: - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class ApertureAttribute(SetAttributeToken): - """Add an aperture attribute to the dictionary or modify it. - - An aperture attribute is attached to an aperture or a region. They are a method to - assign attributes to graphical objects in bulk: all objects that are created with - an aperture inherit its attributes; for example, a via attribute on an aperture - means that all pads flashed with this aperture are via pads. Providing information - about graphical objects via their apertures is elegant, compact and efficient. As - region objects are created without intermediary aperture, aperture objects can be - assigned to regions directly. - - See section 5.3 of The Gerber Layer Format Specification Revision 2023.03 - https://argmaster.github.io/pygerber/latest/gerber_specification/revision_2023_03.html - """ - - def parser2_visit_token(self, context: Parser2Context) -> None: - """Perform actions on the context implicated by this token.""" - context.get_hooks().aperture_attribute.pre_parser_visit_token(self, context) - context.get_hooks().aperture_attribute.on_parser_visit_token(self, context) - context.get_hooks().aperture_attribute.post_parser_visit_token(self, context) - - def get_gerber_code( - self, - indent: str = "", # noqa: ARG002 - endline: str = "\n", # noqa: ARG002 - ) -> str: - """Get gerber code represented by this token.""" - value = f",{self.value}" if self.value else "" - return f"TA{self.name}{value}" - - def __str__(self) -> str: - return f"{super().__str__()}::[{self.name} -> {self.value}]" diff --git a/src/pygerber/gerberx3/tokenizer/tokens/td_delete_attribute.py b/src/pygerber/gerberx3/tokenizer/tokens/td_delete_attribute.py deleted file mode 100644 index 04a3dd0c..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/td_delete_attribute.py +++ /dev/null @@ -1,61 +0,0 @@ -"""Comment token.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING, Optional - -from pygerber.gerberx3.tokenizer.tokens.attribute_token import AttributeToken - -if TYPE_CHECKING: - from pyparsing import ParseResults - from typing_extensions import Self - - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class DeleteAttribute(AttributeToken): - """Delete one or all attributes in the dictionary. - - The TD command deletes an aperture attribute or object attribute from the attributes - dictionary. (File attributes are immutable and are not deleted.) - - See section 5.5 of The Gerber Layer Format Specification Revision 2023.03 - https://argmaster.github.io/pygerber/latest/gerber_specification/revision_2023_03.html - """ - - def __init__(self, string: str, location: int, name: Optional[str]) -> None: - super().__init__(string, location) - self.name = name - - @classmethod - def new(cls, string: str, location: int, tokens: ParseResults) -> Self: - """Create instance of this class. - - Created to be used as callback in `ParserElement.set_parse_action()`. - """ - name = tokens.get("attribute_name") - if name is not None: - name = str(name) - - return cls( - string=string, - location=location, - name=name, - ) - - def parser2_visit_token(self, context: Parser2Context) -> None: - """Perform actions on the context implicated by this token.""" - context.get_hooks().delete_attribute.pre_parser_visit_token(self, context) - context.get_hooks().delete_attribute.on_parser_visit_token(self, context) - context.get_hooks().delete_attribute.post_parser_visit_token(self, context) - - def get_gerber_code( - self, - indent: str = "", # noqa: ARG002 - endline: str = "\n", # noqa: ARG002 - ) -> str: - """Get gerber code represented by this token.""" - name = self.name if self.name is not None else "" - return f"TD{name}" - - def __str__(self) -> str: - return f"{super().__str__()}::[{self.name}]" diff --git a/src/pygerber/gerberx3/tokenizer/tokens/tf_file_attribute.py b/src/pygerber/gerberx3/tokenizer/tokens/tf_file_attribute.py deleted file mode 100644 index 902f40ec..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/tf_file_attribute.py +++ /dev/null @@ -1,41 +0,0 @@ -"""Comment token.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from pygerber.gerberx3.tokenizer.tokens.attribute_token import ( - SetAttributeToken, -) - -if TYPE_CHECKING: - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class FileAttribute(SetAttributeToken): - """File attribute token. - - The semantics of a file attribute specifies where it must be defined, typically in - the header of the file. File attributes are immutable. They cannot be redefined or - deleted. - - See section 5.2 of The Gerber Layer Format Specification Revision 2023.03 - https://argmaster.github.io/pygerber/latest/gerber_specification/revision_2023_03.html - """ - - def parser2_visit_token(self, context: Parser2Context) -> None: - """Perform actions on the context implicated by this token.""" - context.get_hooks().file_attribute.pre_parser_visit_token(self, context) - context.get_hooks().file_attribute.on_parser_visit_token(self, context) - context.get_hooks().file_attribute.post_parser_visit_token(self, context) - - def get_gerber_code( - self, - indent: str = "", # noqa: ARG002 - endline: str = "\n", # noqa: ARG002 - ) -> str: - """Get gerber code represented by this token.""" - value = f",{self.value}" if self.value else "" - return f"TF{self.name}{value}" - - def __str__(self) -> str: - return f"{super().__str__()}::[{self.name} -> {self.value}]" diff --git a/src/pygerber/gerberx3/tokenizer/tokens/to_object_attribute.py b/src/pygerber/gerberx3/tokenizer/tokens/to_object_attribute.py deleted file mode 100644 index eabe7ffd..00000000 --- a/src/pygerber/gerberx3/tokenizer/tokens/to_object_attribute.py +++ /dev/null @@ -1,43 +0,0 @@ -"""Comment token.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from pygerber.gerberx3.tokenizer.tokens.attribute_token import ( - SetAttributeToken, -) - -if TYPE_CHECKING: - from pygerber.gerberx3.parser2.context2 import Parser2Context - - -class ObjectAttribute(SetAttributeToken): - """Add an object attribute to the dictionary or modify it. - - An object attribute is attached to graphical objects. When a D01, D03 or region - statement (G36/G37) creates an object all object attributes in the attribute - dictionary are attached to it. As attribute commands are not allowed inside a region - statement, all regions created by that statement have the same object attributes. - Once attached to an object they cannot be chan - - See section 5.4 of The Gerber Layer Format Specification Revision 2023.03 - https://argmaster.github.io/pygerber/latest/gerber_specification/revision_2023_03.html - """ - - def parser2_visit_token(self, context: Parser2Context) -> None: - """Perform actions on the context implicated by this token.""" - context.get_hooks().object_attribute.pre_parser_visit_token(self, context) - context.get_hooks().object_attribute.on_parser_visit_token(self, context) - context.get_hooks().object_attribute.post_parser_visit_token(self, context) - - def get_gerber_code( - self, - indent: str = "", # noqa: ARG002 - endline: str = "\n", # noqa: ARG002 - ) -> str: - """Get gerber code represented by this token.""" - value = f",{self.value}" if self.value else "" - return f"TO{self.name}{value}" - - def __str__(self) -> str: - return f"{super().__str__()}::[{self.name} -> {self.value}]" diff --git a/src/pygerber/vm/commands/shape_segments/shape_segment.py b/src/pygerber/vm/commands/shape_segments/shape_segment.py index aa4a1833..60e4d668 100644 --- a/src/pygerber/vm/commands/shape_segments/shape_segment.py +++ b/src/pygerber/vm/commands/shape_segments/shape_segment.py @@ -8,7 +8,7 @@ import pyparsing as pp -from pygerber.gerberx3.ast.nodes.model import ModelType +from pygerber.gerber.ast.nodes.model import ModelType from pygerber.vm.types import Box, Matrix3x3 if TYPE_CHECKING: diff --git a/src/pygerber/vm/pillow/vm.py b/src/pygerber/vm/pillow/vm.py index cb27da23..73b0b56c 100644 --- a/src/pygerber/vm/pillow/vm.py +++ b/src/pygerber/vm/pillow/vm.py @@ -60,6 +60,14 @@ def get_image(self, style: Style = Style.presets.COPPER) -> Image.Image: image, (0, 0, 0, 255), style.background.as_rgba_int() ) + def get_image_no_style(self) -> Image.Image: + """Get image without any color scheme.""" + if self.image is None: + msg = "Image is not available." + raise ValueError(msg) + + return self.image + def replace_color( input_image: Image.Image, diff --git a/src/pygerber/vm/types/matrix.py b/src/pygerber/vm/types/matrix.py index 6d283125..2f7232ff 100644 --- a/src/pygerber/vm/types/matrix.py +++ b/src/pygerber/vm/types/matrix.py @@ -7,7 +7,7 @@ from math import cos, radians, sin from typing import TYPE_CHECKING, Tuple, TypeVar -from pygerber.gerberx3.ast.nodes.types import Double +from pygerber.gerber.ast.nodes.types import Double from pygerber.vm.types.vector import Vector if TYPE_CHECKING: diff --git a/test/assets/generated/macro.py b/test/assets/generated/macro.py index 4d0b4eaa..6652a5c8 100644 --- a/test/assets/generated/macro.py +++ b/test/assets/generated/macro.py @@ -1,7 +1,7 @@ from __future__ import annotations -from pygerber.gerberx3.ast.builder import GerberX3Builder -from pygerber.gerberx3.ast.nodes import File +from pygerber.gerber.ast.builder import GerberX3Builder +from pygerber.gerber.ast.nodes import File def get_custom_circle_local_2_0() -> File: diff --git a/test/benchmark/a64_olinuxino_rev_g_bottom_copper.py b/test/benchmark/a64_olinuxino_rev_g_bottom_copper.py index 89882b36..cf055b4e 100644 --- a/test/benchmark/a64_olinuxino_rev_g_bottom_copper.py +++ b/test/benchmark/a64_olinuxino_rev_g_bottom_copper.py @@ -4,8 +4,8 @@ from pathlib import Path from typing import cast -from pygerber.gerberx3.compiler import compile -from pygerber.gerberx3.parser import parse +from pygerber.gerber.compiler import compile +from pygerber.gerber.parser import parse from pygerber.vm import render from pygerber.vm.pillow.vm import PillowResult from test.assets.gerberx3.A64_OLinuXino_rev_G import A64_OlinuXino_Rev_G diff --git a/test/e2e/gerberx3/test_pillow.py b/test/e2e/gerberx3/test_pillow.py index db444156..7f2091a8 100644 --- a/test/e2e/gerberx3/test_pillow.py +++ b/test/e2e/gerberx3/test_pillow.py @@ -4,9 +4,9 @@ from pathlib import Path from typing import ClassVar, Type -from pygerber.gerberx3.ast.nodes import File -from pygerber.gerberx3.compiler import Compiler -from pygerber.gerberx3.parser.pyparsing.parser import Parser +from pygerber.gerber.ast.nodes import File +from pygerber.gerber.compiler import Compiler +from pygerber.gerber.parser.pyparsing.parser import Parser from pygerber.vm.pillow.vm import PillowResult, PillowVirtualMachine from test.assets.asset import GerberX3Asset from test.assets.generated.macro import ( @@ -54,7 +54,7 @@ def _save(self, result: PillowResult) -> None: else: dump_directory = OUTPUT_DUMP_DIRECTORY - result.get_image().save(dump_directory / f"{caller_function_name}.png") + result.get_image_no_style().save(dump_directory / f"{caller_function_name}.png") class TestOLinuXinoRevG(PillowRenderE2E): diff --git a/test/examples/gerberx3/api/_00_single_file_render_with_pillow_defaults_str.example.py b/test/examples/gerberx3/api/_00_single_file_render_with_pillow_defaults_str.example.py index 47eb9f98..66e4de91 100644 --- a/test/examples/gerberx3/api/_00_single_file_render_with_pillow_defaults_str.example.py +++ b/test/examples/gerberx3/api/_00_single_file_render_with_pillow_defaults_str.example.py @@ -1,4 +1,4 @@ -from pygerber.gerberx3.api import GerberFile +from pygerber.gerber.api import GerberFile from pygerber.examples import ExamplesEnum, load_example diff --git a/test/examples/gerberx3/api/_01_single_file_render_with_pillow_defaults_file.example.py b/test/examples/gerberx3/api/_01_single_file_render_with_pillow_defaults_file.example.py index a4e151a2..e9cce7b5 100644 --- a/test/examples/gerberx3/api/_01_single_file_render_with_pillow_defaults_file.example.py +++ b/test/examples/gerberx3/api/_01_single_file_render_with_pillow_defaults_file.example.py @@ -1,4 +1,4 @@ -from pygerber.gerberx3.api import GerberFile +from pygerber.gerber.api import GerberFile from pygerber.examples import ExamplesEnum, get_example_path diff --git a/test/examples/gerberx3/api/_02_single_file_render_with_pillow_options_str.example.py b/test/examples/gerberx3/api/_02_single_file_render_with_pillow_options_str.example.py index 65d5040c..4cbb0eb2 100644 --- a/test/examples/gerberx3/api/_02_single_file_render_with_pillow_options_str.example.py +++ b/test/examples/gerberx3/api/_02_single_file_render_with_pillow_options_str.example.py @@ -1,4 +1,4 @@ -from pygerber.gerberx3.api import GerberFile +from pygerber.gerber.api import GerberFile from pygerber.examples import ExamplesEnum, load_example diff --git a/test/examples/gerberx3/api/_10_single_file_image_space_info.example.py b/test/examples/gerberx3/api/_10_single_file_image_space_info.example.py index e2fc77eb..1b6de176 100644 --- a/test/examples/gerberx3/api/_10_single_file_image_space_info.example.py +++ b/test/examples/gerberx3/api/_10_single_file_image_space_info.example.py @@ -1,4 +1,4 @@ -from pygerber.gerberx3.api import GerberFile +from pygerber.gerber.api import GerberFile from pygerber.examples import ExamplesEnum, load_example diff --git a/test/examples/gerberx3/api/_30_project_render_with_pillow_defaults_str.example.py b/test/examples/gerberx3/api/_30_project_render_with_pillow_defaults_str.example.py index 5abda6e4..3f8487e2 100644 --- a/test/examples/gerberx3/api/_30_project_render_with_pillow_defaults_str.example.py +++ b/test/examples/gerberx3/api/_30_project_render_with_pillow_defaults_str.example.py @@ -1,4 +1,4 @@ -from pygerber.gerberx3.api import GerberFile, Project +from pygerber.gerber.api import GerberFile, Project from pygerber.examples import ExamplesEnum, load_example diff --git a/test/examples/gerberx3/api/_31_project_render_with_pillow_manual_file_type.example.py b/test/examples/gerberx3/api/_31_project_render_with_pillow_manual_file_type.example.py index d1cdc075..f758d827 100644 --- a/test/examples/gerberx3/api/_31_project_render_with_pillow_manual_file_type.example.py +++ b/test/examples/gerberx3/api/_31_project_render_with_pillow_manual_file_type.example.py @@ -1,4 +1,4 @@ -from pygerber.gerberx3.api import GerberFile, Project, FileTypeEnum +from pygerber.gerber.api import GerberFile, Project, FileTypeEnum from pygerber.examples import ExamplesEnum, load_example diff --git a/test/examples/introspect_handle_no_unit.py b/test/examples/introspect_handle_no_unit.py deleted file mode 100644 index e5d984ed..00000000 --- a/test/examples/introspect_handle_no_unit.py +++ /dev/null @@ -1,58 +0,0 @@ -"""Example for introspection with selective inheritance from Parser2HooksBase and Parser2Hooks.""" - -from __future__ import annotations - -from pygerber.gerberx3.parser2.context2 import Parser2Context, Parser2ContextOptions -from pygerber.gerberx3.parser2.errors2 import Parser2Error, UnitNotSet2Error -from pygerber.gerberx3.parser2.parser2 import ( - Parser2, - Parser2OnErrorAction, - Parser2Options, -) -from pygerber.gerberx3.parser2.parser2hooks import Parser2Hooks -from pygerber.gerberx3.state_enums import Unit -from pygerber.gerberx3.tokenizer.tokenizer import Tokenizer - - -class CustomHooks(Parser2Hooks): - def on_parser_error(self, context: Parser2Context, error: Parser2Error) -> None: - if isinstance(error, UnitNotSet2Error): - context.set_draw_units(Unit.Inches) - return super().on_parser_error(context, error) - - -GERBER_SOURCE = r""" -%FSLAX46Y46*% -G04 Let's not include MO command. * -%LPD*% -G04 APERTURE LIST* -%TA.AperFunction,EtchedComponent*% -%ADD10C,0.508000*% -%TD*% -%TA.AperFunction,EtchedComponent*% -%ADD11C,0.254000*% -%TD*% -%TA.AperFunction,ComponentPad*% -%ADD12O,2.800000X2.000000*% -%TD*% -%TA.AperFunction,ComponentPad*% -%ADD13C,1.650000*% -M02* -""" - - -def main() -> None: - tokenizer = Tokenizer() - ast = tokenizer.tokenize(GERBER_SOURCE) - hooks = CustomHooks() - parser = Parser2( - Parser2Options( - context_options=Parser2ContextOptions(hooks=hooks), - on_update_drawing_state_error=Parser2OnErrorAction.UseHook, - ), - ) - parser.parse(ast) - - -if __name__ == "__main__": - main() diff --git a/test/examples/introspect_minimal_example.py b/test/examples/introspect_minimal_example.py deleted file mode 100644 index 41ee443b..00000000 --- a/test/examples/introspect_minimal_example.py +++ /dev/null @@ -1,84 +0,0 @@ -"""Example for introspection with selective inheritance from Parser2HooksBase and Parser2Hooks.""" - -from __future__ import annotations - -from pygerber.gerberx3.parser2.context2 import Parser2Context, Parser2ContextOptions -from pygerber.gerberx3.parser2.parser2 import Parser2, Parser2Options -from pygerber.gerberx3.parser2.parser2hooks import Parser2Hooks -from pygerber.gerberx3.tokenizer.tokenizer import Tokenizer -from pygerber.gerberx3.tokenizer.tokens.g04_comment import Comment - - -class CustomHooks(Parser2Hooks): - def __init__(self) -> None: - super().__init__() - self.comments: list[str] = [] - - class CommentTokenHooks(Parser2Hooks.CommentTokenHooks): - hooks: CustomHooks - - def on_parser_visit_token( - self, - token: Comment, - context: Parser2Context, - ) -> None: - self.hooks.comments.append(token.content) - return super().on_parser_visit_token(token, context) - - -GERBER_SOURCE = r""" -G04 Ucamco ex. 2: Shapes* G04 A comment * -G04 Ucamco ex. 2: Shapes* G04 Comment * -%MOMM*% G04 Units are mm * -%FSLAX36Y36*% G04 Format specification: * - G04 Leading zeros omitted * - G04 Absolute coordinates * - G04 Coordinates in 3 integer and 6 fractional digits. * -%TF.FileFunction,Other,Sample*% G04 Attribute: the is not a PCB layer, it is just an * - G04 example * -G04 Define Apertures* G04 Comment * -%AMTHERMAL80* G04 Define the aperture macro 'THERMAL80' * -7,0,0,0.800,0.550,0.125,45*% G04 Use thermal primitive in the macro * -%ADD10C,0.1*% G04 Define aperture 10 as a circle with diameter 0.1 mm * -%ADD11C,0.6*% G04 Define aperture 11 as a circle with diameter 0.6 mm * -%ADD12R,0.6X0.6*% G04 Define aperture 12 as a rectangle with size 0.6 x 0.6 mm * -%ADD13R,0.4X1.00*% G04 Define aperture 13 as a rectangle with size 0.4 x 1 mm * -%ADD14R,1.00X0.4*% G04 Define aperture 14 as a rectangle with size 1 x 0.4 mm * -%ADD15O,0.4X01.00*% G04 Define aperture 15 as an obround with size 0.4 x 1 mm * -%ADD16P,1.00X3*% G04 Define aperture 16 as a polygon with 3 vertices and * - G04 circumscribed circle with diameter 1 mm * -%ADD19THERMAL80*% G04 Define aperture 19 as an instance of macro aperture * - G04 'THERMAL80' defined earlier * -G04 Start image generation* G04 A comment * -D10* G04 Select aperture 10 as current aperture * -X0Y2500000D02* G04 Set the current point to (0, 2.5) mm * -G01* G04 Set linear plot mode * -X0Y0D01* G04 Create draw with the current aperture * -X2500000Y0D01* G04 Create draw with the current aperture * -X10000000Y10000000D02* G04 Set the current point * -X15000000D01* G04 Create draw with the current aperture * -X20000000Y15000000D01* G04 Create draw with the current aperture * -X25000000D02* G04 Set the current point. * -Y10000000D01* G04 Create draw with the current aperture * -D11* G04 Select aperture 11 as current aperture * -X10000000Y10000000D03* G04 Create flash with the current aperture (11) at (10, 10). * -X20000000D03* G04 Create a flash with the current aperture at (20, 10). * -M02* G04 End of file * -""" - - -def main() -> None: - tokenizer = Tokenizer() - ast = tokenizer.tokenize(GERBER_SOURCE) - hooks = CustomHooks() - parser = Parser2( - Parser2Options(context_options=Parser2ContextOptions(hooks=hooks)), - ) - parser.parse(ast) - - for comment in hooks.comments: - print(comment) - - -if __name__ == "__main__": - main() diff --git a/test/examples/introspect_mixed_inheritance.py b/test/examples/introspect_mixed_inheritance.py deleted file mode 100644 index 425057ee..00000000 --- a/test/examples/introspect_mixed_inheritance.py +++ /dev/null @@ -1,95 +0,0 @@ -"""Example for introspection with selective inheritance from Parser2HooksBase and Parser2Hooks.""" - -from __future__ import annotations - -from pygerber.gerberx3.parser2.attributes2 import ApertureAttributes -from pygerber.gerberx3.parser2.context2 import Parser2Context, Parser2ContextOptions -from pygerber.gerberx3.parser2.parser2 import ( - Parser2, - Parser2OnErrorAction, - Parser2Options, -) -from pygerber.gerberx3.parser2.parser2hooks import Parser2Hooks -from pygerber.gerberx3.parser2.parser2hooks_base import DefineAnyT, Parser2HooksBase -from pygerber.gerberx3.tokenizer.aperture_id import ApertureID -from pygerber.gerberx3.tokenizer.tokenizer import Tokenizer - - -class CustomHooks(Parser2HooksBase): - def __init__(self) -> None: - super().__init__() - self.aperture_attributes: dict[ApertureID, ApertureAttributes] = {} - - class ApertureAttributeHooks(Parser2Hooks.ApertureAttributeHooks): - pass - - class FileAttributeHooks(Parser2Hooks.FileAttributeHooks): - pass - - class ObjectAttributeHooks(Parser2Hooks.ObjectAttributeHooks): - pass - - class DeleteAttributeHooks(Parser2Hooks.DeleteAttributeHooks): - pass - - class DefineApertureTokenHooks(Parser2HooksBase.DefineApertureTokenHooks): - hooks: CustomHooks - - def on_parser_visit_token( - self, - token: DefineAnyT, - context: Parser2Context, - ) -> None: - self.hooks.aperture_attributes[token.aperture_id] = ( - context.aperture_attributes - ) - return super().on_parser_visit_token(token, context) - - -GERBER_SOURCE = r""" -%TF.GenerationSoftware,KiCad,Pcbnew,5.1.5-52549c5~84~ubuntu18.04.1*% -%TF.CreationDate,2020-02-11T15:54:30+02:00*% -%TF.ProjectId,A64-OlinuXino_Rev_G,4136342d-4f6c-4696-9e75-58696e6f5f52,G*% -%TF.SameCoordinates,Original*% -%TF.FileFunction,Copper,L6,Bot*% -%TF.FilePolarity,Positive*% -%FSLAX46Y46*% -G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)* -G04 Created by KiCad (PCBNEW 5.1.5-52549c5~84~ubuntu18.04.1) date 2020-02-11 15:54:30* -%MOMM*% -%LPD*% -G04 APERTURE LIST* -%TA.AperFunction,EtchedComponent*% -%ADD10C,0.508000*% -%TD*% -%TA.AperFunction,EtchedComponent*% -%ADD11C,0.254000*% -%TD*% -%TA.AperFunction,ComponentPad*% -%ADD12O,2.800000X2.000000*% -%TD*% -%TA.AperFunction,ComponentPad*% -%ADD13C,1.650000*% -M02* -""" - - -def main() -> None: - tokenizer = Tokenizer() - ast = tokenizer.tokenize(GERBER_SOURCE) - hooks = CustomHooks() - parser = Parser2( - Parser2Options( - context_options=Parser2ContextOptions(hooks=hooks), - on_update_drawing_state_error=Parser2OnErrorAction.UseHook, - ), - ) - parser.parse(ast) - - for aperture, attributes in hooks.aperture_attributes.items(): - print(aperture) - print(attributes) - - -if __name__ == "__main__": - main() diff --git a/test/examples/renderer_2_raster_render.py b/test/examples/renderer_2_raster_render.py deleted file mode 100644 index 149ea84f..00000000 --- a/test/examples/renderer_2_raster_render.py +++ /dev/null @@ -1,40 +0,0 @@ -from __future__ import annotations - -from pygerber.gerberx3.parser2.parser2 import Parser2 -from pygerber.gerberx3.renderer2.raster import ( - PixelFormat, - RasterFormatOptions, - RasterRenderer2, - RasterRenderer2Hooks, -) -from pygerber.gerberx3.tokenizer.tokenizer import Tokenizer - -SOURCE = r""" -%FSLAX26Y26*% -%MOMM*% -%ADD100R,1.5X1.0X0.5*% -%ADD200C,1.5X1.0*% -%ADD300O,1.5X1.0X0.6*% -%ADD400P,1.5X3X5.0*% -D100* -X0Y0D03* -D200* -X0Y2000000D03* -D300* -X2000000Y0D03* -D400* -X2000000Y2000000D03* -M02* - -""" - - -def render() -> None: - stack = Tokenizer().tokenize(SOURCE) - cmd_buf = Parser2().parse(stack) - ref = RasterRenderer2(RasterRenderer2Hooks(dpmm=100)).render(cmd_buf) - ref.save_to("output.jpeg", RasterFormatOptions(pixel_format=PixelFormat.RGB)) - - -if __name__ == "__main__": - render() diff --git a/test/examples/renderer_2_svg_render.py b/test/examples/renderer_2_svg_render.py deleted file mode 100644 index 69ad8e91..00000000 --- a/test/examples/renderer_2_svg_render.py +++ /dev/null @@ -1,37 +0,0 @@ -from __future__ import annotations - -from decimal import Decimal - -from pygerber.gerberx3.parser2.parser2 import Parser2 -from pygerber.gerberx3.renderer2.svg import SvgRenderer2, SvgRenderer2Hooks -from pygerber.gerberx3.tokenizer.tokenizer import Tokenizer - -SOURCE = r""" -%FSLAX26Y26*% -%MOMM*% -%ADD100R,1.5X1.0X0.5*% -%ADD200C,1.5X1.0*% -%ADD300O,1.5X1.0X0.6*% -%ADD400P,1.5X3X5.0*% -D100* -X0Y0D03* -D200* -X0Y2000000D03* -D300* -X2000000Y0D03* -D400* -X2000000Y2000000D03* -M02* - -""" - - -def render() -> None: - stack = Tokenizer().tokenize(SOURCE) - cmd_buf = Parser2().parse(stack) - ref = SvgRenderer2(SvgRenderer2Hooks(scale=Decimal(10))).render(cmd_buf) - ref.save_to("output.svg") - - -if __name__ == "__main__": - render() diff --git a/test/examples/test_examples.py b/test/examples/test_examples.py deleted file mode 100644 index 91c0f4ac..00000000 --- a/test/examples/test_examples.py +++ /dev/null @@ -1,32 +0,0 @@ -from __future__ import annotations - -from pathlib import Path - -import pytest - -from pygerber.gerberx3.renderer2.svg import IS_SVG_BACKEND_AVAILABLE -from test.examples import ( - introspect_minimal_example, - introspect_mixed_inheritance, - renderer_2_raster_render, - renderer_2_svg_render, -) - -DIRECTORY = Path(__file__).parent - - -def test_introspect_minimal_example() -> None: - introspect_minimal_example.main() - - -def test_introspect_mixed_inheritance() -> None: - introspect_mixed_inheritance.main() - - -def test_renderer_2_raster_render() -> None: - renderer_2_raster_render.render() - - -@pytest.mark.skipif(not IS_SVG_BACKEND_AVAILABLE, reason="SVG backend required") -def test_renderer_2_svg_render() -> None: - renderer_2_svg_render.render() diff --git a/test/gerberx3/common.py b/test/gerberx3/common.py index d6768670..7ce8636b 100644 --- a/test/gerberx3/common.py +++ b/test/gerberx3/common.py @@ -6,7 +6,6 @@ from dataclasses import dataclass from pathlib import Path from typing import ( - TYPE_CHECKING, Any, Callable, Generator, @@ -25,13 +24,7 @@ from filelock import FileLock from PIL import Image, ImageDraw -from pygerber.gerberx3.api._enums import GERBER_EXTENSION_TO_FILE_TYPE_MAPPING -from pygerber.gerberx3.tokenizer.tokenizer import Tokenizer -from pygerber.gerberx3.tokenizer.tokens.groups.ast import AST - -if TYPE_CHECKING: - from test.conftest import AssetLoader - +from pygerber.gerber.api._enums import GERBER_EXTENSION_TO_FILE_TYPE_MAPPING ASSET_PATH_BASE = "test/assets/gerberx3" @@ -45,36 +38,6 @@ def find_gerberx3_asset_files(directory: str | Path) -> Iterable[tuple[str, str] yield relative_path.parent.as_posix(), relative_path.name -def tokenize_gerberx3( - asset_loader: AssetLoader, - directory: Path, - file_name: str, - *, - only_expressions: bool = False, -) -> AST: - string = asset_loader.load_asset(f"gerberx3/{directory}/{file_name}").decode( - "utf-8", - ) - if only_expressions: - return Tokenizer().tokenize_expressions(string) - - return Tokenizer().tokenize(string) - - -def save_token_stack( - stack: AST, - test_file_path: str, - directory: Path, - file_name: str, -) -> None: - output_directory = Path(test_file_path).parent / ".output" / directory - output_directory.mkdir(0o777, parents=True, exist_ok=True) - token_file_path = (output_directory / file_name).with_suffix(".txt") - content = stack.get_gerber_code() - token_file_path.touch(0o777, exist_ok=True) - token_file_path.write_text(content) - - ASSETS_DIRECTORY = Path(__file__).parent.parent / "assets" GERBER_ASSETS_DIRECTORY = ASSETS_DIRECTORY / "gerberx3" REFERENCE_ASSETS_HASH = "" diff --git a/test/gerberx3/test_ast/test_ast_visitor.py b/test/gerberx3/test_ast/test_ast_visitor.py index 97bb3c53..21c59cbc 100644 --- a/test/gerberx3/test_ast/test_ast_visitor.py +++ b/test/gerberx3/test_ast/test_ast_visitor.py @@ -7,27 +7,27 @@ import pytest import tzlocal -from pygerber.gerberx3.ast.ast_visitor import AstVisitor -from pygerber.gerberx3.ast.nodes.aperture.AB_close import ABclose -from pygerber.gerberx3.ast.nodes.aperture.AB_open import ABopen -from pygerber.gerberx3.ast.nodes.aperture.ADC import ADC -from pygerber.gerberx3.ast.nodes.aperture.ADmacro import ADmacro -from pygerber.gerberx3.ast.nodes.aperture.ADO import ADO -from pygerber.gerberx3.ast.nodes.aperture.ADP import ADP -from pygerber.gerberx3.ast.nodes.aperture.ADR import ADR -from pygerber.gerberx3.ast.nodes.aperture.AM_close import AMclose -from pygerber.gerberx3.ast.nodes.aperture.AM_open import AMopen -from pygerber.gerberx3.ast.nodes.aperture.SR_close import SRclose -from pygerber.gerberx3.ast.nodes.aperture.SR_open import SRopen -from pygerber.gerberx3.ast.nodes.attribute.TA import ( +from pygerber.gerber.ast.ast_visitor import AstVisitor +from pygerber.gerber.ast.nodes.aperture.AB_close import ABclose +from pygerber.gerber.ast.nodes.aperture.AB_open import ABopen +from pygerber.gerber.ast.nodes.aperture.ADC import ADC +from pygerber.gerber.ast.nodes.aperture.ADmacro import ADmacro +from pygerber.gerber.ast.nodes.aperture.ADO import ADO +from pygerber.gerber.ast.nodes.aperture.ADP import ADP +from pygerber.gerber.ast.nodes.aperture.ADR import ADR +from pygerber.gerber.ast.nodes.aperture.AM_close import AMclose +from pygerber.gerber.ast.nodes.aperture.AM_open import AMopen +from pygerber.gerber.ast.nodes.aperture.SR_close import SRclose +from pygerber.gerber.ast.nodes.aperture.SR_open import SRopen +from pygerber.gerber.ast.nodes.attribute.TA import ( AperFunction, TA_AperFunction, TA_DrillTolerance, TA_FlashText, TA_UserName, ) -from pygerber.gerberx3.ast.nodes.attribute.TD import TD -from pygerber.gerberx3.ast.nodes.attribute.TF import ( +from pygerber.gerber.ast.nodes.attribute.TD import TD +from pygerber.gerber.ast.nodes.attribute.TF import ( TF_MD5, FileFunction, Part, @@ -40,7 +40,7 @@ TF_SameCoordinates, TF_UserName, ) -from pygerber.gerberx3.ast.nodes.attribute.TO import ( +from pygerber.gerber.ast.nodes.attribute.TO import ( TO_C, TO_CMNP, TO_N, @@ -59,71 +59,71 @@ TO_CVal, TO_UserName, ) -from pygerber.gerberx3.ast.nodes.base import Node -from pygerber.gerberx3.ast.nodes.d_codes.D01 import D01 -from pygerber.gerberx3.ast.nodes.d_codes.D02 import D02 -from pygerber.gerberx3.ast.nodes.d_codes.D03 import D03 -from pygerber.gerberx3.ast.nodes.d_codes.Dnn import Dnn -from pygerber.gerberx3.ast.nodes.enums import CoordinateNotation, ImagePolarity, Zeros -from pygerber.gerberx3.ast.nodes.file import File -from pygerber.gerberx3.ast.nodes.g_codes.G01 import G01 -from pygerber.gerberx3.ast.nodes.g_codes.G02 import G02 -from pygerber.gerberx3.ast.nodes.g_codes.G03 import G03 -from pygerber.gerberx3.ast.nodes.g_codes.G04 import G04 -from pygerber.gerberx3.ast.nodes.g_codes.G36 import G36 -from pygerber.gerberx3.ast.nodes.g_codes.G37 import G37 -from pygerber.gerberx3.ast.nodes.g_codes.G54 import G54 -from pygerber.gerberx3.ast.nodes.g_codes.G55 import G55 -from pygerber.gerberx3.ast.nodes.g_codes.G70 import G70 -from pygerber.gerberx3.ast.nodes.g_codes.G71 import G71 -from pygerber.gerberx3.ast.nodes.g_codes.G74 import G74 -from pygerber.gerberx3.ast.nodes.g_codes.G75 import G75 -from pygerber.gerberx3.ast.nodes.g_codes.G90 import G90 -from pygerber.gerberx3.ast.nodes.g_codes.G91 import G91 -from pygerber.gerberx3.ast.nodes.load.LM import LM, Mirroring -from pygerber.gerberx3.ast.nodes.load.LN import LN -from pygerber.gerberx3.ast.nodes.load.LP import LP, Polarity -from pygerber.gerberx3.ast.nodes.load.LR import LR -from pygerber.gerberx3.ast.nodes.load.LS import LS -from pygerber.gerberx3.ast.nodes.m_codes.M00 import M00 -from pygerber.gerberx3.ast.nodes.m_codes.M01 import M01 -from pygerber.gerberx3.ast.nodes.m_codes.M02 import M02 -from pygerber.gerberx3.ast.nodes.math.assignment import Assignment -from pygerber.gerberx3.ast.nodes.math.constant import Constant -from pygerber.gerberx3.ast.nodes.math.operators.binary.add import Add -from pygerber.gerberx3.ast.nodes.math.operators.binary.div import Div -from pygerber.gerberx3.ast.nodes.math.operators.binary.mul import Mul -from pygerber.gerberx3.ast.nodes.math.operators.binary.sub import Sub -from pygerber.gerberx3.ast.nodes.math.operators.unary.neg import Neg -from pygerber.gerberx3.ast.nodes.math.operators.unary.pos import Pos -from pygerber.gerberx3.ast.nodes.math.point import Point -from pygerber.gerberx3.ast.nodes.math.variable import Variable -from pygerber.gerberx3.ast.nodes.other.coordinate import ( +from pygerber.gerber.ast.nodes.base import Node +from pygerber.gerber.ast.nodes.d_codes.D01 import D01 +from pygerber.gerber.ast.nodes.d_codes.D02 import D02 +from pygerber.gerber.ast.nodes.d_codes.D03 import D03 +from pygerber.gerber.ast.nodes.d_codes.Dnn import Dnn +from pygerber.gerber.ast.nodes.enums import CoordinateNotation, ImagePolarity, Zeros +from pygerber.gerber.ast.nodes.file import File +from pygerber.gerber.ast.nodes.g_codes.G01 import G01 +from pygerber.gerber.ast.nodes.g_codes.G02 import G02 +from pygerber.gerber.ast.nodes.g_codes.G03 import G03 +from pygerber.gerber.ast.nodes.g_codes.G04 import G04 +from pygerber.gerber.ast.nodes.g_codes.G36 import G36 +from pygerber.gerber.ast.nodes.g_codes.G37 import G37 +from pygerber.gerber.ast.nodes.g_codes.G54 import G54 +from pygerber.gerber.ast.nodes.g_codes.G55 import G55 +from pygerber.gerber.ast.nodes.g_codes.G70 import G70 +from pygerber.gerber.ast.nodes.g_codes.G71 import G71 +from pygerber.gerber.ast.nodes.g_codes.G74 import G74 +from pygerber.gerber.ast.nodes.g_codes.G75 import G75 +from pygerber.gerber.ast.nodes.g_codes.G90 import G90 +from pygerber.gerber.ast.nodes.g_codes.G91 import G91 +from pygerber.gerber.ast.nodes.load.LM import LM, Mirroring +from pygerber.gerber.ast.nodes.load.LN import LN +from pygerber.gerber.ast.nodes.load.LP import LP, Polarity +from pygerber.gerber.ast.nodes.load.LR import LR +from pygerber.gerber.ast.nodes.load.LS import LS +from pygerber.gerber.ast.nodes.m_codes.M00 import M00 +from pygerber.gerber.ast.nodes.m_codes.M01 import M01 +from pygerber.gerber.ast.nodes.m_codes.M02 import M02 +from pygerber.gerber.ast.nodes.math.assignment import Assignment +from pygerber.gerber.ast.nodes.math.constant import Constant +from pygerber.gerber.ast.nodes.math.operators.binary.add import Add +from pygerber.gerber.ast.nodes.math.operators.binary.div import Div +from pygerber.gerber.ast.nodes.math.operators.binary.mul import Mul +from pygerber.gerber.ast.nodes.math.operators.binary.sub import Sub +from pygerber.gerber.ast.nodes.math.operators.unary.neg import Neg +from pygerber.gerber.ast.nodes.math.operators.unary.pos import Pos +from pygerber.gerber.ast.nodes.math.point import Point +from pygerber.gerber.ast.nodes.math.variable import Variable +from pygerber.gerber.ast.nodes.other.coordinate import ( CoordinateI, CoordinateJ, CoordinateX, CoordinateY, ) -from pygerber.gerberx3.ast.nodes.primitives.code_0 import Code0 -from pygerber.gerberx3.ast.nodes.primitives.code_1 import Code1 -from pygerber.gerberx3.ast.nodes.primitives.code_2 import Code2 -from pygerber.gerberx3.ast.nodes.primitives.code_4 import Code4 -from pygerber.gerberx3.ast.nodes.primitives.code_5 import Code5 -from pygerber.gerberx3.ast.nodes.primitives.code_6 import Code6 -from pygerber.gerberx3.ast.nodes.primitives.code_7 import Code7 -from pygerber.gerberx3.ast.nodes.primitives.code_20 import Code20 -from pygerber.gerberx3.ast.nodes.primitives.code_21 import Code21 -from pygerber.gerberx3.ast.nodes.primitives.code_22 import Code22 -from pygerber.gerberx3.ast.nodes.properties.AS import AS, AxisCorrespondence -from pygerber.gerberx3.ast.nodes.properties.FS import FS -from pygerber.gerberx3.ast.nodes.properties.IN import IN -from pygerber.gerberx3.ast.nodes.properties.IP import IP -from pygerber.gerberx3.ast.nodes.properties.IR import IR -from pygerber.gerberx3.ast.nodes.properties.MI import MI -from pygerber.gerberx3.ast.nodes.properties.MO import MO, UnitMode -from pygerber.gerberx3.ast.nodes.properties.OF import OF -from pygerber.gerberx3.ast.nodes.properties.SF import SF -from pygerber.gerberx3.ast.nodes.types import ApertureIdStr, PackedCoordinateStr +from pygerber.gerber.ast.nodes.primitives.code_0 import Code0 +from pygerber.gerber.ast.nodes.primitives.code_1 import Code1 +from pygerber.gerber.ast.nodes.primitives.code_2 import Code2 +from pygerber.gerber.ast.nodes.primitives.code_4 import Code4 +from pygerber.gerber.ast.nodes.primitives.code_5 import Code5 +from pygerber.gerber.ast.nodes.primitives.code_6 import Code6 +from pygerber.gerber.ast.nodes.primitives.code_7 import Code7 +from pygerber.gerber.ast.nodes.primitives.code_20 import Code20 +from pygerber.gerber.ast.nodes.primitives.code_21 import Code21 +from pygerber.gerber.ast.nodes.primitives.code_22 import Code22 +from pygerber.gerber.ast.nodes.properties.AS import AS, AxisCorrespondence +from pygerber.gerber.ast.nodes.properties.FS import FS +from pygerber.gerber.ast.nodes.properties.IN import IN +from pygerber.gerber.ast.nodes.properties.IP import IP +from pygerber.gerber.ast.nodes.properties.IR import IR +from pygerber.gerber.ast.nodes.properties.MI import MI +from pygerber.gerber.ast.nodes.properties.MO import MO, UnitMode +from pygerber.gerber.ast.nodes.properties.OF import OF +from pygerber.gerber.ast.nodes.properties.SF import SF +from pygerber.gerber.ast.nodes.types import ApertureIdStr, PackedCoordinateStr NODE_SAMPLES: Dict[Type[Node], Node] = { ABclose: ABclose(), diff --git a/test/gerberx3/test_ast/test_builder.py b/test/gerberx3/test_ast/test_builder.py index c62fd49c..621736e7 100644 --- a/test/gerberx3/test_ast/test_builder.py +++ b/test/gerberx3/test_ast/test_builder.py @@ -2,7 +2,7 @@ import pytest -from pygerber.gerberx3.ast.builder import GerberX3Builder +from pygerber.gerber.ast.builder import GerberX3Builder @pytest.fixture diff --git a/test/gerberx3/test_ast/test_expression_eval_visitor.py b/test/gerberx3/test_ast/test_expression_eval_visitor.py index 9db308a7..17f8b6b2 100644 --- a/test/gerberx3/test_ast/test_expression_eval_visitor.py +++ b/test/gerberx3/test_ast/test_expression_eval_visitor.py @@ -2,8 +2,8 @@ import pytest -from pygerber.gerberx3.ast.expression_eval_visitor import ExpressionEvalVisitor -from pygerber.gerberx3.ast.nodes import ( +from pygerber.gerber.ast.expression_eval_visitor import ExpressionEvalVisitor +from pygerber.gerber.ast.nodes import ( Add, Constant, Div, diff --git a/test/gerberx3/test_ast/test_node_finder.py b/test/gerberx3/test_ast/test_node_finder.py index 2280cffb..25337972 100644 --- a/test/gerberx3/test_ast/test_node_finder.py +++ b/test/gerberx3/test_ast/test_node_finder.py @@ -4,8 +4,8 @@ import pytest -from pygerber.gerberx3.ast.node_finder import NodeFinder, OneBasedPosition -from pygerber.gerberx3.ast.nodes import ( +from pygerber.gerber.ast.node_finder import NodeFinder, OneBasedPosition +from pygerber.gerber.ast.nodes import ( AD, D03, FS, @@ -18,8 +18,8 @@ SRclose, SRopen, ) -from pygerber.gerberx3.ast.nodes.math.constant import Constant -from pygerber.gerberx3.parser import parse +from pygerber.gerber.ast.nodes.math.constant import Constant +from pygerber.gerber.parser import parse from test.assets.gerberx3.flashes import Flashes from test.assets.gerberx3.macro.codes import MacroCodeAssets from test.assets.gerberx3.step_and_repeat import StepAndRepeatAssets diff --git a/test/gerberx3/test_ast/test_nodes/test_attribute/test_tf.py b/test/gerberx3/test_ast/test_nodes/test_attribute/test_tf.py index 458da815..83ab2776 100644 --- a/test/gerberx3/test_ast/test_nodes/test_attribute/test_tf.py +++ b/test/gerberx3/test_ast/test_nodes/test_attribute/test_tf.py @@ -2,9 +2,9 @@ from pathlib import Path -from pygerber.gerberx3.ast.ast_visitor import AstVisitor -from pygerber.gerberx3.ast.nodes.attribute.TF import TF_MD5 -from pygerber.gerberx3.parser.pyparsing.parser import Parser +from pygerber.gerber.ast.ast_visitor import AstVisitor +from pygerber.gerber.ast.nodes.attribute.TF import TF_MD5 +from pygerber.gerber.parser.pyparsing.parser import Parser def test_check_source_hash() -> None: diff --git a/test/gerberx3/test_ast/test_state_tracking_visitor.py b/test/gerberx3/test_ast/test_state_tracking_visitor.py index 317d1834..7719a39d 100644 --- a/test/gerberx3/test_ast/test_state_tracking_visitor.py +++ b/test/gerberx3/test_ast/test_state_tracking_visitor.py @@ -5,12 +5,12 @@ import pytest -from pygerber.gerberx3.ast.errors import ( +from pygerber.gerber.ast.errors import ( ApertureNotSelectedError, PackedCoordinateTooLongError, PackedCoordinateTooShortError, ) -from pygerber.gerberx3.ast.nodes import ( +from pygerber.gerber.ast.nodes import ( AB, ADC, ADO, @@ -43,14 +43,14 @@ TA_DrillTolerance, TF_FileFunction, ) -from pygerber.gerberx3.ast.nodes.enums import ( +from pygerber.gerber.ast.nodes.enums import ( AperFunction, CoordinateNotation, FileFunction, Zeros, ) -from pygerber.gerberx3.ast.nodes.types import ApertureIdStr -from pygerber.gerberx3.ast.state_tracking_visitor import ( +from pygerber.gerber.ast.nodes.types import ApertureIdStr +from pygerber.gerber.ast.state_tracking_visitor import ( CoordinateFormat, StateTrackingVisitor, ) diff --git a/test/gerberx3/test_console/test_commands.py b/test/gerberx3/test_console/test_commands.py index 9532cc2e..1abbc9c8 100644 --- a/test/gerberx3/test_console/test_commands.py +++ b/test/gerberx3/test_console/test_commands.py @@ -4,9 +4,9 @@ from click.testing import CliRunner from PIL import Image -from pygerber.backend.rasterized_2d.color_scheme import ColorScheme from pygerber.console.commands import _project, _raster, _vector from pygerber.examples import ExamplesEnum, get_example_path +from pygerber.vm.types.style import Style from test.conftest import cd_to_tempdir @@ -31,7 +31,7 @@ def test_raster_render_all_default() -> None: assert image.getpixel((0, 0)) == (0, 0, 0) assert ( image.getpixel((400, 100)) - == ColorScheme.DEBUG_1_ALPHA.solid_region_color.as_rgb_int() + == Style.presets.DEBUG_1_ALPHA.foreground.as_rgb_int() ) @@ -79,7 +79,7 @@ def test_raster_render_pixel_format_rgba_png() -> None: assert image.getpixel((0, 0)) == (0, 0, 0, 0) assert ( image.getpixel((400, 100)) - == ColorScheme.DEBUG_1_ALPHA.solid_region_color.as_rgba_int() + == Style.presets.DEBUG_1_ALPHA.foreground.as_rgba_int() ) @@ -107,8 +107,7 @@ def test_raster_render_file_type_copper_rgb_png() -> None: assert image.getpixel((0, 0)) == (0, 0, 0) assert ( - image.getpixel((400, 100)) - == ColorScheme.COPPER.solid_region_color.as_rgb_int() + image.getpixel((400, 100)) == Style.presets.COPPER.foreground.as_rgb_int() ) @@ -137,7 +136,7 @@ def test_raster_render_file_type_copper_rgba_png() -> None: assert image.getpixel((0, 0)) == (0, 0, 0, 0) assert ( image.getpixel((400, 100)) - == ColorScheme.COPPER_ALPHA.solid_region_color.as_rgba_int() + == Style.presets.COPPER_ALPHA.foreground.as_rgba_int() ) @@ -212,7 +211,7 @@ def test_vector_render_all_default() -> None: assert len(image) > 0 assert image.startswith(b"""""") - color_hex = ColorScheme.DEBUG_1_ALPHA.solid_region_color.to_hex() + color_hex = Style.presets.DEBUG_1_ALPHA.foreground.to_hex() assert f"""fill="{color_hex}" """.encode() in image @@ -238,7 +237,7 @@ def test_vector_render_file_type_copper() -> None: assert len(image) > 0 assert image.startswith(b"""""") - color_hex = ColorScheme.COPPER.solid_region_color.to_hex() + color_hex = Style.presets.COPPER.foreground.to_hex() assert f"""fill="{color_hex}" """.encode() in image @@ -266,17 +265,13 @@ def test_project_render_default() -> None: assert image.getpixel((0, 0)) == (0, 0, 0) assert ( - image.getpixel((400, 100)) - == ColorScheme.COPPER.solid_region_color.as_rgb_int() + image.getpixel((400, 100)) == Style.presets.COPPER.foreground.as_rgb_int() ) assert ( image.getpixel((80, 100)) - == ColorScheme.PASTE_MASK.solid_region_color.as_rgb_int() - ) - assert ( - image.getpixel((190, 392)) - == ColorScheme.SILK.solid_region_color.as_rgb_int() + == Style.presets.PASTE_MASK.foreground.as_rgb_int() ) + assert image.getpixel((190, 392)) == Style.presets.SILK.foreground.as_rgb_int() @pytest.mark.xfail(reason="Not implemented") @@ -302,14 +297,10 @@ def test_project_render_with_file_type_tags() -> None: assert image.getpixel((0, 0)) == (0, 0, 0) assert ( - image.getpixel((400, 100)) - == ColorScheme.COPPER.solid_region_color.as_rgb_int() + image.getpixel((400, 100)) == Style.presets.COPPER.foreground.as_rgb_int() ) assert ( image.getpixel((80, 100)) - == ColorScheme.PASTE_MASK.solid_region_color.as_rgb_int() - ) - assert ( - image.getpixel((190, 392)) - == ColorScheme.SILK.solid_region_color.as_rgb_int() + == Style.presets.PASTE_MASK.foreground.as_rgb_int() ) + assert image.getpixel((190, 392)) == Style.presets.SILK.foreground.as_rgb_int() diff --git a/test/gerberx3/test_formatter.py b/test/gerberx3/test_formatter.py index 8e7db955..b4be0b99 100644 --- a/test/gerberx3/test_formatter.py +++ b/test/gerberx3/test_formatter.py @@ -6,8 +6,8 @@ import pytest -from pygerber.gerberx3.formatter import Formatter -from pygerber.gerberx3.parser.pyparsing.parser import Parser +from pygerber.gerber.formatter import Formatter +from pygerber.gerber.parser.pyparsing.parser import Parser from test.gerberx3.common import ( GERBER_ASSETS_INDEX, Asset, diff --git a/test/gerberx3/test_language_server/tests.py b/test/gerberx3/test_language_server/tests.py index 8df14fc0..87519d12 100644 --- a/test/gerberx3/test_language_server/tests.py +++ b/test/gerberx3/test_language_server/tests.py @@ -30,7 +30,7 @@ server_command=[ sys.executable, "-m", - "pygerber.gerberx3.language_server", + "pygerber.gerber.language_server", "--quiet", ], ), diff --git a/test/gerberx3/test_parser/test_pyparsing/test_parser.py b/test/gerberx3/test_parser/test_pyparsing/test_parser.py index 892f93bc..cab3724a 100644 --- a/test/gerberx3/test_parser/test_pyparsing/test_parser.py +++ b/test/gerberx3/test_parser/test_pyparsing/test_parser.py @@ -4,7 +4,7 @@ import pytest -from pygerber.gerberx3.parser.pyparsing.parser import Parser +from pygerber.gerber.parser.pyparsing.parser import Parser from test.gerberx3.common import ( GERBER_ASSETS_INDEX, Asset,