Source code for pygerber.renderer

# -*- coding: utf-8 -*-
from __future__ import annotations

import os
from pathlib import Path
from typing import Deque, List, Tuple

from pygerber.constants import Interpolation
from pygerber.drawing_state import DrawingState
from pygerber.exceptions import EndOfStream
from pygerber.mathclasses import BoundingBox, Vector2D
from pygerber.tokens.token import Token

from .aperture import RegionApertureManager
from .aperture_manager import ApertureManager
from .apertureset import ApertureSet
from .spec import ArcSpec, FlashSpec, LineSpec, Spec

DEBUG = False
if DEBUG:
    TEMP_PATH = Path(os.getcwd()) / ".temp"
    TEMP_PATH.mkdir(parents=True, exist_ok=True)


[docs]class Renderer: current_point: Vector2D region_bounds: List[Spec] apertures: ApertureManager state: DrawingState def __init__(self, apertureSet: ApertureSet) -> None: self.apertures = ApertureManager(apertureSet, self) self.state = DrawingState() self.set_defaults()
[docs] def set_defaults(self): self.apertures.set_defaults() self.state.set_defaults() self.current_point = Vector2D(0, 0) self.region_bounds = []
if DEBUG: def render(self, token_stack: Deque[Token]) -> None: from PyR3.shortcut.io import export_to total = len(token_stack) current = 0 try: for token in token_stack: token.alter_state(self.state) token.pre_render(self) token.render(self) token.post_render(self) current += 1 if current % 10 == 0: print(f"Rendered {current} / {total}") if (current > 500 and current % 20 == 0) or current % 100 == 0: export_to(TEMP_PATH / f"render_{current}_{total}.blend") except EndOfStream: return else:
[docs] def render(self, token_stack: Deque[Token]) -> None: try: for token in token_stack: token.alter_state(self.state) token.pre_render(self) token.render(self) token.post_render(self) except EndOfStream: return
[docs] def define_aperture(self, *args, **kwargs): self.apertures.define_aperture(*args, **kwargs)
[docs] def select_aperture(self, id: int): self.apertures.select_aperture(id)
[docs] def total_bounding_box(self, token_stack: Deque[Token]): total_bbox: BoundingBox = None try: for token in token_stack: total_bbox = self.__update_bounding_box(token, total_bbox) except EndOfStream: pass self.set_defaults() if total_bbox is None: return BoundingBox(0, 0, 0, 0) else: return total_bbox
def __update_bounding_box(self, token, total_bbox): token.alter_state(self.state) token.pre_render(self) bbox = token.bbox(self) if bbox is not None: if total_bbox is None: total_bbox = bbox else: total_bbox = total_bbox + bbox token.post_render(self) return total_bbox
[docs] def draw_interpolated(self, end: Vector2D, offset: Vector2D) -> None: if self.state.interpolation == Interpolation.Linear: self.draw_line(end) else: self.draw_arc(end, offset)
[docs] def bbox_interpolated(self, end: Vector2D, offset: Vector2D) -> BoundingBox: if self.state.interpolation == Interpolation.Linear: return self.bbox_line(end) else: return self.bbox_arc(end, offset)
[docs] def draw_line(self, end: Vector2D) -> None: spec = self.__get_line_spec(end) if self.state.is_regionmode: self.__push_region_step(spec) else: self.apertures.get_current_aperture().line(spec)
[docs] def bbox_line(self, end: Vector2D) -> None: spec = self.__get_line_spec(end) if self.state.is_regionmode: self.__push_region_step(spec) else: return self.apertures.get_current_aperture().line_bbox(spec)
def __get_line_spec(self, end: Vector2D) -> LineSpec: return LineSpec( self.current_point, self.replace_none_with_current(end), self.state.is_regionmode, )
[docs] def draw_arc(self, end: Vector2D, offset: Vector2D) -> None: spec = self.__get_arc_spec(end, offset) if self.state.is_regionmode: self.__push_region_step(spec) else: self.apertures.get_current_aperture().arc(spec)
[docs] def bbox_arc(self, end: Vector2D, offset: Vector2D) -> None: spec = self.__get_arc_spec(end, offset) if self.state.is_regionmode: self.__push_region_step(spec) else: return self.apertures.get_current_aperture().arc_bbox(spec)
def __get_arc_spec(self, end: Vector2D, offset: Vector2D) -> ArcSpec: return ArcSpec( self.current_point, self.replace_none_with_current(end), self.current_point + self.replace_none_with_0(offset), self.state.is_regionmode, )
[docs] def draw_flash(self, point: Vector2D) -> None: if self.state.is_regionmode: raise RuntimeError("Flashes can't be used in region mode.") else: aperture = self.apertures.get_current_aperture() spec = self.__get_flash_spec(point) aperture.flash(spec)
[docs] def bbox_flash(self, point: Vector2D) -> BoundingBox: self.move_pointer(point) aperture = self.apertures.get_current_aperture() spec = self.__get_flash_spec(point) return aperture.flash_bbox(spec)
def __get_flash_spec(self, point: Vector2D) -> FlashSpec: spec = FlashSpec( self.replace_none_with_current(point), self.state.is_regionmode, ) return spec def __push_region_step(self, spec: Spec): self.region_bounds.append(spec) self.move_pointer(spec.end)
[docs] def end_region(self): self.state.end_region()
[docs] def finish_drawing_region(self) -> Tuple[RegionApertureManager, List[Spec]]: bounds = self.__get_and_clean_region_bounds() apertureClass = self.apertures.getApertureClass(None, True) return apertureClass(self), bounds
def __get_and_clean_region_bounds(self): bounds = self.region_bounds self.region_bounds = [] return bounds
[docs] def move_pointer(self, location: Vector2D) -> None: self.current_point = Vector2D( location.x if location.x is not None else self.current_point.x, location.y if location.y is not None else self.current_point.y, )
[docs] def isCCW(self): return self.state.interpolation == Interpolation.CounterclockwiseCircular
[docs] def replace_none_with_current(self, vector: Vector2D): if vector.x is None: vector.x = self.current_point.x if vector.y is None: vector.y = self.current_point.y return vector
[docs] def replace_none_with_0(self, vector: Vector2D): if vector.x is None: vector.x = 0.0 if vector.y is None: vector.y = 0.0 return vector